vispy-0.14.1/0000755000175000017510000000000014507104251012275 5ustar nileshnileshvispy-0.14.1/vispy.egg-info/0000755000175000017510000000000014507104251015141 5ustar nileshnileshvispy-0.14.1/vispy.egg-info/top_level.txt0000644000175000017510000000000614507104250017666 0ustar nileshnileshvispy vispy-0.14.1/vispy.egg-info/requires.txt0000644000175000017510000000054014507104250017537 0ustar nileshnileshnumpy freetype-py hsluv kiwisolver packaging [doc] pydata-sphinx-theme numpydoc sphinxcontrib-apidoc sphinx-gallery myst-parser pillow pytest pyopengl [io] meshio Pillow [ipython-static] ipython [pyglet] pyglet>=1.2 [pyqt5] pyqt5 [pyqt6] pyqt6 [pyside] PySide [pyside2] PySide2 [pyside6] PySide6 [sdl2] PySDL2 [tk] pyopengltk [wx] wxPython vispy-0.14.1/vispy.egg-info/not-zip-safe0000644000175000017510000000000114507104247017374 0ustar nileshnilesh vispy-0.14.1/vispy.egg-info/dependency_links.txt0000644000175000017510000000000114507104250021206 0ustar nileshnilesh vispy-0.14.1/vispy.egg-info/SOURCES.txt0000644000175000017510000006374514507104250017043 0ustar nileshnilesh.coveragerc .git_archival.txt .gitattributes .github_changelog_generator .gitignore CHANGELOG.md CITATION.rst CODE_OF_CONDUCT.md CONTRIBUTING.md LICENSE.txt MANIFEST.in README.rst pyproject.toml pytest.ini setup.cfg setup.py .github/dependabot.yml .github/workflows/main.yml .github/workflows/wheels.yml ci/build_website.sh ci/requirements/linux_full_deps_apt.txt ci/requirements/linux_full_deps_conda.txt ci/requirements/linux_full_deps_pip.txt ci/requirements/linux_full_newqtdeps_apt.txt ci/requirements/linux_full_newqtdeps_conda.txt ci/requirements/linux_full_newqtdeps_pip.txt ci/requirements/linux_min_deps_conda.txt ci/requirements/linux_osmesa_deps_conda.txt ci/requirements/linux_website_deps_pip.txt ci/requirements/py310.yml ci/requirements/py38.yml doc/.gitignore doc/.nojekyll doc/CNAME doc/Makefile doc/README.rst doc/community.rst doc/conf.py doc/faq.rst doc/index.rst doc/installation.rst doc/news.rst doc/overview.rst doc/raspberry.rst doc/resources.rst doc/roadmap.rst doc/thirdparty.rst doc/_static/ViewFrustum.png doc/_static/ViewFrustum.svg doc/_static/favicon.ico doc/_static/frustum-matrix.png doc/_static/gl-history.png doc/_static/gl-pipeline.png doc/_static/gl-pipeline.svg doc/_static/ortho-matrix.png doc/_static/perspective-matrix.png doc/_static/style.css doc/_static/vispy-logo-V.png doc/_static/vispy-logo-V.svg doc/_static/vispy-teaser-short.png doc/_static/vispy-teaser-short.svg doc/_static/vispy-teaser.png doc/_static/vispy-teaser.svg doc/_static/vispy-text-white.png doc/_static/vispy-text-white.svg doc/_static/carousel/galaxy.png doc/_static/carousel/high-frequency.png doc/_static/carousel/mandelbrot.png doc/_static/carousel/signals.png doc/api/.gitkeep doc/dev_guide/contributor_guide.rst doc/dev_guide/index.rst doc/dev_guide/writing_examples.rst doc/gallery/index.rst doc/getting_started/_canvas_app.rst doc/getting_started/gloo.rst doc/getting_started/index.rst doc/getting_started/modern-gl.rst doc/getting_started/plot.rst doc/getting_started/scene.rst doc/getting_started/visuals.rst doc/governance/GOVERNANCE.md doc/governance/MAINTAINERS.md doc/org/ANTITRUST.md doc/org/CHARTER.md doc/org/STEERING-COMMITTEE.md doc/org/TRADEMARKS.md examples/README.rst examples/basics/scene/shared_context.py examples/basics/scene/stereo.py examples/basics/scene/modular_shaders/editor.py examples/basics/scene/modular_shaders/sandbox.py examples/basics/visuals/arcball.py examples/basics/visuals/arrows.py examples/basics/visuals/arrows_quiver.py examples/basics/visuals/axially_symmetric_surfaces.py examples/basics/visuals/bezier.py examples/basics/visuals/box.py examples/basics/visuals/colorbar_visual.py examples/basics/visuals/colorbar_visual_types.py examples/basics/visuals/custom_visual.py examples/basics/visuals/dynamic_polygon.py examples/basics/visuals/graph.py examples/basics/visuals/grid_mesh.py examples/basics/visuals/image_transforms.py examples/basics/visuals/image_visual.py examples/basics/visuals/line.py examples/basics/visuals/line_draw.py examples/basics/visuals/line_plot.py examples/basics/visuals/line_plot3d.py examples/basics/visuals/line_plot_axes.py examples/basics/visuals/line_prototype.py examples/basics/visuals/line_transform.py examples/basics/visuals/markers.py examples/basics/visuals/mesh.py examples/basics/visuals/networkx_layout.py examples/basics/visuals/plane.py examples/basics/visuals/polygon_visual.py examples/basics/visuals/rescalingmarkers.py examples/basics/visuals/rotating_box.py examples/basics/visuals/text_scatter.py examples/basics/visuals/text_visual.py examples/basics/visuals/tube.py examples/basics/visuals/visual_filters.py examples/basics/visuals/windbarb_quiver.py examples/benchmark/scene_test_1.py examples/benchmark/scene_test_2.py examples/benchmark/simple_glut.py examples/benchmark/simple_vispy.py examples/collections/chloropleth.py examples/collections/path_collection.py examples/collections/point_collection.py examples/collections/polygon_collection.py examples/collections/segment_collection.py examples/collections/tiger.py examples/collections/triangle_collection.py examples/demo/gloo/atom.py examples/demo/gloo/boids.py examples/demo/gloo/brain.py examples/demo/gloo/camera.py examples/demo/gloo/cloud.py examples/demo/gloo/donut.py examples/demo/gloo/fireworks.py examples/demo/gloo/galaxy.py examples/demo/gloo/game_of_life.py examples/demo/gloo/glsl_sandbox_cube.py examples/demo/gloo/graph.py examples/demo/gloo/grayscott.py examples/demo/gloo/high_frequency.py examples/demo/gloo/imshow.py examples/demo/gloo/imshow_cuts.py examples/demo/gloo/mandelbrot.py examples/demo/gloo/mandelbrot_double.py examples/demo/gloo/molecular_viewer.py examples/demo/gloo/ndscatter.py examples/demo/gloo/offscreen.py examples/demo/gloo/primitive_mesh_viewer_qt.py examples/demo/gloo/quiver.py examples/demo/gloo/rain.py examples/demo/gloo/raytracing.py examples/demo/gloo/realtime_signals.py examples/demo/gloo/signals.py examples/demo/gloo/skybox.py examples/demo/gloo/spacy.py examples/demo/gloo/terrain.py examples/demo/gloo/two_qt_widgets.py examples/demo/gloo/unstructured_2d.py examples/demo/gloo/voronoi.py examples/demo/gloo/galaxy/galaxy.py examples/demo/gloo/galaxy/galaxy_simulation.py examples/demo/gloo/galaxy/galaxy_specrend.py examples/demo/gloo/jfa/fragment_display.glsl examples/demo/gloo/jfa/fragment_flood.glsl examples/demo/gloo/jfa/fragment_seed.glsl examples/demo/gloo/jfa/jfa_translation.py examples/demo/gloo/jfa/jfa_vispy.py examples/demo/gloo/jfa/vertex.glsl examples/demo/gloo/jfa/vertex_vispy.glsl examples/demo/plot/plot.py examples/demo/scene/flow_lines.py examples/demo/scene/force_directed_graph.py examples/demo/scene/klein.py examples/demo/scene/oscilloscope.py examples/demo/scene/picking.py examples/demo/scene/scrolling_plots.py examples/demo/visuals/wiggly_bar.py examples/gloo/README.rst examples/gloo/animate_images.py examples/gloo/animate_images_slice.py examples/gloo/animate_shape.py examples/gloo/colored_cube.py examples/gloo/colored_cube_instanced.py examples/gloo/colored_quad.py examples/gloo/display_lines.py examples/gloo/display_points.py examples/gloo/display_shape.py examples/gloo/geometry_shader.py examples/gloo/gpuimage.py examples/gloo/hello_fbo.py examples/gloo/lighted_cube.py examples/gloo/multi_texture.py examples/gloo/outlined_cube.py examples/gloo/post_processing.py examples/gloo/rotate_cube.py examples/gloo/rotating_quad.py examples/gloo/spatial_filters.py examples/gloo/start.py examples/gloo/start_shaders.py examples/gloo/texture_precision.py examples/gloo/textured_cube.py examples/gloo/textured_quad.py examples/gloo/gl/README.rst examples/gloo/gl/cube.py examples/gloo/gl/fireworks.py examples/gloo/gl/quad.py examples/gloo/gl/quad_instanced.py examples/jupyter/Rotating Cube.ipynb examples/jupyter/colormaps.ipynb examples/jupyter/gloo_display_lines.ipynb examples/jupyter/gloo_molecular_viewer.ipynb examples/offscreen/simple_egl.py examples/offscreen/simple_osmesa.py examples/plotting/README.rst examples/plotting/colorbar.py examples/plotting/export.py examples/plotting/ipython_fig_playground.py examples/plotting/plot.py examples/plotting/plot_colorbars.py examples/plotting/scatter_histogram.py examples/plotting/spectrogram.py examples/plotting/volume_plot.py examples/scene/README.rst examples/scene/axes_plot.py examples/scene/background_borders.py examples/scene/clipping_planes.py examples/scene/colorbar_widget.py examples/scene/colored_line.py examples/scene/complex_image.py examples/scene/console.py examples/scene/contour.py examples/scene/example..gif examples/scene/example.png examples/scene/face_picking.py examples/scene/flipped_axis.py examples/scene/graph.py examples/scene/image.py examples/scene/image_custom_kernel.py examples/scene/infinite_line.py examples/scene/instanced_mesh.py examples/scene/instanced_mesh_visual.py examples/scene/instanced_quad_visual.py examples/scene/isocurve.py examples/scene/isocurve_for_trisurface.py examples/scene/isocurve_for_trisurface_qt.py examples/scene/isocurve_updates.py examples/scene/isosurface.py examples/scene/lasso.py examples/scene/line.py examples/scene/line_update.py examples/scene/linear_region.py examples/scene/magnify.py examples/scene/marker_picking.py examples/scene/marker_spheres.py examples/scene/mesh_normals.py examples/scene/mesh_shading.py examples/scene/mesh_texture.py examples/scene/nested_viewbox.py examples/scene/one_cam_two_scenes.py examples/scene/one_scene_four_cams.py examples/scene/point_cloud.py examples/scene/polygon.py examples/scene/save_animation.py examples/scene/sensitivity.py examples/scene/shape_draw.py examples/scene/sphere.py examples/scene/surface_plot.py examples/scene/text.py examples/scene/turntable_box.py examples/scene/viewbox.py examples/scene/volume.py examples/scene/volume_plane.py examples/scene/grid_layout/README.rst examples/scene/grid_layout/grid.py examples/scene/grid_layout/grid_basic.py examples/scene/grid_layout/grid_holed.py examples/scene/grid_layout/grid_large.py examples/scene/grid_layout/grid_uneven_col.py examples/scene/grid_layout/grid_x_y_viewbox.py examples/scene/realtime_data/README.rst examples/scene/realtime_data/ex01_embedded_vispy.py examples/scene/realtime_data/ex02_control_vispy_from_qt.py examples/scene/realtime_data/ex03a_data_sources_timer.py examples/scene/realtime_data/ex03b_data_sources_threaded_loop.py examples/scene/realtime_data/ex03c_data_sources_threaded_events.py examples/tutorial/app/app_events.py examples/tutorial/app/fps.py examples/tutorial/app/shared_context.py examples/tutorial/app/simple.py examples/tutorial/app/simple_wx.py examples/tutorial/visuals/T01_basic_visual.py examples/tutorial/visuals/T02_measurements.py examples/tutorial/visuals/T03_antialiasing.py examples/tutorial/visuals/T04_fragment_programs.py examples/tutorial/visuals/T05_viewer_location.py vispy/__init__.py vispy/conftest.py vispy/version.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/_detect_eventloop.py vispy/app/application.py vispy/app/base.py vispy/app/canvas.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/_jupyter_rfb.py vispy/app/backends/_offscreen_util.py vispy/app/backends/_osmesa.py vispy/app/backends/_pyglet.py vispy/app/backends/_pyqt4.py vispy/app/backends/_pyqt5.py vispy/app/backends/_pyqt6.py vispy/app/backends/_pyside.py vispy/app/backends/_pyside2.py vispy/app/backends/_pyside6.py vispy/app/backends/_qt.py vispy/app/backends/_sdl2.py vispy/app/backends/_template.py vispy/app/backends/_test.py vispy/app/backends/_tk.py vispy/app/backends/_wx.py vispy/app/backends/tests/__init__.py vispy/app/backends/tests/test_offscreen_util.py vispy/app/backends/tests/test_rfb.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_canvas.py vispy/app/tests/test_context.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/cocoapy.py vispy/ext/cubehelix.py vispy/ext/egl.py vispy/ext/fontconfig.py vispy/ext/gdi32plus.py vispy/ext/osmesa.py vispy/geometry/__init__.py vispy/geometry/_triangulation_debugger.py vispy/geometry/calculations.py vispy/geometry/curves.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/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/arrowheads/angle.glsl vispy/glsl/arrowheads/arrowheads.frag vispy/glsl/arrowheads/arrowheads.glsl vispy/glsl/arrowheads/arrowheads.vert vispy/glsl/arrowheads/curved.glsl vispy/glsl/arrowheads/inhibitor.glsl vispy/glsl/arrowheads/stealth.glsl vispy/glsl/arrowheads/triangle.glsl vispy/glsl/arrowheads/util.glsl 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/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/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/lines/agg.frag vispy/glsl/lines/agg.vert 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/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/regular-grid.frag vispy/glsl/misc/spatial-filters.frag vispy/glsl/misc/viewport-NDC.glsl 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/io/__init__.py vispy/io/datasets.py vispy/io/image.py vispy/io/mesh.py vispy/io/stl.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/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/visuals.py vispy/scene/cameras/__init__.py vispy/scene/cameras/_base.py vispy/scene/cameras/arcball.py vispy/scene/cameras/base_camera.py vispy/scene/cameras/fly.py vispy/scene/cameras/magnify.py vispy/scene/cameras/panzoom.py vispy/scene/cameras/perspective.py vispy/scene/cameras/turntable.py vispy/scene/cameras/tests/__init__.py vispy/scene/cameras/tests/test_cameras.py vispy/scene/cameras/tests/test_link.py vispy/scene/cameras/tests/test_perspective.py vispy/scene/tests/__init__.py vispy/scene/tests/test_canvas.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/axis.py vispy/scene/widgets/colorbar.py vispy/scene/widgets/console.py vispy/scene/widgets/grid.py vispy/scene/widgets/label.py vispy/scene/widgets/viewbox.py vispy/scene/widgets/widget.py vispy/scene/widgets/tests/__init__.py vispy/scene/widgets/tests/test_colorbar.py vispy/testing/__init__.py vispy/testing/_runners.py vispy/testing/_testing.py vispy/testing/image_tester.py vispy/testing/rendered_array_tester.py vispy/testing/tests/__init__.py vispy/testing/tests/test_testing.py vispy/util/__init__.py vispy/util/bunch.py vispy/util/check_environment.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/frozen.py vispy/util/gallery_scraper.py vispy/util/keys.py vispy/util/logs.py vispy/util/osmesa_gl.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/data/OpenSans-Bold.ttf vispy/util/fonts/data/OpenSans-BoldItalic.ttf vispy/util/fonts/data/OpenSans-Italic.ttf vispy/util/fonts/data/OpenSans-Regular.ttf 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_gallery_scraper.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/_scalable_textures.py vispy/visuals/axis.py vispy/visuals/border.py vispy/visuals/box.py vispy/visuals/colorbar.py vispy/visuals/cube.py vispy/visuals/ellipse.py vispy/visuals/gridlines.py vispy/visuals/gridmesh.py vispy/visuals/histogram.py vispy/visuals/image.py vispy/visuals/image_complex.py vispy/visuals/infinite_line.py vispy/visuals/instanced_mesh.py vispy/visuals/isocurve.py vispy/visuals/isoline.py vispy/visuals/isosurface.py vispy/visuals/line_plot.py vispy/visuals/linear_region.py vispy/visuals/markers.py vispy/visuals/mesh.py vispy/visuals/mesh_normals.py vispy/visuals/plane.py vispy/visuals/polygon.py vispy/visuals/rectangle.py vispy/visuals/regular_polygon.py vispy/visuals/scrolling_lines.py vispy/visuals/spectrogram.py vispy/visuals/sphere.py vispy/visuals/surface_plot.py vispy/visuals/tube.py vispy/visuals/visual.py vispy/visuals/volume.py vispy/visuals/windbarb.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/filters/__init__.py vispy/visuals/filters/base_filter.py vispy/visuals/filters/clipper.py vispy/visuals/filters/clipping_planes.py vispy/visuals/filters/color.py vispy/visuals/filters/markers.py vispy/visuals/filters/mesh.py vispy/visuals/filters/picking.py vispy/visuals/filters/tests/__init__.py vispy/visuals/filters/tests/test_primitive_picking_filters.py vispy/visuals/filters/tests/test_wireframe_filter.py vispy/visuals/glsl/__init__.py vispy/visuals/glsl/antialiasing.py vispy/visuals/glsl/color.py vispy/visuals/graphs/__init__.py vispy/visuals/graphs/graph.py vispy/visuals/graphs/util.py vispy/visuals/graphs/layouts/__init__.py vispy/visuals/graphs/layouts/circular.py vispy/visuals/graphs/layouts/force_directed.py vispy/visuals/graphs/layouts/networkx_layout.py vispy/visuals/graphs/layouts/random.py vispy/visuals/graphs/tests/__init__.py vispy/visuals/graphs/tests/test_layouts.py vispy/visuals/graphs/tests/test_networkx_layout.py vispy/visuals/line/__init__.py vispy/visuals/line/arrow.py vispy/visuals/line/dash_atlas.py vispy/visuals/line/line.py vispy/visuals/shaders/__init__.py vispy/visuals/shaders/compiler.py vispy/visuals/shaders/expression.py vispy/visuals/shaders/function.py vispy/visuals/shaders/multiprogram.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_multiprogram.py vispy/visuals/shaders/tests/test_parsing.py vispy/visuals/tests/__init__.py vispy/visuals/tests/test_arrows.py vispy/visuals/tests/test_axis.py vispy/visuals/tests/test_collections.py vispy/visuals/tests/test_colorbar.py vispy/visuals/tests/test_colormap.py vispy/visuals/tests/test_ellipse.py vispy/visuals/tests/test_histogram.py vispy/visuals/tests/test_image.py vispy/visuals/tests/test_image_complex.py vispy/visuals/tests/test_infinite_line.py vispy/visuals/tests/test_instanced_mesh.py vispy/visuals/tests/test_isosurface.py vispy/visuals/tests/test_linear_region.py vispy/visuals/tests/test_markers.py vispy/visuals/tests/test_mesh.py vispy/visuals/tests/test_mesh_normals.py vispy/visuals/tests/test_polygon.py vispy/visuals/tests/test_rectangle.py vispy/visuals/tests/test_regular_polygon.py vispy/visuals/tests/test_scalable_textures.py vispy/visuals/tests/test_sdf.py vispy/visuals/tests/test_spectrogram.py vispy/visuals/tests/test_text.py vispy/visuals/tests/test_volume.py vispy/visuals/tests/test_windbarb.py vispy/visuals/text/__init__.py vispy/visuals/text/_sdf_cpu.pyx vispy/visuals/text/_sdf_gpu.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.py vispy/visuals/transforms/tests/__init__.py vispy/visuals/transforms/tests/test_transforms.pyvispy-0.14.1/vispy.egg-info/PKG-INFO0000644000175000017510000001626114507104250016243 0ustar nileshnileshMetadata-Version: 2.1 Name: vispy Version: 0.14.1 Summary: Interactive visualization in Python Home-page: http://vispy.org Download-URL: https://pypi.python.org/pypi/vispy Author: Vispy contributors Author-email: vispy@googlegroups.com License: BSD-3-Clause Keywords: visualization,OpenGl,ES,medical,imaging,3D,plotting,numpy,bigdata,ipython,jupyter,widgets 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 :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Framework :: IPython Provides: vispy Requires-Python: >=3.8 Description-Content-Type: text/x-rst Provides-Extra: ipython-static Provides-Extra: pyglet Provides-Extra: pyqt5 Provides-Extra: pyqt6 Provides-Extra: pyside Provides-Extra: pyside2 Provides-Extra: pyside6 Provides-Extra: sdl2 Provides-Extra: wx Provides-Extra: tk Provides-Extra: doc Provides-Extra: io License-File: LICENSE.txt VisPy: interactive scientific visualization in Python ----------------------------------------------------- Main website: http://vispy.org |Build Status| |Coverage Status| |Zenodo Link| |Contributor Covenant| ---- 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). Releases -------- See `CHANGELOG.md <./CHANGELOG.md>`_. Announcements ------------- See the `VisPy Website `_. 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 ------------ Please follow the detailed `installation instructions `_ on the VisPy website. 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, jupyter notebook, and others). Relatively stable API. - **gloo**: a Pythonic, object-oriented interface to OpenGL. Relatively stable API. - **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. - **plot**: high-level plotting interfaces. The API of all public interfaces are subject to change in the future, although **app** and **gloo** are *relatively* stable at this point. Code of Conduct --------------- The VisPy community requires its members to abide by the `Code of Conduct <./CODE_OF_CONDUCT.md>`_. In this CoC you will find the expectations of members, the penalties for violating these expectations, and how violations can be reported to the members of the community in charge of enforcing this Code of Conduct. Governance ---------- The VisPy project maintainers make decisions about the project based on a simple consensus model. This is described in more detail on the `governance page `_ of the vispy website as well as the `list of maintainers `_. In addition to decisions about the VisPy project, there is also a steering committee for the overall VisPy organization. More information about this committee can also be found on the `steering committee page `_ of the vispy website, along with the organization's `charter `_ and other related documents (linked in the charter). Genesis ------- VisPy began when four developers with their own visualization libraries decided to team up: `Luke Campagnola `__ with `PyQtGraph `__, `Almar Klein `__ with `Visvis `__, `Cyrille Rossant `__ with `Galry `__, `Nicolas Rougier `__ with `Glumpy `__. Now VisPy looks to build on the expertise of these developers and the broader open-source community to build a high-performance OpenGL library. ---- External links -------------- - `User mailing list `__ - `Dev mailing list `__ - `Chat room `__ - `Developer chat room `__ - `Wiki `__ - `Gallery `__ - `Documentation `__ .. |Build Status| image:: https://github.com/vispy/vispy/workflows/CI/badge.svg :target: https://github.com/vispy/vispy/actions .. |Coverage Status| image:: https://img.shields.io/coveralls/vispy/vispy/main.svg :target: https://coveralls.io/r/vispy/vispy?branch=main .. |Zenodo Link| image:: https://zenodo.org/badge/5822/vispy/vispy.svg :target: http://dx.doi.org/10.5281/zenodo.17869 .. |Contributor Covenant| image:: https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg :target: CODE_OF_CONDUCT.md vispy-0.14.1/vispy/0000755000175000017510000000000014507104251013447 5ustar nileshnileshvispy-0.14.1/vispy/visuals/0000755000175000017510000000000014507104251015135 5ustar nileshnileshvispy-0.14.1/vispy/visuals/xyz_axis.py0000644000175000017510000000167514507104216017377 0ustar nileshnilesh 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): pos = 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]]) connect = 'segments' method = 'gl' kwargs.setdefault('pos', pos) kwargs.setdefault('color', color) kwargs.setdefault('connect', connect) kwargs.setdefault('method', method) LineVisual.__init__(self, **kwargs) vispy-0.14.1/vispy/visuals/windbarb.py0000644000175000017510000002213214507104216017300 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """Windbarb Visual and shader definitions.""" import numpy as np from vispy.color import ColorArray from vispy.gloo import VertexBuffer from vispy.visuals.shaders import Variable from vispy.visuals.visual import Visual _VERTEX_SHADER = """ uniform float u_antialias; uniform float u_px_scale; uniform float u_scale; attribute vec3 a_position; attribute vec2 a_wind; attribute vec4 a_fg_color; attribute vec4 a_bg_color; attribute float a_edgewidth; attribute float a_size; attribute float a_trig; varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_wind; varying float v_trig; varying float v_edgewidth; varying float v_antialias; void main (void) { $v_size = a_size * u_px_scale * u_scale; v_edgewidth = a_edgewidth * float(u_px_scale); v_wind = a_wind.xy; v_trig = a_trig; 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 = ($v_size) + 4.*(edgewidth + 1.5*v_antialias); } """ _FRAGMENT_SHADER = """ #include "math/constants.glsl" #include "math/signed-segment-distance.glsl" #include "antialias/antialias.glsl" varying vec4 v_fg_color; varying vec4 v_bg_color; varying vec2 v_wind; varying float v_trig; varying float v_edgewidth; varying float v_antialias; // SDF-Triangle by @rougier // https://github.com/rougier/python-opengl/blob/master/code/chapter-06/SDF-triangle.py float sdf_triangle(vec2 p, vec2 p0, vec2 p1, vec2 p2) { vec2 e0 = p1 - p0; vec2 e1 = p2 - p1; vec2 e2 = p0 - p2; vec2 v0 = p - p0; vec2 v1 = p - p1; vec2 v2 = p - p2; vec2 pq0 = v0 - e0*clamp( dot(v0,e0)/dot(e0,e0), 0.0, 1.0 ); vec2 pq1 = v1 - e1*clamp( dot(v1,e1)/dot(e1,e1), 0.0, 1.0 ); vec2 pq2 = v2 - e2*clamp( dot(v2,e2)/dot(e2,e2), 0.0, 1.0 ); float s = sign( e0.x*e2.y - e0.y*e2.x ); vec2 d = min( min( vec2( dot( pq0, pq0 ), s*(v0.x*e0.y-v0.y*e0.x) ), vec2( dot( pq1, pq1 ), s*(v1.x*e1.y-v1.y*e1.x) )), vec2( dot( pq2, pq2 ), s*(v2.x*e2.y-v2.y*e2.x) )); return -sqrt(d.x)*sign(d.y); } void main() { // Discard plotting marker body and edge if zero-size if ($v_size <= 0.) discard; float edgewidth = max(v_edgewidth, 1.0); float linewidth = max(v_edgewidth, 1.0); float edgealphafactor = min(v_edgewidth, 1.0); float size = $v_size + 4.*(edgewidth + 1.5*v_antialias); // factor 6 for acute edge angles that need room as for star marker vec2 wind = v_wind; if (v_trig > 0.) { float u = wind.x * cos(radians(wind.y)); float v = wind.x * sin(radians(wind.y)); wind = vec2(u, v); } // knots to m/s wind *= 2.; // normalized distance float dx = 0.5; // normalized center point vec2 O = vec2(dx); // normalized x-component vec2 X = normalize(wind) * dx / M_SQRT2 / 1.1 * vec2(1, -1); // normalized y-component // here the barb can be mirrored for southern earth * (vec2(1., -1.) //vec2 Y = X.yx * vec2(1., -1.); // southern hemisphere vec2 Y = X.yx * vec2(-1., 1.); // northern hemisphere // PointCoordinate vec2 P = gl_PointCoord; // calculate barb items float speed = length(wind); int flag = int(floor(speed / 50.)); speed -= float (50 * flag); int longbarb = int(floor(speed / 10.)); speed -= float (longbarb * 10); int shortbarb = int(floor(speed / 5.)); int calm = shortbarb + longbarb + flag; // starting distance float r; // calm, plot circles if (calm == 0) { r = abs(length(O-P)- dx * 0.2); r = min(r, abs(length(O-P)- dx * 0.1)); } else { // plot shaft r = segment_distance(P, O, O-X); float pos = 1.; // plot flag(s) while(flag >= 1) { r = min(r, sdf_triangle(P, O-X*pos, O-X*pos-X*.4-Y*.4, O-X*pos-X*.4)); flag -= 1; pos -= 0.15; } // plot longbarb(s) while(longbarb >= 1) { r = min(r, segment_distance(P, O-X*pos, O-X*pos-X*.4-Y*.4)); longbarb -= 1; pos -= 0.15; } // plot shortbarb while(shortbarb >= 1) { if (pos == 1.0) pos -= 0.15; r = min(r, segment_distance(P, O-X*pos, O-X*pos-X*.2-Y*.2)); shortbarb -= 1; pos -= 0.15; } } // apply correction for size r *= size; 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; } gl_FragColor = filled(r, edgewidth, v_antialias, edgecolor); } """ class WindbarbVisual(Visual): """Visual displaying windbarbs.""" _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } def __init__(self, **kwargs): self._vbo = VertexBuffer() self._v_size_var = Variable('varying float v_size') self._marker_fun = None self._data = None Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) self.shared_program.vert['v_size'] = self._v_size_var self.shared_program.frag['v_size'] = self._v_size_var self.set_gl_state(depth_test=True, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self._draw_mode = 'points' if len(kwargs) > 0: self.set_data(**kwargs) self.freeze() def set_data(self, pos=None, wind=None, trig=True, size=50., antialias=1., edge_width=1., edge_color='black', face_color='white'): """Set the data used to display this visual. Parameters ---------- pos : array The array of locations to display each windbarb. wind : array The array of wind vector components to display each windbarb. in m/s. For knots divide by two. trig : bool True - wind contains (mag, ang) False - wind contains (u, v) defaults to True size : float or array The windbarb size in px. antialias : float The antialiased area (in pixels). edge_width : float | None The width of the windbarb outline in pixels. edge_color : Color | ColorArray The color used to draw each symbol outline. face_color : Color | ColorArray The color used to draw each symbol interior. """ assert (isinstance(pos, np.ndarray) and pos.ndim == 2 and pos.shape[1] in (2, 3)) assert (isinstance(wind, np.ndarray) and pos.ndim == 2 and pos.shape[1] == 2) if edge_width < 0: raise ValueError('edge_width cannot be negative') # since the windbarb starts in the fragment center, # we need to multiply by 2 for correct length size *= 2 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_wind', np.float32, 2), ('a_trig', np.float32, 0), ('a_fg_color', np.float32, 4), ('a_bg_color', np.float32, 4), ('a_size', np.float32), ('a_edgewidth', np.float32)]) data['a_fg_color'] = edge_color data['a_bg_color'] = face_color data['a_edgewidth'] = edge_width data['a_position'][:, :pos.shape[1]] = pos data['a_wind'][:, :wind.shape[1]] = wind if trig: data['a_trig'] = 1. else: data['a_trig'] = 0. data['a_size'] = size self.shared_program['u_antialias'] = antialias self._data = data self._vbo.set_data(data) self.shared_program.bind(self._vbo) self.update() def _prepare_transforms(self, view): xform = view.transforms.get_transform() view.view_program.vert['transform'] = xform def _prepare_draw(self, view): view.view_program['u_px_scale'] = view.transforms.pixel_scale view.view_program['u_scale'] = 1 def _compute_bounds(self, axis, view): 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.14.1/vispy/visuals/volume.py0000644000175000017510000013775114507104216017035 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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' for most volumes, since with 'nearest' the result can look very ugly; however for volumes with discrete data 'nearest' is sometimes appropriate. 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 __future__ import annotations from typing import Optional from functools import lru_cache import warnings from ._scalable_textures import CPUScaledTexture3D, GPUScaledTextured3D, Texture2D from ..gloo import VertexBuffer, IndexBuffer from . import Visual from .shaders import Function from ..color import get_colormap from ..io import load_spatial_filters 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 = """ attribute vec3 a_position; uniform vec3 u_shape; varying vec3 v_position; varying vec4 v_nearpos; varying vec4 v_farpos; void main() { 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 = """ // uniforms uniform $sampler_type u_volumetex; uniform vec3 u_shape; uniform vec2 clim; uniform float gamma; uniform float u_threshold; uniform float u_attenuation; uniform float u_relative_step_size; uniform float u_mip_cutoff; uniform float u_minip_cutoff; //varyings 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.2, 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; // uniforms for plane definition. Defined in data coordinates. uniform vec3 u_plane_normal; uniform vec3 u_plane_position; uniform float u_plane_thickness; //varying vec3 lightDirs[1]; // global holding view direction in local coordinates vec3 view_ray; float rand(vec2 co) { // Create a pseudo-random number between 0 and 1. // http://stackoverflow.com/questions/4200224 return fract(sin(dot(co.xy ,vec2(12.9898, 78.233))) * 43758.5453); } float colorToVal(vec4 color1) { return color1.r; // todo: why did I have this abstraction in visvis? } vec4 applyColormap(float data) { data = clamp(data, min(clim.x, clim.y), max(clim.x, clim.y)); data = (data - clim.x) / (clim.y - clim.x); vec4 color = $cmap(pow(data, gamma)); return color; } vec4 calculateColor(vec4 betterColor, vec3 loc, vec3 step) { // Calculate color by incorporating lighting vec4 color1; vec4 color2; // View direction vec3 V = normalize(view_ray); // calculate normal vector from gradient vec3 N; // normal color1 = $get_data(loc+vec3(-step[0],0.0,0.0) ); color2 = $get_data(loc+vec3(step[0],0.0,0.0) ); N[0] = colorToVal(color1) - colorToVal(color2); betterColor = max(max(color1, color2),betterColor); color1 = $get_data(loc+vec3(0.0,-step[1],0.0) ); color2 = $get_data(loc+vec3(0.0,step[1],0.0) ); N[1] = colorToVal(color1) - colorToVal(color2); betterColor = max(max(color1, color2),betterColor); color1 = $get_data(loc+vec3(0.0,0.0,-step[2]) ); color2 = $get_data(loc+vec3(0.0,0.0,step[2]) ); N[2] = colorToVal(color1) - colorToVal(color2); betterColor = max(max(color1, color2),betterColor); float gm = length(N); // gradient magnitude N = normalize(N); // Flip normal so it points towards viewer float Nselect = float(dot(N,V) > 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; } vec3 intersectLinePlane(vec3 linePosition, vec3 lineVector, vec3 planePosition, vec3 planeNormal) { // function to find the intersection between a line and a plane // line is defined by position and vector // plane is defined by position and normal vector // https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection // find scale factor for line vector float scaleFactor = dot(planePosition - linePosition, planeNormal) / dot(lineVector, planeNormal); // calculate intersection return linePosition + ( scaleFactor * lineVector ); } // for some reason, this has to be the last function in order for the // filters to be inserted in the correct place... 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); // Variables to keep track of where to set the frag depth. // frag_depth_point is in data coordinates. vec3 frag_depth_point; // Set up the ray casting // This snippet must define three variables: // vec3 start_loc - the starting location of the ray in texture coordinates // vec3 step - the step vector in texture coordinates // int nsteps - the number of steps to make through the texture $raycasting_setup // For testing: show the number of steps. This helps to establish // whether the rays are correctly oriented //gl_FragColor = vec4(0.0, f_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; // keep track if the texture is ever sampled; if not, fragment will be discarded // this allows us to discard fragments that only traverse clipped parts of the texture bool texture_sampled = false; while (iter < nsteps) { for (iter=iter; iter= 0) { // Get sample color vec4 color = $get_data(loc); float val = color.r; texture_sampled = true; $in_loop } // Advance location deeper into the volume loc += step; } } if (!texture_sampled) discard; $after_loop // set frag depth vec4 frag_depth_vector = vec4(frag_depth_point, 1); vec4 iproj = $viewtransformf(frag_depth_vector); iproj.z /= iproj.w; gl_FragDepth = (iproj.z+1.0)/2.0; } """ # noqa _RAYCASTING_SETUP_VOLUME = """ // 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); float f_nsteps = float(nsteps); if( nsteps < 1 ) discard; // Get starting location and step vector in texture coordinates vec3 step = ((v_position - front) / u_shape) / f_nsteps; // 0.5 offset needed to get back to correct texture coordinates (vispy#2239) vec3 start_loc = (front + 0.5) / u_shape; // set frag depth to the cube face; this can be overridden by projection snippets frag_depth_point = front; """ _RAYCASTING_SETUP_PLANE = """ // find intersection of view ray with plane in data coordinates // 0.5 offset needed to get back to correct texture coordinates (vispy#2239) vec3 intersection = intersectLinePlane(v_position.xyz, view_ray, u_plane_position, u_plane_normal); // and texture coordinates vec3 intersection_tex = (intersection + 0.5) / u_shape; // discard if intersection not in texture float out_of_bounds = 0; out_of_bounds += float(intersection_tex.x > 1); out_of_bounds += float(intersection_tex.x < 0); out_of_bounds += float(intersection_tex.y > 1); out_of_bounds += float(intersection_tex.y < 0); out_of_bounds += float(intersection_tex.z > 1); out_of_bounds += float(intersection_tex.z < 0); if (out_of_bounds > 0) discard; // Decide how many steps to take int nsteps = int(u_plane_thickness / u_relative_step_size + 0.5); float f_nsteps = float(nsteps); if( nsteps < 1 ) discard; // Get step vector and starting location in texture coordinates // step vector is along plane normal vec3 N = normalize(u_plane_normal); vec3 step = N / u_shape; vec3 start_loc = intersection_tex - ((step * f_nsteps) / 2); // Ensure that frag depth value will be set to plane intersection frag_depth_point = intersection; """ _MIP_SNIPPETS = dict( before_loop=""" float maxval = u_mip_cutoff; // The maximum encountered value int maxi = -1; // Where the maximum value was encountered """, in_loop=""" if ( val > maxval ) { maxval = val; maxi = iter; if ( maxval >= clim.y ) { // stop if no chance of finding a higher maxval iter = nsteps; } } """, after_loop=""" // Refine search for max value, but only if anything was found if ( maxi > -1 ) { // Calculate starting location of ray for sampling vec3 start_loc_refine = start_loc + step * (float(maxi) - 0.5); loc = start_loc_refine; // Variables to keep track of current value and where max was encountered vec3 max_loc_tex = start_loc_refine; vec3 small_step = step * 0.1; for (int i=0; i<10; i++) { float val = $get_data(loc).r; if ( val > maxval) { maxval = val; max_loc_tex = start_loc_refine + (small_step * i); } loc += small_step; } frag_depth_point = max_loc_tex * u_shape; gl_FragColor = applyColormap(maxval); } else { discard; } """, ) _ATTENUATED_MIP_SNIPPETS = dict( before_loop=""" float maxval = u_mip_cutoff; // The maximum encountered value float sumval = 0.0; // The sum of the encountered values float scale = 0.0; // The cumulative attenuation int maxi = -1; // Where the maximum value was encountered vec3 max_loc_tex = vec3(0.0); // Location where the maximum value was encountered """, in_loop=""" // Scale and clamp accumulation in `sumval` by contrast limits so that: // * attenuation value does not depend on data values // * negative values do not amplify instead of attenuate sumval = sumval + clamp((val - clim.x) / (clim.y - clim.x), 0.0, 1.0); scale = exp(-u_attenuation * (sumval - 1) / u_relative_step_size); if( maxval > scale * clim.y ) { // stop if no chance of finding a higher maxval iter = nsteps; } else if( val * scale > maxval ) { maxval = val * scale; maxi = iter; max_loc_tex = loc; } """, after_loop=""" if ( maxi > -1 ) { frag_depth_point = max_loc_tex * u_shape; gl_FragColor = applyColormap(maxval); } else { discard; } """, ) _MINIP_SNIPPETS = dict( before_loop=""" float minval = u_minip_cutoff; // The minimum encountered value int mini = -1; // Where the minimum value was encountered """, in_loop=""" if ( val < minval ) { minval = val; mini = iter; if ( minval <= clim.x ) { // stop if no chance of finding a lower minval iter = nsteps; } } """, after_loop=""" // Refine search for min value, but only if anything was found if ( mini > -1 ) { // Calculate starting location of ray for sampling vec3 start_loc_refine = start_loc + step * (float(mini) - 0.5); loc = start_loc_refine; // Variables to keep track of current value and where max was encountered vec3 min_loc_tex = start_loc_refine; vec3 small_step = step * 0.1; for (int i=0; i<10; i++) { float val = $get_data(loc).r; if ( val < minval) { minval = val; min_loc_tex = start_loc_refine + (small_step * i); } loc += small_step; } frag_depth_point = min_loc_tex * u_shape; gl_FragColor = applyColormap(minval); } else { discard; } """, ) _TRANSLUCENT_SNIPPETS = dict( before_loop=""" vec4 integrated_color = vec4(0., 0., 0., 0.); """, in_loop=""" color = applyColormap(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; """, ) _ADDITIVE_SNIPPETS = dict( before_loop=""" vec4 integrated_color = vec4(0., 0., 0., 0.); """, in_loop=""" color = applyColormap(val); integrated_color = 1.0 - (1.0 - integrated_color) * (1.0 - color); """, after_loop=""" gl_FragColor = integrated_color; """, ) _ISO_SNIPPETS = dict( before_loop=""" vec4 color3 = vec4(0.0); // final color vec3 dstep = 1.5 / u_shape; // step to sample derivative gl_FragColor = vec4(0.0); bool discard_fragment = true; """, 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++) { color = $get_data(iloc); if (color.r > u_threshold) { color = calculateColor(color, iloc, dstep); gl_FragColor = applyColormap(color.r); // set the variables for the depth buffer frag_depth_point = iloc * u_shape; discard_fragment = false; iter = nsteps; break; } iloc += step * 0.1; } } """, after_loop=""" if (discard_fragment) discard; """, ) _AVG_SNIPPETS = dict( before_loop=""" float n = 0; // Counter for encountered values float meanval = 0.0; // The mean of encountered values float prev_mean = 0.0; // Variable to store the previous incremental mean """, in_loop=""" // Incremental mean value used for numerical stability n += 1; // Increment the counter prev_mean = meanval; // Update the mean for previous iteration meanval = prev_mean + (val - prev_mean) / n; // Calculate the mean """, after_loop=""" // Apply colormap on mean value gl_FragColor = applyColormap(meanval); """, ) _INTERPOLATION_TEMPLATE = """ #include "misc/spatial-filters.frag" vec4 texture_lookup_filtered(vec3 texcoord) { // no need to discard out of bounds, already checked during raycasting return %s($texture, $shape, texcoord); }""" _TEXTURE_LOOKUP = """ vec4 texture_lookup(vec3 texcoord) { // no need to discard out of bounds, already checked during raycasting return texture3D($texture, texcoord); }""" class VolumeVisual(Visual): """Displays a 3D Volume Parameters ---------- vol : ndarray The volume to display. Must be ndim==3. Array is assumed to be stored as ``(z, y, x)``. clim : str | tuple Limits to use for the colormap. I.e. the values that map to black and white in a gray colormap. Can be 'auto' to auto-set bounds to the min and max of the data. If not given or None, 'auto' is used. method : {'mip', 'attenuated_mip', 'minip', 'translucent', 'additive', 'iso', 'average'} The render method to use. See corresponding docs for details. Default 'mip'. threshold : float The threshold to use for the isosurface render method. By default the mean of the given volume is used. attenuation: float The attenuation rate to apply for the attenuated mip render method. Default: 1.0. 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. gamma : float Gamma to use during colormap lookup. Final color will be cmap(val**gamma). by default: 1. interpolation : str Selects method of texture interpolation. Makes use of the two hardware interpolation methods and the available interpolation methods defined in vispy/gloo/glsl/misc/spatial_filters.frag * 'nearest': Default, uses 'nearest' with Texture interpolation. * 'linear': uses 'linear' with Texture interpolation. * 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', 'cubic', 'catrom', 'mitchell', 'spline16', 'spline36', 'gaussian', 'bessel', 'sinc', 'lanczos', 'blackman' texture_format : numpy.dtype | str | None How to store data on the GPU. OpenGL allows for many different storage formats and schemes for the low-level texture data stored in the GPU. Most common is unsigned integers or floating point numbers. Unsigned integers are the most widely supported while other formats may not be supported on older versions of OpenGL or with older GPUs. Default value is ``None`` which means data will be scaled on the CPU and the result stored in the GPU as an unsigned integer. If a numpy dtype object, an internal texture format will be chosen to support that dtype and data will *not* be scaled on the CPU. Not all dtypes are supported. If a string, then it must be one of the OpenGL internalformat strings described in the table on this page: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml The name should have `GL_` removed and be lowercase (ex. `GL_R32F` becomes ``'r32f'``). Lastly, this can also be the string ``'auto'`` which will use the data type of the provided volume data to determine the internalformat of the texture. When this is specified (not ``None``) data is scaled on the GPU which allows for faster color limit changes. Additionally, when 32-bit float data is provided it won't be copied before being transferred to the GPU. Note this visual is limited to "luminance" formatted data (single band). This is equivalent to `GL_RED` format in OpenGL 4.0. raycasting_mode : {'volume', 'plane'} Whether to cast a ray through the whole volume or perpendicular to a plane through the volume defined. plane_position : ArrayLike A (3,) array containing a position on a plane of interest in the volume. The position is defined in data coordinates. Only relevant in raycasting_mode = 'plane'. plane_normal : ArrayLike A (3,) array containing a vector normal to the plane of interest in the volume. The normal vector is defined in data coordinates. Only relevant in raycasting_mode = 'plane'. plane_thickness : float A value defining the total length of the ray perpendicular to the plane interrogated during rendering. Defined in data coordinates. Only relevant in raycasting_mode = 'plane'. .. versionchanged: 0.7 Deprecate 'emulate_texture' keyword argument. """ _rendering_methods = { 'mip': _MIP_SNIPPETS, 'minip': _MINIP_SNIPPETS, 'attenuated_mip': _ATTENUATED_MIP_SNIPPETS, 'iso': _ISO_SNIPPETS, 'translucent': _TRANSLUCENT_SNIPPETS, 'additive': _ADDITIVE_SNIPPETS, 'average': _AVG_SNIPPETS } _raycasting_modes = { 'volume': _RAYCASTING_SETUP_VOLUME, 'plane': _RAYCASTING_SETUP_PLANE } _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } _func_templates = { 'texture_lookup_interpolated': _INTERPOLATION_TEMPLATE, 'texture_lookup': _TEXTURE_LOOKUP, } def __init__(self, vol, clim="auto", method='mip', threshold=None, attenuation=1.0, relative_step_size=0.8, cmap='grays', gamma=1.0, interpolation='linear', texture_format=None, raycasting_mode='volume', plane_position=None, plane_normal=None, plane_thickness=1.0, clipping_planes=None, clipping_planes_coord_system='scene', mip_cutoff=None, minip_cutoff=None): tr = ['visual', 'scene', 'document', 'canvas', 'framebuffer', 'render'] if clipping_planes_coord_system not in tr: raise ValueError(f'Invalid coordinate system {clipping_planes_coord_system}. Must be one of {tr}.') self._clipping_planes_coord_system = clipping_planes_coord_system self._clip_transform = None # Storage of information of volume self._vol_shape = () self._gamma = gamma self._raycasting_mode = raycasting_mode self._need_vertex_update = True # Set the colormap self._cmap = get_colormap(cmap) self._is_zyx = True # Create gloo objects self._vertices = VertexBuffer() kernel, interpolation_methods = load_spatial_filters() self._kerneltex = Texture2D(kernel, interpolation='nearest') interpolation_methods, interpolation_fun = self._init_interpolation( interpolation_methods) self._interpolation_methods = interpolation_methods self._interpolation_fun = interpolation_fun self._interpolation = interpolation if self._interpolation not in self._interpolation_methods: raise ValueError("interpolation must be one of %s" % ', '.join(self._interpolation_methods)) self._data_lookup_fn = None self._need_interpolation_update = True self._texture = self._create_texture(texture_format, vol) # used to store current data for later CPU-side scaling if # texture_format is None self._last_data = None # Create program Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) self.shared_program['u_volumetex'] = self._texture self.shared_program['a_position'] = self._vertices self.shared_program['gamma'] = self._gamma self._draw_mode = 'triangle_strip' self._index_buffer = IndexBuffer() # 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) # Apply clim and set data at the same time self.set_data(vol, clim or "auto") # Set params self.raycasting_mode = raycasting_mode self.mip_cutoff = mip_cutoff self.minip_cutoff = minip_cutoff self.method = method self.relative_step_size = relative_step_size self.threshold = threshold if threshold is not None else vol.mean() self.attenuation = attenuation # Set plane params if plane_position is None: self.plane_position = [x / 2 for x in vol.shape] else: self.plane_position = plane_position if plane_normal is None: self.plane_normal = [1, 0, 0] else: self.plane_normal = plane_normal self.plane_thickness = plane_thickness self.clipping_planes = clipping_planes self.freeze() def _init_interpolation(self, interpolation_methods): # create interpolation shader functions for available # interpolations fun = [Function(self._func_templates['texture_lookup_interpolated'] % (n + '3D')) for n in interpolation_methods] interpolation_methods = [n.lower() for n in interpolation_methods] interpolation_fun = dict(zip(interpolation_methods, fun)) interpolation_methods = tuple(sorted(interpolation_methods)) # overwrite "nearest" and "linear" spatial-filters # with "hardware" interpolation _data_lookup_fn hardware_lookup = Function(self._func_templates['texture_lookup']) interpolation_fun['nearest'] = hardware_lookup interpolation_fun['linear'] = hardware_lookup # alias bicubic to cubic (but deprecate) interpolation_methods = interpolation_methods + ('bicubic',) return interpolation_methods, interpolation_fun def _create_texture(self, texture_format, data): if texture_format is not None: tex_cls = GPUScaledTextured3D else: tex_cls = CPUScaledTexture3D if self._interpolation == 'linear': texture_interpolation = 'linear' else: texture_interpolation = 'nearest' # clamp_to_edge means any texture coordinates outside of 0-1 should be # clamped to 0 and 1. # NOTE: This doesn't actually set the data in the texture. Only # creates a placeholder texture that will be resized later on. return tex_cls(data, interpolation=texture_interpolation, internalformat=texture_format, format='luminance', wrapping='clamp_to_edge') def set_data(self, vol, clim=None, copy=True): """Set the volume data. Parameters ---------- vol : ndarray The 3D volume. clim : tuple Colormap limits to use (min, max). None will use the min and max values. Defaults to ``None``. copy : bool Whether to copy the input volume prior to applying clim normalization on the CPU. Has no effect if visual was created with 'texture_format' not equal to None as data is not modified on the CPU and data must already be copied to the GPU. Data must be 32-bit floating point data to completely avoid any data copying when scaling on the CPU. Defaults to ``True`` for CPU scaled data. It is forced to ``False`` for GPU scaled data. """ # 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] > 1)): raise ValueError('Volume visual needs a 3D array.') if isinstance(self._texture, GPUScaledTextured3D): copy = False if clim is not None and clim != self._texture.clim: self._texture.set_clim(clim) # Apply to texture self._texture.check_data_format(vol) self._last_data = vol self._texture.scale_and_set_data(vol, copy=copy) self.shared_program['clim'] = self._texture.clim_normalized self.shared_program['u_shape'] = (vol.shape[2], vol.shape[1], vol.shape[0]) shape = vol.shape[:3] if self._vol_shape != shape: self._vol_shape = shape self._need_vertex_update = True self._vol_shape = shape @property def rendering_methods(self): return list(self._rendering_methods) @property def raycasting_modes(self): return list(self._raycasting_modes) @property def clim(self): """The contrast limits that were applied to the volume data. Volume display is mapped from black to white with these values. Settable via set_data() as well as @clim.setter. """ return self._texture.clim @clim.setter def clim(self, value): """Set contrast limits used when rendering the image. ``value`` should be a 2-tuple of floats (min_clim, max_clim), where each value is within the range set by self.clim. If the new value is outside of the (min, max) range of the clims previously used to normalize the texture data, then data will be renormalized using set_data. """ if self._texture.set_clim(value): self.set_data(self._last_data, clim=value) self.shared_program['clim'] = self._texture.clim_normalized self.update() @property def gamma(self): """The gamma used when rendering the image.""" return self._gamma @gamma.setter def gamma(self, value): """Set gamma used when rendering the image.""" if value <= 0: raise ValueError("gamma must be > 0") self._gamma = float(value) self.shared_program['gamma'] = self._gamma self.update() @property def cmap(self): return self._cmap @cmap.setter def cmap(self, cmap): self._cmap = get_colormap(cmap) self.shared_program.frag['cmap'] = Function(self._cmap.glsl_map) self.shared_program['texture2D_LUT'] = self.cmap.texture_lut() self.update() @property def interpolation_methods(self): return self._interpolation_methods @property def interpolation(self): """Get interpolation algorithm name.""" return self._interpolation @interpolation.setter def interpolation(self, i): if i not in self._interpolation_methods: raise ValueError("interpolation must be one of %s" % ', '.join(self._interpolation_methods)) if self._interpolation != i: self._interpolation = i self._need_interpolation_update = True self.update() # The interpolation code could be transferred to a dedicated filter # function in visuals/filters as discussed in #1051 def _build_interpolation(self): """Rebuild the _data_lookup_fn for different interpolations.""" interpolation = self._interpolation # alias bicubic to cubic if interpolation == 'bicubic': warnings.warn( "'bicubic' interpolation is Deprecated. Use 'cubic' instead.", DeprecationWarning, stacklevel=2, ) interpolation = 'cubic' self._data_lookup_fn = self._interpolation_fun[interpolation] try: self.shared_program.frag['get_data'] = self._data_lookup_fn except Exception as e: print(e) # only 'linear' uses 'linear' texture interpolation if interpolation == 'linear': texture_interpolation = 'linear' else: # 'nearest' (and also 'linear') doesn't use spatial_filters.frag # so u_kernel and shape setting is skipped texture_interpolation = 'nearest' if interpolation != 'nearest': self.shared_program['u_kernel'] = self._kerneltex self._data_lookup_fn['shape'] = self._last_data.shape[:3][::-1] if self._texture.interpolation != texture_interpolation: self._texture.interpolation = texture_interpolation self._data_lookup_fn['texture'] = self._texture self._need_interpolation_update = False @staticmethod @lru_cache(maxsize=10) def _build_clipping_planes_glsl(n_planes: int) -> str: """Build the code snippet used to clip the volume based on self.clipping_planes.""" func_template = ''' float clip_planes(vec3 loc, vec3 vol_shape) {{ vec3 loc_transf = $clip_transform(vec4(loc * vol_shape, 1)).xyz; float distance_from_clip = 3.4e38; // max float {clips}; return distance_from_clip; }} ''' # the vertex is considered clipped if on the "negative" side of the plane clip_template = ''' vec3 relative_vec{idx} = loc_transf - $clipping_plane_pos{idx}; float distance_from_clip{idx} = dot(relative_vec{idx}, $clipping_plane_norm{idx}); distance_from_clip = min(distance_from_clip{idx}, distance_from_clip); ''' all_clips = [] for idx in range(n_planes): all_clips.append(clip_template.format(idx=idx)) formatted_code = func_template.format(clips=''.join(all_clips)) return formatted_code @property def clipping_planes(self) -> np.ndarray: """The set of planes used to clip the volume. Values on the negative side of the normal are discarded. Each plane is defined by a position and a normal vector (magnitude is irrelevant). Shape: (n_planes, 2, 3). The order is xyz, as opposed to data's zyx (for consistency with the rest of vispy) Example: one plane in position (0, 0, 0) and with normal (0, 0, 1), and a plane in position (1, 1, 1) with normal (0, 1, 0): >>> volume.clipping_planes = np.array([ >>> [[0, 0, 0], [0, 0, 1]], >>> [[1, 1, 1], [0, 1, 0]], >>> ]) """ return self._clipping_planes @clipping_planes.setter def clipping_planes(self, value: Optional[np.ndarray]): if value is None: value = np.empty([0, 2, 3]) self._clipping_planes = value self._clip_func = Function(self._build_clipping_planes_glsl(len(value))) self.shared_program.frag['clip_with_planes'] = self._clip_func self._clip_func['clip_transform'] = self._clip_transform for idx, plane in enumerate(value): self._clip_func[f'clipping_plane_pos{idx}'] = tuple(plane[0]) self._clip_func[f'clipping_plane_norm{idx}'] = tuple(plane[1]) self.update() @property def clipping_planes_coord_system(self) -> str: """ Coordinate system used by the clipping planes (see visuals.transforms.transform_system.py) """ return self._clipping_planes_coord_system @property def _before_loop_snippet(self): return self._rendering_methods[self.method]['before_loop'] @property def _in_loop_snippet(self): return self._rendering_methods[self.method]['in_loop'] @property def _after_loop_snippet(self): return self._rendering_methods[self.method]['after_loop'] @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. * minip: minimum intensity projection. Cast a ray and display the minimum value that was encountered. * attenuated_mip: attenuated maximum intensity projection. Cast a ray and display the maximum value encountered. Values are attenuated as the ray moves deeper into the volume. * 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. * average: average intensity projection. Cast a ray and display the average of values that were encountered. """ return self._method @method.setter def method(self, method): # Check and save known_methods = list(self._rendering_methods.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_data needs to be unset and re-set, since it's present inside the snippets. # Program should probably be able to do this automatically self.shared_program.frag['get_data'] = None self.shared_program.frag['raycasting_setup'] = self._raycasting_setup_snippet self.shared_program.frag['before_loop'] = self._before_loop_snippet self.shared_program.frag['in_loop'] = self._in_loop_snippet self.shared_program.frag['after_loop'] = self._after_loop_snippet self.shared_program.frag['sampler_type'] = self._texture.glsl_sampler_type self.shared_program.frag['cmap'] = Function(self._cmap.glsl_map) self.shared_program['texture2D_LUT'] = self.cmap.texture_lut() self.shared_program['u_mip_cutoff'] = self._mip_cutoff self.shared_program['u_minip_cutoff'] = self._minip_cutoff self._need_interpolation_update = True self.update() @property def _raycasting_setup_snippet(self): return self._raycasting_modes[self.raycasting_mode] @property def raycasting_mode(self): """The raycasting mode to use. This defines whether to cast a ray through the whole volume or perpendicular to a plane through the volume. must be in {'volume', 'plane'} """ return self._raycasting_mode @raycasting_mode.setter def raycasting_mode(self, value: str): valid_raycasting_modes = self._raycasting_modes.keys() if value not in valid_raycasting_modes: raise ValueError(f"Raycasting mode should be in {valid_raycasting_modes}, not {value}") self._raycasting_mode = value self.shared_program.frag['raycasting_setup'] = self._raycasting_setup_snippet 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.shared_program['u_threshold'] = self._threshold self.update() @property def attenuation(self): """The attenuation rate to apply for the attenuated mip render method.""" return self._attenuation @attenuation.setter def attenuation(self, value): self._attenuation = float(value) self.shared_program['u_attenuation'] = self._attenuation 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 self.shared_program['u_relative_step_size'] = value @property def plane_position(self): """Position on a plane through the volume. A (3,) array containing a position on a plane of interest in the volume. The position is defined in data coordinates. Only relevant in raycasting_mode = 'plane'. """ return self._plane_position @plane_position.setter def plane_position(self, value): value = np.array(value, dtype=np.float32).ravel() if value.shape != (3, ): raise ValueError('plane_position must be a 3 element array-like object') self._plane_position = value self.shared_program['u_plane_position'] = value[::-1] self.update() @property def plane_normal(self): """Direction normal to a plane through the volume. A (3,) array containing a vector normal to the plane of interest in the volume. The normal vector is defined in data coordinates. Only relevant in raycasting_mode = 'plane'. """ return self._plane_normal @plane_normal.setter def plane_normal(self, value): value = np.array(value, dtype=np.float32).ravel() if value.shape != (3, ): raise ValueError('plane_normal must be a 3 element array-like object') self._plane_normal = value self.shared_program['u_plane_normal'] = value[::-1] self.update() @property def plane_thickness(self): """Thickness of a plane through the volume. A value defining the total length of the ray perpendicular to the plane interrogated during rendering. Defined in data coordinates. Only relevant in raycasting_mode = 'plane'. """ return self._plane_thickness @plane_thickness.setter def plane_thickness(self, value: float): value = float(value) if value < 1: raise ValueError('plane_thickness should be at least 1.0') self._plane_thickness = value self.shared_program['u_plane_thickness'] = value self.update() @property def mip_cutoff(self): """The lower cutoff value for `mip` and `attenuated_mip`. When using the `mip` or `attenuated_mip` rendering methods, fragments with values below the cutoff will be discarded. """ return self._mip_cutoff @mip_cutoff.setter def mip_cutoff(self, value): if value is None: value = np.finfo('float32').min self._mip_cutoff = float(value) self.shared_program['u_mip_cutoff'] = self._mip_cutoff self.update() @property def minip_cutoff(self): """The upper cutoff value for `minip`. When using the `minip` rendering method, fragments with values above the cutoff will be discarded. """ return self._minip_cutoff @minip_cutoff.setter def minip_cutoff(self, value): if value is None: value = np.finfo('float32').max self._minip_cutoff = float(value) self.shared_program['u_minip_cutoff'] = self._minip_cutoff self.update() 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 # 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 pos = 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) """ 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) # Apply self._vertices.set_data(pos) self._index_buffer.set_data(indices) def _compute_bounds(self, axis, view): if self._is_zyx: # axis=(x, y, z) -> shape(..., z, y, x) ndim = len(self._vol_shape) return 0, self._vol_shape[ndim - 1 - axis] else: # axis=(x, y, z) -> shape(x, y, z) return 0, self._vol_shape[axis] def _prepare_transforms(self, view): trs = view.transforms view.view_program.vert['transform'] = trs.get_transform() view_tr_f = trs.get_transform('visual', 'document') view_tr_i = view_tr_f.inverse view.view_program.vert['viewtransformf'] = view_tr_f view.view_program.vert['viewtransformi'] = view_tr_i view.view_program.frag['viewtransformf'] = view_tr_f self._clip_transform = trs.get_transform('visual', self._clipping_planes_coord_system) def _prepare_draw(self, view): if self._need_vertex_update: self._create_vertex_data() if self._need_interpolation_update: self._build_interpolation() vispy-0.14.1/vispy/visuals/visual.py0000644000175000017510000007213714507104216017025 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Definitions =========== Visual : an object that (1) can be drawn on-screen, (2) can be manipulated by configuring the coordinate transformations that it uses. View : a special type of visual that (1) draws the contents of another visual, (2) using a different set of transforms. Views have only the basic visual interface (draw, bounds, attach, etc.) and lack access to the specific features of the visual they are linked to (for example, LineVisual has a ``set_data()`` method, but there is no corresponding method on a view of a LineVisual). Class Structure =============== * `BaseVisual` - provides transforms and view creation This class lays out the basic API for all visuals: ``draw()``, ``bounds()``, ``view()``, and ``attach()`` methods, as well as a `TransformSystem` instance that determines where the visual will be drawn. * `Visual` - defines a shader program to draw. Subclasses are responsible for supplying the shader code and configuring program variables, including transforms. * `VisualView` - clones the shader program from a Visual instance. Instances of `VisualView` contain their own shader program, transforms and filter attachments, and generally behave like a normal instance of `Visual`. * `CompoundVisual` - wraps multiple Visual instances. These visuals provide no program of their own, but instead rely on one or more internally generated `Visual` instances to do their drawing. For example, a PolygonVisual consists of an internal LineVisual and MeshVisual. * `CompoundVisualView` - wraps multiple VisualView instances. This allows a `CompoundVisual` to be viewed with a different set of transforms and filters. Making Visual Subclasses ======================== When making subclasses of `Visual`, it is only necessary to reimplement the ``_prepare_draw()``, ``_prepare_transforms()``, and ``_compute_bounds()`` methods. These methods will be called by the visual automatically when it is needed for itself or for a view of the visual. It is important to remember when implementing these methods that most changes made to the visual's shader program should also be made to the programs for each view. To make this easier, the visual uses a `MultiProgram`, which allows all shader programs across the visual and its views to be accessed simultaneously. For example:: def _prepare_draw(self, view): # This line applies to the visual and all of its views self.shared_program['a_position'] = self._vbo # This line applies only to the view that is about to be drawn view.view_program['u_color'] = (1, 1, 1, 1) Under most circumstances, it is not necessary to reimplement `VisualView` because a view will directly access the ``_prepare`` and ``_compute`` methods from the visual it is viewing. However, if the `Visual` to be viewed is a subclass that reimplements other methods such as ``draw()`` or ``bounds()``, then it will be necessary to provide a new matching `VisualView` subclass. Making CompoundVisual Subclasses ================================ Compound visual subclasses are generally very easy to construct:: class PlotLineVisual(visuals.CompoundVisual): def __init__(self, ...): self._line = LineVisual(...) self._point = PointVisual(...) visuals.CompoundVisual.__init__(self, [self._line, self._point]) A compound visual will automatically handle forwarding transform system changes and filter attachments to its internally-wrapped visuals. To the user, this will appear to behave as a single visual. """ from __future__ import division import weakref from contextlib import contextmanager import numpy as np from .. import gloo from ..util.event import EmitterGroup, Event from ..util import logger, Frozen from .shaders import StatementList, MultiProgram from .transforms import TransformSystem class VisualShare(object): """Contains data that is shared between all views of a visual. This includes: * GL state variables (blending, depth test, etc.) * A weak dictionary of all views * A list of filters that should be applied to all views * A cache for bounds. """ def __init__(self): # Note: in some cases we will need to compute bounds independently for # each view. That will have to be worked out later.. self.bounds = {} self.gl_state = {} self.views = weakref.WeakKeyDictionary() self.filters = [] self.visible = True class BaseVisual(Frozen): """Superclass for all visuals. This class provides: * A TransformSystem. * Two events: `update` and `bounds_change`. * Minimal framework for creating views of the visual. * A data structure that is shared between all views of the visual. * Abstract `draw`, `bounds`, `attach`, and `detach` methods. Parameters ---------- vshare : instance of VisualShare | None The visual share. 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. This subclasses Frozen so that subclasses can easily freeze their properties. """ def __init__(self, vshare=None): self._view_class = getattr(self, '_view_class', VisualView) self._vshare = VisualShare() if vshare is None else vshare self._vshare.views[self] = None self.events = EmitterGroup(source=self, auto_connect=True, update=Event, bounds_change=Event ) self._transforms = None self.transforms = TransformSystem() @property def transform(self): return self.transforms.visual_transform.transforms[0] @transform.setter def transform(self, tr): self.transforms.visual_transform = tr @property def transforms(self): return self._transforms @transforms.setter def transforms(self, trs): if trs is self._transforms: return if self._transforms is not None: self._transforms.changed.disconnect(self._transform_changed) self._transforms = trs trs.changed.connect(self._transform_changed) self._transform_changed() def get_transform(self, map_from='visual', map_to='render'): """Return a transform mapping between any two coordinate systems. Parameters ---------- map_from : str The starting coordinate system to map from. Must be one of: visual, scene, document, canvas, framebuffer, or render. map_to : str The ending coordinate system to map to. Must be one of: visual, scene, document, canvas, framebuffer, or render. """ return self.transforms.get_transform(map_from, map_to) @property def visible(self): return self._vshare.visible @visible.setter def visible(self, v): if v != self._vshare.visible: self._vshare.visible = v self.update() def view(self): """Return a new view of this visual.""" return self._view_class(self) def draw(self): raise NotImplementedError(self) def attach(self, filt, view=None): """Attach a Filter to this visual. Each filter modifies the appearance or behavior of the visual. Parameters ---------- filt : object The filter to attach. view : instance of VisualView | None The view to use. """ raise NotImplementedError(self) def detach(self, filt, view=None): """Detach a filter. Parameters ---------- filt : object The filter to detach. view : instance of VisualView | None The view to use. """ raise NotImplementedError(self) def bounds(self, axis, view=None): """Get the bounds of the Visual Parameters ---------- axis : int The axis. view : instance of VisualView The view to use. """ if view is None: view = self if axis not in self._vshare.bounds: self._vshare.bounds[axis] = self._compute_bounds(axis, view) return self._vshare.bounds[axis] def _compute_bounds(self, axis, view): raise NotImplementedError(self) def _bounds_changed(self): self._vshare.bounds.clear() def update(self): """Update the Visual""" self.events.update() def _transform_changed(self, event=None): self.update() class BaseVisualView(object): """Base class for a view on a visual. This class must be mixed with another Visual class to work properly. It works mainly by forwarding the calls to _prepare_draw, _prepare_transforms, and _compute_bounds to the viewed visual. """ def __init__(self, visual): self._visual = visual @property def visual(self): return self._visual def _prepare_draw(self, view=None): self._visual._prepare_draw(view=view) def _prepare_transforms(self, view): self._visual._prepare_transforms(view) def _compute_bounds(self, axis, view): self._visual._compute_bounds(axis, view) def __repr__(self): return '<%s on %r>' % (self.__class__.__name__, self._visual) class Visual(BaseVisual): """Base class for all visuals that can be drawn using a single shader program. This class creates a MultiProgram, which is an object that behaves like a normal shader program (you can assign shader code, upload values, set template variables, etc.) but internally manages multiple ModularProgram instances, one per view. Subclasses generally only need to reimplement _compute_bounds, _prepare_draw, and _prepare_transforms. Parameters ---------- vcode : str Vertex shader code. fcode : str Fragment shader code. gcode : str or None Optional geometry shader code. program : instance of Program | None The program to use. If None, a program will be constructed using ``vcode`` and ``fcode``. vshare : instance of VisualShare | None The visual share, if necessary. """ def __init__(self, vcode='', fcode='', gcode=None, program=None, vshare=None): self._view_class = VisualView BaseVisual.__init__(self, vshare) if vshare is None: self._vshare.draw_mode = None self._vshare.index_buffer = None if program is None: self._vshare.program = MultiProgram(vcode, fcode, gcode) else: self._vshare.program = program if len(vcode) > 0 or len(fcode) > 0: raise ValueError("Cannot specify both program and " "vcode/fcode arguments.") self._prev_gl_state = [] self._program = self._vshare.program.add_program() self._prepare_transforms(self) self._filters = [] self._hooks = {} def set_gl_state(self, preset=None, **kwargs): """Define the set of GL state parameters to use when drawing. The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`. This can also be used as a context manager that will revert the gl_state on exit. When used as a context manager, this function is designed to be constructed directly in the header of the `with` statement to avoid confusion about what state will be restored on exit. Parameters ---------- preset : str Preset to use. **kwargs : dict Keyword arguments. """ prev_gl_state = self._vshare.gl_state.copy() self._vshare.gl_state = kwargs self._vshare.gl_state['preset'] = preset return _revert_gl_state([(self, prev_gl_state)]) def update_gl_state(self, *args, **kwargs): """Modify the set of GL state parameters to use when drawing. The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`. This can also be used as a context manager that will revert the gl_state on exit. Parameters ---------- *args : tuple Arguments. **kwargs : dict Keyword arguments. """ prev_gl_state = self._vshare.gl_state.copy() if len(args) == 1: self._vshare.gl_state['preset'] = args[0] elif len(args) != 0: raise TypeError("Only one positional argument allowed.") self._vshare.gl_state.update(kwargs) return _revert_gl_state([(self, prev_gl_state)]) def push_gl_state(self, *args, **kwargs): """Define the set of GL state parameters to use when drawing. The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`. This differs from :py:meth:`.set_gl_state` in that it stashes the current state. See :py:meth:`.pop_gl_state` for restoring the state. Parameters ---------- *args : tuple Arguments. **kwargs : dict Keyword arguments. """ self._prev_gl_state.append(self._vshare.gl_state.copy()) self.set_gl_state(*args, **kwargs) def push_gl_state_update(self, *args, **kwargs): """Modify the set of GL state parameters to use when drawing. The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`. This differs from :py:meth:`.update_gl_state` in that it stashes the current state. See :py:meth:`.pop_gl_state` for restoring the state. Parameters ---------- *args : tuple Arguments. **kwargs : dict Keyword arguments. """ self._prev_gl_state.append(self._vshare.gl_state.copy()) self.update_gl_state(*args, **kwargs) def pop_gl_state(self): """Restore a previous set of GL state parameters if available. If no previous GL state is available (see :py:meth:`.push_gl_state`), this has no effect. """ if self._prev_gl_state: self.set_gl_state(**self._prev_gl_state.pop()) def _compute_bounds(self, axis, view): """Return the (min, max) bounding values of this visual along *axis* in the local coordinate system. """ return None def _prepare_draw(self, view=None): """This visual is about to be drawn. Visuals should implement this method to ensure that all program and GL state variables are updated immediately before drawing. Return False to indicate that the visual should not be drawn. """ return True def _prepare_transforms(self, view): """This method is called whenever the TransformSystem instance is changed for a view. Assign a view's transforms to the proper shader template variables on the view's shader program. Note that each view has its own TransformSystem. In this method we connect the appropriate mapping functions from the view's TransformSystem to the view's program. """ raise NotImplementedError() # Todo: this method can be removed if we somehow enable the shader # to specify exactly which transform functions it needs by name. For # example: # # // mapping function is automatically defined from the # // corresponding transform in the view's TransformSystem # gl_Position = visual_to_render(a_position); # @property def shared_program(self): return self._vshare.program @property def view_program(self): return self._program @property def _draw_mode(self): return self._vshare.draw_mode @_draw_mode.setter def _draw_mode(self, m): self._vshare.draw_mode = m @property def _index_buffer(self): return self._vshare.index_buffer @_index_buffer.setter def _index_buffer(self, buf): self._vshare.index_buffer = buf def draw(self): if not self.visible: return if self._prepare_draw(view=self) is False: return if self._vshare.draw_mode is None: raise ValueError("_draw_mode has not been set for visual %r" % self) self._configure_gl_state() try: self._program.draw(self._vshare.draw_mode, self._vshare.index_buffer) except Exception: logger.warning("Error drawing visual %r" % self) raise def _configure_gl_state(self): gloo.set_state(**self._vshare.gl_state) def _get_hook(self, shader, name): """Return a FunctionChain that Filters may use to modify the program. *shader* should be "vert", "geom", or "frag" *name* should be "pre" or "post" """ assert name in ('pre', 'post') key = (shader, name) if key in self._hooks: return self._hooks[key] hook = StatementList() if shader == 'vert': self.view_program.vert[name] = hook elif shader == 'frag': self.view_program.frag[name] = hook elif shader == 'geom': self.view_program.geom[name] = hook else: raise ValueError("shader must be vert, geom, or frag") self._hooks[key] = hook return hook def attach(self, filt, view=None): """Attach a Filter to this visual Each filter modifies the appearance or behavior of the visual. Parameters ---------- filt : object The filter to attach. view : instance of VisualView | None The view to use. """ if view is None: self._vshare.filters.append(filt) for view in self._vshare.views.keys(): filt._attach(view) else: view._filters.append(filt) filt._attach(view) def detach(self, filt, view=None): """Detach a filter. Parameters ---------- filt : object The filter to detach. view : instance of VisualView | None The view to use. """ if view is None: self._vshare.filters.remove(filt) for view in self._vshare.views.keys(): filt._detach(view) else: view._filters.remove(filt) filt._detach(view) class VisualView(BaseVisualView, Visual): """A view on another Visual instance. View instances are created by calling ``visual.view()``. Because this is a subclass of `Visual`, all instances of `VisualView` define their own shader program (which is a clone of the viewed visual's program), transforms, and filter attachments. """ def __init__(self, visual): BaseVisualView.__init__(self, visual) Visual.__init__(self, vshare=visual._vshare) # Attach any shared filters for filt in self._vshare.filters: filt._attach(self) class CompoundVisual(BaseVisual): """Visual consisting entirely of sub-visuals. To the user, a compound visual behaves exactly like a normal visual--it has a transform system, draw() and bounds() methods, etc. Internally, the compound visual automatically manages proxying these transforms and methods to its sub-visuals. Parameters ---------- subvisuals : list of BaseVisual instances The list of visuals to be combined in this compound visual. """ def __init__(self, subvisuals): self._view_class = CompoundVisualView self._subvisuals = [] BaseVisual.__init__(self) for v in subvisuals: self.add_subvisual(v) self.freeze() def add_subvisual(self, visual): """Add a subvisual Parameters ---------- visual : instance of Visual The visual to add. """ visual.transforms = self.transforms visual._prepare_transforms(visual) self._subvisuals.append(visual) visual.events.update.connect(self._subv_update) self.update() def remove_subvisual(self, visual): """Remove a subvisual Parameters ---------- visual : instance of Visual The visual to remove. """ visual.events.update.disconnect(self._subv_update) self._subvisuals.remove(visual) self.update() def _subv_update(self, event): self.update() def _transform_changed(self, event=None): for v in self._subvisuals: v.transforms = self.transforms BaseVisual._transform_changed(self) def draw(self): """Draw the visual""" if not self.visible: return if self._prepare_draw(view=self) is False: return for v in self._subvisuals: if v.visible: v.draw() def _prepare_draw(self, view): pass def _prepare_transforms(self, view): for v in view._subvisuals: v._prepare_transforms(v) def set_gl_state(self, preset=None, **kwargs): """Define the set of GL state parameters to use when drawing. The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`. This can also be used as a context manager that will revert the gl_state on exit. Parameters ---------- preset : str Preset to use. **kwargs : dict Keyword arguments. """ prev_gl_state = [] for v in self._subvisuals: prev_gl_state.append((v, v._vshare.gl_state)) v.set_gl_state(preset=preset, **kwargs) return _revert_gl_state(prev_gl_state) def update_gl_state(self, *args, **kwargs): """Modify the set of GL state parameters to use when drawing. The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`. This can also be used as a context manager that will revert the gl_state on exit. Parameters ---------- *args : tuple Arguments. **kwargs : dict Keyword arguments. """ prev_gl_state = [] for v in self._subvisuals: prev_gl_state.append((v, v._vshare.gl_state)) v.update_gl_state(*args, **kwargs) return _revert_gl_state(prev_gl_state) def push_gl_state(self, *args, **kwargs): """Define the set of GL state parameters to use when drawing. The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`. This differs from :py:meth:`.set_gl_state` in that it stashes the current state. See :py:meth:`.pop_gl_state` for restoring the state. Parameters ---------- *args : tuple Arguments. **kwargs : dict Keyword arguments. """ for v in self._subvisuals: v.push_gl_state(*args, **kwargs) def push_gl_state_update(self, *args, **kwargs): """Modify the set of GL state parameters to use when drawing. The arguments are forwarded to :func:`vispy.gloo.wrappers.set_state`. This differs from :py:meth:`.update_gl_state` in that it stashes the current state. See :py:meth:`.pop_gl_state` for restoring the state. Parameters ---------- *args : tuple Arguments. **kwargs : dict Keyword arguments. """ for v in self._subvisuals: v.push_gl_state_update(*args, **kwargs) def pop_gl_state(self): """Restore a previous set of GL state parameters if available. If no previous GL state is available (see :py:meth:`.push_gl_state`), this has no effect. """ for v in self._subvisuals: v.pop_gl_state() def attach(self, filt, view=None): """Attach a Filter to this visual Each filter modifies the appearance or behavior of the visual. Parameters ---------- filt : object The filter to attach. view : instance of VisualView | None The view to use. """ for v in self._subvisuals: v.attach(filt, v) def detach(self, filt, view=None): """Detach a filter. Parameters ---------- filt : object The filter to detach. view : instance of VisualView | None The view to use. """ for v in self._subvisuals: v.detach(filt, v) def _compute_bounds(self, axis, view): bounds = None for v in view._subvisuals: if v.visible: vb = v.bounds(axis) if bounds is None: bounds = vb elif vb is not None: bounds = [min(bounds[0], vb[0]), max(bounds[1], vb[1])] return bounds @contextmanager def _revert_gl_state(prev_gl_state): """Context manager to store and revert GL state for a list of visuals. Parameters ---------- prev_gl_state : list A list of (Visual, gl_state) tuples, where gl_state is a dictionary of `gl_state` params as would be passed to :py:func:`set_gl_state`. """ for v, state in prev_gl_state: v._prev_gl_state.append(state) try: yield finally: for v, _ in prev_gl_state: v.pop_gl_state() class CompoundVisualView(BaseVisualView, CompoundVisual): def __init__(self, visual): BaseVisualView.__init__(self, visual) # Create a view on each sub-visual subv = [v.view() for v in visual._subvisuals] CompoundVisual.__init__(self, subv) # Attach any shared filters for filt in self._vshare.filters: for v in self._subvisuals: filt._attach(v) class updating_property: """A property descriptor that autoupdates the Visual during attribute setting. Use this as a decorator in place of the @property when you want the attribute to trigger an immediate update to the visual upon change. You may additionally declare an @setter, and if you do, it will be called in addition to the standard logic: `self._attr_name = value`. For example, the following code examples are equivalent:: class SomeVisual1(Visual): def __init__(self, someprop=None): self._someprop = someprop @property def someprop(self): return self._someprop @someprop.setter def someprop(self, value): previous = self._someprop if (previous is None) or np.any(value != previous): self._someprop = value self._need_update = True if hasattr(self, 'events'): self.update() class SomeVisual2(Visual): def __init__(self, someprop=None): self._someprop = someprop @updating_property def someprop(self): pass NOTE: by default the __get__ method here will look for the conventional `_attr_name` property on the object. The result of this is that you don't actually have to put anything in the body of a method decorated with @updating_property if you don't want to do anything other than retrieve the property. So you may see this slightly strange pattern being used:: class SomeVisual2(Visual): def __init__(self, someprop=None): self._someprop = someprop @updating_property def someprop(self): '''the docstring (or pass) is all that is needed''' """ def __init__(self, fget=None, fset=None, doc=None): self.fget = fget self.fset = fset if self.fget is not None: self.attr_name = f'_{self.fget.__name__}' self.__doc__ = doc or self.fget.__doc__ def __get__(self, obj, objtype=None): if obj is None: return self # if the @updating_property getter returns a value, use that val = self.fget(obj) if val is not None: return val # otherwise get the private attribute by the same name return getattr(obj, self.attr_name, None) def __set__(self, obj, value): previous = getattr(obj, self.attr_name, None) if (previous is None) or np.any(value != previous): setattr(obj, self.attr_name, value) # if a @.setter method has been declared, run that as well # (overriding the standard setter behavior) if self.fset is not None: self.fset(obj, value) obj._need_update = True # prevent update during obj.__init__ if hasattr(obj, 'events'): obj.update() def __delete__(self, obj): raise AttributeError("can't delete attribute") def setter(self, fset): return type(self)(self.fget, fset, self.__doc__) vispy-0.14.1/vispy/visuals/tube.py0000644000175000017510000001354114507104216016453 0ustar nileshnileshfrom __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 import collections 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 | ndarray The radius of the tube. Use array of floats as input to set radii of points individually. 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'): # make sure we are working with floats points = np.array(points).astype(float) tangents, normals, binormals = _frenet_frames(points, closed) segments = len(points) - 1 # if single radius, convert to list of radii if not isinstance(radius, collections.abc.Iterable): radius = [radius] * len(points) elif len(radius) != len(points): raise ValueError('Length of radii list must match points.') # 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] r = radius[i] # Add a vertex for each point on the circle v = np.arange(tube_points, dtype=np.float32) / tube_points * 2 * np.pi cx = -1. * r * np.cos(v) cy = r * 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 _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.14.1/vispy/visuals/transforms/0000755000175000017510000000000014507104251017333 5ustar nileshnileshvispy-0.14.1/vispy/visuals/transforms/transform_system.py0000644000175000017510000003267714507104216023344 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from .linear import STTransform, NullTransform from .chain import ChainTransform from ._util import TransformCache from ...util.event import EventEmitter import numpy as np class TransformSystem(object): """TransformSystem encapsulates information about the coordinate systems needed to draw a Visual. Visual rendering operates in six coordinate systems: * **Visual** - arbitrary local coordinate frame of the visual. Vertex buffers used by the visual are usually specified in this coordinate system. * **Scene** - This is an isometric coordinate system used mainly for lighting calculations. * **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.). In most circumstances, this is exactly the same as the canvas coordinate system. * **Canvas** - This coordinate system represents the logical pixel coordinates of the canvas. It has its origin in the top-left corner of the canvas, and is typically the coordinate system that mouse and touch events are reported in. Note that, by convention, _logical_ pixels are not necessarily the same size as the _physical_ pixels in the framebuffer that is being rendered to. * **Framebuffer** - The buffer coordinate system has units of _physical_ pixels, and should usually represent the coordinates of the current framebuffer (on the canvas or an FBO) being rendered to. Visuals use this coordinate system primarily for antialiasing calculations. It is also the coorinate system used by glFragCoord. In most cases, this will have the same scale as the document and canvas coordinate systems because the active framebuffer is the back buffer of the canvas, and the canvas will have _logical_ and _physical_ pixels of the same size. However, 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 vertices returned by a vertex shader. It has coordinates (-1, -1) to (1, 1) across the current glViewport. In OpenGL terminology, this is called clip 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 -------- 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_transform * tr_sys.document_transform) 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 """ def __init__(self, canvas=None, dpi=None): self.changed = EventEmitter(source=self, type='transform_changed') self._canvas = None self._fbo_bounds = None self.canvas = canvas self._cache = TransformCache() self._dpi = dpi self._mappings = {'ct0': None, 'ct1': None, 'ft0': None} # Assign a ChainTransform for each step. This allows us to always # return the same transform objects regardless of how the user # configures the system. self._visual_transform = ChainTransform([NullTransform()]) self._scene_transform = ChainTransform([NullTransform()]) self._document_transform = ChainTransform([NullTransform()]) self._canvas_transform = ChainTransform([STTransform(), STTransform()]) self._framebuffer_transform = ChainTransform([STTransform()]) for tr in (self._visual_transform, self._scene_transform, self._document_transform, self._canvas_transform, self._framebuffer_transform): tr.changed.connect(self.changed) def _update_if_maps_changed(self, transform, map_key, new_maps): """Helper to store and check current (from, to) maps against new ones being provided. The new mappings are only applied if a change has occurred (and also stored in the current mappings). """ if self._mappings[map_key] is None: self._mappings[map_key] = new_maps transform.set_mapping(new_maps[0], new_maps[1]) else: if np.any(self._mappings[map_key] != new_maps): self._mappings[map_key] = new_maps transform.set_mapping(new_maps[0], new_maps[1]) def configure(self, viewport=None, fbo_size=None, fbo_rect=None, canvas=None): """Automatically configure the TransformSystem: * canvas_transform maps from the Canvas logical pixel coordinate system to the framebuffer coordinate system, taking into account the logical/physical pixel scale factor, current FBO position, and y-axis inversion. * framebuffer_transform maps from the current GL viewport on the framebuffer coordinate system to clip coordinates (-1 to 1). Parameters ========== viewport : tuple or None The GL viewport rectangle (x, y, w, h). If None, then it is assumed to cover the entire canvas. fbo_size : tuple or None The size of the active FBO. If None, then it is assumed to have the same size as the canvas's framebuffer. fbo_rect : tuple or None The position and size (x, y, w, h) of the FBO in the coordinate system of the canvas's framebuffer. If None, then the bounds are assumed to cover the entire active framebuffer. canvas : Canvas instance Optionally set the canvas for this TransformSystem. See the `canvas` property. """ # TODO: check that d2f and f2r transforms still contain a single # STTransform (if the user has modified these, then auto-config should # either fail or replace the transforms) if canvas is not None: self.canvas = canvas canvas = self._canvas if canvas is None: raise RuntimeError("No canvas assigned to this TransformSystem.") # By default, this should invert the y axis--canvas origin is in top # left, whereas framebuffer origin is in bottom left. map_from = [(0, 0), canvas.size] map_to = [(0, canvas.physical_size[1]), (canvas.physical_size[0], 0)] self._update_if_maps_changed(self._canvas_transform.transforms[1], 'ct1', np.array((map_from, map_to))) if fbo_rect is None: self._canvas_transform.transforms[0].scale = (1, 1, 1) self._canvas_transform.transforms[0].translate = (0, 0, 0) else: # Map into FBO coordinates map_from = [(fbo_rect[0], fbo_rect[1]), (fbo_rect[0] + fbo_rect[2], fbo_rect[1] + fbo_rect[3])] map_to = [(0, 0), fbo_size] self._update_if_maps_changed(self._canvas_transform.transforms[0], 'ct0', np.array((map_from, map_to))) if viewport is None: if fbo_size is None: # viewport covers entire canvas map_from = [(0, 0), canvas.physical_size] else: # viewport covers entire FBO map_from = [(0, 0), fbo_size] else: map_from = [viewport[:2], (viewport[0] + viewport[2], viewport[1] + viewport[3])] map_to = [(-1, -1), (1, 1)] self._update_if_maps_changed(self._framebuffer_transform.transforms[0], 'ft0', np.array((map_from, map_to))) @property def canvas(self): """The Canvas being drawn to.""" return self._canvas @canvas.setter def canvas(self, canvas): self._canvas = canvas @property def dpi(self): """Physical resolution of the document coordinate system (dots per inch).""" if self._dpi is None: if self._canvas is None: return None else: return self.canvas.dpi else: return self._dpi @dpi.setter def dpi(self, dpi): assert dpi > 0 self._dpi = dpi @property def visual_transform(self): """Transform mapping from visual local coordinate frame to scene coordinate frame.""" return self._visual_transform @visual_transform.setter def visual_transform(self, tr): self._visual_transform.transforms = tr @property def scene_transform(self): """Transform mapping from scene coordinate frame to document coordinate frame.""" return self._scene_transform @scene_transform.setter def scene_transform(self, tr): self._scene_transform.transforms = tr @property def document_transform(self): """Transform mapping from document coordinate frame to the framebuffer (physical pixel) coordinate frame.""" return self._document_transform @document_transform.setter def document_transform(self, tr): self._document_transform.transforms = tr @property def canvas_transform(self): """Transform mapping from canvas coordinate frame to framebuffer coordinate frame.""" return self._canvas_transform @canvas_transform.setter def canvas_transform(self, tr): self._canvas_transform.transforms = tr @property def framebuffer_transform(self): """Transform mapping from pixel coordinate frame to rendering coordinate frame.""" return self._framebuffer_transform @framebuffer_transform.setter def framebuffer_transform(self, tr): self._framebuffer_transform.transforms = tr def get_transform(self, map_from='visual', map_to='render'): """Return a transform mapping between any two coordinate systems. Parameters ---------- map_from : str The starting coordinate system to map from. Must be one of: visual, scene, document, canvas, framebuffer, or render. map_to : str The ending coordinate system to map to. Must be one of: visual, scene, document, canvas, framebuffer, or render. """ tr = ['visual', 'scene', 'document', 'canvas', 'framebuffer', 'render'] ifrom = tr.index(map_from) ito = tr.index(map_to) if ifrom < ito: trs = [getattr(self, '_' + t + '_transform') for t in tr[ifrom:ito]][::-1] else: trs = [getattr(self, '_' + t + '_transform').inverse for t in tr[ito:ifrom]] return self._cache.get(trs) @property def pixel_scale(self): tr = self._canvas_transform return (tr.map((1, 0)) - tr.map((0, 0)))[0] vispy-0.14.1/vispy/visuals/transforms/tests/0000755000175000017510000000000014507104251020475 5ustar nileshnileshvispy-0.14.1/vispy/visuals/transforms/tests/test_transforms.py0000644000175000017510000001532714507104216024315 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from numpy.testing import assert_allclose import pytest import vispy.visuals.transforms as tr from vispy.geometry import Rect from vispy.testing import run_tests_if_main NT = tr.NullTransform ST = tr.STTransform AT = tr.MatrixTransform RT = tr.MatrixTransform PT = tr.PolarTransform LT = tr.LogTransform CT = tr.ChainTransform def assert_chain_types(chain, types): assert list(map(type, chain.transforms)) == types def assert_chain_objects(chain1, chain2): assert chain1.transforms == chain2.transforms def test_multiplication(): n = NT() s = ST() a = AT() p = PT() log_trans = LT() c1 = CT([s, a, p]) assert c1 c2 = CT([s, a, s]) assert isinstance(n * n, NT) assert isinstance(n * s, ST) assert isinstance(s * s, ST) assert isinstance(a * s, AT) assert isinstance(a * a, AT) assert isinstance(s * a, AT) assert isinstance(n * p, PT) assert isinstance(s * p, CT) assert isinstance(a * p, CT) assert isinstance(p * a, CT) assert isinstance(p * s, CT) assert_chain_types(p * a, [PT, AT]) assert_chain_types(p * s, [PT, ST]) assert_chain_types(s * p, [ST, PT]) assert_chain_types(s * p * a, [ST, PT, AT]) assert_chain_types(s * a * p, [AT, PT]) assert_chain_types(p * s * a, [PT, ST, AT]) assert_chain_types(s * p * s, [ST, PT, ST]) assert_chain_types(s * a * p * s * a, [AT, PT, ST, AT]) assert_chain_types(c2 * a, [ST, AT, ST, AT]) assert_chain_types(p * log_trans * s, [PT, LT, ST]) def test_transform_chain(): # Make dummy classes for easier distinguishing the transforms class DummyTrans(tr.BaseTransform): glsl_map = "vec4 trans(vec4 pos) {return pos;}" glsl_imap = "vec4 trans(vec4 pos) {return pos;}" class TransA(DummyTrans): pass class TransB(DummyTrans): pass class TransC(DummyTrans): pass # Create test transforms a, b, c = TransA(), TransB(), TransC() # Test Chain creation assert tr.ChainTransform().transforms == [] assert tr.ChainTransform(a).transforms == [a] assert tr.ChainTransform(a, b).transforms == [a, b] assert tr.ChainTransform(a, b, c, a).transforms == [a, b, c, a] # Test composition by multiplication assert_chain_objects(a * b, tr.ChainTransform(a, b)) assert_chain_objects(a * b * c, tr.ChainTransform(a, b, c)) assert_chain_objects(a * b * c * a, tr.ChainTransform(a, b, c, a)) # Test adding/prepending to transform chain = tr.ChainTransform() chain.append(a) assert chain.transforms == [a] chain.append(b) assert chain.transforms == [a, b] chain.append(c) assert chain.transforms == [a, b, c] chain.prepend(b) assert chain.transforms == [b, a, b, c] chain.prepend(c) assert chain.transforms == [c, b, a, b, c] # Test simplifying t1 = tr.STTransform(scale=(2, 3)) t2 = tr.STTransform(translate=(3, 4)) t3 = tr.STTransform(translate=(3, 4)) # Create multiplied versions t123 = t1*t2*t3 t321 = t3*t2*t1 c123 = tr.ChainTransform(t1, t2, t3) c321 = tr.ChainTransform(t3, t2, t1) c123s = c123.simplified c321s = c321.simplified # assert isinstance(t123, tr.STTransform) # or the test is useless assert isinstance(t321, tr.STTransform) # or the test is useless assert isinstance(c123s, tr.ChainTransform) # or the test is useless assert isinstance(c321s, tr.ChainTransform) # or the test is useless # Test Mapping t1 = tr.STTransform(scale=(2, 3)) t2 = tr.STTransform(translate=(3, 4)) chain1 = tr.ChainTransform(t1, t2) chain2 = tr.ChainTransform(t2, t1) # assert chain1.transforms == [t1, t2] # or the test is useless assert chain2.transforms == [t2, t1] # or the test is useless # m12 = (t1*t2).map((1, 1)).tolist() m21 = (t2*t1).map((1, 1)).tolist() m12_ = chain1.map((1, 1)).tolist() m21_ = chain2.map((1, 1)).tolist() # # print(m12, m21, m12_, m21_) assert m12 != m21 assert m12 == m12_ assert m21 == m21_ # Test shader map t1 = tr.STTransform(scale=(2, 3)) t2 = tr.STTransform(translate=(3, 4)) chain = tr.ChainTransform(t1, t2) # funcs = chain.shader_map().dependencies() funcsi = chain.shader_imap().dependencies() # assert t1.shader_map() in funcs assert t2.shader_map() in funcs assert t1.shader_imap() in funcsi assert t2.shader_imap() in funcsi def test_map_rect(): r = Rect((2, 7), (13, 19)) r1 = ST(scale=(2, 2), translate=(-10, 10)).map(r) assert r1 == Rect((-6, 24), (26, 38)) def test_st_transform(): # Check that STTransform maps exactly like MatrixTransform pts = np.random.normal(size=(10, 4)) scale = (1, 7.5, -4e-8) translate = (1e6, 0.2, 0) st = tr.STTransform(scale=scale, translate=translate) at = tr.MatrixTransform() at.scale(scale) at.translate(translate) assert np.allclose(st.map(pts), at.map(pts)) assert np.allclose(st.inverse.map(pts), at.inverse.map(pts)) def test_st_mapping(): p1 = [[5., 7.], [23., 8.]] p2 = [[-1.3, -1.4], [1.1, 1.2]] t = tr.STTransform() t.set_mapping(p1, p2) assert np.allclose(t.map(p1)[:, :len(p2)], p2) def test_affine_mapping(): t = tr.MatrixTransform() p1 = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]]) # test pure translation p2 = p1 + 5.5 t.set_mapping(p1, p2) assert np.allclose(t.map(p1)[:, :p2.shape[1]], p2) # test pure scaling p2 = p1 * 5.5 t.set_mapping(p1, p2) assert np.allclose(t.map(p1)[:, :p2.shape[1]], p2) # test scale + translate p2 = (p1 * 5.5) + 3.5 t.set_mapping(p1, p2) assert np.allclose(t.map(p1)[:, :p2.shape[1]], p2) # test SRT p2 = np.array([[10, 5, 3], [10, 15, 3], [30, 5, 3], [10, 5, 3.5]]) t.set_mapping(p1, p2) assert np.allclose(t.map(p1)[:, :p2.shape[1]], p2) m = np.random.RandomState(0).normal(size=(4, 4)) transforms = [ NT(), ST(scale=(1e-4, 2e5), translate=(10, -6e9)), AT(m), RT(m), ] @pytest.mark.parametrize('trn', transforms) def test_inverse(trn): rng = np.random.RandomState(0) N = 20 x = rng.normal(size=(N, 3)) pw = rng.normal(size=(N, 3), scale=3) pos = x * 10 ** pw assert_allclose(pos, trn.inverse.map(trn.map(pos))[:, :3], atol=1e-7) # log transform only works on positive values # abs_pos = np.abs(pos) # tr = LT(base=(2, 4.5, 0)) # assert np.allclose(abs_pos, tr.inverse.map(tr.map(abs_pos))[:,:3]) run_tests_if_main() vispy-0.14.1/vispy/visuals/transforms/tests/__init__.py0000644000175000017510000000000014507104216022575 0ustar nileshnileshvispy-0.14.1/vispy/visuals/transforms/nonlinear.py0000644000175000017510000003037214507104216021700 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) 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 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'] = float(self._mag) fn['radii'] = (self._radii[0] / float(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 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 / np.where(dist != 0, dist, 1) # 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.14.1/vispy/visuals/transforms/linear.py0000644000175000017510000003651114507104216021166 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) 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 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._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) self._update_shaders() @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): return self._shader_map def shader_imap(self): 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=True): need_update = False if scale is not None and not np.all(scale == self._scale): self._scale[:] = scale need_update = True if translate is not None and not np.all(translate == self._translate): self._translate[:] = translate need_update = True if update and need_update: self._update_shaders() self.update() # inform listeners there has been a change def _update_shaders(self): self._shader_map['scale'] = self.scale self._shader_map['translate'] = self.translate self._shader_imap['scale'] = self.scale self._shader_imap['translate'] = self.translate 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_matrix(self): m = MatrixTransform() 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, update=True): """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. update : bool If False, then the update event is not emitted. 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, update=update) 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, MatrixTransform): return self.as_matrix() * tr else: return super(STTransform, self).__mul__(tr) def __rmul__(self, tr): if isinstance(tr, MatrixTransform): return tr * self.as_matrix() return super(STTransform, self).__rmul__(tr) def __repr__(self): return ("" % (self.scale, self.translate, id(self))) class MatrixTransform(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(MatrixTransform, 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(MatrixTransform, self).shader_map() fn['matrix'] = self.matrix # uniform mat4 return fn def shader_imap(self): fn = super(MatrixTransform, 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): # noqa """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, MatrixTransform) and not any(tr.matrix[:3, 3] != 0)): # don't multiply if the perspective column is used return MatrixTransform(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 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): # noqa """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) # 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 MatrixTransform does. However, an incorrect order of # operations in MatrixTransform 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 vispy-0.14.1/vispy/visuals/transforms/interactive.py0000644000175000017510000000532214507104216022225 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) 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 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.14.1/vispy/visuals/transforms/chain.py0000644000175000017510000002130314507104216020767 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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. Parameters ---------- 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__() self._transforms = [] self._simplified = None self._null_transform = NullTransform() nmap = self._null_transform.shader_map() # ChainTransform does not have shader maps self._shader_map = FunctionChain("transform_map_chain", [nmap]) self._shader_imap = FunctionChain("transform_imap_chain", [nmap]) # Set input transforms trs = [] for tr in transforms: if isinstance(tr, (tuple, list)): trs.extend(tr) else: trs.append(tr) self.transforms = trs @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 isinstance(tr, BaseTransform): tr = [tr] if not isinstance(tr, list): raise TypeError("Transform chain must be a list") # Avoid extra effort if we already have the correct chain if len(tr) == len(self._transforms): changed = False for i in range(len(tr)): if tr[i] is not self._transforms[i]: changed = True break if not changed: return for t in self._transforms: t.changed.disconnect(self._subtr_changed) self._transforms = tr for t in self._transforms: t.changed.connect(self._subtr_changed) self._rebuild_shaders() self.update() @property def simplified(self): """A simplified representation of the same transformation.""" if self._simplified is None: self._simplified = SimplifiedChainTransform(self) return self._simplified @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): return self._shader_map def shader_imap(self): return self._shader_imap def _rebuild_shaders(self): trs = self.transforms if len(trs) == 0: trs = [self._null_transform] self._shader_map.functions = [tr.shader_map() for tr in reversed(trs)] self._shader_imap.functions = [tr.shader_imap() for tr in trs] 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) tr.changed.connect(self._subtr_changed) self._rebuild_shaders() self.update() 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) tr.changed.connect(self._subtr_changed) self._rebuild_shaders() self.update() def _subtr_changed(self, ev): """One of the internal transforms changed; propagate the signal.""" self.update(ev) def __setitem__(self, index, tr): self._transforms[index].changed.disconnect(self._subtr_changed) self._transforms[index] = tr tr.changed.connect(self.subtr_changed) self._rebuild_shaders() 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)) def __del__(self): # remove all the children transforms from our callback, since we are # being deleted. (But we do *not* want to remove children from other # callbacks) for t in self._transforms: t.changed.disconnect(self._subtr_changed) self.changed.disconnect() class SimplifiedChainTransform(ChainTransform): def __init__(self, chain): ChainTransform.__init__(self) self._chain = chain chain.changed.connect(self.source_changed) self.source_changed(None) def source_changed(self, event): """Generate a simplified chain by joining adjacent transforms.""" # bail out early if the chain is empty transforms = self._chain.transforms[:] if len(transforms) == 0: self.transforms = [] return # If the change signal comes from a transform that already appears in # our simplified transform list, then there is no need to re-simplify. if event is not None: for source in event.sources[::-1]: if source in self.transforms: self.update(event) return # First flatten the chain by expanding all nested chains new_chain = [] while len(transforms) > 0: tr = transforms.pop(0) if isinstance(tr, ChainTransform) and not tr.dynamic: transforms = tr.transforms[:] + transforms else: new_chain.append(tr) # Now combine together all compatible adjacent transforms cont = True tr = new_chain while cont: new_tr = [tr[0]] cont = False for t2 in tr[1:]: t1 = new_tr[-1] pr = t1 * t2 if (not t1.dynamic and not t2.dynamic and not isinstance(pr, ChainTransform)): cont = True new_tr.pop() new_tr.append(pr) else: new_tr.append(t2) tr = new_tr self.transforms = tr vispy-0.14.1/vispy/visuals/transforms/base_transform.py0000644000175000017510000001647214507104216022725 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ API Issues to work out: - MatrixTransform 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._dynamic = False 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 @property def dynamic(self): """Boolean flag that indicates whether this transform is expected to change frequently. Transforms that are flagged as dynamic will not be collapsed in ``ChainTransform.simplified``. This allows changes to the transform to propagate through the chain without requiring the chain to be re-simplified. """ return self._dynamic @dynamic.setter def dynamic(self, d): self._dynamic = d 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, *args): """Called to inform any listeners that this transform has changed.""" self.changed(*args) 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)) def __del__(self): # we can remove ourselves from *all* events in this situation. self.changed.disconnect() class InverseTransform(BaseTransform): def __init__(self, transform): BaseTransform.__init__(self) self._inverse = transform self.map = transform.imap self.imap = transform.map @property def Linear(self): return self._inverse.Linear @property def Orthogonal(self): return self._inverse.Orthogonal @property def NonScaling(self): return self._inverse.NonScaling @property def Isometric(self): return self._inverse.Isometric @property def shader_map(self): return self._inverse.shader_imap @property def shader_imap(self): return self._inverse.shader_map def __repr__(self): return ("" % repr(self._inverse)) # import here to avoid import cycle; needed for BaseTransform.__mul__. from .chain import ChainTransform # noqa vispy-0.14.1/vispy/visuals/transforms/_util.py0000644000175000017510000001271314507104216021026 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import functools import numpy as np from ...util import logger from functools import wraps def arg_to_array(func): """ Decorator to convert argument to array. Parameters ---------- func : function The function to decorate. Returns ------- func : function The decorated function. """ @wraps(func) 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 def arg_to_vec4(func): """ 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. """ @functools.wraps(func) def wrapper(self_, arg, *args, **kwargs): 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) return wrapper 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.14.1/vispy/visuals/transforms/__init__.py0000644000175000017510000000231614507104216021447 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 MatrixTransform) # 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', 'MatrixTransform', '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.14.1/vispy/visuals/text/0000755000175000017510000000000014507104251016121 5ustar nileshnileshvispy-0.14.1/vispy/visuals/text/text.py0000644000175000017510000006124514507104216017470 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 import sys from ._sdf_gpu import SDFRendererGPU from ._sdf_cpu import _calc_distance_field from ...gloo import (TextureAtlas, IndexBuffer, VertexBuffer) from ...gloo import context from ...gloo.wrappers import _check_valid from ...util.fonts import _load_glyph from ..transforms import STTransform from ...color import ColorArray from ..visual import Visual from ...io import load_spatial_filters class TextureFont(object): """Gather a set of glyphs relative to a given font name and size This currently stores characters in a `TextureAtlas` object which uses a 2D RGB texture to store unsigned 8-bit integer data. In the future this could be changed to a ``GL_R8`` texture instead of RGB when OpenGL ES 3.0+ is standard. Since VisPy tries to stay compatible with OpenGL ES 2.0 we are using an ``RGB`` texture. Using a single channel texture should improve performance by requiring less data to be sent to the GPU and to remote backends (jupyter notebook). 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(dtype=np.uint8) self._atlas.wrapping = 'clamp_to_edge' self._kernel, _ = load_spatial_filters() 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 # This is also used in SDFRendererCPU, so changing this needs to # propagate at least 2 other places. 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, str) 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, str) 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""" # XXX: should store a font-manager on each context, # or let TextureFont use a TextureAtlas for each context def __init__(self, method='cpu'): self._fonts = {} if not isinstance(method, str) or \ method not in ('cpu', 'gpu'): raise ValueError('method must be "cpu" or "gpu", got %s (%s)' % (method, type(method))) if method == 'cpu': self._renderer = SDFRendererCPU() else: # method == 'gpu': self._renderer = SDFRendererGPU() 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 _VERTEX_SHADER = """ attribute float a_rotation; // rotation in rad attribute vec2 a_position; // in point units attribute vec2 a_texcoord; attribute vec3 a_pos; // anchor position varying vec2 v_texcoord; varying vec4 v_color; void main(void) { // Eventually "rot" should be handled by SRTTransform or so... mat4 rot = mat4(cos(a_rotation), -sin(a_rotation), 0, 0, sin(a_rotation), cos(a_rotation), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); vec4 pos = $transform(vec4(a_pos, 1.0)) + vec4($text_scale(rot * vec4(a_position, 0.0, 1.0)).xyz, 0.0); gl_Position = pos; v_texcoord = a_texcoord; v_color = $color; } """ _FRAGMENT_SHADER = """ // Extensions for WebGL #extension GL_OES_standard_derivatives : enable #extension GL_OES_element_index_uint : enable #include "misc/spatial-filters.frag" // Adapted from glumpy with permission const float M_SQRT1_2 = 0.707106781186547524400844362104849039; uniform sampler2D u_font_atlas; uniform vec2 u_font_atlas_shape; varying vec4 v_color; uniform float u_npix; varying vec2 v_texcoord; const float center = 0.5; 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) { vec2 uv = v_texcoord.xy; vec4 rgb; // Use interpolation at high font sizes if(u_npix >= 50.0) rgb = CatRom2D(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; } if (alpha <= 0) discard; gl_FragColor = vec4(v_color.rgb, v_color.a * alpha); } """ def _text_to_vbo(text, font, anchor_x, anchor_y, lowres_size): """Convert text characters to VBO""" # Necessary to flush commands before requesting current viewport because # There may be a set_viewport command waiting in the queue. # TODO: would be nicer if each canvas just remembers and manages its own # viewport, rather than relying on the context for this. canvas = context.get_current_canvas() canvas.context.flush_commands() text_vtype = np.dtype([('a_position', np.float32, 2), ('a_texcoord', np.float32, 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! # 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) # Get/set the fonts whitespace length and line height (size of this ok?) glyph = font[' '] spacewidth = glyph['advance'] * ratio lineheight = height * 1.5 # Added escape sequences characters: {unicode:offset,...} # ord('\a') = 7 # ord('\b') = 8 # ord('\f') = 12 # ord('\n') = 10 => linebreak # ord('\r') = 13 # ord('\t') = 9 => tab, set equal 4 whitespaces? # ord('\v') = 11 => vertical tab, set equal 4 linebreaks? # If text coordinate offset > 0 -> it applies to x-direction # If text coordinate offset < 0 -> it applies to y-direction esc_seq = {7: 0, 8: 0, 9: -4, 10: 1, 11: 4, 12: 0, 13: 0} # Keep track of y_offset to set lines at right position y_offset = 0 # When a line break occur, record the vertices index value vi_marker = 0 ii_offset = 0 # Offset since certain characters won't be drawn # The running tracker of characters vertex index vi = 0 orig_viewport = canvas.context.get_viewport() for ii, char in enumerate(text): if ord(char) in esc_seq: if esc_seq[ord(char)] < 0: # Add offset in x-direction x_off += abs(esc_seq[ord(char)]) * spacewidth width += abs(esc_seq[ord(char)]) * spacewidth elif esc_seq[ord(char)] > 0: # Add offset in y-direction and reset things in x-direction dx = dy = 0 if anchor_x == 'right': dx = -width elif anchor_x == 'center': dx = -width / 2. vertices['a_position'][vi_marker:vi+4] += (dx, dy) vi_marker = vi+4 ii_offset -= 1 # Reset variables that affects x-direction positioning x_off = -slop width = 0 # Add offset in y-direction y_offset += esc_seq[ord(char)] * lineheight else: # For ordinary characters, normal procedure glyph = font[char] kerning = glyph['kerning'].get(prev, 0.) * ratio x0 = x_off + glyph['offset'][0] * ratio + kerning y0 = glyph['offset'][1] * ratio + slop - y_offset 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 + ii_offset) * 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 if orig_viewport is not None: canvas.context.set_viewport(*orig_viewport) dx = dy = 0 if anchor_y == 'top': dy = -descender elif anchor_y in ('center', 'middle'): dy = (-descender - ascender) / 2 elif anchor_y == 'bottom': dy = -ascender if anchor_x == 'right': dx = -width elif anchor_x == 'center': dx = -width / 2. # If any linebreaks occured in text, we only want to translate characters # in the last line in text (those after the vi_marker) vertices['a_position'][0:vi_marker] += (0, dy) vertices['a_position'][vi_marker:] += (dx, dy) vertices['a_position'] /= lowres_size return vertices class TextVisual(Visual): """Visual that displays text Parameters ---------- text : str | list of str Text to display. Can also be a list of strings. Note: support for list of str might be removed soon in favor of text collections. 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 | list of tuple Position (x, y) or (x, y, z) of the text. Can also be a list of tuple if `text` is a list. rotation : float Rotation (in degrees) of the text clockwise. anchor_x : str Horizontal text anchor. anchor_y : str Vertical text anchor. method : str Rendering method for text characters. Either 'cpu' (default) or 'gpu'. The 'cpu' method should perform better on remote backends. The 'gpu' method should produce higher quality results. font_manager : object | None Font manager to use (can be shared if the GLContext is shared). depth_test : bool Whether to apply depth testing. Default False. If False, the text behaves like an overlay that does not get hidden behind other visuals in the scene. """ _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } def __init__(self, text=None, color='black', bold=False, italic=False, face='OpenSans', font_size=12, pos=[0, 0, 0], rotation=0., anchor_x='center', anchor_y='center', method='cpu', font_manager=None, depth_test=False): Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) # 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(method=method) self._face = face self._bold = bold self._italic = italic self._update_font() self._vertices = None self._color_vbo = 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() self._draw_mode = 'triangles' self.set_gl_state(blend=True, depth_test=depth_test, cull_face=False, blend_func=('src_alpha', 'one_minus_src_alpha')) self.freeze() @property def text(self): """The text string""" return self._text @text.setter def text(self, text): if isinstance(text, list): assert all(isinstance(t, str) for t in text) if text is None: text = [] self._text = text self._vertices = None self._pos_changed = True # need to update this as well self._color_changed = True self.update() @property def anchors(self): return self._anchors @anchors.setter def anchors(self, a): self._anchors = a self._vertices = None self._pos_changed = True self.update() @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)) self.update() @property def color(self): """The color of the text""" return self._color @color.setter def color(self, color): self._color = ColorArray(color) self._color_changed = True self.update() @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 = np.asarray(rotation) * np.pi / 180. self._pos_changed = True self.update() @property def pos(self): """The position of the text anchor in the local coordinate frame""" return self._pos @pos.setter def pos(self, pos): pos = np.atleast_2d(pos).astype(np.float32) if pos.shape[1] == 2: pos = np.concatenate((pos, np.zeros((pos.shape[0], 1), np.float32)), axis=1) elif pos.shape[1] != 3: raise ValueError('pos must have 2 or 3 elements') elif pos.shape[0] == 0: raise ValueError('at least one position must be given') self._pos = pos self._pos_changed = True self.update() def _prepare_draw(self, view): # attributes / uniforms are not available until program is built if len(self.text) == 0: return False if self._vertices is None: text = self.text if isinstance(text, str): text = [text] n_char = sum(len(t) for t in text) # we delay creating vertices because it requires a context, # which may or may not exist when the object is initialized self._vertices = np.concatenate([ _text_to_vbo(t, self._font, self._anchors[0], self._anchors[1], self._font._lowres_size) for t in text]) self._vertices = VertexBuffer(self._vertices) idx = (np.array([0, 1, 2, 0, 2, 3], np.uint32) + np.arange(0, 4*n_char, 4, dtype=np.uint32)[:, np.newaxis]) self._index_buffer = IndexBuffer(idx.ravel()) self.shared_program.bind(self._vertices) # This is necessary to reset the GL drawing state after generating # SDF textures. A better way would be to enable the state to be # pushed/popped by the context. self._configure_gl_state() if self._pos_changed: # now we promote pos to the proper shape (attribute) text = self.text if not isinstance(text, str): repeats = [4 * len(t) for t in text] text = ''.join(text) else: repeats = [4 * len(text)] n_text = len(repeats) pos = self.pos # Rotation _rot = self._rotation if isinstance(_rot, (int, float)): _rot = np.full((pos.shape[0],), self._rotation) _rot = np.asarray(_rot) if _rot.shape[0] < n_text: _rep = [1] * (len(_rot) - 1) + [n_text - len(_rot) + 1] _rot = np.repeat(_rot, _rep, axis=0) _rot = np.repeat(_rot[:n_text], repeats, axis=0) self.shared_program['a_rotation'] = _rot.astype(np.float32) # Position if pos.shape[0] < n_text: _rep = [1] * (len(pos) - 1) + [n_text - len(pos) + 1] pos = np.repeat(pos, _rep, axis=0) pos = np.repeat(pos[:n_text], repeats, axis=0) assert pos.shape[0] == self._vertices.size == len(_rot) self.shared_program['a_pos'] = pos self._pos_changed = False if self._color_changed: # now we promote color to the proper shape (varying) text = self.text if not isinstance(text, str): repeats = [4 * len(t) for t in text] text = ''.join(text) else: repeats = [4 * len(text)] n_text = len(repeats) color = self.color.rgba if color.shape[0] < n_text: color = np.repeat(color, [1]*(len(color)-1) + [n_text-len(color)+1], axis=0) color = np.repeat(color[:n_text], repeats, axis=0) assert color.shape[0] == self._vertices.size self._color_vbo = VertexBuffer(color) self.shared_program.vert['color'] = self._color_vbo self._color_changed = False transforms = self.transforms n_pix = (self._font_size / 72.) * transforms.dpi # logical pix tr = transforms.get_transform('document', 'render') px_scale = (tr.map((1, 0)) - tr.map((0, 1)))[:2] self._text_scale.scale = px_scale * n_pix self.shared_program.vert['text_scale'] = self._text_scale self.shared_program['u_npix'] = n_pix self.shared_program['u_kernel'] = self._font._kernel self.shared_program['u_color'] = self._color.rgba self.shared_program['u_font_atlas'] = self._font._atlas self.shared_program['u_font_atlas_shape'] = self._font._atlas.shape[:2] def _prepare_transforms(self, view): self._pos_changed = True # Note that we access `view_program` instead of `shared_program` # because we do not want this function assigned to other views. tr = view.transforms.get_transform() view.view_program.vert['transform'] = tr # .simplified() def _compute_bounds(self, axis, view): return self._pos[:, axis].min(), self._pos[:, axis].max() @property def face(self): return self._face @face.setter def face(self, value): self._face = value self._update_font() @property def bold(self): return self._bold @bold.setter def bold(self, value): self._bold = value self._update_font() @property def italic(self): return self._italic @italic.setter def italic(self, value): self._italic = value self._update_font() def _update_font(self): self._font = self._font_manager.get_font(self._face, self._bold, self._italic) self.update() class SDFRendererCPU(object): """Render SDFs using the CPU.""" # This should probably live in _sdf_cpu.pyx, but doing so makes # debugging substantially more annoying def render_to_texture(self, data, texture, offset, size): sdf = (data / 255).astype(np.float32) # from ubyte -> float h, w = sdf.shape tex_w, tex_h = size _calc_distance_field(sdf, w, h, 32) # This tweaking gets us a result more similar to the GPU SDFs, # for which the text rendering code was optimized sdf = 2 * sdf - 1. sdf = np.sign(sdf) * np.abs(sdf) ** 0.75 / 2. + 0.5 # Downsample using NumPy (because we can't guarantee SciPy) xp = (np.arange(w) + 0.5) / float(w) x = (np.arange(tex_w) + 0.5) / float(tex_w) bitmap = np.array([np.interp(x, xp, ss) for ss in sdf]) xp = (np.arange(h) + 0.5) / float(h) x = (np.arange(tex_h) + 0.5) / float(tex_h) bitmap = np.array([np.interp(x, xp, ss) for ss in bitmap.T]).T assert bitmap.shape[::-1] == size # convert to uint8 bitmap = (bitmap * 255).astype(np.uint8) # convert single channel to RGB by repeating bitmap = np.tile(bitmap[..., np.newaxis], (1, 1, 3)) texture[offset[1]:offset[1] + size[1], offset[0]:offset[0] + size[0], :] = bitmap vispy-0.14.1/vispy/visuals/text/_sdf_gpu.py0000644000175000017510000002436014507104216020267 0ustar nileshnilesh# -*- 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 ...gloo import (Program, FrameBuffer, VertexBuffer, Texture2D, set_viewport, set_state) 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 SDFRendererGPU(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', np.float32, 2), ('a_texcoord', np.float32, 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.14.1/vispy/visuals/text/_sdf_cpu.pyx0000644000175000017510000000731714507104216020456 0ustar nileshnilesh# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False # A Cython implementation of the "eight-points signed sequential Euclidean # distance transform algorithm" (8SSEDT) import numpy as np cimport numpy as np from libc.math cimport sqrt cimport cython __all__ = ['_get_distance_field'] dtype = np.float32 dtype_c = np.complex64 ctypedef np.float32_t DTYPE_t ctypedef np.complex64_t DTYPE_ct cdef DTYPE_ct MAX_VAL = (1e6 + 1e6j) def _calc_distance_field(np.ndarray[DTYPE_t, ndim=2] pixels, int w, int h, DTYPE_t sp_f): # initialize grids cdef np.ndarray[DTYPE_ct, ndim=2] g0_arr = np.zeros((h, w), dtype_c) cdef np.ndarray[DTYPE_ct, ndim=2] g1_arr = np.zeros((h, w), dtype_c) cdef DTYPE_ct[:, ::1] g0 = g0_arr cdef DTYPE_ct[:, ::1] g1 = g1_arr cdef DTYPE_t[:, :] pixels_view = pixels cdef Py_ssize_t y, x for y in range(h): g0[y, 0] = MAX_VAL g0[y, w-1] = MAX_VAL g1[y, 0] = MAX_VAL g1[y, w-1] = MAX_VAL for x in range(1, w-1): if pixels_view[y, x] > 0: g0[y, x] = MAX_VAL if pixels_view[y, x] < 1: g1[y, x] = MAX_VAL for x in range(w): g0[0, x] = MAX_VAL g0[h-1, x] = MAX_VAL g1[0, x] = MAX_VAL g1[h-1, x] = MAX_VAL # Propagate grids _propagate(g0) _propagate(g1) # Subtracting and normalizing cdef DTYPE_t r_sp_f_2 = 1. / (sp_f * 2.) for y in range(1, h-1): for x in range(1, w-1): pixels_view[y, x] = sqrt(dist(g0[y, x])) - sqrt(dist(g1[y, x])) if pixels_view[y, x] < 0: pixels_view[y, x] = (pixels_view[y, x] + sp_f) * r_sp_f_2 else: pixels_view[y, x] = 0.5 + pixels_view[y, x] * r_sp_f_2 pixels_view[y, x] = max(min(pixels_view[y, x], 1), 0) cdef inline Py_ssize_t compare(DTYPE_ct *cell, DTYPE_ct xy, DTYPE_t *current) noexcept nogil: cdef DTYPE_t val = dist(xy) if val < current[0]: cell[0] = xy current[0] = val cdef DTYPE_t dist(DTYPE_ct val) noexcept nogil: return val.real*val.real + val.imag*val.imag cdef void _propagate(DTYPE_ct[:, :] grid) noexcept nogil: cdef Py_ssize_t height = grid.shape[0] cdef Py_ssize_t width = grid.shape[1] cdef Py_ssize_t y, x cdef DTYPE_t current cdef DTYPE_ct a0, a1, a2, a3 a0 = -1 a1 = -1j a2 = -1 - 1j a3 = 1 - 1j cdef DTYPE_ct b0=1 cdef DTYPE_ct c0=1, c1=1j, c2=-1+1j, c3=1+1j cdef DTYPE_ct d0=-1 height -= 1 width -= 1 for y in range(1, height): for x in range(1, width): current = dist(grid[y, x]) # (-1, +0), (+0, -1), (-1, -1), (+1, -1) compare(&grid[y, x], grid[y, x-1] + a0, ¤t) compare(&grid[y, x], grid[y-1, x] + a1, ¤t) compare(&grid[y, x], grid[y-1, x-1] + a2, ¤t) compare(&grid[y, x], grid[y-1, x+1] + a3, ¤t) for x in range(width - 1, 0, -1): current = dist(grid[y, x]) # (+1, +0) compare(&grid[y, x], grid[y, x+1] + b0, ¤t) for y in range(height - 1, 0, -1): for x in range(width - 1, 0, -1): current = dist(grid[y, x]) # (+1, +0), (+0, +1), (-1, +1), (+1, +1) compare(&grid[y, x], grid[y, x+1] + c0, ¤t) compare(&grid[y, x], grid[y+1, x] + c1, ¤t) compare(&grid[y, x], grid[y+1, x-1] + c2, ¤t) compare(&grid[y, x], grid[y+1, x+1] + c3, ¤t) for x in range(1, width): current = dist(grid[y, x]) # (-1, +0) compare(&grid[y, x], grid[y, x-1] + d0, ¤t) vispy-0.14.1/vispy/visuals/text/__init__.py0000644000175000017510000000054514507104216020237 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/visuals/tests/0000755000175000017510000000000014507104251016277 5ustar nileshnileshvispy-0.14.1/vispy/visuals/tests/test_windbarb.py0000644000175000017510000000211614507104216021501 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from vispy.scene.visuals import Windbarb from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved length = 15. gridx = np.arange(length, 376, length * 2, dtype=np.float32) gridy = np.ones_like(gridx) * length grid = np.stack((gridx, gridy), axis=-1) origin = (length, length) vectors = (grid - origin).astype(np.float32) vectors[:] /= length // 2 vectors[:, 1] *= -1 @requires_application() def test_windbarb_draw(): """Test drawing arrows without transforms""" with TestingCanvas(size=(250, 33), bgcolor='white') as c: Windbarb(pos=grid, wind=vectors, trig=False, edge_color='black', face_color='black', size=length, parent=c.scene) assert_image_approved(c.render(), 'visuals/windbarb.png') run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_volume.py0000644000175000017510000004225214507104216021225 0ustar nileshnilesh# -*- coding: utf-8 -*- import pytest import numpy as np from vispy import scene from vispy.testing import (TestingCanvas, requires_application, run_tests_if_main, requires_pyopengl, raises) from vispy.testing.image_tester import assert_image_approved, downsample from vispy.testing.rendered_array_tester import compare_render, max_for_dtype @requires_pyopengl() def test_volume(): vol = np.zeros((20, 20, 20), 'float32') vol[8:16, 8:16, :] = 1.0 # Create V = scene.visuals.Volume(vol) assert V.clim == (0, 1) assert V.method == 'mip' assert V.interpolation == 'linear' # Set wrong data with raises(ValueError): V.set_data(np.zeros((20, 20), 'float32')) # Clim V.set_data(vol, (0.5, 0.8)) assert V.clim == (0.5, 0.8) with raises(ValueError): V.set_data((0.5, 0.8, 1.0)) # Method V.method = 'iso' assert V.method == 'iso' # Interpolation V.interpolation = 'nearest' assert V.interpolation == 'nearest' # Step size V.relative_step_size = 1.1 assert V.relative_step_size == 1.1 # Disallow 0 step size to avoid GPU stalling with raises(ValueError): V.relative_step_size = 0 @requires_pyopengl() def test_volume_bounds(): vol = np.zeros((20, 30, 40), 'float32') vol[8:16, 8:16, :] = 1.0 # Create V = scene.visuals.Volume(vol) assert V._compute_bounds(0, V) == (0, 40) # x assert V._compute_bounds(1, V) == (0, 30) # y assert V._compute_bounds(2, V) == (0, 20) # z @requires_pyopengl() @requires_application() def test_volume_draw(): with TestingCanvas(bgcolor='k', size=(100, 100)) as c: v = c.central_widget.add_view() v.camera = 'turntable' v.camera.fov = 70 # Create np.random.seed(2376) vol = np.random.normal(size=(20, 20, 20), loc=0.5, scale=0.2) vol[8:16, 8:16, :] += 1.0 scene.visuals.Volume(vol, parent=v.scene) # noqa v.camera.set_range() assert_image_approved(c.render(), 'visuals/volume.png') @requires_pyopengl() @requires_application() @pytest.mark.parametrize('clim_on_init', [False, True]) @pytest.mark.parametrize('texture_format', [None, '__dtype__', 'auto']) @pytest.mark.parametrize('input_dtype', [np.uint8, np.uint16, np.float32, np.float64]) def test_volume_clims_and_gamma(texture_format, input_dtype, clim_on_init): """Test volume visual with clims and gamma on shader. Test is parameterized based on ``texture_format`` and should produce relatively the same results for each format. Currently just using np.ones since the angle of view made more complicated samples challenging, but this confirms gamma and clims works in the shader. The VolumeVisual defaults to the "grays" colormap so although we compare data using RGBA arrays, each R/G/B channel should be the same. """ size = (40, 40) if texture_format == '__dtype__': texture_format = input_dtype np.random.seed(0) # make tests the same every time data = _make_test_data(size[:1] * 3, input_dtype) clim = (0, 1) new_clim = (0.3, 0.8) max = max_for_dtype(input_dtype) if max != 1: clim = (clim[0] * max, clim[1] * max) new_clim = (new_clim[0] * max, new_clim[1] * max) kwargs = {} if clim_on_init: kwargs['clim'] = clim with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) volume = scene.visuals.Volume( data, interpolation='nearest', parent=v.scene, method='mip', texture_format=texture_format, **kwargs ) v.camera = 'arcball' v.camera.fov = 0 v.camera.scale_factor = 40.0 v.camera.center = (19.5, 19.5, 19.5) rendered = c.render() _dtype = rendered.dtype shape_ratio = rendered.shape[0] // data.shape[0] rendered1 = downsample(rendered, shape_ratio, axis=(0, 1)).astype(_dtype) predicted = data.max(axis=1) compare_render(predicted, rendered1) # adjust contrast limits volume.clim = new_clim rendered2 = downsample(c.render(), shape_ratio, axis=(0, 1)).astype(_dtype) scaled_data = (np.clip(data, new_clim[0], new_clim[1]) - new_clim[0]) / (new_clim[1] - new_clim[0]) predicted = scaled_data.max(axis=1) compare_render(predicted, rendered2, previous_render=rendered) # adjust gamma volume.gamma = 2 rendered3 = downsample(c.render(), shape_ratio, axis=(0, 1)).astype(_dtype) predicted = (scaled_data ** 2).max(axis=1) compare_render(predicted, rendered3, previous_render=rendered2) @requires_pyopengl() @requires_application() @pytest.mark.parametrize('method_name', scene.visuals.Volume._rendering_methods.keys()) def test_all_render_methods(method_name): """Test that render methods don't produce any errors.""" size = (40, 40) np.random.seed(0) # make tests the same every time data = _make_test_data(size[:1] * 3, np.float32) # modify the data for 'minip' method so that there is at least one segment # of the volume with no 'empty'/zero space data[:, :, 40 // 3: 2 * 40 // 3] = 1.0 clim = (0, 1) kwargs = {} with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) volume = scene.visuals.Volume( data, interpolation='nearest', clim=clim, parent=v.scene, method=method_name, **kwargs ) v.camera = 'arcball' v.camera.fov = 0 v.camera.scale_factor = 40.0 v.camera.center = (19.5, 19.5, 19.5) assert volume.method == method_name rendered = c.render()[..., :3] # not all black assert rendered.sum() != 0 # not all white assert rendered.sum() != 255 * rendered.size @requires_pyopengl() @requires_application() @pytest.mark.parametrize('texture_format', [None, 'auto']) def test_equal_clims(texture_format): """Test that equal clims produce a min cmap value.""" size = (40, 40) np.random.seed(0) # make tests the same every time data = _make_test_data(size[:1] * 3, np.float32) with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) scene.visuals.Volume( data, interpolation='nearest', clim=(128.0, 128.0), # equal clims cmap='viridis', # something with a non-black min value parent=v.scene, method='mip', texture_format=texture_format, ) v.camera = 'arcball' v.camera.fov = 0 v.camera.scale_factor = 40.0 v.camera.center = (19.5, 19.5, 19.5) rendered = c.render()[..., :3] # not all black assert rendered.sum() != 0 # not all white assert rendered.sum() != 255 * rendered.size # should be all the same value r_unique = np.unique(rendered[..., 0]) g_unique = np.unique(rendered[..., 1]) b_unique = np.unique(rendered[..., 2]) assert r_unique.size == 1 assert g_unique.size == 1 assert b_unique.size == 1 def _make_test_data(shape, input_dtype): one_third = shape[0] // 3 two_third = 2 * one_third data = np.zeros(shape, dtype=np.float64) # 0.00 | 1.00 | 0.00 # 0.50 | 0.00 | 0.25 # 0.00 | 0.00 | 0.00 data[:, :one_third, one_third:two_third] = 1.0 data[:, one_third:two_third, :one_third] = 0.5 data[:, one_third:two_third, two_third:] = 0.25 max_val = max_for_dtype(input_dtype) if max_val != 1: data *= max_val data = data.astype(input_dtype) return data @requires_pyopengl() def test_set_data_does_not_change_input(): # Create volume V = scene.visuals.Volume(np.zeros((20, 20, 20), dtype=np.float32)) # calling Volume.set_data() should NOT alter the values of the input array # regardless of data type vol = np.random.randint(0, 200, (20, 20, 20)) for dtype in ['uint8', 'int16', 'uint16', 'float32', 'float64']: vol_copy = np.array(vol, dtype=dtype, copy=True) # setting clim so that normalization would otherwise change the data V.set_data(vol_copy, clim=(0, 200)) assert np.allclose(vol, vol_copy) # dtype has to be the same as the one used to init the texture, or it will # be first coerced to the same dtype as the init vol2 = np.array(vol, dtype=np.float32, copy=True) assert np.allclose(vol, vol2) # we explicitly create a copy when data would be altered by the texture, # no matter what the user asks, so the data outside should never change V.set_data(vol2, clim=(0, 200), copy=False) assert np.allclose(vol, vol2) @requires_pyopengl() def test_set_data_changes_shape(): dtype = np.float32 # Create initial volume V = scene.visuals.Volume(np.zeros((20, 20, 20), dtype=dtype)) # Sending new three dimensional data of different shape should alter volume shape vol = np.zeros((25, 25, 10), dtype=dtype) V.set_data(vol) assert V._vol_shape == (25, 25, 10) # Sending data of dimension other than 3 should raise a ValueError vol2 = np.zeros((20, 20), dtype=dtype) with pytest.raises(ValueError): V.set_data(vol2) vol2 = np.zeros((20, 20, 20, 20), dtype=dtype) with pytest.raises(ValueError): V.set_data(vol2) @requires_pyopengl() @requires_application() def test_changing_cmap(): """Test that changing colormaps updates the display.""" size = (40, 40) np.random.seed(0) # make tests the same every time data = _make_test_data(size[:1] * 3, np.float32) cmap = 'grays' test_cmaps = ('reds', 'greens', 'blues') clim = (0, 1) kwargs = {} with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) volume = scene.visuals.Volume( data, interpolation='nearest', clim=clim, cmap=cmap, parent=v.scene, **kwargs ) v.camera = 'arcball' v.camera.fov = 0 v.camera.scale_factor = 40.0 # render with grays colormap grays = c.render() # update cmap, compare rendered array with the grays cmap render for cmap in test_cmaps: volume.cmap = cmap current_cmap = c.render() with pytest.raises(AssertionError): np.testing.assert_allclose(grays, current_cmap) @requires_pyopengl() @requires_application() def test_plane_depth(): with TestingCanvas(size=(80, 80)) as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' v.camera.fov = 0 v.camera.center = (40, 40, 40) v.camera.scale_factor = 80.0 # two planes at 45 degrees relative to the camera. If depth is set correctly, we should see one half # of the screen red and the other half white scene.visuals.Volume( np.ones((80, 80, 80), dtype=np.uint8), interpolation="nearest", clim=(0, 1), cmap="grays", raycasting_mode="plane", plane_normal=(0, 1, 1), parent=v.scene, ) scene.visuals.Volume( np.ones((80, 80, 80), dtype=np.uint8), interpolation="nearest", clim=(0, 1), cmap="reds", raycasting_mode="plane", plane_normal=(0, 1, -1), parent=v.scene, ) # render with grays colormap rendered = c.render() left = rendered[40, 20] right = rendered[40, 60] assert np.array_equal(left, [255, 0, 0, 255]) assert np.array_equal(right, [255, 255, 255, 255]) @requires_pyopengl() @requires_application() def test_volume_depth(): """Check that depth setting is properly performed for the volume visual Render a volume with a blue ball in front of a red plane in front of a blue plane, checking that the output image contains both red and blue pixels. """ # A blue strip behind a red strip # If depth is set correctly, we should see only red pixels # the screen blue_vol = np.zeros((80, 80, 80), dtype=np.uint8) blue_vol[:, -1, :] = 1 # back plane blue blue_vol[30:50, 30:50, 30:50] = 1 # blue in center red_vol = np.zeros((80, 80, 80), dtype=np.uint8) red_vol[:, -5, :] = 1 # red plane in front of blue plane with TestingCanvas(size=(80, 80)) as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' v.camera.fov = 0 v.camera.center = (40, 40, 40) v.camera.scale_factor = 80.0 scene.visuals.Volume( red_vol, interpolation="nearest", clim=(0, 1), cmap="reds", parent=v.scene, ) scene.visuals.Volume( blue_vol, interpolation="nearest", clim=(0, 1), cmap="blues", parent=v.scene, ) # render rendered = c.render() reds = np.sum(rendered[:, :, 0]) greens = np.sum(rendered[:, :, 1]) blues = np.sum(rendered[:, :, 2]) assert reds > 0 np.testing.assert_allclose(greens, 0) assert blues > 0 @requires_pyopengl() @requires_application() def test_mip_cutoff(): """ Ensure fragments are properly discarded based on the mip_cutoff for the mip and attenuated_mip rendering methods """ with TestingCanvas(size=(80, 80)) as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' v.camera.fov = 0 v.camera.center = (40, 40, 40) v.camera.scale_factor = 80.0 vol = scene.visuals.Volume( np.ones((80, 80, 80), dtype=np.uint8), interpolation="nearest", clim=(0, 1), cmap="grays", parent=v.scene, ) # we should see white rendered = c.render() assert np.array_equal(rendered[40, 40], [255, 255, 255, 255]) vol.mip_cutoff = 10 # we should see black rendered = c.render() assert np.array_equal(rendered[40, 40], [0, 0, 0, 255]) # repeat for attenuated_mip vol.method = 'attenuated_mip' vol.mip_cutoff = None # we should see white rendered = c.render() assert np.array_equal(rendered[40, 40], [255, 255, 255, 255]) vol.mip_cutoff = 10 # we should see black rendered = c.render() assert np.array_equal(rendered[40, 40], [0, 0, 0, 255]) @requires_pyopengl() @requires_application() def test_minip_cutoff(): """ Ensure fragments are properly discarded based on the minip_cutoff for the minip rendering method """ with TestingCanvas(size=(80, 80)) as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' v.camera.fov = 0 v.camera.center = (40, 40, 40) v.camera.scale_factor = 120.0 # just surface of the cube is ones, but it should win over the twos inside data = np.ones((80, 80, 80), dtype=np.uint8) data[1:-1, 1:-1, 1:-1] = 2 vol = scene.visuals.Volume( data, interpolation="nearest", method='minip', clim=(0, 2), cmap="grays", parent=v.scene, ) # we should see gray (half of cmap) rendered = c.render() assert np.array_equal(rendered[40, 40], [128, 128, 128, 255]) # discard fragments above -10 (everything) vol.minip_cutoff = -10 # we should see black rendered = c.render() assert np.array_equal(rendered[40, 40], [0, 0, 0, 255]) @requires_pyopengl() @requires_application() def test_volume_set_data_different_dtype(): size = (80, 80) data = np.array([[[0, 127]]], dtype=np.int8) left = (40, 10) right = (40, 70) white = (255, 255, 255, 255) black = (0, 0, 0, 255) with TestingCanvas(size=size[::-1], bgcolor="w") as c: view = c.central_widget.add_view() view.camera = 'arcball' view.camera.fov = 0 view.camera.center = 0.5, 0, 0 view.camera.scale_factor = 2 volume = scene.visuals.Volume( data, cmap='grays', clim=[0, 127], parent=view.scene ) render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], white) # same data as float should change nothing volume.set_data(data.astype(np.float32)) render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], white) # something inverted, different dtype new_data = np.array([[[127, 0]]], dtype=np.float16) volume.set_data(new_data) render = c.render() assert np.allclose(render[left], white) assert np.allclose(render[right], black) # out of bounds should clip (2000 > 127) new_data = np.array([[[0, 2000]]], dtype=np.float64) volume.set_data(new_data) render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], white) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_text.py0000644000175000017510000000536014507104216020701 0ustar nileshnilesh# -*- coding: utf-8 -*- import numpy as np from numpy.testing import assert_allclose from vispy.scene.visuals import Text from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved @requires_application() def test_text(): """Test basic text support""" with TestingCanvas(bgcolor='w', size=(92, 92), dpi=92) as c: pos = [92 // 2] * 2 text = Text('testing', font_size=20, color='k', pos=pos, anchor_x='center', anchor_y='baseline', parent=c.scene) # Test image created in Illustrator CS5, 1"x1" output @ 92 DPI assert_image_approved(c.render(), 'visuals/text1.png') text.text = ['foo', 'bar'] text.pos = [10, 10] # should auto-replicate text.rotation = [180, 270] try: text.pos = [10] except Exception: pass else: raise AssertionError('Exception not raised') c.update() c.app.process_events() text.pos = [[10, 10], [10, 20]] text.text = 'foobar' c.update() c.app.process_events() @requires_application() def test_text_rotation_update(): # Regression test for a bug that caused text labels to not be redrawn # if the rotation angle was updated with TestingCanvas() as c: text = Text('testing', pos=(100, 100), parent=c.scene) c.update() c.app.process_events() assert_allclose(text.shared_program['a_rotation'], 0.) text.rotation = 30. c.update() c.app.process_events() assert_allclose(text.shared_program['a_rotation'], np.radians(30.)) @requires_application() def test_face_bold_italic(): with TestingCanvas() as c: # Check defaults text = Text('testing', pos=(100, 100), parent=c.scene) assert not text.bold and not text.italic # Check getter properties text = Text('testing', pos=(100, 100), bold=True, italic=True, parent=c.scene) assert text.bold and text.italic # Check that changing a property changes the font object font1 = text._font text.bold = False font2 = text._font assert font1 is not font2 text.italic = False font3 = text._font assert font2 is not font3 text.bold = True text.italic = True font4 = text._font assert font1 is font4 def test_text_depth_test(): t = Text(depth_test=False) assert not t._vshare.gl_state["depth_test"] t = Text(depth_test=True) assert t._vshare.gl_state["depth_test"] t = Text() # Default is false assert not t._vshare.gl_state["depth_test"] run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_spectrogram.py0000644000175000017510000000246614507104216022247 0ustar nileshnilesh# -*- coding: utf-8 -*- import numpy as np from vispy.scene.visuals import Spectrogram from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main, raises) from vispy.testing.image_tester import assert_image_approved @requires_application() def test_spectrogram(): """Test spectrogram visual""" n_fft = 256 n_freqs = n_fft // 2 + 1 size = (100, n_freqs) with TestingCanvas(size=size) as c: np.random.seed(67853498) data = np.random.normal(size=n_fft * 100) spec = Spectrogram(data, n_fft=n_fft, step=n_fft, window=None, color_scale='linear', cmap='grays') c.draw_visual(spec) # expected = np.zeros(size[::-1] + (3,)) # expected[0] = 1. assert_image_approved("screenshot", "visuals/spectrogram.png") freqs = spec.freqs assert len(freqs) == n_freqs assert freqs[0] == 0 assert freqs[-1] == 0.5 # Try changing all properties spec.n_fft = 128 spec.step = 128 spec.fs = 2 spec.window = 'hann' spec.normalize = True spec.color_scale = 'log' # Check color scale can be only 'log' or 'linear' with raises(ValueError): spec.color_scale = 'line_log' run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_sdf.py0000644000175000017510000000557114507104216020475 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import gc import numpy as np from numpy.testing import assert_allclose from vispy.app import Canvas from vispy.visuals.text.text import SDFRendererCPU from vispy.visuals.text._sdf_gpu import SDFRendererGPU from vispy import gloo from vispy.testing import requires_application, run_tests_if_main @requires_application() def test_sdf(): """Test basic text support - sdf""" # test a simple case data = (np.array( [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 1, 0, 0]]) * 255).astype(np.uint8) # The GPU data at one point were derived this way: # gpu = np.array( # [[8, 5, 4, 4, 4, 4, 4, 5, 8], # [5, 2, 1, 1, 1, 1, 1, 2, 5], # [4, 1, 0, 0, 0, 0, 0, 1, 4], # [4, 1, 0, -1, -4, -1, 0, 1, 4], # XXX artifact # [4, 1, 0, -1, -4, -1, 0, 1, 4], # [4, 1, 0, -1, -4, -1, 0, 1, 4]]) # gpu = 0.5 - (np.sqrt(np.abs(gpu)) * np.sign(gpu)) / 256. * 8 # gpu = np.round(256 * gpu).astype(np.int) # # But it's perhaps clearer just to give what will actually be compared: gpu = np.array( [[105, 110, 112, 112, 112, 112, 112, 110, 105], [110, 117, 120, 120, 120, 120, 120, 117, 110], [112, 120, 128, 128, 128, 128, 128, 120, 112], [112, 120, 128, 136, 144, 136, 128, 120, 112], [112, 120, 128, 136, 144, 136, 128, 120, 112], [112, 120, 128, 136, 144, 136, 128, 120, 112]]) cpu = np.array( [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 115, 118, 118, 118, 118, 118, 115, 0], [0, 118, 137, 137, 137, 137, 137, 118, 0], [0, 118, 137, 143, 143, 143, 137, 118, 0], [0, 118, 137, 143, 149, 143, 137, 118, 0], [0, 0, 255, 255, 255, 255, 255, 0, 0]]) # XXX: The GPU and CPU solutions are quite different. # It doesn't seem to have much effect on the visualizations but would be # good to fix eventually. for Rend, expd in zip((SDFRendererGPU, SDFRendererCPU), (gpu, cpu)): with Canvas(size=(100, 100)) as c: tex = gloo.Texture2D(data.shape + (3,), format='rgb') Rend().render_to_texture(data, tex, (0, 0), data.shape[::-1]) gloo.set_viewport(0, 0, *data.shape[::-1]) gloo.util.draw_texture(tex) result = gloo.util._screenshot()[:, :, 0].astype(np.int64) assert_allclose(result, expd, atol=1, err_msg=Rend.__name__) del tex, result del c # Do some garbage collection to make sure backend applications (PyQt5) actually clear things out gc.collect() run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_scalable_textures.py0000644000175000017510000001200114507104216023414 0ustar nileshnileshimport numpy as np import pytest from vispy.testing import run_tests_if_main from vispy.visuals._scalable_textures import ( get_default_clim_from_dtype, get_default_clim_from_data, CPUScaledTextureMixin, GPUScaledTextureMixin, ) class Stub: _ndim = 2 def __init__(self, data, **kwargs): pass def set_data(self, data, *args, **kwargs): self._data = data class CPUScaledStub(CPUScaledTextureMixin, Stub): pass class GPUScaledStub(GPUScaledTextureMixin, Stub): internalformat = "r32f" def _get_texture_format_for_data(self, data, internalformat=None): return None def test_default_clim(): ref_data = np.array([10, 5, 15, 25, 15]) # f32 data = ref_data.astype(np.float32) clim = get_default_clim_from_dtype(data.dtype) assert clim == (0, 1) clim = get_default_clim_from_data(data) assert clim == (5, 25) # i32 data = ref_data.astype(np.int32) clim = get_default_clim_from_dtype(data.dtype) assert clim == (-2**31, 2**31 - 1) clim = get_default_clim_from_data(data) assert clim == (5, 25) # u8 data = ref_data.astype(np.uint8) clim = get_default_clim_from_dtype(data.dtype) assert clim == (0, 255) clim = get_default_clim_from_data(data) assert clim == (5, 25) def test_default_clim_non_finite(): data = np.array([10, np.nan, 5, 15, 25, 15]).astype(np.float32) clim = get_default_clim_from_dtype(data.dtype) assert clim == (0, 1) clim = get_default_clim_from_data(data) assert clim == (5, 25) data = np.array([10, np.inf, 5, 15, 25, 15]).astype(np.float32) clim = get_default_clim_from_dtype(data.dtype) assert clim == (0, 1) clim = get_default_clim_from_data(data) assert clim == (5, 25) data = np.array([10, -np.inf, 5, 15, 25, 15]).astype(np.float32) clim = get_default_clim_from_dtype(data.dtype) assert clim == (0, 1) clim = get_default_clim_from_data(data) assert clim == (5, 25) data = np.array([np.nan, np.nan, np.nan]).astype(np.float32) clim = get_default_clim_from_dtype(data.dtype) assert clim == (0, 1) clim = get_default_clim_from_data(data) assert clim == (0, 0) data = np.array([np.nan, np.inf, -np.inf]).astype(np.float32) clim = get_default_clim_from_dtype(data.dtype) assert clim == (0, 1) clim = get_default_clim_from_data(data) assert clim == (0, 0) def test_clim_handling_cpu(): ref_data = np.array([[10, 10, 5], [15, 25, 15]]) # f32 - auto clim st = CPUScaledStub() st.set_clim("auto") st.scale_and_set_data(ref_data.astype(np.float32)) assert st.clim == (5, 25) assert st.clim_normalized == (0, 1) assert np.all(st._data == (ref_data - 5) / 20) # Updating clim keeps data the same if not changing too much st.set_clim((0, 20)) assert st.clim == (0, 20) assert st.clim_normalized == (-0.25, 0.75) assert np.all(st._data == (ref_data - 5) / 20) # f32 - custom clim st = CPUScaledStub() st.set_clim((0, 20)) st.scale_and_set_data(ref_data.astype(np.float32)) assert st.clim == (0, 20) assert st.clim_normalized == (0, 1) assert np.all(st._data == ref_data / 20) # f32 - flat clim st = CPUScaledStub() st.set_clim((10, 10)) st.scale_and_set_data(ref_data.astype(np.float32)) assert st.clim == (10, 10) assert st.clim_normalized == (0, np.inf) # assert np.min(st._data) == 0 - does not matter # f32 - auto clim st = CPUScaledStub() st.set_clim("auto") assert st.clim == "auto" pytest.raises(RuntimeError, getattr, st, "clim_normalized") st.scale_and_set_data(ref_data.astype(np.float32)) assert st.clim == (5, 25) assert st.clim_normalized == (0, 1) def test_clim_handling_gpu(): ref_data = np.array([[10, 10, 5], [15, 25, 15]]) # f32 - auto clim st = GPUScaledStub() st.set_clim("auto") st.scale_and_set_data(ref_data.astype(np.float32)) assert st.clim == (5, 25) assert st.clim_normalized == (5, 25) assert np.all(st._data == ref_data) # Updating clim keeps data the same if not changing too much st.set_clim((0, 20)) assert st.clim == (0, 20) assert st.clim_normalized == (0, 20) assert np.all(st._data == ref_data) # f32 - custom clim st = GPUScaledStub() st.set_clim((0, 20)) st.scale_and_set_data(ref_data.astype(np.float32)) assert st.clim == (0, 20) assert st.clim_normalized == (0, 20) assert np.all(st._data == ref_data) # f32 - flat clim st = GPUScaledStub() st.set_clim((10, 10)) st.scale_and_set_data(ref_data.astype(np.float32)) assert st.clim == (10, 10) assert st.clim_normalized == (10, np.inf) # assert np.min(st._data) == 0 - does not matter # f32 - auto clim st = GPUScaledStub() st.set_clim("auto") assert st.clim == "auto" pytest.raises(RuntimeError, getattr, st, "clim_normalized") st.scale_and_set_data(ref_data.astype(np.float32)) assert st.clim == (5.0, 25.0) assert st.clim_normalized == (5.0, 25.0) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_regular_polygon.py0000644000175000017510000001133714507104216023126 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Tests for RegularPolygonVisual All images are of size (100,100) to keep a small file size """ from vispy.scene import visuals, transforms from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved @requires_application() def test_regular_polygon_draw1(): """Test drawing regular polygons without transforms using RegularPolygonVisual""" # noqa with TestingCanvas() as c: rpolygon = visuals.RegularPolygon(center=(0., 0.), radius=0.4, sides=8, color=(1, 0, 0, 1), parent=c.scene) rpolygon.transform = transforms.STTransform(scale=(50, 50), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/regular_polygon1.png') rpolygon.parent = None rpolygon = visuals.RegularPolygon(center=(0., 0.), radius=0.4, sides=8, color=(1, 0, 0, 1), border_color=(0, 1, 1, 1), parent=c.scene) rpolygon.transform = transforms.STTransform(scale=(50, 50), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/regular_polygon2.png') rpolygon.parent = None rpolygon = visuals.RegularPolygon(center=(0., 0.), radius=0.4, sides=8, border_color=(0, 1, 1, 1), parent=c.scene) rpolygon.transform = transforms.STTransform(scale=(50, 50), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/regular_polygon3.png', min_corr=0.7) @requires_application() def test_regular_polygon_draw2(): """Test drawing transformed regular polygons using RegularPolygonVisual""" # noqa with TestingCanvas() as c: rpolygon = visuals.RegularPolygon(center=(0., 0.), radius=0.4, sides=8, color=(0, 0, 1, 1), parent=c.scene) rpolygon.transform = transforms.STTransform(scale=(75, 100), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/regular_polygon4.png') rpolygon.parent = None rpolygon = visuals.RegularPolygon(center=(0., 0.), radius=0.4, sides=8, color=(0, 0, 1, 1), border_color=(1, 0, 0, 1), parent=c.scene) rpolygon.transform = transforms.STTransform(scale=(75, 100), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/regular_polygon5.png') rpolygon.parent = None rpolygon = visuals.RegularPolygon(center=(0., 0.), radius=0.4, sides=8, border_color=(1, 0, 0, 1), parent=c.scene) rpolygon.transform = transforms.STTransform(scale=(75, 100), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/regular_polygon6.png', min_corr=0.6) @requires_application() def test_reactive_draw(): """Test reactive regular polygon attributes""" with TestingCanvas() as c: rpolygon = visuals.RegularPolygon(center=[50, 50, 0.], radius=20, sides=8, color='yellow', parent=c.scene) rpolygon.center = [70, 40, 0.] assert_image_approved(c.render(), 'visuals/reactive_regular_polygon1.png') rpolygon.radius = 25 assert_image_approved(c.render(), 'visuals/reactive_regular_polygon2.png') rpolygon.color = 'red' assert_image_approved(c.render(), 'visuals/reactive_regular_polygon3.png') rpolygon.border_color = 'yellow' assert_image_approved(c.render(), 'visuals/reactive_regular_polygon4.png') rpolygon.sides = 6 assert_image_approved(c.render(), 'visuals/reactive_regular_polygon5.png') run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_rectangle.py0000644000175000017510000001500214507104216021653 0ustar nileshnilesh# -*- coding: utf-8 -*- """ Tests for RectPolygonVisual All images are of size (100,100) to keep a small file size """ from vispy.scene import visuals, transforms from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main, raises) from vispy.testing.image_tester import assert_image_approved @requires_application() def test_rectangle_draw(): """Test drawing rectpolygons without transform using RectPolygonVisual""" with TestingCanvas() as c: rectpolygon = visuals.Rectangle(center=(50, 50, 0), height=40., width=80., color='red', parent=c.scene) assert_image_approved(c.render(), 'visuals/rectpolygon1.png') rectpolygon.parent = None rectpolygon = visuals.Rectangle(center=(50, 50, 0), height=40., width=80., radius=10., color='red', parent=c.scene) assert_image_approved(c.render(), 'visuals/rectpolygon2.png') rectpolygon.parent = None rectpolygon = visuals.Rectangle(center=(50, 50, 0), height=40., width=80., radius=10., color='red', border_color=(0, 1, 1, 1), parent=c.scene) assert_image_approved(c.render(), 'visuals/rectpolygon3.png') rectpolygon.parent = None rectpolygon = visuals.Rectangle(center=(50, 50, 0), height=40., width=80., radius=10., border_color='white', parent=c.scene) assert_image_approved(c.render(), 'visuals/rectpolygon4.png', min_corr=0.5) rectpolygon.parent = None rectpolygon = visuals.Rectangle(center=(50, 50, 0), height=60., width=80., radius=[25, 10, 0, 15], color='red', border_color=(0, 1, 1, 1), parent=c.scene) assert_image_approved(c.render(), 'visuals/rectpolygon5.png') rectpolygon.parent = None rectpolygon = visuals.Rectangle(center=(50, 50, 0), height=60., width=80., radius=[25, 10, 0, 15], color='red', border_color=(0, 1, 1, 1), border_width=5, border_method='agg', parent=c.scene) assert_image_approved(c.render(), 'visuals/rectpolygon10.png') @requires_application() def test_rectpolygon_draw(): """Test drawing transformed rectpolygons using RectPolygonVisual""" with TestingCanvas() as c: rectpolygon = visuals.Rectangle(center=(0., 0.), height=20., width=20., radius=10., color='blue', parent=c.scene) rectpolygon.transform = transforms.STTransform(scale=(2.0, 3.0), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/rectpolygon6.png') rectpolygon.parent = None rectpolygon = visuals.Rectangle(center=(0., 0.), height=20., width=20., radius=10., color='blue', border_color='red', parent=c.scene) rectpolygon.transform = transforms.STTransform(scale=(2.0, 3.0), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/rectpolygon7.png') rectpolygon.parent = None rectpolygon = visuals.Rectangle(center=(0., 0.), height=60., width=60., radius=10., border_color='red', parent=c.scene) rectpolygon.transform = transforms.STTransform(scale=(1.5, 0.5), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/rectpolygon8.png', min_corr=0.5) rectpolygon.parent = None rectpolygon = visuals.Rectangle(center=(0., 0.), height=60., width=60., radius=[25, 10, 0, 15], color='blue', border_color='red', parent=c.scene) rectpolygon.transform = transforms.STTransform(scale=(1.5, 0.5), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/rectpolygon9.png') @requires_application() def test_reactive_draw(): """Test reactive RectPolygon attributes""" with TestingCanvas() as c: rectpolygon = visuals.Rectangle(center=(50, 50, 0), height=40., width=80., color='red', parent=c.scene) rectpolygon.radius = [20., 20, 0., 10.] assert_image_approved(c.render(), 'visuals/reactive_rectpolygon1.png') rectpolygon.center = (60, 60, 0) assert_image_approved(c.render(), 'visuals/reactive_rectpolygon2.png') rectpolygon.color = 'blue' assert_image_approved(c.render(), 'visuals/reactive_rectpolygon3.png') rectpolygon.border_color = 'yellow' assert_image_approved(c.render(), 'visuals/reactive_rectpolygon4.png') rectpolygon.radius = 10. assert_image_approved(c.render(), 'visuals/reactive_rectpolygon5.png') @requires_application() def test_attributes(): """Test if attribute checks are in place""" with TestingCanvas() as c: rectpolygon = visuals.Rectangle(center=(50, 50, 0), height=40., width=80., color='red', parent=c.scene) with raises(ValueError): rectpolygon.height = 0 with raises(ValueError): rectpolygon.width = 0 with raises(ValueError): rectpolygon.radius = [10, 0, 5] with raises(ValueError): rectpolygon.radius = [10.] with raises(ValueError): rectpolygon.radius = 21. run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_polygon.py0000644000175000017510000001007514507104216021403 0ustar nileshnilesh# -*- coding: utf-8 -*- """ Tests for PolygonVisual All images are of size (100,100) to keep a small file size """ import numpy as np from vispy.scene import visuals, transforms from vispy.testing import (requires_application, requires_scipy, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved @requires_application() @requires_scipy() def test_square_draw(): """Test drawing squares without transforms using PolygonVisual""" pos = np.array([[-0.5, 0.5, 0], [0.5, 0.5, 0], [0.5, -0.5, 0], [-0.5, -0.5, 0]]) with TestingCanvas() as c: polygon = visuals.Polygon(pos=pos, color=(1, 0, 0, 1), parent=c.scene) polygon.transform = transforms.STTransform(scale=(50, 50), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/square1.png') polygon.parent = None polygon = visuals.Polygon(pos=pos, color=(1, 0, 0, 1), border_color=(1, 1, 1, 1), parent=c.scene) polygon.transform = transforms.STTransform(scale=(50, 50), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/square2.png') polygon.parent = None polygon = visuals.Polygon(pos=pos, border_color=(1, 1, 1, 1), parent=c.scene) polygon.transform = transforms.STTransform(scale=(50, 50), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/square3.png', min_corr=0.45) @requires_application() @requires_scipy() def test_rectangle_draw(): """Test drawing rectangles with transforms using PolygonVisual""" pos = np.array([[-0.1, 0.5, 0], [0.1, 0.5, 0], [0.1, -0.5, 0], [-0.1, -0.5, 0]]) with TestingCanvas() as c: polygon = visuals.Polygon(pos=pos, color=(1, 1, 0, 1), parent=c.scene) polygon.transform = transforms.STTransform(scale=(200.0, 25), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/rectangle1.png') polygon.parent = None polygon = visuals.Polygon(pos=pos, color=(1, 1, 0, 1), border_color=(1, 0, 0, 1), parent=c.scene) polygon.transform = transforms.STTransform(scale=(200.0, 25), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/rectangle2.png') polygon.parent = None polygon = visuals.Polygon(pos=pos, border_color=(1, 0, 0, 1), border_width=1, parent=c.scene) polygon.transform = transforms.STTransform(scale=(200.0, 25), translate=(50, 49)) assert_image_approved(c.render(), 'visuals/rectangle3.png', min_corr=0.7) @requires_application() @requires_scipy() def test_reactive_draw(): """Test reactive polygon attributes""" pos = np.array([[-0.1, 0.5, 0], [0.1, 0.5, 0], [0.1, -0.5, 0], [-0.1, -0.5, 0]]) with TestingCanvas() as c: polygon = visuals.Polygon(pos=pos, color='yellow', parent=c.scene) polygon.transform = transforms.STTransform(scale=(50, 50), translate=(50, 50)) polygon.pos += [0.1, -0.1, 0] assert_image_approved(c.render(), 'visuals/reactive_polygon1.png') polygon.color = 'red' assert_image_approved(c.render(), 'visuals/reactive_polygon2.png') polygon.border_color = 'yellow' assert_image_approved(c.render(), 'visuals/reactive_polygon3.png', min_corr=0.8) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_mesh_normals.py0000644000175000017510000002075314507104216022407 0ustar nileshnilesh# -*- coding: utf-8 -*- import numpy as np import pytest from vispy import scene from vispy.geometry import create_sphere from vispy.testing import (TestingCanvas, requires_application, run_tests_if_main, requires_pyopengl) @requires_pyopengl() @requires_application() def test_mesh_normals(): size = (45, 40) with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' # Create visual. mdata = create_sphere(radius=1.0) mesh = scene.visuals.Mesh(meshdata=mdata, shading=None, color=(0.1, 0.1, 0.1, 1.0)) v.add(mesh) rendered_without_normals = c.render() # The color should be of low intensity. assert np.all((rendered_without_normals[..., 0:3]) < 32) face_normals = scene.visuals.MeshNormals(mdata, primitive="face", color=(1, 0, 0)) face_normals.parent = mesh rendered_with_face_normals = c.render() face_normals.parent = None # There should be some pixels with brighter red. assert np.sum(rendered_with_face_normals[..., 0] > 128) > 64 pytest.raises(AssertionError, np.testing.assert_allclose, rendered_without_normals, rendered_with_face_normals) vertex_normals = scene.visuals.MeshNormals(mdata, primitive="vertex", color=(0, 1, 0)) vertex_normals.parent = mesh rendered_with_vertex_normals = c.render() vertex_normals.parent = None # There should be some pixels with brighter green. assert np.sum(rendered_with_vertex_normals[..., 1] > 128) > 64 pytest.raises(AssertionError, np.testing.assert_allclose, rendered_without_normals, rendered_with_vertex_normals) pytest.raises(AssertionError, np.testing.assert_allclose, rendered_with_face_normals, rendered_with_vertex_normals) @requires_pyopengl() @requires_application() def test_mesh_normals_length_scalar(): size = (45, 40) with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' # Create visual. mdata = create_sphere(radius=1.0) mesh = scene.visuals.Mesh(meshdata=mdata, shading=None, color=(0.1, 0.1, 0.1, 1.0)) v.add(mesh) length = 0.5 normals_0_5 = scene.visuals.MeshNormals(mdata, color=(1, 0, 0), length=length) normals_0_5.parent = mesh rendered_length_0_5 = c.render() normals_0_5.parent = None length = 1.0 normals_1_0 = scene.visuals.MeshNormals(mdata, color=(1, 0, 0), length=length) normals_1_0.parent = mesh rendered_length_1_0 = c.render() normals_1_0.parent = None # There should be more red pixels with the longer normals. n_pixels_0_5 = np.sum(rendered_length_0_5[..., 0] > 128) n_pixels_1_0 = np.sum(rendered_length_1_0[..., 0] > 128) assert n_pixels_1_0 > n_pixels_0_5 @requires_pyopengl() @requires_application() @pytest.mark.parametrize('primitive', ['face', 'vertex']) def test_mesh_normals_length_array(primitive): size = (45, 40) with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' v.camera.fov = 90 # Create visual. meshdata = create_sphere(radius=1.0) mesh = scene.visuals.Mesh(meshdata=meshdata, shading=None, color=(0.1, 0.1, 0.1, 1.0)) v.add(mesh) if primitive == 'face': n_normals = len(meshdata.get_faces()) elif primitive == 'vertex': n_normals = len(meshdata.get_vertices()) lengths_0_5 = np.full(n_normals, 0.5, dtype=float) normals_0_5 = scene.visuals.MeshNormals(meshdata, primitive=primitive, color=(1, 0, 0), length=lengths_0_5) normals_0_5.parent = mesh rendered_lengths_0_5 = c.render() normals_0_5.parent = None lengths_1_0 = np.full(n_normals, 1.0, dtype=float) normals_1_0 = scene.visuals.MeshNormals(meshdata, primitive=primitive, color=(1, 0, 0), length=lengths_1_0) normals_1_0.parent = mesh rendered_lengths_1_0 = c.render() normals_1_0.parent = None # There should be more red pixels with the longer normals. n_pixels_0_5 = np.sum(rendered_lengths_0_5[..., 0] > 128) n_pixels_1_0 = np.sum(rendered_lengths_1_0[..., 0] > 128) assert n_pixels_1_0 > n_pixels_0_5 lengths_ramp = np.linspace(0.5, 1.0, n_normals, dtype=float) normals_ramp = scene.visuals.MeshNormals(meshdata, primitive=primitive, color=(1, 0, 0), length=lengths_ramp) normals_ramp.parent = mesh rendered_lengths_ramp = c.render() normals_ramp.parent = None # With the normals lengths from a ramp in [0.5, 1.0], there should be # more red pixels than with all normals of length 0.5 and less than # with all normals of length 1.0. n_pixels_ramp = np.sum(rendered_lengths_ramp[..., 0] > 128) assert n_pixels_0_5 < n_pixels_ramp < n_pixels_1_0 @requires_pyopengl() @requires_application() def test_mesh_normals_length_scale(): size = (45, 40) with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' # Create visual. meshdata = create_sphere(radius=1.0) mesh = scene.visuals.Mesh(meshdata=meshdata, shading=None, color=(0.1, 0.1, 0.1, 1.0)) v.add(mesh) length = 1.0 length_scale_up = 2.0 length_scale_down = 0.5 normals = scene.visuals.MeshNormals(meshdata, color=(1, 0, 0), length=length) normals.parent = mesh rendered_length_default = c.render() normals.parent = None normals_scaled_up = scene.visuals.MeshNormals( meshdata, color=(1, 0, 0), length=length, length_scale=length_scale_up) normals_scaled_up.parent = mesh rendered_length_scaled_up = c.render() normals_scaled_up.parent = None normals_scaled_down = scene.visuals.MeshNormals( meshdata, color=(1, 0, 0), length=length, length_scale=length_scale_down) normals_scaled_down.parent = mesh rendered_length_scaled_down = c.render() normals_scaled_down.parent = None # There should be more red pixels with the scaled-up normals and less # with the ones scaled down. n_pixels_default = np.sum(rendered_length_default[..., 0] > 128) n_pixels_scaled_up = np.sum(rendered_length_scaled_up[..., 0] > 128) n_pixels_scaled_down = np.sum(rendered_length_scaled_down[..., 0] > 128) assert n_pixels_scaled_down < n_pixels_default < n_pixels_scaled_up @requires_pyopengl() @requires_application() @pytest.mark.parametrize('length_method', ['median_edge', 'max_extent']) def test_mesh_normals_length_method(length_method): size = (45, 40) with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' # Create visual. meshdata = create_sphere(radius=1.0) mesh = scene.visuals.Mesh(meshdata=meshdata, shading=None, color=(0.1, 0.1, 0.1, 1.0)) v.add(mesh) # The code below should not raise. # XXX(asnt): Not sure how to better test `length_method`. normals = scene.visuals.MeshNormals(meshdata, color=(1, 0, 0), length_method=length_method) normals.parent = mesh _ = c.render() def test_mesh_normals_empty(): mesh = scene.visuals.Mesh() scene.visuals.MeshNormals(mesh.mesh_data) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_mesh.py0000644000175000017510000002373014507104216020652 0ustar nileshnilesh# -*- coding: utf-8 -*- import numpy as np from vispy import scene from vispy.color import Color from vispy.geometry import create_cube, create_sphere from vispy.testing import (TestingCanvas, requires_application, run_tests_if_main, requires_pyopengl) from vispy.visuals.filters import ShadingFilter, WireframeFilter from vispy.visuals.filters.mesh import _as_rgba import pytest @requires_pyopengl() def test_mesh_color(): # Create visual vertices, filled_indices, outline_indices = create_cube() axis = scene.visuals.Mesh(vertices['position'], outline_indices, color='black', mode='lines') # Change color (regression test for a bug that caused this to reset # the vertex data to None) axis.color = (0.1, 0.3, 0.7, 0.9) new_vertices = axis.mesh_data.get_vertices() np.testing.assert_allclose(axis.color.rgba, (0.1, 0.3, 0.7, 0.9)) np.testing.assert_allclose(vertices['position'], new_vertices) @requires_pyopengl() @requires_application() def test_mesh_with_vertex_values(): size = (45, 40) with TestingCanvas(size=size) as c: v = c.central_widget.add_view(border_width=0) vertices = np.array([(0, 0, 0), (0, 0, 1), (1, 0, 0)], dtype=float) faces = np.array([(0, 1, 2)]) mesh = scene.visuals.Mesh( vertices=vertices, faces=faces, vertex_values=np.ones(len(vertices)), ) v.add(mesh) c.render() @requires_pyopengl() @requires_application() @pytest.mark.parametrize('shading', [None, 'flat', 'smooth']) def test_mesh_shading_change_from_none(shading): # Regression test for #2041: exception raised when changing the shading # mode with shading=None initially. size = (45, 40) with TestingCanvas(size=size) as c: v = c.central_widget.add_view(border_width=0) vertices = np.array([(0, 0, 0), (0, 0, 1), (1, 0, 0)], dtype=float) faces = np.array([(0, 1, 2)]) mesh = scene.visuals.Mesh(vertices=vertices, faces=faces, shading=None) v.add(mesh) c.render() # This below should not fail. mesh.shading = shading c.render() @requires_pyopengl() @requires_application() @pytest.mark.parametrize('shading', [None, 'flat', 'smooth']) def test_mesh_shading_filter(shading): size = (45, 40) with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' mdata = create_sphere(20, 30, radius=1) mesh = scene.visuals.Mesh(meshdata=mdata, shading=shading, color=(0.2, 0.3, 0.7, 1.0)) v.add(mesh) rendered = c.render()[..., 0] # R channel only if shading in ("flat", "smooth"): # there should be a gradient, not solid colors assert np.unique(rendered).size >= 28 # sphere/circle is "dark" on the right and gets brighter as you # move to the left, then hits a bright spot and decreases after invest_row = rendered[34].astype(np.float64) # overall, we should be increasing brightness up to a "bright spot" assert (np.diff(invest_row[34:60]) <= 0).all() else: assert np.unique(rendered).size == 2 def test_intensity_or_color_as_rgba(): assert _as_rgba(0.3) == Color((1.0, 1.0, 1.0, 0.3)) assert _as_rgba((0.3, 0.2, 0.1)) == Color((0.3, 0.2, 0.1, 1.0)) assert _as_rgba((0.3, 0.2, 0.1, 0.5)) == Color((0.3, 0.2, 0.1, 0.5)) @requires_pyopengl() @requires_application() @pytest.mark.parametrize('shading', [None, 'flat', 'smooth']) def test_mesh_shading_filter_enabled(shading): size = (45, 40) with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' mdata = create_sphere(20, 30, radius=1) mesh = scene.visuals.Mesh(meshdata=mdata, shading=None, color=(0.2, 0.3, 0.7, 1.0)) shading_filter = ShadingFilter(shading=shading) mesh.attach(shading_filter) v.add(mesh) shading_filter.enabled = False rendered_without_shading = c.render() shading_filter.enabled = True rendered_with_shading = c.render() if shading is None: # There should be no shading applied, regardless of the value of # `enabled`. assert np.allclose(rendered_without_shading, rendered_with_shading) else: # The result should be different with shading applied. assert not np.allclose(rendered_without_shading, rendered_with_shading) @requires_pyopengl() @requires_application() @pytest.mark.parametrize('attribute', ['ambient_coefficient', 'diffuse_coefficient', 'specular_coefficient', 'ambient_light', 'diffuse_light', 'specular_light']) def test_mesh_shading_filter_colors(attribute): size = (45, 40) with TestingCanvas(size=size, bgcolor="k") as c: base_color_white = (1.0, 1.0, 1.0, 1.0) overlay_color_red = (1.0, 0.0, 0.0, 1.0) v = c.central_widget.add_view(border_width=0) v.camera = 'arcball' mdata = create_sphere(20, 30, radius=1) mesh = scene.visuals.Mesh(meshdata=mdata, color=base_color_white) v.add(mesh) shading_filter = ShadingFilter(shading='smooth', # Set the light source on the side of # and around the camera to get a clearly # visible reflection. light_dir=(-5, -5, 5), # Activate all illumination types as # white light but reduce the intensity # to prevent saturation. ambient_light=0.3, diffuse_light=0.3, specular_light=0.3, # Get a wide highlight. shininess=4) mesh.attach(shading_filter) rendered_white = c.render() setattr(shading_filter, attribute, overlay_color_red) rendered_red = c.render() # The results should be different. assert not np.allclose(rendered_white, rendered_red) # There should be an equal amount of all colors in the white rendering. color_count_white = rendered_white.sum(axis=(0, 1)) r, g, b, _ = color_count_white assert r == g and r == b color_count_red = rendered_red.sum(axis=(0, 1)) # There should be more red in the red-colored rendering. r, g, b, _ = color_count_red assert r > g and r > b @requires_pyopengl() def test_mesh_bounds(): # Create 3D visual vertices, filled_indices, outline_indices = create_cube() axis = scene.visuals.Mesh(vertices['position'], outline_indices, color='black', mode='lines') # Test bounds for all 3 axes for i in range(3): np.testing.assert_allclose(axis.bounds(i), (-1.0, 1.0)) # Create 2D visual using projection of cube axis = scene.visuals.Mesh(vertices['position'][:, :2], outline_indices, color='black', mode='lines') # Test bounds for first 2 axes for i in range(2): np.testing.assert_allclose(axis.bounds(i), (-1.0, 1.0)) # Test bounds for 3rd axis np.testing.assert_allclose(axis.bounds(2), (0.0, 0.0)) @requires_pyopengl() @requires_application() def test_mesh_wireframe_filter(): size = (45, 40) with TestingCanvas(size=size, bgcolor="k") as c: v = c.central_widget.add_view(border_width=0) # Create visual mdata = create_sphere(20, 40, radius=20) mesh = scene.visuals.Mesh(meshdata=mdata, shading=None, color=(0.1, 0.3, 0.7, 0.9)) wireframe_filter = WireframeFilter(color='red') mesh.attach(wireframe_filter) v.add(mesh) from vispy.visuals.transforms import STTransform mesh.transform = STTransform(translate=(20, 20)) mesh.transforms.scene_transform = STTransform(scale=(1, 1, 0.01)) rendered_with_wf = c.render() assert np.unique(rendered_with_wf[..., 0]).size >= 50 wireframe_filter.enabled = False rendered_wo_wf = c.render() # the result should be completely different # assert not allclose pytest.raises(AssertionError, np.testing.assert_allclose, rendered_with_wf, rendered_wo_wf) wireframe_filter.enabled = True wireframe_filter.wireframe_only = True rendered_with_wf_only = c.render() # the result should be different from the two cases above pytest.raises(AssertionError, np.testing.assert_allclose, rendered_with_wf_only, rendered_with_wf) pytest.raises(AssertionError, np.testing.assert_allclose, rendered_with_wf_only, rendered_wo_wf) wireframe_filter.enabled = True wireframe_filter.wireframe_only = False wireframe_filter.faces_only = True rendered_with_faces_only = c.render() # the result should be different from the cases above pytest.raises(AssertionError, np.testing.assert_allclose, rendered_with_faces_only, rendered_with_wf) pytest.raises(AssertionError, np.testing.assert_allclose, rendered_with_faces_only, rendered_wo_wf) pytest.raises(AssertionError, np.testing.assert_allclose, rendered_with_faces_only, rendered_with_wf_only) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_markers.py0000644000175000017510000000327414507104216021363 0ustar nileshnilesh# -*- coding: utf-8 -*- import numpy as np import pytest from vispy.scene.visuals import Markers from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved @requires_application() def test_markers(): """Test basic marker / point-sprite support""" # this is probably too basic, but it at least ensures that point sprites # work for people np.random.seed(57983) data = np.random.normal(size=(30, 2), loc=50, scale=10) with TestingCanvas() as c: marker = Markers(parent=c.scene) marker.set_data(data) assert_image_approved(c.render(), "visuals/markers.png") # Test good correlation at high-dpi with TestingCanvas(px_scale=2) as c: marker = Markers(parent=c.scene) marker.set_data(data) assert_image_approved(c.render(), "visuals/markers.png") def test_markers_edge_width(): data = np.random.rand(10, 3) edge_width = np.random.rand(10) marker = Markers() with pytest.raises(ValueError): marker.set_data(pos=data, edge_width_rel=1, edge_width=1) marker.set_data(pos=data, edge_width=2) marker.set_data(pos=data, edge_width=edge_width) with pytest.raises(ValueError): marker.set_data(pos=data, edge_width=-1) marker.set_data(pos=data, edge_width_rel=edge_width, edge_width=None) marker.set_data(pos=data, edge_width_rel=edge_width + 1, edge_width=None) with pytest.raises(ValueError): marker.set_data(pos=data, edge_width_rel=edge_width - 1, edge_width=None) def test_empty_markers_symbol(): markers = Markers() markers.symbol = 'o' run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_linear_region.py0000644000175000017510000001411014507104216022523 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Tests for LinearRegionVisual All images are of size (100,100) to keep a small file size """ import numpy as np from vispy.scene import visuals from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved from vispy.testing import assert_raises @requires_application() def test_linear_region_vertical_horizontal(): """Test vertical and horizontal LinearRegionVisual with a single color""" # Definition of the region pos = np.array([5, 15, 24, 36, 40, 42], dtype=np.float32) # Expected internal pos buffer for vertical region expected_pos_v = np.array([[5.0, -1.], [5.0, 1.], [15.0, -1.], [15.0, 1.], [24.0, -1.], [24.0, 1.], [36.0, -1.], [36.0, 1.], [40.0, -1.], [40.0, 1.], [42.0, -1.], [42.0, 1.]], dtype=np.float32) # Expected internal pos buffer for horizontal region expected_pos_h = np.array([expected_pos_v[:, 1] * -1, expected_pos_v[:, 0]], dtype=np.float32).T # Test both horizontal and vertical region for is_vertical, reference_image in [(True, 'linear_region1.png'), (False, 'linear_region1_h.png')]: expected_pos = expected_pos_v if is_vertical else expected_pos_h with TestingCanvas() as c: # Check set_data is working correctly within visual constructor region = visuals.LinearRegion(pos=pos, color=[0.0, 1.0, 0.0, 0.5], vertical=is_vertical, parent=c.scene) assert np.all(region._pos == expected_pos) assert np.all(region.pos == pos) assert region.is_vertical == is_vertical # Check set_data is working as expected when passing a list as # pos argument region.set_data(pos=list(pos)) assert np.all(region._pos == expected_pos) assert np.all(region.pos == pos) # Check set_data is working as expected when passing a tuple as # pos argument region.set_data(pos=tuple(pos)) assert np.all(region._pos == expected_pos) assert np.all(region.pos == pos) # Test with different dtypes that must be converted to float32 for t in [np.int64, np.float64, np.int32]: region.set_data(pos=pos.astype(t)) assert np.all(region._pos == expected_pos) assert np.all(region.pos == pos) assert_image_approved(c.render(), 'visuals/%s' % reference_image) # Check ValueError is raised when pos is not 1D assert_raises(ValueError, region.set_data, pos=[[1, 2], [3, 4]]) @requires_application() def test_linear_region_color(): """Test the color argument of LinearRegionVisual.set_data() method using a single color """ # Definition of the region pos1 = [5, 42] # Definition of the color of the region color1 = np.array([0.0, 1.0, 0.0, 0.5], dtype=np.float32) # Expected internal color buffer color1_expected = np.array([color1, color1, color1, color1], dtype=np.float32) with TestingCanvas() as c: # Check set_data is working correctly within visual constructor region = visuals.LinearRegion(pos=pos1, color=color1, parent=c.scene) assert np.all(region._color == color1_expected) assert np.all(region.color == color1) # Check set_data is working as expected when passing a list as # color argument region.set_data(color=list(color1)) assert np.all(region._color == color1_expected) assert np.all(region.color == color1) # Check set_data is working as expected when passing a tuple as # color argument region.set_data(color=tuple(color1)) assert np.all(region._color == color1_expected) assert np.all(region.color == color1) # Test with different dtypes that must be converted to float32 region.set_data(color=color1.astype(np.float64)) assert np.all(region._color == color1_expected) assert np.all(region.color == color1) assert_image_approved(c.render(), 'visuals/linear_region1.png') # Check a ValueError is raised when the length of color argument # is not 4. assert_raises(ValueError, region.set_data, color=[1.0, 0.5, 0.5]) # Check a ValueError is raised when too many colors are provided assert_raises(ValueError, region.set_data, color=[color1, color1, color1]) @requires_application() def test_linear_region_gradient(): """Test LinearRegionVisual with a gradient as color""" # Definition of the region pos2 = [5, 42, 80] # Definition of the color of the region color2 = np.array([[0.0, 1.0, 0.0, 0.5], [1.0, 0.0, 0.0, 0.75], [0.0, 0.0, 1.0, 1.0]], dtype=np.float32) # Expected internal color buffer color2_expected = np.array([color2[0], color2[0], color2[1], color2[1], color2[2], color2[2]], dtype=np.float32) with TestingCanvas() as c: # Check set_data is working correctly within visual constructor region = visuals.LinearRegion(pos=pos2, color=color2, parent=c.scene) assert np.all(region._color == color2_expected) assert np.all(region.color == color2) assert_image_approved(c.render(), 'visuals/linear_region2.png') run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_isosurface.py0000644000175000017510000000072514507104216022060 0ustar nileshnilesh# -*- coding: utf-8 -*- import numpy as np from vispy import scene from vispy.testing import run_tests_if_main, requires_pyopengl @requires_pyopengl() def test_isosurface(): # Create data vol = np.arange(1000).reshape((10, 10, 10)).astype(np.float32) # Create visual iso = scene.visuals.Isosurface(vol, level=200) # Change color (regression test for a bug that caused this to crash) iso.color = (1.0, 0.8, 0.9, 1.0) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_instanced_mesh.py0000644000175000017510000000255214507104216022701 0ustar nileshnileshimport numpy as np from vispy import scene, use from vispy.testing import (TestingCanvas, requires_application, run_tests_if_main, requires_pyopengl) def setup_module(module): use(gl='gl+') def teardown_module(module): use(gl='gl2') @requires_pyopengl() @requires_application() def test_mesh_with_vertex_values(): size = (80, 60) with TestingCanvas(size=size) as c: use(gl='gl+') vert = np.array([[0, 0, 0], [0, 30, 0], [40, 0, 0]]) faces = np.array([0, 1, 2]) pos = np.array([[0, 0, 0], [80, 60, 0]]) # identity and rotate 180 trans = np.array([ [ [1, 0, 0], [0, 1, 0], [0, 0, 1], ], [ [-1, 0, 0], [0, -1, 0], [0, 0, 1], ], ]) colors = ['red', 'blue'] mesh = scene.visuals.InstancedMesh( vertices=vert, faces=faces, instance_positions=pos, instance_transforms=trans, instance_colors=colors ) v = c.central_widget.add_view(border_width=0) v.add(mesh) render = c.render() assert np.allclose(render[10, 10], (255, 0, 0, 255)) assert np.allclose(render[-10, -10], (0, 0, 255, 255)) assert np.allclose(render[30, 40], (0, 0, 0, 255)) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_infinite_line.py0000644000175000017510000000362014507104216022526 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Tests for InfiniteLineVisual All images are of size (100,100) to keep a small file size """ import numpy as np from vispy.scene import visuals from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved from vispy.testing import assert_raises @requires_application() def test_set_data(): """Test InfiniteLineVisual""" pos = 5.0 color = [1.0, 1.0, 0.5, 0.5] expected_color = np.array(color, dtype=np.float32) for is_vertical, reference_image in [(True, 'infinite_line.png'), (False, 'infinite_line_h.png')]: with TestingCanvas() as c: # Check set_data is working correctly within visual constructor region = visuals.InfiniteLine(pos=pos, color=color, vertical=is_vertical, parent=c.scene) assert region.pos == pos assert np.all(region.color == expected_color) assert region.is_vertical == is_vertical # Check tuple color argument is accepted region.set_data(color=tuple(color)) assert np.all(region.color == expected_color) assert_image_approved(c.render(), 'visuals/%s' % reference_image) # Check only numbers are accepted assert_raises(TypeError, region.set_data, pos=[[1, 2], [3, 4]]) # Check color argument can be only a 4 length 1D array assert_raises(ValueError, region.set_data, color=[[1, 2], [3, 4]]) assert_raises(ValueError, region.set_data, color=[1, 2]) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_image_complex.py0000644000175000017510000000265314507104216022530 0ustar nileshnileshfrom vispy.scene.visuals import ComplexImage from vispy.visuals.image_complex import CPU_COMPLEX_TRANSFORMS from vispy.testing import requires_application, TestingCanvas from vispy.testing.image_tester import downsample import numpy as np import pytest from vispy.testing.rendered_array_tester import compare_render # we add the np.float32 case to test that ComplexImage falls back to Image behavior # if the data is not complex @requires_application() @pytest.mark.parametrize("input_dtype", [np.complex64, np.complex128, np.float32]) @pytest.mark.parametrize("complex_mode", ["magnitude", "real", "imaginary", "phase"]) def test_image_complex(input_dtype, complex_mode): """Test rendering of complex-valued image data.""" shape = (40, 40) np.random.seed(0) data = np.random.random(shape).astype(input_dtype) if np.iscomplexobj(data): data.imag = np.random.random(shape) with TestingCanvas(size=shape, bgcolor="w") as c: ComplexImage(data, cmap="grays", complex_mode=complex_mode, parent=c.scene) # render to canvas rendered = c.render() shape_ratio = rendered.shape[0] // data.shape[0] rendered = downsample(rendered, shape_ratio, axis=(0, 1)) # perform (auto-clim) rendering on cpu exp = CPU_COMPLEX_TRANSFORMS[complex_mode](data) if np.iscomplexobj(data) else data exp -= exp.min() exp /= exp.max() compare_render(exp, rendered) vispy-0.14.1/vispy/visuals/tests/test_image.py0000644000175000017510000003320314507104216020774 0ustar nileshnilesh# -*- coding: utf-8 -*- from unittest import mock from vispy.scene import Image, PanZoomCamera from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main, IS_CI) from vispy.testing.image_tester import assert_image_approved, downsample import numpy as np import pytest from vispy.testing.rendered_array_tester import compare_render, max_for_dtype @requires_application() @pytest.mark.parametrize('is_3d', [True, False]) def test_image(is_3d): """Test image visual""" size = (100, 50) with TestingCanvas(size=size, bgcolor='w') as c: image = Image(cmap='grays', clim=[0, 1], parent=c.scene) shape = (size[1]-10, size[0]-10) + ((3,) if is_3d else ()) np.random.seed(379823) data = np.random.rand(*shape) image.set_data(data) assert_image_approved(c.render(), "visuals/image%s.png" % ("_rgb" if is_3d else "_mono")) # change to auto clims after first draw image.clim = "auto" assert_image_approved(c.render(), "visuals/image%s.png" % ("_rgb" if is_3d else "_mono")) @requires_application() @pytest.mark.parametrize('gamma', [None, -0.5, "0.5"]) def test_bad_init_gamma(gamma): """Test creating an Image with a bad gamma value.""" with TestingCanvas(size=(100, 50)) as c: pytest.raises((TypeError, ValueError), Image, gamma=gamma, parent=c.scene) def _make_test_data(shape, input_dtype): data = np.random.random_sample(shape) if data.ndim == 3 and data.shape[-1] == 4: # RGBA - make alpha fully opaque data[..., -1] = 1.0 max_val = max_for_dtype(input_dtype) if max_val != 1: data *= max_val data = data.astype(input_dtype) return data def _set_image_data(image, data, should_fail): if should_fail: pytest.raises(ValueError, image.set_data, data) return image.set_data(data) def _get_orig_and_new_clims(input_dtype): new_clim = (0.3, 0.8) max_val = max_for_dtype(input_dtype) if np.issubdtype(input_dtype, np.integer): new_clim = (int(new_clim[0] * max_val), int(new_clim[1] * max_val)) return (0, max_val), new_clim @requires_application() @pytest.mark.parametrize('data_on_init', [False, True]) @pytest.mark.parametrize('clim_on_init', [False, True]) @pytest.mark.parametrize('num_channels', [0, 1, 3, 4]) @pytest.mark.parametrize('texture_format', [None, '__dtype__', 'auto']) @pytest.mark.parametrize('input_dtype', [np.uint8, np.uint16, np.float32, np.float64]) def test_image_clims_and_gamma(input_dtype, texture_format, num_channels, clim_on_init, data_on_init): """Test image visual with clims and gamma on shader.""" size = (80, 80) if texture_format == '__dtype__': texture_format = input_dtype shape = size + (num_channels,) if num_channels > 0 else size np.random.seed(0) data = _make_test_data(shape, input_dtype) orig_clim, new_clim = _get_orig_and_new_clims(input_dtype) # 16-bit integers and above seem to have precision loss when scaled on the CPU is_16int_cpu_scaled = (np.dtype(input_dtype).itemsize >= 2 and np.issubdtype(input_dtype, np.integer) and texture_format is None) clim_atol = 2 if is_16int_cpu_scaled else 1 gamma_atol = 3 if is_16int_cpu_scaled else 2 kwargs = {} if clim_on_init: kwargs['clim'] = orig_clim if data_on_init: kwargs['data'] = data # default is RGBA, anything except auto requires reformat set_data_fails = (num_channels != 4 and texture_format is not None and texture_format != 'auto') with TestingCanvas(size=size[::-1], bgcolor="w") as c: image = Image(cmap='grays', texture_format=texture_format, parent=c.scene, **kwargs) if not data_on_init: _set_image_data(image, data, set_data_fails) if set_data_fails: return rendered = c.render() _dtype = rendered.dtype shape_ratio = rendered.shape[0] // data.shape[0] rendered1 = downsample(rendered, shape_ratio, axis=(0, 1)).astype(_dtype) compare_render(data, rendered1) # adjust color limits image.clim = new_clim rendered2 = downsample(c.render(), shape_ratio, axis=(0, 1)).astype(_dtype) scaled_data = (np.clip(data, new_clim[0], new_clim[1]) - new_clim[0]) / (new_clim[1] - new_clim[0]) compare_render(scaled_data, rendered2, rendered1, atol=clim_atol) # adjust gamma image.gamma = 2 rendered3 = downsample(c.render(), shape_ratio, axis=(0, 1)).astype(_dtype) compare_render(scaled_data ** 2, rendered3, rendered2, atol=gamma_atol) @pytest.mark.xfail(IS_CI, reason="CI environments sometimes treat NaN as 0") @requires_application() @pytest.mark.parametrize('texture_format', [None, 'auto']) def test_image_nan_single_band(texture_format): size = (40, 40) data = np.ones((40, 40)) data[:5, :5] = np.nan data[:5, -5:] = 0 expected = (np.ones((40, 40, 4)) * 255).astype(np.uint8) # black square expected[:5, -5:, :3] = 0 if texture_format is None: # CPU scaling's NaNs get converted to 0s expected[:5, :5, :3] = 0 else: # GPU receives NaNs # nan - transparent square expected[:5, :5, 0] = 0 expected[:5, :5, 1] = 255 # match the 'green' background expected[:5, :5, 2] = 0 with TestingCanvas(size=size[::-1], bgcolor=(0, 1, 0)) as c: Image(data, cmap='grays', texture_format=texture_format, parent=c.scene) rendered = c.render() np.testing.assert_allclose(rendered, expected) @requires_application() @pytest.mark.parametrize('num_bands', [3, 4]) @pytest.mark.parametrize('texture_format', [None, 'auto']) def test_image_nan_rgb(texture_format, num_bands): size = (40, 40) data = np.ones((40, 40, num_bands)) data[:5, :5, :3] = np.nan # upper left - RGB all NaN data[:5, 20:25, 0] = np.nan # upper middle - R NaN data[:5, -5:, :3] = 0 # upper right - opaque RGB black square data[-5:, -5:, :] = np.nan # lower right RGBA all NaN if num_bands == 4: data[-5:, :5, 3] = np.nan # lower left - Alpha NaN expected = (np.ones((40, 40, 4)) * 255).astype(np.uint8) # upper left - NaN goes to opaque black expected[:5, :5, :3] = 0 # upper middle -> NaN R goes to 0 expected[:5, 20:25, 0] = 0 # upper right - opaque RGB black square expected[:5, -5:, :3] = 0 # lower right - NaN RGB/A goes to 0 # RGBA case - we see the green background behind the image expected[-5:, -5:, 0] = 0 expected[-5:, -5:, 2] = 0 if num_bands == 3: # RGB case - opaque black because Alpha defaults 1 expected[-5:, -5:, 1] = 0 # lower left - NaN Alpha goes to 0 if num_bands == 4: # see the green background behind the image expected[-5:, :5, 0] = 0 expected[-5:, :5, 2] = 0 with TestingCanvas(size=size[::-1], bgcolor=(0, 1, 0)) as c: Image(data, cmap='grays', texture_format=texture_format, parent=c.scene) rendered = c.render() np.testing.assert_allclose(rendered, expected) @requires_application() @pytest.mark.parametrize('num_channels', [0, 1, 3, 4]) @pytest.mark.parametrize('texture_format', [None, 'auto']) def test_image_equal_clims(texture_format, num_channels): """Test image visual with equal clims.""" size = (40, 40) input_dtype = np.uint8 shape = size + (num_channels,) if num_channels > 0 else size np.random.seed(0) data = _make_test_data(shape, input_dtype) with TestingCanvas(size=size[::-1], bgcolor="w") as c: Image(data, cmap='viridis', texture_format=texture_format, clim=(128.0, 128.0), parent=c.scene) rendered = c.render()[..., :3] if num_channels >= 3: # RGBs don't have colormaps assert rendered.sum() == 0 return # not all black assert rendered.sum() != 0 # not all white assert rendered.sum() != 255 * rendered.size # should be all the same value r_unique = np.unique(rendered[..., 0]) g_unique = np.unique(rendered[..., 1]) b_unique = np.unique(rendered[..., 2]) assert r_unique.size == 1 assert g_unique.size == 1 assert b_unique.size == 1 @requires_application() def test_image_vertex_updates(): """Test image visual coordinates are only built when needed.""" size = (40, 40) with TestingCanvas(size=size, bgcolor="w") as c: shape = size + (3,) np.random.seed(0) image = Image(cmap='grays', clim=[0, 1], parent=c.scene) with mock.patch.object( image, '_build_vertex_data', wraps=image._build_vertex_data) as build_vertex_mock: data = np.random.rand(*shape) image.set_data(data) c.render() build_vertex_mock.assert_called_once() build_vertex_mock.reset_mock() # reset the count to 0 # rendering again shouldn't cause vertex coordinates to be built c.render() build_vertex_mock.assert_not_called() # changing to data of the same shape shouldn't cause it data = np.zeros_like(data) image.set_data(data) c.render() build_vertex_mock.assert_not_called() # changing to another shape should data = data[:-5, :-5] image.set_data(data) c.render() build_vertex_mock.assert_called_once() @requires_application() @pytest.mark.parametrize( ("dtype", "init_clim"), [ (np.float32, "auto"), (np.float32, (0, 5)), (np.uint8, "auto"), (np.uint8, (0, 5)), ] ) def test_change_clim_float(dtype, init_clim): """Test that with an image of floats, clim is correctly set from the first try. See https://github.com/vispy/vispy/pull/2245. """ size = (40, 40) np.random.seed(0) data = (np.random.rand(*size) * 100).astype(dtype) with TestingCanvas(size=size[::-1], bgcolor="w") as c: image = Image(data=data, clim=init_clim, parent=c.scene) # needed to properly initialize the canvas c.render() image.clim = 0, 10 rendered1 = c.render() # set clim to same values image.clim = 0, 10 rendered2 = c.render() assert np.allclose(rendered1, rendered2) @requires_application() def test_image_interpolation(): """Test different interpolations""" size = (81, 81) data = np.array([[0, 1]], dtype=int) left = (40, 0) right = (40, 80) center_left = (40, 39) center = (40, 40) center_right = (40, 41) white = (255, 255, 255, 255) black = (0, 0, 0, 255) gray = (128, 128, 128, 255) with TestingCanvas(size=size[::-1], bgcolor="w") as c: view = c.central_widget.add_view(border_width=0) view.camera = PanZoomCamera((0, 0, 2, 1)) image = Image(data=data, cmap='grays', parent=view.scene) # needed to properly initialize the canvas render = c.render() image.interpolation = 'nearest' render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], white) assert np.allclose(render[center_left], black) assert np.allclose(render[center_right], white) image.interpolation = 'bilinear' render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], white) assert np.allclose(render[center], gray, atol=5) # we just want gray, this is not quantitative image.interpolation = 'custom' image.custom_kernel = np.array([[0]]) # no sampling render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], black) assert np.allclose(render[center], black) image.custom_kernel = np.array([[1]]) # same as linear render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], white) assert np.allclose(render[center], gray, atol=5) # we just want gray, this is not quantitative @requires_application() def test_image_set_data_different_dtype(): size = (80, 80) data = np.array([[0, 127]], dtype=np.int8) left = (40, 10) right = (40, 70) white = (255, 255, 255, 255) black = (0, 0, 0, 255) with TestingCanvas(size=size[::-1], bgcolor="w") as c: view = c.central_widget.add_view() view.camera = PanZoomCamera((0, 0, 2, 1)) image = Image(data=data, cmap='grays', clim=[0, 127], parent=view.scene) render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], white) # same data as float should change nothing image.set_data(data.astype(np.float32)) render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], white) # something inverted, different dtype new_data = np.array([[127, 0]], dtype=np.float16) image.set_data(new_data) render = c.render() assert np.allclose(render[left], white) assert np.allclose(render[right], black) # out of bounds should clip (2000 > 127) new_data = np.array([[0, 2000]], dtype=np.float64) image.set_data(new_data) render = c.render() assert np.allclose(render[left], black) assert np.allclose(render[right], white) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_histogram.py0000644000175000017510000000145614507104216021714 0ustar nileshnilesh# -*- coding: utf-8 -*- import numpy as np from vispy.visuals.transforms import STTransform from vispy.scene.visuals import Histogram from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved @requires_application() def test_histogram(): """Test histogram visual""" size = (200, 100) with TestingCanvas(size=size, bgcolor='w') as c: np.random.seed(2397) data = np.random.normal(size=100) hist = Histogram(data, bins=20, color='k', parent=c.scene) hist.transform = STTransform((size[0] // 10, -size[1] // 20, 1), (100, size[1])) assert_image_approved(c.render(), "visuals/histogram.png") run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_ellipse.py0000644000175000017510000001152114507104216021346 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Tests for EllipseVisual All images are of size (100,100) to keep a small file size """ from vispy.scene import visuals, transforms from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved @requires_application() def test_circle_draw(): """Test drawing circles without transform using EllipseVisual""" with TestingCanvas() as c: ellipse = visuals.Ellipse(center=(75, 35, 0), radius=20, color=(1, 0, 0, 1), parent=c.scene) assert_image_approved(c.render(), 'visuals/circle1.png') ellipse.parent = None ellipse = visuals.Ellipse(center=(75, 35, 0), radius=20, color=(1, 0, 0, 1), border_color=(0, 1, 1, 1), parent=c.scene) assert_image_approved(c.render(), 'visuals/circle2.png') ellipse.parent = None ellipse = visuals.Ellipse(center=(75, 35, 0), radius=20, border_color=(0, 1, 1, 1), parent=c.scene) # low corr here because borders have some variability # esp. w/HiDPI assert_image_approved(c.render(), 'visuals/circle3.png', min_corr=0.7) @requires_application() def test_ellipse_draw(): """Test drawing transformed ellipses using EllipseVisual""" with TestingCanvas() as c: ellipse = visuals.Ellipse(center=(0., 0.), radius=(20, 15), color=(0, 0, 1, 1), parent=c.scene) ellipse.transform = transforms.STTransform(scale=(2.0, 3.0), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/ellipse1.png') ellipse.parent = None ellipse = visuals.Ellipse(center=(0., 0.), radius=(20, 15), color=(0, 0, 1, 1), border_color=(1, 0, 0, 1), parent=c.scene) ellipse.transform = transforms.STTransform(scale=(2.0, 3.0), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/ellipse2.png') ellipse.parent = None ellipse = visuals.Ellipse(center=(0., 0.), radius=(20, 15), border_color=(1, 0, 0, 1), parent=c.scene) ellipse.transform = transforms.STTransform(scale=(2.0, 3.0), translate=(50, 50)) assert_image_approved(c.render(), 'visuals/ellipse3.png', min_corr=0.7) @requires_application() def test_arc_draw1(): """Test drawing arcs using EllipseVisual""" with TestingCanvas() as c: ellipse = visuals.Ellipse(center=(50., 50.), radius=(20, 15), start_angle=150., span_angle=120., color=(0, 0, 1, 1), parent=c.scene) assert_image_approved(c.render(), 'visuals/arc1.png') ellipse.parent = None ellipse = visuals.Ellipse(center=(50., 50.), radius=(20, 15), start_angle=150., span_angle=120., border_color=(1, 0, 0, 1), parent=c.scene) assert_image_approved(c.render(), 'visuals/arc2.png', min_corr=0.6) @requires_application() def test_reactive_draw(): """Test reactive ellipse attributes""" with TestingCanvas() as c: ellipse = visuals.Ellipse(center=[75, 35, 0.], radius=[20, 15], color='yellow', parent=c.scene) ellipse.center = [70, 40, 0.] assert_image_approved(c.render(), 'visuals/reactive_ellipse1.png') ellipse.radius = 25 assert_image_approved(c.render(), 'visuals/reactive_ellipse2.png') ellipse.color = 'red' assert_image_approved(c.render(), 'visuals/reactive_ellipse3.png') ellipse.border_color = 'yellow' assert_image_approved(c.render(), 'visuals/reactive_ellipse4.png') ellipse.start_angle = 140. assert_image_approved(c.render(), 'visuals/reactive_ellipse5.png') ellipse.span_angle = 100. assert_image_approved(c.render(), 'visuals/reactive_ellipse6.png') ellipse.num_segments = 10. assert_image_approved(c.render(), 'visuals/reactive_ellipse7.png') run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_colormap.py0000644000175000017510000000723514507104216021534 0ustar nileshnilesh# -*- coding: utf-8 -*- """ Tests for texture-based colormap All images are of size (100,100) to keep a small file size """ import numpy as np from vispy.scene.visuals import Image from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved from vispy.color import Colormap size = (100, 100) @requires_application() def test_colormap(): """Test colormap support for non-uniformly distributed control-points""" with TestingCanvas(size=size, bgcolor='w') as c: idata = np.linspace(255, 0, size[0]*size[1]).astype(np.ubyte) data = idata.reshape((size[0], size[1])) image = Image(cmap=Colormap(colors=['k', 'w', 'r'], controls=[0.0, 0.1, 1.0]), clim='auto', parent=c.scene) image.set_data(data) assert_image_approved(c.render(), "visuals/colormap_kwr.png") @requires_application() def test_colormap_discrete(): """Test discrete RGB colormap""" with TestingCanvas(size=size, bgcolor='w') as c: idata = np.linspace(255, 0, size[0]*size[1]).astype(np.ubyte) data = idata.reshape((size[0], size[1])) image = Image(cmap=Colormap(colors=['r', 'g', 'b'], interpolation='zero'), clim='auto', parent=c.scene) image.set_data(data) assert_image_approved(c.render(), "visuals/colormap_rgb.png") @requires_application() def test_colormap_discrete_nu(): """Test discrete colormap with non-uniformly distributed control-points""" with TestingCanvas(size=size, bgcolor='w') as c: idata = np.linspace(255, 0, size[0]*size[1]).astype(np.ubyte) data = idata.reshape((size[0], size[1])) image = Image(cmap=Colormap(np.array([[0, .75, 0], [.75, .25, .5]]), [0., .25, 1.], interpolation='zero'), clim='auto', parent=c.scene) image.set_data(data) assert_image_approved(c.render(), "visuals/colormap_nu.png") @requires_application() def test_colormap_single_hue(): """Test colormap support using a single hue()""" from vispy.color.colormap import SingleHue with TestingCanvas(size=size, bgcolor='w') as c: idata = np.linspace(255, 0, size[0]*size[1]).astype(np.ubyte) data = idata.reshape((size[0], size[1])) cmap = SingleHue(255) image = Image(cmap=cmap, clim='auto', parent=c.scene) image.set_data(data) assert_image_approved(c.render(), "visuals/colormap_hue.png") @requires_application() def test_colormap_coolwarm(): """Test colormap support using coolwarm preset colormap""" with TestingCanvas(size=size, bgcolor='w') as c: idata = np.linspace(255, 0, size[0]*size[1]).astype(np.ubyte) data = idata.reshape((size[0], size[1])) image = Image(cmap='coolwarm', clim='auto', parent=c.scene) image.set_data(data) assert_image_approved(c.render(), "visuals/colormap_coolwarm.png") @requires_application() def test_colormap_CubeHelix(): """Test colormap support using cubehelix colormap in only blues""" from vispy.color.colormap import CubeHelixColormap with TestingCanvas(size=size, bgcolor='w') as c: idata = np.linspace(255, 0, size[0]*size[1]).astype(np.ubyte) data = idata.reshape((size[0], size[1])) cmap = CubeHelixColormap(rot=0, start=0) image = Image(cmap=cmap, clim='auto', parent=c.scene) image.set_data(data) assert_image_approved(c.render(), "visuals/colormap_cubehelix.png") run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_colorbar.py0000644000175000017510000001354314507104216021522 0ustar nileshnilesh# -*- coding: utf-8 -*- """ Tests for ColorbarVisual All images are of size (100,100) to keep a small file size """ from vispy.scene import visuals from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main, raises) from vispy.testing.image_tester import assert_image_approved def create_colorbar(pos, size, orientation, label='label string here'): colorbar = visuals.ColorBar(pos=pos, size=size, orientation=orientation, label=label, cmap='autumn', border_color='white', border_width=2) colorbar.label.color = 'white' colorbar.label.font_size = 5 colorbar.ticks[0].color = 'white' colorbar.ticks[0].font_size = 5 colorbar.ticks[1].color = 'white' colorbar.ticks[1].font_size = 5 return colorbar @requires_application() def test_colorbar_draw(): """Test drawing Colorbar without transform using ColorbarVisual""" with TestingCanvas() as c: colorbar_top = create_colorbar(pos=(50, 50), size=(60, 4), orientation='top') c.draw_visual(colorbar_top) assert_image_approved(c.render(), 'visuals/colorbar/top.png') colorbar_top.parent = None colorbar_bottom = create_colorbar(pos=(50, 50), size=(60, 4), orientation='bottom') c.draw_visual(colorbar_bottom) assert_image_approved(c.render(), 'visuals/colorbar/bottom.png') colorbar_bottom.parent = None colorbar_left = create_colorbar(pos=(50, 50), size=(60, 4), orientation='left') c.draw_visual(colorbar_left) assert_image_approved(c.render(), 'visuals/colorbar/left.png') colorbar_left.parent = None colorbar_right = create_colorbar(pos=(50, 50), size=(60, 4), orientation='right') c.draw_visual(colorbar_right) assert_image_approved(c.render(), 'visuals/colorbar/right.png') @requires_application() def test_reactive_draw(): """Test reactive RectPolygon attributes""" with TestingCanvas() as c: colorbar = create_colorbar(pos=(50, 50), size=(60, 4), orientation='top') c.draw_visual(colorbar) colorbar.cmap = "ice" assert_image_approved(c.render(), 'visuals/colorbar/reactive_cmap.png') colorbar.clim = (-20, 20) assert_image_approved(c.render(), 'visuals/colorbar/reactive_clim.png') colorbar.label.text = "new label" assert_image_approved(c.render(), 'visuals/colorbar/reactive_label.png') colorbar.ticks[0].color = "red" colorbar.ticks[1].color = "blue" assert_image_approved(c.render(), 'visuals/colorbar/reactive_ticks.png') colorbar.border_width = 0 assert_image_approved(c.render(), 'visuals/colorbar/reactive_border_width.png') colorbar.border_width = 5 colorbar.border_color = "red" assert_image_approved(c.render(), 'visuals/colorbar/reactive_border_color.png') @requires_application() def test_attributes(): """Test if attribute checks are in place""" with TestingCanvas(): # initialize with major axis < minor axis with raises(ValueError): create_colorbar(pos=(50, 50), size=(1, 30), orientation='top') # set major axis to 0 with raises(ValueError): create_colorbar(pos=(50, 50), size=(0, 1), orientation='right') # set negative major axis with raises(ValueError): create_colorbar(pos=(50, 50), size=(-10, 1), orientation='right') # set negative minor axis with raises(ValueError): create_colorbar(pos=(50, 50), size=(1, -10), orientation='right') # set minor axis to 0 with raises(ValueError): create_colorbar(pos=(50, 50), size=(1, 0), orientation='right') # set invalid orientation with raises(ValueError): create_colorbar(pos=(50, 50), size=(60, 4), orientation='top-invalid') @requires_application() def test_colorbar_label_change(): with TestingCanvas() as c: colorbar = create_colorbar(pos=(50, 50), size=(60, 4), orientation='top') colorbar.cmap = "ice" orig_text_vis = colorbar.label colorbar.label = "New Label" assert colorbar.label.text == "New Label" assert colorbar.label is orig_text_vis c.draw_visual(colorbar) @requires_application() def test_colorbar_label_as_textvisual(): with TestingCanvas() as c: label = visuals.Text("my label") colorbar = create_colorbar(pos=(50, 50), size=(60, 4), orientation='top', label=label) colorbar.cmap = "ice" assert colorbar.label.text == "my label" assert colorbar.label is label c.draw_visual(colorbar) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_collections.py0000644000175000017510000000106714507104216022233 0ustar nileshnilesh# *Very* basic collections tests from vispy.visuals.collections import (PathCollection, PointCollection, PolygonCollection, SegmentCollection, TriangleCollection) from vispy.testing import requires_application, TestingCanvas @requires_application() def test_init(): """Test collection initialization""" with TestingCanvas(): for coll in (PathCollection, PointCollection, PolygonCollection, SegmentCollection, TriangleCollection): coll() vispy-0.14.1/vispy/visuals/tests/test_axis.py0000644000175000017510000001034514507104216020660 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """Tests for AxisVisual""" from numpy.testing import assert_allclose, assert_array_equal from vispy import scene from vispy.scene import visuals from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) @requires_application() def test_axis(): with TestingCanvas() as c: axis = visuals.Axis(pos=[[-1.0, 0], [1.0, 0]], parent=c.scene) c.draw_visual(axis) @requires_application() def test_axis_zero_domain(): # Regression test for a bug that caused an overflow error when the domain # min was same as max with TestingCanvas() as c: axis = visuals.Axis(pos=[[-1.0, 0], [1.0, 0]], domain=(0.5, 0.5), parent=c.scene) c.draw_visual(axis) @requires_application() def test_rotation_angle(): # Make sure the rotation angle calculation is correct canvas = scene.SceneCanvas(keys=None, size=(800, 600), show=True) view = canvas.central_widget.add_view() view.camera = scene.cameras.TurntableCamera(parent=view.scene, fov=0., distance=4.0, elevation=0, azimuth=0, roll=0.) axis1 = visuals.Axis(pos=[[-1.0, 0], [1.0, 0]], parent=view.scene) assert_allclose(axis1._rotation_angle, 0) axis2 = visuals.Axis(pos=[[-3**0.5/2., -0.5], [3**0.5/2., 0.5]], parent=view.scene) assert_allclose(axis2._rotation_angle, 0.) view.camera.elevation = 90. assert_allclose(axis1._rotation_angle, 0) assert_allclose(axis2._rotation_angle, -30, rtol=1e-3) view.camera.elevation = 45. assert_allclose(axis1._rotation_angle, 0) assert_allclose(axis2._rotation_angle, -22.207653, rtol=1e-3) view.camera.fov = 20. assert_allclose(axis1._rotation_angle, 0) # OSX Travis has some small differences...sometimes assert_allclose(axis2._rotation_angle, -17.056795, rtol=0.05) @requires_application() def test_text_position(): # Test the labels text and position of the axis depending on its domain canvas = scene.SceneCanvas(keys=None, size=(800, 600), show=True) view = canvas.central_widget.add_view() view.camera = scene.cameras.PanZoomCamera(parent=view.scene) # tick length and label margin to 0 for nice values axis1 = visuals.Axis(pos=[[-1.0, 0], [1.0, 0]], domain=(0., 1.25), major_tick_length=0, tick_label_margin=0, parent=view.scene) canvas.draw_visual(axis1) assert_allclose(axis1._text.pos[:, 0], (-1, -0.2, 0.6)) assert_array_equal(axis1._text.text, ('0', '0.5', '1')) # Flip the axis domain axis1.domain = (1.25, 0.) canvas.draw_visual(axis1) # Text should be unchanged and positions mirrored assert_allclose(axis1._text.pos[:, 0], (1, 0.2, -0.6)) assert_array_equal(axis1._text.text, ('0', '0.5', '1')) @requires_application() def test_tick_position(): # Test the position of the ticks depending on the axis domain canvas = scene.SceneCanvas(keys=None, size=(800, 600), show=True) view = canvas.central_widget.add_view() view.camera = scene.cameras.PanZoomCamera(parent=view.scene) axis1 = visuals.Axis(pos=[[-1.0, 0], [1.0, 0]], domain=(0., 1.25), parent=view.scene) canvas.draw_visual(axis1) # Get a nice array of x ticks positions x_ticks_positions = axis1._ticks.pos[::2, ::2].flatten() # Compare major ticks first assert_allclose(x_ticks_positions[:3], (-1, -0.2, 0.6)) # Then minor ticks assert_allclose(x_ticks_positions[3:], (-0.84, -0.68, -0.52, -0.36, -0.04, 0.12, 0.28, 0.44, 0.76, 0.92)) # Flip the axis domain axis1.domain = (1.25, 0.) canvas.draw_visual(axis1) x_ticks_positions = axis1._ticks.pos[::2, ::2].flatten() # Positions should be mirrored assert_allclose(x_ticks_positions[:3], (1, 0.2, -0.6)) assert_allclose(x_ticks_positions[3:], (0.84, 0.68, 0.52, 0.36, 0.04, -0.12, -0.28, -0.44, -0.76, -0.92)) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/test_arrows.py0000644000175000017510000000675214507104216021240 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from vispy.visuals.line.arrow import ARROW_TYPES from vispy.scene import visuals, transforms from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main, assert_raises, SkipTest, IS_TRAVIS_CI) from vispy.testing.image_tester import assert_image_approved vertices = np.array([ [25, 25], [25, 75], [50, 25], [50, 75], [75, 25], [75, 75] ]).astype(np.float32) vertices += 0.33 arrows = np.array([ vertices[:2], vertices[3:1:-1], vertices[4:], vertices[-1:-3:-1] ]).reshape((4, 4)) @requires_application() def test_arrow_draw(): """Test drawing arrows without transforms""" with TestingCanvas() as c: if IS_TRAVIS_CI and c.app.backend_name.lower() == 'pyqt4': # TODO: Fix this (issue #1042 raise SkipTest('Travis fails due to FB stack problem') for arrow_type in ARROW_TYPES: arrow = visuals.Arrow(pos=vertices, arrow_type=arrow_type, arrows=arrows, arrow_size=10, color='red', connect="segments", parent=c.scene) assert_image_approved(c.render(), 'visuals/arrow_type_%s.png' % arrow_type) arrow.parent = None @requires_application() def test_arrow_transform_draw(): """Tests the ArrowVisual when a transform is applied""" with TestingCanvas() as c: if IS_TRAVIS_CI and c.app.backend_name.lower() == 'pyqt4': # TODO: Fix this (issue #1042 raise SkipTest('Travis fails due to FB stack problem') for arrow_type in ARROW_TYPES: arrow = visuals.Arrow(pos=vertices, arrow_type=arrow_type, arrows=arrows, arrow_size=10, color='red', connect="segments", parent=c.scene) arrow.transform = transforms.STTransform(scale=(0.5, 0.75), translate=(-20, -20)) assert_image_approved(c.render(), 'visuals/arrow_transform_type_%s.png' % arrow_type) arrow.parent = None @requires_application() def test_arrow_reactive(): """Tests the reactive behaviour of the ArrowVisual properties.""" with TestingCanvas() as c: arrow = visuals.Arrow(pos=vertices, arrows=arrows, connect="segments", parent=c.scene) arrow.arrow_type = "stealth" assert_image_approved(c.render(), 'visuals/arrow_reactive1.png') arrow.arrow_size = 20 assert_image_approved(c.render(), 'visuals/arrow_reactive2.png') @requires_application() def test_arrow_attributes(): """Tests if the ArrowVisual performs the required checks for attributes.""" with TestingCanvas() as c: arrow = visuals.Arrow(pos=vertices, arrow_type="stealth", arrows=arrows, arrow_size=10, color='red', connect="segments", parent=c.scene) def size_test(): arrow.arrow_size = 0.0 def type_test(): arrow.arrow_type = "random_non_existent" assert_raises( ValueError, size_test ) assert_raises( ValueError, type_test ) run_tests_if_main() vispy-0.14.1/vispy/visuals/tests/__init__.py0000644000175000017510000000000014507104216020377 0ustar nileshnileshvispy-0.14.1/vispy/visuals/surface_plot.py0000644000175000017510000001615714507104216020210 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) 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 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/2D array of values specifying the x positions of vertices in the grid. In case 1D array given as input, the values will be replicated to fill the 2D array of size(z). If None, values will be assumed to be integers. y : ndarray | None 1D/2D array of values specifying the y positions of vertices in the grid. In case 1D array given as input, the values will be replicated to fill the 2D array of size(z). 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.__vertices = None self.__faces = None self.__meshdata = MeshData() kwargs.setdefault('shading', 'smooth') MeshVisual.__init__(self, **kwargs) self.set_data(x, y, z, colors) def _update_x_data(self, x): if x is not None: if self._x is None or len(x) != len(self._x): self.__vertices = None self._x = x def _update_y_data(self, y): if y is not None: if self._y is None or len(y) != len(self._y): self.__vertices = None self._y = y def _update_z_data(self, z): 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 def _update_mesh_vertices(self, x_is_new, y_is_new, z_is_new): new_vertices = False update_vertices = False update_faces = False # Generate vertex and face array if self.__vertices is None: self.__vertices = np.empty((self._z.shape[0], self._z.shape[1], 3), dtype=np.float32) self.__faces = self._generate_faces() new_vertices = True update_faces = True # Copy x, y, z data into vertex array if new_vertices or x_is_new: if not x_is_new and self._x is None: x = np.arange(self._z.shape[0]) else: x = self._x if x.ndim == 1: x = x.reshape(len(x), 1) # Copy the 2D data into the appropriate slice self.__vertices[:, :, 0] = x update_vertices = True if new_vertices or y_is_new: if not y_is_new and self._y is None: y = np.arange(self._z.shape[1]) else: y = self._y if y.ndim == 1: y = y.reshape(1, len(y)) # Copy the 2D data into the appropriate slice self.__vertices[:, :, 1] = y update_vertices = True if new_vertices or z_is_new: self.__vertices[..., 2] = self._z update_vertices = True return update_faces, update_vertices def _prepare_mesh_colors(self, colors): if colors is None: return colors = np.asarray(colors) if colors.ndim == 3: # convert (width, height, 4) to (num_verts, 4) vert_shape = self.__vertices.shape num_vertices = vert_shape[0] * vert_shape[1] colors = colors.reshape(num_vertices, 3) return 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/2D array of values specifying the x positions of vertices in the grid. In case 1D array given as input, the values will be replicated to fill the 2D array of size(z). If None, values will be assumed to be integers. y : ndarray | None 1D/2D array of values specifying the x positions of vertices in the grid. In case 1D array given as input, the values will be replicated to fill the 2D array of size(z). 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. """ self._update_x_data(x) self._update_y_data(y) self._update_z_data(z) if self._z is None: # no mesh data to plot so no need to update return update_faces, update_vertices = self._update_mesh_vertices( x is not None, y is not None, z is not None ) colors = self._prepare_mesh_colors(colors) update_colors = colors is not None if update_colors: self.__meshdata.set_vertex_colors(colors) if update_faces: self.__meshdata.set_faces(self.__faces) if update_vertices: self.__meshdata.set_vertices( self.__vertices.reshape(self.__vertices.shape[0] * self.__vertices.shape[1], 3)) if update_faces or update_vertices or update_colors: 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) return faces vispy-0.14.1/vispy/visuals/sphere.py0000644000175000017510000000602314507104216016777 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from ..geometry import create_sphere from .mesh import MeshVisual from .visual import CompoundVisual class SphereVisual(CompoundVisual): """Visual that displays a sphere Parameters ---------- radius : float The size of the sphere. cols : int Number of cols that make up the sphere mesh (for method='latitude' and 'cube'). rows : int Number of rows that make up the sphere mesh (for method='latitude' and 'cube'). depth : int Number of depth segments that make up the sphere mesh (for method='cube'). subdivisions : int Number of subdivisions to perform (for method='ico'). method : str Method for generating sphere. Accepts 'latitude' for latitude-longitude, 'ico' for icosahedron, and 'cube' for cube based tessellation. vertex_colors : ndarray Same as for `MeshVisual` class. See `create_sphere` for vertex ordering. face_colors : ndarray Same as for `MeshVisual` class. See `create_sphere` for vertex ordering. color : Color The `Color` to use when drawing the sphere faces. edge_color : tuple or Color The `Color` to use when drawing the sphere edges. If `None`, then no sphere edges are drawn. shading : str | None Shading to use. """ def __init__(self, radius=1.0, cols=30, rows=30, depth=30, subdivisions=3, method='latitude', vertex_colors=None, face_colors=None, color=(0.5, 0.5, 1, 1), edge_color=None, shading=None, **kwargs): mesh = create_sphere(rows, cols, depth, radius=radius, subdivisions=subdivisions, method=method) self._mesh = MeshVisual(vertices=mesh.get_vertices(), faces=mesh.get_faces(), vertex_colors=vertex_colors, face_colors=face_colors, color=color, shading=shading) if edge_color: self._border = MeshVisual(vertices=mesh.get_vertices(), faces=mesh.get_edges(), color=edge_color, mode='lines') else: self._border = MeshVisual() CompoundVisual.__init__(self, [self._mesh, self._border], **kwargs) self.mesh.set_gl_state(polygon_offset_fill=True, polygon_offset=(1, 1), depth_test=True) @property def mesh(self): """The vispy.visuals.MeshVisual that used to fil in.""" return self._mesh @property def border(self): """The vispy.visuals.MeshVisual that used to draw the border.""" return self._border vispy-0.14.1/vispy/visuals/spectrogram.py0000644000175000017510000001151414507104216020040 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 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. normalize : bool Normalization of spectrogram values across frequencies. 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=None, n_fft=256, step=None, fs=1., window='hann', normalize=False, color_scale='log', cmap='cubehelix', clim='auto'): self._x = np.asarray(x) self._n_fft = int(n_fft) self._step = step self._fs = float(fs) self._window = window self._normalize = normalize self._color_scale = color_scale if clim == 'auto': self._clim_auto = True else: self._clim_auto = False if not isinstance(self._color_scale, str) or \ self._color_scale not in ('log', 'linear'): raise ValueError('color_scale must be "linear" or "log"') data = self._calculate_spectrogram() super(SpectrogramVisual, self).__init__(data, clim=clim, cmap=cmap) @property def freqs(self): """The spectrogram frequencies""" return fft_freqs(self._n_fft, self._fs) @property def x(self): """The original signal""" return self._x @x.setter def x(self, x): self._x = np.asarray(x) self._update_image() @property def n_fft(self): """The length of fft window""" return self._n_fft @n_fft.setter def n_fft(self, n_fft): self._n_fft = int(n_fft) self._update_image() @property def step(self): """The step of fft window""" if self._step is None: return self._n_fft // 2 else: return self._step @step.setter def step(self, step): self._step = step self._update_image() @property def fs(self): """The sampling frequency""" return self._fs @fs.setter def fs(self, fs): self._fs = fs self._update_image() @property def window(self): """The used window function""" return self._window @window.setter def window(self, window): self._window = window self._update_image() @property def color_scale(self): """The color scale""" return self._color_scale @color_scale.setter def color_scale(self, color_scale): if not isinstance(color_scale, str) or \ color_scale not in ('log', 'linear'): raise ValueError('color_scale must be "linear" or "log"') self._color_scale = color_scale self._update_image() @property def normalize(self): """The normalization setting""" return self._normalize @normalize.setter def normalize(self, normalize): self._normalize = normalize self._update_image() def _calculate_spectrogram(self): if self._x is not None: x = self._x nan_mean = np.nanmean(x) idx = np.isnan(x) x[idx] = nan_mean data = stft(x, self._n_fft, self._step, self._fs, self._window) data = np.abs(data) data = 20 * np.log10(data) if self._color_scale == 'log' else data if self._normalize: for i in range(data.shape[0]): data[i, :] -= np.mean(data[i, :]) data[i, :] /= np.std(data[i, :]) return data.astype(np.float32) # ImageVisual will warn if 64-bit else: return None def _update_image(self): data = self._calculate_spectrogram() self.set_data(data) self.update() if self._clim_auto: self.clim = 'auto' vispy-0.14.1/vispy/visuals/shaders/0000755000175000017510000000000014507104251016566 5ustar nileshnileshvispy-0.14.1/vispy/visuals/shaders/variable.py0000644000175000017510000002212214507104216020725 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np 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. """ _vtype_32_conversion = {'in': 'attribute', 'out': 'varying'} _vtype_23_conversion = {'attribute': 'in', 'varying': 'out'} 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, str) or name is None): raise TypeError("Variable name must be string or None.") self._state_counter = 0 self._name = name self._vtype = self._vtype_32_conversion.get(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, or varying). For in/out variables (GLSL 150+), vtype is 'varying'. """ 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 _vtype_for_version(self, version): """Return the vtype for this variable, converted based on the GLSL version.""" vtype = self.vtype if version is None or version[0] == 120: return self._vtype_32_conversion.get(vtype, vtype) else: return self._vtype_23_conversion.get(vtype, vtype) def definition(self, names, version, shader): 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] vtype = self._vtype_for_version(version) if vtype == 'const': return '%s %s %s = %s;' % (vtype, self.dtype, name, self.value) else: return '%s %s %s;' % (vtype, self.dtype, name) class Varying(Variable): """Representation of a varying (variables passed from one shader to the next). Varyings can inherit their dtype from another Variable, allowing for more flexibility in composing shaders. """ def __init__(self, name, dtype=None): self._link = None self._src_func = None self._dst_func = 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() def invar(self, array=False): """Return a varying that defines itself using the same name as this, but as an `in` variable instead of `out`. """ return InVar(self, array=array) class InVar(Variable): def __init__(self, var, array=False): self._var = var self._array = array Variable.__init__(self, var.name) @property def value(self): """The value associated with this variable.""" return self._var.value @value.setter def value(self, value): if value is not None: raise TypeError("Cannot assign value directly to varying.") @property def dtype(self): return self._var.dtype def definition(self, names, version, shader): # inherit name from source variable name = names[self._var] dtype = self._var.dtype if version[0] <= 120: return "varying %s %s;" % (dtype, name) else: if self._array: return "in %s %s[];" % (dtype, name) else: return "in %s %s;" % (dtype, name) def expression(self, names): return names[self._var] vispy-0.14.1/vispy/visuals/shaders/tests/0000755000175000017510000000000014507104251017730 5ustar nileshnileshvispy-0.14.1/vispy/visuals/shaders/tests/test_parsing.py0000644000175000017510000000270014507104216023004 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/visuals/shaders/tests/test_multiprogram.py0000644000175000017510000000427214507104216024071 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy.visuals.shaders import MultiProgram, Function, StatementList from vispy.visuals.transforms import STTransform, MatrixTransform def test_multiprogram(): vert = """ uniform float u_scale; void main() { gl_Position = $transform(vec4(0, 0, 0, 0)); } """ frag = """ void main() { gl_FragColor = $color; $post_hook } """ # test adding programs mp = MultiProgram(vert, frag) p1 = mp.add_program() p2 = mp.add_program('p2') assert 'p2' in mp._programs # test weak reference to program mp.add_program('junk') assert 'junk' not in mp._programs and len(mp._programs) == 2 # test setting variables on multiprogram or individual programs mp['u_scale'] = 2 assert p1['u_scale'] == 2 assert p2['u_scale'] == 2 p1['u_scale'] = 3 assert p1['u_scale'] == 3 assert p2['u_scale'] == 2 # test setting template variables globally mp.frag['color'] = (1, 1, 1, 1) assert p1.frag['color'].value == (1, 1, 1, 1) assert p2.frag['color'].value == (1, 1, 1, 1) # test setting template variables per-program func = Function(""" void filter() { gl_FragColor.r = 0.5; } """) p1.frag['post_hook'] = StatementList() p2.frag['post_hook'] = StatementList() p2.frag['post_hook'].add(func()) tr1 = STTransform() tr2 = MatrixTransform() p1.vert['transform'] = tr1 p2.vert['transform'] = tr2 assert 'st_transform_map' in p1.vert.compile() assert 'affine_transform_map' in p2.vert.compile() assert 'filter' not in p1.frag.compile() assert 'filter' in p2.frag.compile() # test changing shader code mp.vert = vert + '\n//test\n' mp.vert['transform'] = tr1 assert '//test' in p1.vert.compile() # test that newly-added programs inherit all previously set variables p3 = mp.add_program() assert p3['u_scale'] == 2 assert p3.frag['color'].value == (1, 1, 1, 1) assert '//test' in p3.vert.compile() assert 'st_transform_map' in p3.vert.compile() vispy-0.14.1/vispy/visuals/shaders/tests/test_function.py0000644000175000017510000003174114507104216023175 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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, ('120', ''))) 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'}, ('120', ''), None), '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) # Test that `var in fun` syntax works as well assert 'foo' in fun assert 'bar' in fun assert 'baz' not in fun 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'].add(expr, 0) assert list(main['pre'].items) == [expr] main['pre'].add(expr) assert list(main['pre'].items) == [expr] code = main.compile() assert " func();" in code main['pre'].remove(expr) assert list(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('vertex', 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.14.1/vispy/visuals/shaders/tests/__init__.py0000644000175000017510000000000014507104216022030 0ustar nileshnileshvispy-0.14.1/vispy/visuals/shaders/shader_object.py0000644000175000017510000001321314507104216021735 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from collections import OrderedDict from weakref import WeakKeyDictionary 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, str) 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, str): 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 @property def version_pragma(self): """Return version number and extra qualifiers from pragma if present.""" return None def definition(self, obj_names, version, shader): """Return the GLSL definition for this object. Use *obj_names* to determine the names of dependencies, and *version* (number, qualifier) to adjust code output. """ 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.""" 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.14.1/vispy/visuals/shaders/program.py0000644000175000017510000001267114507104216020617 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import logging from weakref import WeakKeyDictionary 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='', fcode='', gcode=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_cache = WeakKeyDictionary() # List of settable variables to be checked for value changes self._variables = [] self._vert = MainFunction('vertex', '') self._frag = MainFunction('fragment', '') self._vert._dependents[self] = None self._frag._dependents[self] = None self._geom = None self.vert = vcode self.frag = fcode self.geom = gcode @property def vert(self): return self._vert @vert.setter def vert(self, vcode): vcode = preprocess(vcode) self._vert.code = vcode 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): fcode = preprocess(fcode) self._frag.code = fcode self._need_build = True self.changed(code_changed=True, value_changed=False) @property def geom(self): return self._geom @geom.setter def geom(self, gcode): if gcode is None: self._geom = None return gcode = preprocess(gcode) if self._geom is None: self._geom = MainFunction('geometry', '') self._geom._dependents[self] = None self._geom.code = gcode self._need_build = True self.changed(code_changed=True, value_changed=False) def _dep_changed(self, dep, code_changed=False, value_changed=False): if code_changed and logger.level <= logging.DEBUG: logger.debug("ModularProgram changed: %s source=%s, values=%s", self, code_changed, value_changed) import traceback traceback.print_stack() 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() self.update_variables() Program.draw(self, *args, **kwargs) def build_if_needed(self): """Reset shader source if necesssary.""" if self._need_build: self._build() # after recompile, we need to upload all variables again # (some variables may have changed name) self._variable_cache.clear() # Collect a list of all settable variables settable_vars = 'attribute', 'uniform', 'in' deps = [d for d in self.vert.dependencies() if ( isinstance(d, Variable) and d.vtype in settable_vars)] deps += [d for d in self.frag.dependencies() if ( isinstance(d, Variable) and d.vtype == 'uniform')] if self.geom is not None: deps += [d for d in self.geom.dependencies() if ( isinstance(d, Variable) and d.vtype == 'uniform')] self._variables = deps self._need_build = False def _build(self): logger.debug("Rebuild ModularProgram: %s", self) shaders = {'vert': self.vert, 'frag': self.frag} if self.geom is not None: shaders['geom'] = self.geom self.compiler = Compiler(**shaders) code = self.compiler.compile() # Update shader code, but don't let the program update variables yet code['update_variables'] = False self.set_shaders(**code) logger.debug('==== Vertex Shader ====\n\n%s\n', code['vert']) if 'geom' in code: logger.debug('==== Geometry shader ====\n\n%s\n', code['geom']) logger.debug('==== Fragment shader ====\n\n%s\n', code['frag']) def update_variables(self): # Set any variables that have a new value logger.debug("Apply variables:") for dep in sorted(self._variables, key=lambda d: self.compiler[d]): name = self.compiler[dep] state_id = dep.state_id if self._variable_cache.get(dep, None) != state_id: self[name] = dep.value self._variable_cache[dep] = state_id logger.debug(" %s = %s **", name, dep.value) else: logger.debug(" %s = %s", name, dep.value) # Process any pending variables and discard anything else that is # not active in the program (otherwise we get lots of warnings). self._process_pending_variables() logger.debug("Discarding unused variables before draw: %s" % self._pending_variables.keys()) self._pending_variables = {} vispy-0.14.1/vispy/visuals/shaders/parsing.py0000644000175000017510000001077014507104216020611 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import re # regular expressions for parsing GLSL re_version_pragma = r'#version\s+(\d+)(.*)?(//.*)?' 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|in|out|inout)' # template variables like # $func_name re_template_var = (r"(?:(?:\$" + re_identifier + r")|(?:\$\{" + re_identifier + r"\}))") # 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 + r")\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 + r"?\s*(" + re_type + r")\s+(" + re_identifier + r"(\s*,\s*(" + re_identifier + "))*))") # list of variable declarations like "vec4 var_name, float other_var_name" re_arg_list = "(" + re_declaration + r"(?:,\s*" + re_declaration + ")*)?" # function declaration like "vec4 function_name(float x, float y)" re_func_decl = ("(" + re_type + r")\s+" + re_func_name + r"\s*\((void|" + re_arg_list + r")\)") # anonymous variable declarations may or may not include a name: # "vec4" or "vec4 var_name" re_anon_decl = "(?:(" + re_type + r")(?:\s+" + re_identifier + ")?)" # list of anonymous declarations re_anon_arg_list = "(" + re_anon_decl + r"(?:,\s*" + re_anon_decl + ")*)?" # function prototype declaration like # "vec4 function_name(float, float);" re_func_prot = ("(" + re_type + r")\s+" + re_func_name + r"\((void|" + re_anon_arg_list + r")\)\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(r"^\s*" + re_func_decl + r"\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 = r"^\s*" + re_func_decl + r"\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(r"\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.14.1/vispy/visuals/shaders/multiprogram.py0000644000175000017510000001050014507104216021657 0ustar nileshnileshimport weakref from .program import ModularProgram class MultiProgram(object): """A collection of ModularPrograms that emulates the API of a single ModularProgram. A single Visual is often drawn in many different ways--viewed under different transforms, with different clipping boundaries, or with different colors as in picking and anaglyph stereo. Each draw may require a different program. To simplify this process, MultiProgram exposes an API that looks very much like a single ModularProgram, but internally manages many programs. """ def __init__(self, vcode='', fcode='', gcode=None): self._vcode = vcode self._fcode = fcode self._gcode = gcode self._programs = weakref.WeakValueDictionary() self._set_items = {} self._next_prog_id = 0 self._vert = MultiShader(self, 'vert') self._frag = MultiShader(self, 'frag') self._geom = None if gcode is None else MultiShader(self, 'geom') def add_program(self, name=None): """Create a program and add it to this MultiProgram. It is the caller's responsibility to keep a reference to the returned program. The *name* must be unique, but is otherwise arbitrary and used for debugging purposes. """ if name is None: name = 'program' + str(self._next_prog_id) self._next_prog_id += 1 if name in self._programs: raise KeyError("Program named '%s' already exists." % name) # create a program and update it to look like the rest prog = ModularProgram(self._vcode, self._fcode, self._gcode) for key, val in self._set_items.items(): prog[key] = val self.frag._new_program(prog) self.vert._new_program(prog) if self._geom is not None: self.geom._new_program(prog) self._programs[name] = prog return prog @property def vert(self): """A wrapper around all vertex shaders contained in this MultiProgram.""" return self._vert @vert.setter def vert(self, code): self._vcode = code for p in self._programs.values(): p.vert = code @property def frag(self): """A wrapper around all fragment shaders contained in this MultiProgram.""" return self._frag @frag.setter def frag(self, code): self._fcode = code for p in self._programs.values(): p.frag = code @property def geom(self): """A wrapper around all geometry shaders contained in this MultiProgram.""" return self._geom @geom.setter def geom(self, code): self._gcode = code if self._geom is None: self._geom = MultiShader(self, 'geom') for p in self._programs.values(): p.geom = code def __contains__(self, key): return any(key in p for p in self._programs.values()) def __getitem__(self, key): return self._set_items[key] def __setitem__(self, key, value): self._set_items[key] = value for program in self._programs.values(): program[key] = value def __iter__(self): for p in self._programs.values(): yield p def bind(self, data): for name in data.dtype.names: self[name] = data[name] class MultiShader(object): """Emulates the API of a MainFunction while wrapping all vertex or fragment shaders in a MultiProgram. Example:: mp = MultiProgram(vert, frag) mp.add_program('p1') mp.add_program('p2') # applies to all programs mp.vert['u_scale'] = (1, 2) # applies to one program mp.get_program('p1').frag['u_color'] = (1, 1, 1, 1) """ def __init__(self, program, shader): self._program = program self._shader = shader self._set_items = {} def __getitem__(self, key): return self._set_items[key] def __setitem__(self, key, value): self._set_items[key] = value for p in self._program._programs.values(): getattr(p, self._shader)[key] = value def _new_program(self, p): """New program was added to the multiprogram; update items in the shader.""" for k, v in self._set_items.items(): getattr(p, self._shader)[k] = v vispy-0.14.1/vispy/visuals/shaders/function.py0000644000175000017510000006342014507104216020773 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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. """ from collections import OrderedDict import logging import re import numpy as np from ...util.eq import eq from ...util import logger 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. Notes ----- 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) self.code = code # 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() 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, str): 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: %r\nReplacement: %r\nSource: %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) def __contains__(self, key): return key in self.template_vars # 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 @code.setter def code(self, code): # Get and strip code if isinstance(code, Function): code = code._code elif not isinstance(code, str): 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 # Create static Variable instances for any global variables declared # in the code self._static_vars = None @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, version, shader): """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, version, shader): return self._get_replaced_code(names, version, shader) 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, shader_type, *args, **kwargs): self.shader_type = shader_type self._chains = {} Function.__init__(self, *args, **kwargs) @property def signature(self): return ('main', [], 'void') @property def version_pragma(self): """Return version number and extra qualifiers from pragma if present.""" m = re.search(parsing.re_version_pragma, self.code) if m is None: return None return int(m.group(1)), m.group(2) def definition(self, obj_names, version, shader): code = Function.definition(self, obj_names, version, shader) # strip out version pragma before returning code; this will be # added to the final compiled code later. code = re.sub(parsing.re_version_pragma, '', code) return code def static_names(self): if self._static_vars is not None: return self._static_vars # 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]) self._static_vars = names 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, str)): 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 code(self): # Code is generated at compile time; hopefully it is not requested # before then.. return None @code.setter def code(self, c): raise TypeError("Cannot set code property on FunctionChain.") @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, version, shader): 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 = {} self.order = [] ShaderObject.__init__(self) def add(self, item, position=5): """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.items[item] = position self._add_dep(item) self.order = None self.changed(code_changed=True) def remove(self, item): """Remove an item from the list.""" self.items.pop(item) self._remove_dep(item) self.order = None self.changed(code_changed=True) def expression(self, obj_names): if self.order is None: self.order = list(self.items.items()) self.order.sort(key=lambda x: x[1]) code = "" for item, pos in self.order: code += item.expression(obj_names) + ';\n' return code vispy-0.14.1/vispy/visuals/shaders/expression.py0000644000175000017510000000532714507104216021347 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. 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, version): # 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, str): 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, str): 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.14.1/vispy/visuals/shaders/compiler.py0000644000175000017510000001620414507104216020756 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division 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, namespace=None, **shaders): # cache of compilation results for each function and variable if namespace is None: namespace = {} self._object_names = namespace # {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 = [] version = shader.version_pragma for dep in self._shader_deps[shader_name]: dep_code = dep.definition(obj_names, version, shader) if dep_code is not None: code.append(dep_code) if version is not None: code.insert(0, '#version %s %s' % version) 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.14.1/vispy/visuals/shaders/__init__.py0000644000175000017510000000124214507104216020677 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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', 'MultiProgram'] 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 from .multiprogram import MultiProgram # noqa vispy-0.14.1/vispy/visuals/scrolling_lines.py0000644000175000017510000001563114507104216020704 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from .visual import Visual from .. import gloo class ScrollingLinesVisual(Visual): """Displays many line strips of equal length, with the option to add new vertex data to one end of the lines. Parameters ---------- n_lines : int The number of independent line strips to draw. line_size : int The number of samples in each line strip. dx : float The x distance between samples color : array-like An array of colors to assign to each line strip. pos_offset : array-like An array of x, y position offsets to apply to each line strip. columns : int Arrange line strips into a grid with this number of columns. This option is not compatible with *pos_offset*. cell_size : tuple The x, y distance between cells in the grid. """ vertex_code = """ attribute vec2 index; // .x=line_n, .y=vertex_n uniform sampler2D position; uniform sampler1D pos_offset; uniform sampler1D color_tex; uniform vec2 pos_size; // x=n_lines, y=n_verts_per_line uniform float offset; // rolling pointer into vertexes uniform float dx; // x step per sample varying vec2 v_index; varying vec4 v_color; void main() { v_index = vec2(mod(index.y + offset, pos_size.y), index.x); vec2 uv = (v_index + 0.5) / (pos_size.yx); vec4 pos = vec4(index.y * dx, texture2D(position, uv).r, 0, 1); // fetch starting position from texture lookup: pos += vec4(texture1D(pos_offset, (index.x + 0.5) / pos_size.x).rg, 0, 0); gl_Position = $transform(pos); v_color = texture1D(color_tex, (index.x + 0.5) / pos_size.x); } """ fragment_code = """ varying vec2 v_index; varying vec4 v_color; void main() { if (v_index.y - floor(v_index.y) > 0) { discard; } gl_FragColor = $color; } """ def __init__(self, n_lines, line_size, dx, color=None, pos_offset=None, columns=None, cell_size=None): self._pos_data = None self._offset = 0 self._dx = dx data = np.zeros((n_lines, line_size), dtype='float32') self._pos_tex = gloo.Texture2D(data, format='luminance', internalformat='r32f') self._index_buf = gloo.VertexBuffer() self._data_shape = data.shape Visual.__init__(self, vcode=self.vertex_code, fcode=self.fragment_code) self.shared_program['position'] = self._pos_tex self.shared_program['index'] = self._index_buf self.shared_program['dx'] = dx self.shared_program['pos_size'] = data.shape self.shared_program['offset'] = self._offset # set an array giving the x/y origin for each plot if pos_offset is None: # construct positions as a grid rows = int(np.ceil(n_lines / columns)) pos_offset = np.empty((rows, columns, 3), dtype='float32') pos_offset[..., 0] = (np.arange(columns)[np.newaxis, :] * cell_size[0]) pos_offset[..., 1] = np.arange(rows)[:, np.newaxis] * cell_size[1] # limit position texture to the number of lines in case there are # more row/column cells than lines pos_offset = pos_offset.reshape((rows*columns), 3)[:n_lines, :] self._pos_offset = gloo.Texture1D(pos_offset, internalformat='rgb32f', interpolation='nearest') self.shared_program['pos_offset'] = self._pos_offset if color is None: # default to white (1, 1, 1, 1) self._color_tex = gloo.Texture1D( np.ones((n_lines, 4), dtype=np.float32)) self.shared_program['color_tex'] = self._color_tex self.shared_program.frag['color'] = 'v_color' else: self._color_tex = gloo.Texture1D(color) self.shared_program['color_tex'] = self._color_tex self.shared_program.frag['color'] = 'v_color' # construct a vertex buffer index containing (plot_n, vertex_n) for # each vertex index = np.empty((data.shape[0], data.shape[1], 2), dtype='float32') index[..., 0] = np.arange(data.shape[0])[:, np.newaxis] index[..., 1] = np.arange(data.shape[1])[np.newaxis, :] index = index.reshape((index.shape[0]*index.shape[1], index.shape[2])) self._index_buf.set_data(index) self._draw_mode = 'line_strip' self.set_gl_state('translucent', line_width=1) self.freeze() def set_pos_offset(self, po): """Set the array of position offsets for each line strip. Parameters ---------- po : array-like An array of xy offset values. """ self._pos_offset.set_data(po) def set_color(self, color): """Set the array of colors for each line strip. Parameters ---------- color : array-like An array of rgba values. """ self._color_tex.set_data(color) def _prepare_transforms(self, view): view.view_program.vert['transform'] = view.get_transform().simplified def _prepare_draw(self, view): pass def _compute_bounds(self, axis, view): if self._pos_data is None: return None return self._pos_data[..., axis].min(), self.pos_data[..., axis].max() def roll_data(self, data): """Append new data to the right side of every line strip and remove as much data from the left. Parameters ---------- data : array-like A data array to append. """ data = data.astype('float32')[..., np.newaxis] s1 = self._data_shape[1] - self._offset if data.shape[1] > s1: self._pos_tex[:, self._offset:] = data[:, :s1] self._pos_tex[:, :data.shape[1] - s1] = data[:, s1:] self._offset = (self._offset + data.shape[1]) % self._data_shape[1] else: self._pos_tex[:, self._offset:self._offset+data.shape[1]] = data self._offset += data.shape[1] self.shared_program['offset'] = self._offset self.update() def set_data(self, index, data): """Set the complete data for a single line strip. Parameters ---------- index : int The index of the line strip to be replaced. data : array-like The data to assign to the selected line strip. """ self._pos_tex[index, :] = data self.update() vispy-0.14.1/vispy/visuals/regular_polygon.py0000644000175000017510000000343514507104216020725 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """RegularPolygonVisual visual based on EllipseVisual""" from __future__ import division from .ellipse import EllipseVisual class RegularPolygonVisual(EllipseVisual): """ Displays a regular polygon Parameters ---------- center : array-like (x, y) 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 border_width: float The width of the border in pixels radius : float Radius of the regular polygon Defaults to 0.1 sides : int Number of sides of the regular polygon """ def __init__(self, center=None, color='black', border_color=None, border_width=1, radius=0.1, sides=4, **kwargs): EllipseVisual.__init__(self, center=center, radius=radius, color=color, border_color=border_color, border_width=border_width, num_segments=sides, **kwargs) @property def sides(self): """The number of sides in the regular polygon.""" # return using the property accessor for num_segments return self.num_segments @sides.setter def sides(self, sides): if sides < 3: raise ValueError('PolygonVisual must have at least 3 sides, not %s' % sides) # edit using the property accessor of num_segments so this # internally calls the update() self.num_segments = sides vispy-0.14.1/vispy/visuals/rectangle.py0000644000175000017510000001501014507104216017451 0ustar nileshnilesh# -*- 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 ---------- center : array Center of the rectangle color : instance of Color The fill color to use. border_color : instance of Color The border color to use. border_width : int Border width in pixels. Line widths > 1px are only guaranteed to work when using `border_method='agg'` method. 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. **kwargs : dict Keyword arguments to pass to `PolygonVisual`. """ def __init__(self, center=None, color='black', border_color=None, border_width=1, height=1.0, width=1.0, radius=[0., 0., 0., 0.], **kwargs): self._height = height self._width = width self._color = Color(color) self._border_color = Color(border_color) self._border_width = border_width self._radius = radius self._center = center # triangulation can be very slow kwargs.setdefault('triangulate', False) PolygonVisual.__init__(self, pos=None, color=color, border_color=border_color, border_width=border_width, **kwargs) self._mesh.mode = 'triangle_fan' self._regen_pos() self._update() @staticmethod def _generate_vertices(center, radius, height, width): half_height = height / 2. half_width = 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.ones(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.ones(4) * radius num_segments = (radius / hw * 500.).astype(int) bias1 = np.ones(4) * half_width - radius bias2 = np.ones(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] = center[0] - bias1[0] - radius[0] * np.sin(theta) corner1[:, 1] = center[1] - bias2[0] - radius[0] * np.cos(theta) corner1[:, 2] = 0 theta = np.linspace(start_angle, end_angle, num_segments[1]+1) corner2[:, 0] = center[0] + bias1[1] + radius[1] * np.sin(theta) corner2[:, 1] = center[1] - bias2[1] - radius[1] * np.cos(theta) corner2[:, 2] = 0 theta = np.linspace(end_angle, start_angle, num_segments[2]+1) corner3[:, 0] = center[0] + bias1[2] + radius[2] * np.sin(theta) corner3[:, 1] = center[1] + bias2[2] + radius[2] * np.cos(theta) corner3[:, 2] = 0 theta = np.linspace(start_angle, end_angle, num_segments[3]+1) corner4[:, 0] = center[0] - bias1[3] - radius[3] * np.sin(theta) corner4[:, 1] = center[1] + bias2[3] + radius[3] * np.cos(theta) corner4[:, 2] = 0 output = np.concatenate(([[center[0], center[1], 0.]], [[center[0] - half_width, center[1], 0.]], corner1, [[center[0], center[1] - half_height, 0.]], corner2, [[center[0] + half_width, center[1], 0.]], corner3, [[center[0], center[1] + half_height, 0.]], corner4, [[center[0] - half_width, center[1], 0.]])) vertices = np.array(output, dtype=np.float32) return vertices @property def center(self): """The center of the ellipse""" return self._center @center.setter def center(self, center): """The center of the ellipse""" self._center = center self._regen_pos() self._update() @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._regen_pos() 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._regen_pos() self._update() @property def radius(self): """The radius of curvature of rounded corners.""" return self._radius @radius.setter def radius(self, radius): self._radius = radius self._regen_pos() self._update() def _regen_pos(self): vertices = self._generate_vertices(center=self._center, radius=self._radius, height=self._height, width=self._width) # don't use the center point and only use X/Y coordinates vertices = vertices[1:, ..., :2] self._pos = vertices vispy-0.14.1/vispy/visuals/polygon.py0000644000175000017510000001100214507104216017171 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 .visual import CompoundVisual from .mesh import MeshVisual from .line import LineVisual from ..color import Color from ..geometry import PolygonData from ..gloo import set_state class PolygonVisual(CompoundVisual): """ 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. Line widths > 1px are only guaranteed to work when using `border_method='agg'` method. border_method : str Mode to use for drawing the border line (see `LineVisual`). * "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. triangulate : boolean Triangulate the set of vertices **kwargs : dict Keyword arguments to pass to `CompoundVisual`. """ def __init__(self, pos=None, color='black', border_color=None, border_width=1, border_method='gl', triangulate=True, **kwargs): self._mesh = MeshVisual() self._border = LineVisual(method=border_method) self._pos = pos self._color = Color(color) self._border_width = border_width self._border_color = Color(border_color) self._triangulate = triangulate self._update() CompoundVisual.__init__(self, [self._mesh, self._border], **kwargs) self._mesh.set_gl_state(polygon_offset_fill=True, polygon_offset=(1, 1), cull_face=False) self.freeze() def _update(self): if self._pos is None: return if not self._color.is_blank and self._triangulate: data = PolygonData(vertices=np.array(self._pos, dtype=np.float32)) pts, tris = data.triangulate() set_state(polygon_offset_fill=False) self._mesh.set_data(vertices=pts, faces=tris.astype(np.uint32), color=self._color.rgba) elif not self._color.is_blank: self.mesh.set_data(vertices=self._pos, 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) self._border.update() @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() @property def mesh(self): """The vispy.visuals.MeshVisual that is owned by the PolygonVisual. It is used to fill in the polygon """ return self._mesh @mesh.setter def mesh(self, mesh): self._mesh = mesh self._update() @property def border(self): """The vispy.visuals.LineVisual that is owned by the PolygonVisual. It is used to draw the border of the polygon """ return self._border @border.setter def border(self, border): self._border = border self._update() vispy-0.14.1/vispy/visuals/plane.py0000644000175000017510000000423314507104216016611 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from ..geometry import create_plane from .visual import CompoundVisual from .mesh import MeshVisual class PlaneVisual(CompoundVisual): """Visual that displays a plane. Parameters ---------- width : float Plane width. height : float Plane height. width_segments : int Plane segments count along the width. height_segments : float Plane segments count along the height. direction: unicode ``{'-x', '+x', '-y', '+y', '-z', '+z'}`` Direction the plane will be facing. vertex_colors : ndarray Same as for `MeshVisual` class. See `create_plane` for vertex ordering. face_colors : ndarray Same as for `MeshVisual` class. See `create_plane` 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, width=1, height=1, width_segments=1, height_segments=1, direction='+z', vertex_colors=None, face_colors=None, color=(0.5, 0.5, 1, 1), edge_color=None): vertices, filled_indices, outline_indices = create_plane( width, height, width_segments, height_segments, direction) self._mesh = MeshVisual(vertices['position'], filled_indices, vertex_colors, face_colors, color) self._mesh.update_gl_state(polygon_offset=(1, 1), polygon_offset_fill=True) self._outline = None CompoundVisual.__init__(self, [self._mesh]) if edge_color: self._outline = MeshVisual(vertices['position'], outline_indices, color=edge_color, mode='lines') self.add_subvisual(self._outline) vispy-0.14.1/vispy/visuals/mesh_normals.py0000644000175000017510000001432214507104216020201 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """A visual for displaying mesh normals as lines.""" import numpy as np from . import LineVisual class MeshNormalsVisual(LineVisual): """Display mesh normals as lines. Parameters ---------- meshdata : instance of :class:`~vispy.geometry.meshdata.MeshData` The mesh data. primitive : {'face', 'vertex'} The primitive type on which to compute and display the normals. length : None or float or array-like, optional The length(s) of the normals. If None, the length is computed with `length_method`. length_method : {'median_edge', 'max_extent'}, default='median_edge' The method to compute the length of the normals (when `length=None`). Methods: 'median_edge', the median edge length; 'max_extent', the maximum side length of the bounding box of the mesh. length_scale : float, default=1.0 A scale factor applied to the length computed with `length_method`. **kwargs : dict, optional Extra arguments to define the appearance of lines. Refer to :class:`~vispy.visuals.line.line.LineVisual`. Examples -------- Create a :class:`~vispy.visuals.mesh.MeshVisual` on which to display the normals and get the :class:`~vispy.geometry.meshdata.MeshData`: >>> mesh = MeshVisual(vertices=vertices, faces=faces, ...) >>> meshdata = mesh.mesh_data Create a visual for the mesh normals: >>> normals = MeshNormalsVisual(meshdata) Display the face normals: >>> MeshNormalsVisual(..., primitive='face') >>> MeshNormalsVisual(...) # equivalent (default values) Display the vertex normals: >>> MeshNormalsVisual(..., primitive='vertex') Fixed length for all normals: >>> MeshNormalsVisual(..., length=0.25) Individual length per normal: >>> lengths = np.array([0.5, 0.2, 0.7, ..., 0.7], dtype=float) >>> MeshNormalsVisual(..., length=lengths) >>> assert len(lengths) == len(faces) # for face normals >>> assert len(lengths) == len(vertices) # for vertex normals Normals at about the length of a triangle: >>> MeshNormalsVisual(..., length_method='median_edge', length_scale=1.0) >>> MeshNormalsVisual(...) # equivalent (default values) Normals at about 10% the size of the mesh: >>> MeshNormalsVisual(..., length_method='max_extent', length_scale=0.1) """ def __init__(self, meshdata=None, primitive='face', length=None, length_method='median_edge', length_scale=1.0, **kwargs): self._previous_meshdata = None super().__init__(connect='segments') self.set_data(meshdata, primitive, length, length_method, length_scale, **kwargs) def set_data(self, meshdata=None, primitive='face', length=None, length_method='median_edge', length_scale=1.0, **kwargs): """Set the data used to draw this visual Parameters ---------- meshdata : instance of :class:`~vispy.geometry.meshdata.MeshData` The mesh data. primitive : {'face', 'vertex'} The primitive type on which to compute and display the normals. length : None or float or array-like, optional The length(s) of the normals. If None, the length is computed with `length_method`. length_method : {'median_edge', 'max_extent'}, default='median_edge' The method to compute the length of the normals (when `length=None`). Methods: 'median_edge', the median edge length; 'max_extent', the maximum side length of the bounding box of the mesh. length_scale : float, default=1.0 A scale factor applied to the length computed with `length_method`. **kwargs : dict, optional Extra arguments to define the appearance of lines. Refer to :class:`~vispy.visuals.line.line.LineVisual`. """ if meshdata is None: meshdata = self._previous_meshdata if meshdata is None or meshdata.is_empty(): normals = None elif primitive == 'face': normals = meshdata.get_face_normals() elif primitive == 'vertex': normals = meshdata.get_vertex_normals() else: raise ValueError('primitive must be "face" or "vertex", got %s' % primitive) # remove connect from kwargs to make sure we don't change it kwargs.pop('connect', None) if normals is None: super().set_data(pos=np.empty((0, 3), dtype=np.float32), connect='segments', **kwargs) return self._previous_meshdata = meshdata norms = np.sqrt((normals ** 2).sum(axis=-1, keepdims=True)) unit_normals = normals / norms if length is None and length_method == 'median_edge': face_corners = meshdata.get_vertices(indexed='faces') edges = np.stack(( face_corners[:, 1, :] - face_corners[:, 0, :], face_corners[:, 2, :] - face_corners[:, 1, :], face_corners[:, 0, :] - face_corners[:, 2, :], )) edge_lengths = np.sqrt((edges ** 2).sum(axis=-1)) length = np.median(edge_lengths) elif length is None and length_method == 'max_extent': vertices = meshdata.get_vertices() max_extent = np.max(vertices.max(axis=0) - vertices.min(axis=0)) length = max_extent length *= length_scale if primitive == 'face': origins = meshdata.get_vertices(indexed='faces') origins = origins.mean(axis=1) elif primitive == 'vertex': origins = meshdata.get_vertices() # Ensure the broadcasting if the input is an `(n,)` array. length = np.atleast_1d(length) length = length[:, None] ends = origins + length * unit_normals segments = np.hstack((origins, ends)).reshape(-1, 3) super().set_data(pos=segments, connect='segments', **kwargs) vispy-0.14.1/vispy/visuals/mesh.py0000644000175000017510000002727714507104216016463 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 from functools import lru_cache import numpy as np from .visual import Visual from .shaders import Function, FunctionChain from ..gloo import VertexBuffer from ..geometry import MeshData from ..color import Color, get_colormap from ..color.colormap import CubeHelixColormap from ..util.event import Event _VERTEX_SHADER = """ varying vec4 v_base_color; void main() { v_base_color = $color_transform($base_color); gl_Position = $transform($to_vec4($position)); } """ _FRAGMENT_SHADER = """ varying vec4 v_base_color; void main() { gl_FragColor = v_base_color; } """ 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. vertex_values : array-like | None The values to use for each vertex (for colormapping). meshdata : instance of MeshData | None The meshdata. shading : str | None Shading to use. This uses the :class:`~vispy.visuals.filters.mesh.ShadingFilter` filter introduced in VisPy 0.7. This class provides additional features that are available when the filter is attached manually. See 'examples/basics/scene/mesh_shading.py' for an example. mode : str The drawing mode. **kwargs : dict Keyword arguments to pass to `Visual`. Notes ----- Additional functionality is available through filters. Mesh-specific filters can be found in the :mod:`vispy.visuals.filters.mesh` module. This class emits a `data_updated` event when the mesh data is updated. This is used for example by filters for synchronization. Examples -------- Create a primitive shape from a helper function: >>> from vispy.geometry import create_sphere >>> meshdata = create_sphere() >>> mesh = MeshVisual(meshdata=meshdata) Create a custom shape: >>> # A rectangle made out of two triangles. >>> vertices = [(0, 0, 0), (1, 0, 1), (1, 1, 1), (0, 1, 0)] >>> faces = [(0, 1, 2), (0, 2, 3)] >>> mesh = MeshVisual(vertices=vertices, faces=faces) """ _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } def __init__(self, vertices=None, faces=None, vertex_colors=None, face_colors=None, color=(0.5, 0.5, 1, 1), vertex_values=None, meshdata=None, shading=None, mode='triangles', **kwargs): Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment'], **kwargs) self.set_gl_state('translucent', depth_test=True, cull_face=False) self.events.add(data_updated=Event) self._meshdata = None # Define buffers self._vertices = VertexBuffer(np.zeros((0, 3), dtype=np.float32)) self._cmap = CubeHelixColormap() self._clim = 'auto' # Uniform color self._color = Color(color) # add filters for various modifiers self.shading_filter = None self.shading = shading # Init 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, vertex_values=vertex_values, meshdata=meshdata, color=color) # primitive mode self._draw_mode = mode self.freeze() @property def shading(self): """The shading method.""" return self._shading @shading.setter def shading(self, shading): assert shading in (None, 'flat', 'smooth') self._shading = shading if shading is None and self.shading_filter is None: # Delay creation of filter until necessary. return if self.shading_filter is None: from vispy.visuals.filters import ShadingFilter self.shading_filter = ShadingFilter(shading=shading) self.attach(self.shading_filter) else: self.shading_filter.shading = shading def set_data(self, vertices=None, faces=None, vertex_colors=None, face_colors=None, color=None, vertex_values=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. vertex_values : array-like | None Values for each vertex. 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, vertex_values=vertex_values) self._bounds = self._meshdata.get_bounds() if color is not None: self._color = Color(color) self.mesh_data_changed() @property def clim(self): return (self._clim if isinstance(self._clim, str) else tuple(self._clim)) @clim.setter def clim(self, clim): if isinstance(clim, str): 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.mesh_data_changed() @property def _clim_values(self): if isinstance(self._clim, str): # == 'auto' if self._meshdata.has_vertex_value(): clim = self._meshdata.get_vertex_values() clim = (np.min(clim), np.max(clim)) else: clim = (0, 1) else: clim = self._clim return clim @property def cmap(self): return self._cmap @cmap.setter def cmap(self, cmap): self._cmap = get_colormap(cmap) 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._draw_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._draw_mode = m @property def mesh_data(self): """The mesh data""" return self._meshdata @property def color(self): """The uniform color for this mesh""" return self._color @color.setter def color(self, c): """Set the uniform color of the mesh This value is only used if per-vertex or per-face colors are not specified. Parameters ---------- c : instance of Color The color to use. """ if c is not None: self._color = Color(c) self.mesh_data_changed() def mesh_data_changed(self): self._data_changed = True self.update() def _build_color_transform(self, colors): # Eventually this could be de-duplicated with visuals/image.py, which does # something similar (but takes a ``color`` instead of ``float``) null_color_transform = 'vec4 pass(vec4 color) { return color; }' clim_func = 'float cmap(float val) { return (val - $cmin) / ($cmax - $cmin); }' if colors.ndim == 2 and colors.shape[1] == 1: fun = Function(clim_func) fun['cmin'] = self._clim_values[0] fun['cmax'] = self._clim_values[1] fun = FunctionChain(None, [fun, Function(self.cmap.glsl_map)]) else: fun = Function(null_color_transform) return fun @staticmethod @lru_cache(maxsize=2) def _ensure_vec4_func(dims): if dims == 2: func = Function(""" vec4 vec2to4(vec2 xyz) { return vec4(xyz, 0.0, 1.0); } """) elif dims == 3: func = Function(""" vec4 vec3to4(vec3 xyz) { return vec4(xyz, 1.0); } """) else: raise TypeError("Vertex data must have shape (...,2) or (...,3).") return func def _update_data(self): md = self.mesh_data 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 md.has_vertex_color(): colors = md.get_vertex_colors(indexed='faces') colors = colors.astype(np.float32) elif md.has_face_color(): colors = md.get_face_colors(indexed='faces') colors = colors.astype(np.float32) elif md.has_vertex_value(): colors = md.get_vertex_values(indexed='faces') colors = colors.ravel()[:, np.newaxis] colors = colors.astype(np.float32) else: colors = self._color.rgba self.shared_program.vert['position'] = self._vertices self.shared_program['texture2D_LUT'] = self._cmap.texture_lut() # Position input handling ensure_vec4 = self._ensure_vec4_func(v.shape[-1]) self.shared_program.vert['to_vec4'] = ensure_vec4 # Set the base color. # # The base color is mixed further by the material filters for texture # or shading effects. self.shared_program.vert['color_transform'] = self._build_color_transform(colors) if colors.ndim == 1: self.shared_program.vert['base_color'] = colors else: self.shared_program.vert['base_color'] = VertexBuffer(colors) self._data_changed = False self.events.data_updated() def _prepare_draw(self, view): if self._data_changed: if self._update_data() is False: return False self._data_changed = False @staticmethod def _prepare_transforms(view): tr = view.transforms.get_transform() view.view_program.vert['transform'] = tr def _compute_bounds(self, axis, view): if self._bounds is None: return None if axis >= len(self._bounds): return (0, 0) else: return self._bounds[axis] vispy-0.14.1/vispy/visuals/markers.py0000644000175000017510000006442214507104216017164 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 from .shaders import Function, Variable from .visual import Visual from ..util.event import Event _VERTEX_SHADER = """ uniform float u_antialias; uniform float u_px_scale; uniform bool u_scaling; uniform bool u_spherical; attribute vec3 a_position; attribute vec4 a_fg_color; attribute vec4 a_bg_color; attribute float a_edgewidth; attribute float a_size; attribute float a_symbol; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_edgewidth; varying float v_depth_middle; varying float v_alias_ratio; varying float v_symbol; float big_float = 1e10; // prevents numerical imprecision void main (void) { v_fg_color = a_fg_color; v_bg_color = a_bg_color; // fluctuations can mess "fake integers" up, so we do +0.5 and floor to make sure it's right v_symbol = a_symbol + 0.5; vec4 pos = vec4(a_position, 1); vec4 fb_pos = $visual_to_framebuffer(pos); vec4 x; vec4 size_vec; gl_Position = $framebuffer_to_render(fb_pos); // NOTE: gl_stuff uses framebuffer coords! if (u_scaling) { // scaling == "scene": scale marker using entire visual -> framebuffer set of transforms // scaling == "visual": scale marker using only the Visual's transform pos = $framebuffer_to_scene_or_visual(fb_pos); x = $framebuffer_to_scene_or_visual(fb_pos + vec4(big_float, 0, 0, 0)); x = (x - pos); // multiply that direction by the size and add it to the position // this gives us the position of the edge of the point, which we convert in screen space size_vec = $scene_or_visual_to_framebuffer(pos + normalize(x) * a_size); // divide by `w` for perspective, and subtract pos // this gives us the actual screen-space size of the point $v_size = size_vec.x / size_vec.w - fb_pos.x / fb_pos.w; v_edgewidth = ($v_size / a_size) * a_edgewidth; } else { // scaling == "fixed": marker is always the same number of pixels $v_size = a_size * u_px_scale; v_edgewidth = a_edgewidth * u_px_scale; } // gl_PointSize is the diameter gl_PointSize = $v_size + 4. * (v_edgewidth + 1.5 * u_antialias); if (u_spherical == true) { // similar as above for scaling, but in towards the screen direction // Get the framebuffer z direction relative to this sphere in visual coords vec4 z = $framebuffer_to_scene_or_visual(fb_pos + vec4(0, 0, big_float, 0)); z = (z - pos); // Get the depth of the sphere in its middle point on the screen // size/2 because we need the radius, not the diameter vec4 depth_z_vec = $scene_or_visual_to_framebuffer(pos + normalize(z) * a_size / 2); v_depth_middle = depth_z_vec.z / depth_z_vec.w - fb_pos.z / fb_pos.w; // size ratio between aliased and non-aliased, needed for correct depth v_alias_ratio = gl_PointSize / $v_size; } } """ _FRAGMENT_SHADER = """#version 120 uniform vec3 u_light_position; uniform vec3 u_light_color; uniform float u_light_ambient; uniform float u_alpha; uniform float u_antialias; uniform bool u_spherical; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_edgewidth; varying float v_depth_middle; varying float v_alias_ratio; varying float v_symbol; void main() { // Discard plotting marker body and edge if zero-size if ($v_size <= 0.) discard; float edgealphafactor = min(v_edgewidth, 1.0); float size = $v_size + 4.*(v_edgewidth + 1.5*u_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, int(v_symbol)); // it takes into account an antialising layer // of size u_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 // use max because we don't want negative transition zone float t = max(0.5*v_edgewidth - u_antialias, 0); float d = abs(r) - t; if (r > 0.5*v_edgewidth + u_antialias) { // out of the marker (beyond the outer edge of the edge // including transition zone due to antialiasing) discard; } vec4 facecolor = v_bg_color; vec4 edgecolor = vec4(v_fg_color.rgb, edgealphafactor*v_fg_color.a); float depth_change = 0; // change color and depth if spherical mode is active if (u_spherical == true) { // multiply by alias_ratio and then clamp, so we're back to non-alias coordinates // and the aliasing ring has the same coordinates as the point just inside, // which is important for lighting vec2 texcoord = (gl_PointCoord * 2 - 1) * v_alias_ratio; float x = clamp(texcoord.x, -1, 1); float y = clamp(texcoord.y, -1, 1); float z = sqrt(clamp(1 - x*x - y*y, 0, 1)); vec3 normal = vec3(x, y, z); // Diffuse color float diffuse = dot(u_light_position, normal); // clamp, because 0 < theta < pi/2 diffuse = clamp(diffuse, 0, 1); vec3 diffuse_color = u_light_ambient + u_light_color * diffuse; // Specular color // reflect light wrt normal for the reflected ray, then // find the angle made with the eye vec3 eye = vec3(0, 0, -1); float specular = dot(reflect(u_light_position, normal), eye); specular = clamp(specular, 0, 1); // raise to the material's shininess, multiply with a // small factor for spread specular = pow(specular, 80); vec3 specular_color = u_light_color * specular; facecolor = vec4(facecolor.rgb * diffuse_color + specular_color, facecolor.a * u_alpha); edgecolor = vec4(edgecolor.rgb * diffuse_color + specular_color, edgecolor.a * u_alpha); // TODO: figure out why this 0.5 is needed, despite already having the radius, not diameter depth_change = -0.5 * z * v_depth_middle; } 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 > -u_antialias) {// outside float alpha = 1.0 + r/u_antialias; alpha = exp(-alpha*alpha); gl_FragColor = vec4(facecolor.rgb, alpha*facecolor.a); } else {// inside gl_FragColor = facecolor; } } else {// non-zero edge float alpha = d/u_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 { // inner part of the edge: fade into the face color gl_FragColor = mix(facecolor, edgecolor, alpha); } } gl_FragDepth = gl_FragCoord.z + depth_change; } """ disc = """ float r = length((pointcoord.xy - vec2(0.5,0.5))*size); r -= $v_size/2.; return r; """ arrow = """ const float sqrt2 = sqrt(2.); 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 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.); 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 r = max(abs(pointcoord.x -.5)*size, abs(pointcoord.y -.5)*size); r -= $v_size/2.; return r; """ x = """ 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 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 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 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 = """ //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 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 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 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 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 = float(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; """ cross_lines = """ //vbar float r1 = abs(pointcoord.x - 0.5)*size; 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; float r4 = abs(pointcoord.x - 0.5)*size - $v_size/2; float hbar = max(r3,r4); return min(vbar, hbar); """ symbol_shaders = { 'disc': disc, 'arrow': arrow, 'ring': ring, 'clobber': clobber, 'square': square, 'x': x, 'diamond': diamond, 'vbar': vbar, 'hbar': hbar, 'cross': cross, 'tailed_arrow': tailed_arrow, 'triangle_up': triangle_up, 'triangle_down': triangle_down, 'star': star, 'cross_lines': cross_lines, } # combine all the symbol shaders in a big if-else statement symbol_func = f""" float symbol(vec2 pointcoord, float size, int symbol) {{ {' else'.join( f''' if (symbol == {i}) {{ // {name} {shader} }}''' for i, (name, shader) in enumerate(symbol_shaders.items()) )} }}""" # aliases symbol_aliases = { 'o': 'disc', '+': 'cross', '++': 'cross_lines', 's': 'square', '-': 'hbar', '|': 'vbar', '->': 'tailed_arrow', '>': 'arrow', '^': 'triangle_up', 'v': 'triangle_down', '*': 'star', } symbol_shader_values = {name: i for i, name in enumerate(symbol_shaders)} symbol_shader_values.update({ **{alias: symbol_shader_values[name] for alias, name in symbol_aliases.items()}, }) class MarkersVisual(Visual): """Visual displaying marker symbols. Parameters ---------- pos : array The array of locations to display each symbol. size : float or array The symbol size in screen (or data, if scaling is on) px. edge_width : float or array or None The width of the symbol outline in screen (or data, if scaling is on) px. edge_width_rel : float or array or 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. symbol : str or array The style of symbol used to draw each marker (see Notes). scaling : str | bool Scaling method of individual markers. If set to "fixed" (default) then no scaling is done and markers will always be the same number of pixels on the screen. If set to "scene" then the chain of transforms from the Visual's transform to the transform mapping to the OpenGL framebuffer are used to scaling the marker. This has the effect of the marker staying the same size in the "scene" coordinate space and changing size as the visualization is zoomed in and out. If set to "visual" the marker is scaled only using the transform of the Visual and not the rest of the scene/camera. This means that something like a camera changing the view will not affect the size of the marker, but the user can still scale it using the Visual's transform. For backwards compatibility this can be set to the boolean ``False`` for "fixed" or ``True`` for "scene". alpha : float The opacity level of the visual. antialias : float Antialiasing amount (in px). spherical : bool Whether to add a spherical effect on the marker using lighting. light_color : Color | ColorArray The color of the light used to create the spherical effect. light_position : array The coordinates of the light used to create the spherical effect. light_ambient : float The amount of ambient light used to create the spherical effect. Notes ----- Allowed style strings are: disc, arrow, ring, clobber, square, diamond, vbar, hbar, cross, tailed_arrow, x, triangle_up, triangle_down, and star. """ _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } _symbol_shader_values = symbol_shader_values _symbol_shader = symbol_func def __init__(self, scaling="fixed", alpha=1, antialias=1, spherical=False, light_color='white', light_position=(1, -1, 1), light_ambient=0.3, **kwargs): self._vbo = VertexBuffer() self._data = None self._scaling = "fixed" Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) self._symbol_func = Function(self._symbol_shader) self.shared_program.frag['marker'] = self._symbol_func self._v_size_var = Variable('varying float v_size') self.shared_program.vert['v_size'] = self._v_size_var self.shared_program.frag['v_size'] = self._v_size_var self._symbol_func['v_size'] = self._v_size_var self.set_gl_state(depth_test=True, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self._draw_mode = 'points' self.events.add(data_updated=Event) if len(kwargs) > 0: self.set_data(**kwargs) self.scaling = scaling self.antialias = antialias self.light_color = light_color self.light_position = light_position self.light_ambient = light_ambient self.alpha = alpha self.spherical = spherical self.freeze() def set_data(self, pos=None, size=10., edge_width=1., edge_width_rel=None, edge_color='black', face_color='white', symbol='o'): """Set the data used to display this visual. Parameters ---------- pos : array The array of locations to display each symbol. size : float or array The symbol size in screen (or data, if scaling is on) px. edge_width : float or array or None The width of the symbol outline in screen (or data, if scaling is on) px. edge_width_rel : float or array or 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. symbol : str or array The style of symbol used to draw each marker (see Notes). """ 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: edge_width = np.asarray(edge_width) if np.any(edge_width < 0): raise ValueError('edge_width cannot be negative') else: edge_width_rel = np.asarray(edge_width_rel) if np.any(edge_width_rel < 0): raise ValueError('edge_width_rel cannot be negative') if symbol is not None: if not np.all(np.isin(np.asarray(symbol), self.symbols)): raise ValueError(f'symbols must one of {self.symbols}') 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] if pos is not None: assert (isinstance(pos, np.ndarray) and pos.ndim == 2 and pos.shape[1] in (2, 3)) 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), ('a_edgewidth', np.float32), ('a_symbol', np.float32)]) 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 data['a_symbol'] = np.vectorize(self._symbol_shader_values.get)(symbol) self._data = data self._vbo.set_data(data) self.shared_program.bind(self._vbo) self.events.data_updated() self.update() @property def symbols(self): return list(self._symbol_shader_values) @property def symbol(self): if self._data is None: return None value_to_symbol = {v: k for k, v in self._symbol_shader_values.items()} return np.vectorize(value_to_symbol.get)(self._data['a_symbol']) @symbol.setter def symbol(self, value): if self._data is not None: rec_to_kw = { 'a_position': 'pos', 'a_fg_color': 'edge_color', 'a_bg_color': 'face_color', 'a_size': 'size', 'a_edgewidth': 'edge_width', 'a_symbol': 'symbol', } kwargs = {kw: self._data[rec] for rec, kw in rec_to_kw.items()} else: kwargs = {} kwargs['symbol'] = value self.set_data(**kwargs) @property def scaling(self): """ If set to True, marker scales when rezooming. """ return self._scaling @scaling.setter def scaling(self, value): scaling_modes = { False: "fixed", True: "scene", "fixed": "fixed", "scene": "scene", "visual": "visual", } if value not in scaling_modes: possible_options = ", ".join(repr(opt) for opt in scaling_modes) raise ValueError(f"Unknown scaling option {value!r}, expected one of: {possible_options}") self._scaling = scaling_modes[value] self.shared_program['u_scaling'] = self._scaling != "fixed" self.update() @property def antialias(self): """ Antialiasing amount (in px). """ return self._antialias @antialias.setter def antialias(self, value): value = float(value) self.shared_program['u_antialias'] = value self._antialias = value self.update() @property def light_position(self): """ The coordinates of the light used to create the spherical effect. """ return self._light_position @light_position.setter def light_position(self, value): value = np.array(value) self.shared_program['u_light_position'] = value / np.linalg.norm(value) self._light_position = value self.update() @property def light_ambient(self): """ The amount of ambient light used to create the spherical effect. """ return self._light_ambient @light_ambient.setter def light_ambient(self, value): self.shared_program['u_light_ambient'] = value self._light_ambient = value self.update() @property def light_color(self): """ The color of the light used to create the spherical effect. """ return self._light_color @light_color.setter def light_color(self, value): self.shared_program['u_light_color'] = ColorArray(value).rgb self._light_color = value self.update() @property def alpha(self): """ The opacity level of the visual. """ return self._alpha @alpha.setter def alpha(self, value): self.shared_program['u_alpha'] = value self._alpha = value self.update() @property def spherical(self): """ Whether to add a spherical effect on the marker using lighting. """ return self._spherical @spherical.setter def spherical(self, value): self.shared_program['u_spherical'] = value self._spherical = value self.update() def _prepare_transforms(self, view): view.view_program.vert['visual_to_framebuffer'] = view.get_transform('visual', 'framebuffer') view.view_program.vert['framebuffer_to_render'] = view.get_transform('framebuffer', 'render') scaling = view._scaling if view._scaling != "fixed" else "scene" view.view_program.vert['framebuffer_to_scene_or_visual'] = view.get_transform('framebuffer', scaling) view.view_program.vert['scene_or_visual_to_framebuffer'] = view.get_transform(scaling, 'framebuffer') def _prepare_draw(self, view): if self._data is None: return False view.view_program['u_px_scale'] = view.transforms.pixel_scale def _compute_bounds(self, axis, view): 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.14.1/vispy/visuals/linear_region.py0000644000175000017510000001472614507104216020337 0ustar nileshnileshimport numpy as np from .. import gloo from .visual import Visual _VERTEX_SHADER = """ attribute vec2 a_pos; varying vec4 v_color; void main() { vec4 pos = vec4(a_pos, 0, 1); if($is_vertical==1) { pos.y = $render_to_visual(pos).y; } else { pos.x = $render_to_visual(pos).x; } gl_Position = $transform(pos); gl_PointSize = 10.; v_color = $color; } """ _FRAGMENT_SHADER = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class LinearRegionVisual(Visual): """Infinite horizontal or vertical region for 2D plots. Parameters ---------- pos : list, tuple or numpy array Bounds of the region along the axis. len(pos) must be >=2. color : list, tuple, or array The color to use when drawing the line. It must have a shape of (1, 4) for a single color region or (len(pos), 4) for a multicolor region. vertical: True for drawing a vertical region, False for an horizontal region """ _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } def __init__(self, pos=None, color=[1.0, 1.0, 1.0, 1.0], vertical=True, **kwargs): """ """ Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) self._changed = {'pos': False, 'color': False} self.pos_buf = gloo.VertexBuffer() self.color_buf = gloo.VertexBuffer() # The Visual superclass contains a MultiProgram, which is an object # that behaves like a normal shader program (you can assign shader # code, upload values, set template variables, etc.) but internally # manages multiple ModularProgram instances, one per view. # The MultiProgram is accessed via the `shared_program` property, so # the following modifications to the program will be applied to all # views: self.shared_program['a_pos'] = self.pos_buf self._program.vert['is_vertical'] = 1 if vertical else 0 self._need_upload = False self._is_vertical = bool(vertical) self._pos = np.zeros((4, 2), dtype=np.float32) self._color = np.ones((1, 4), dtype=np.float32) # Visual keeps track of draw mode, index buffer, and GL state. These # are shared between all views. self._draw_mode = 'triangle_strip' self.set_gl_state('translucent', depth_test=False) self.set_data(pos=pos, color=color) def set_data(self, pos=None, color=None): """Set the data Parameters ---------- pos : list, tuple or numpy array Bounds of the region along the axis. len(pos) must be >=2. color : list, tuple, or array The color to use when drawing the line. It must have a shape of (1, 4) for a single color region or (len(pos), 4) for a multicolor region. """ new_pos = self._pos new_color = self._color if pos is not None: num_elements = len(pos) pos = np.array(pos, dtype=np.float32) if pos.ndim != 1: raise ValueError('Expected 1D array') vertex = np.empty((num_elements * 2, 2), dtype=np.float32) if self._is_vertical: vertex[:, 0] = np.repeat(pos, 2) vertex[:, 1] = np.tile([-1, 1], num_elements) else: vertex[:, 1] = np.repeat(pos, 2) vertex[:, 0] = np.tile([1, -1], num_elements) new_pos = vertex self._changed['pos'] = True if color is not None: color = np.array(color, dtype=np.float32) num_elements = new_pos.shape[0] / 2 if color.ndim == 2: if color.shape[0] != num_elements: raise ValueError('Expected a color for each pos') if color.shape[1] != 4: raise ValueError('Each color must be a RGBA array') color = np.repeat(color, 2, axis=0).astype(np.float32) elif color.ndim == 1: if color.shape[0] != 4: raise ValueError('Each color must be a RGBA array') color = np.repeat([color], new_pos.shape[0], axis=0) color = color.astype(np.float32) else: raise ValueError('Expected a numpy array of shape ' '(%d, 4) or (1, 4)' % num_elements) new_color = color self._changed['color'] = True # Ensure pos and color have the same size if new_pos.shape[0] != new_color.shape[0]: raise ValueError('pos and color does must have the same size') self._color = new_color self._pos = new_pos @property def color(self): return self._color[::2] @property def pos(self): if self._is_vertical: return self._pos[:, 0].ravel()[::2] else: return self._pos[:, 1].ravel()[::2] def _compute_bounds(self, axis, view): """Return the (min, max) bounding values of this visual along *axis* in the local coordinate system. """ is_vertical = self._is_vertical pos = self._pos if axis == 0 and is_vertical: return (pos[0, 0], pos[-1, 0]) elif axis == 1 and not is_vertical: return (pos[0, 1], pos[-1, 1]) return None @property def is_vertical(self): return self._is_vertical def _prepare_transforms(self, view=None): program = view.view_program transforms = view.transforms program.vert['render_to_visual'] = transforms.get_transform('render', 'visual') program.vert['transform'] = transforms.get_transform('visual', 'render') def _prepare_draw(self, view=None): """This method is called immediately before each draw. The *view* argument indicates which view is about to be drawn. """ if self._changed['pos']: self.pos_buf.set_data(self._pos) self._changed['pos'] = False if self._changed['color']: self.color_buf.set_data(self._color) self._program.vert['color'] = self.color_buf self._changed['color'] = False return True vispy-0.14.1/vispy/visuals/line_plot.py0000644000175000017510000001142214507104216017475 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 CompoundVisual class LinePlotVisual(CompoundVisual): """Visual displaying a plot line with optional markers. Parameters ---------- data : array-like Arguments can be passed as ``(Y,)``, ``(X, Y)``, ``(X, Y, Z)`` or ``np.array((X, Y))``, ``np.array((X, Y, Z))``. 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 """ _line_kwargs = ('color', 'width', 'connect') _marker_kwargs = ('edge_color', 'face_color', 'edge_width', 'marker_size', 'symbol') _valid_kwargs = set(_line_kwargs).union(set(_marker_kwargs)) _kw_trans = dict(marker_size='size') def __init__(self, data=None, color='k', symbol=None, line_kind='-', width=1., marker_size=10., edge_color='k', face_color='w', edge_width=1., connect='strip'): if line_kind != '-': raise ValueError('Only solid lines currently supported') self._line = LineVisual(method='gl', antialias=False) self._markers = MarkersVisual() self._kwargs = {} CompoundVisual.__init__(self, [self._line, self._markers]) 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=None, **kwargs): """Set the line data Parameters ---------- data : array-like The data. **kwargs : dict Keywoard arguments to pass to MarkerVisual and LineVisal. """ bad_keys = set(kwargs) - self._valid_kwargs if bad_keys: raise TypeError("Invalid keyword arguments: {}".format(", ".join(bad_keys))) # remember these kwargs for future updates self._kwargs.update(kwargs) if data is None: pos = None else: if isinstance(data, tuple): pos = np.array(data).T.astype(np.float32) else: 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(self._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] > 3: raise TypeError("Too many coordinates given (%s; max is 3)." % pos.shape[1]) # todo: have both sub-visuals share the same buffers. line_kwargs = {} for k in self._line_kwargs: if k in self._kwargs: k_ = self._kw_trans[k] if k in self._kw_trans else k line_kwargs[k] = self._kwargs.get(k_) if pos is not None or len(line_kwargs) > 0: self._line.set_data(pos=pos, **line_kwargs) marker_kwargs = {} for k in self._marker_kwargs: if k in self._kwargs: k_ = self._kw_trans[k] if k in self._kw_trans else k marker_kwargs[k_] = self._kwargs.get(k) if pos is not None or len(marker_kwargs) > 0: self._markers.set_data(pos=pos, **marker_kwargs) vispy-0.14.1/vispy/visuals/line/0000755000175000017510000000000014507104251016064 5ustar nileshnileshvispy-0.14.1/vispy/visuals/line/line.py0000644000175000017510000004413014507104216017370 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 from functools import lru_cache import numpy as np from ... import gloo, glsl from ...color import Color, ColorArray, get_colormap from ..shaders import Function from ..visual import Visual, CompoundVisual from ...util.profiler import Profiler from .dash_atlas import DashAtlas """ 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(CompoundVisual): """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. """ _join_types = joins _cap_types = caps def __init__(self, pos=None, color=(0.5, 0.5, 0.5, 1), width=1, connect='strip', method='gl', antialias=False): self._line_visual = None self._changed = {'pos': False, 'color': False, 'connect': False} self._pos = None self._color = None self._width = None self._connect = None self._bounds = None self._antialias = None self._method = 'none' CompoundVisual.__init__(self, []) # don't call subclass set_data; these often have different # signatures. LineVisual.set_data(self, pos=pos, color=color, width=width, connect=connect) self.antialias = antialias self.method = method @property def join_types(self): return self._join_types @property def cap_types(self): return self._cap_types @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 self._line_visual is not None: self.remove_subvisual(self._line_visual) if method == 'gl': self._line_visual = _GLLineVisual(self) elif method == 'agg': self._line_visual = _AggLineVisual(self) self.add_subvisual(self._line_visual) 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 < 1 px will be rounded up to 1 px when using the 'gl' 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: # width is always updated self._width = width 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, color_in=None): color_in = self._color if color_in is None else color_in colormap = None if isinstance(color_in, str): try: colormap = get_colormap(color_in) color = Function(colormap.glsl_map) except KeyError: color = Color(color_in).rgba elif isinstance(color_in, Function): color = Function(color_in) else: color = ColorArray(color_in).rgba if len(color) == 1: color = color[0] return color, colormap def _compute_bounds(self, axis, view): """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 _prepare_draw(self, view): if self._width == 0: return False CompoundVisual._prepare_draw(self, view) class _GLLineVisual(Visual): _shaders = { 'vertex': """ varying vec4 v_color; void main(void) { gl_Position = $transform($to_vec4($position)); v_color = $color; } """, 'fragment': """ 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 Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) self.set_gl_state('translucent') @staticmethod @lru_cache(maxsize=2) def _ensure_vec4_func(dims): if dims == 2: func = Function(""" vec4 vec2to4(vec2 xyz) { return vec4(xyz, 0.0, 1.0); } """) elif dims == 3: func = Function(""" vec4 vec3to4(vec3 xyz) { return vec4(xyz, 1.0); } """) else: raise TypeError("Vertex data must have shape (...,2) or (...,3).") return func def _prepare_transforms(self, view): xform = view.transforms.get_transform() view.view_program.vert['transform'] = xform def _prepare_draw(self, view): prof = Profiler() if self._parent._changed['pos']: if self._parent._pos is None: return False pos = np.ascontiguousarray(self._parent._pos, dtype=np.float32) self._pos_vbo.set_data(pos) self._program.vert['position'] = self._pos_vbo self._program.vert['to_vec4'] = self._ensure_vec4_func(pos.shape[-1]) self._parent._changed['pos'] = False if self._parent._changed['color']: color, cmap = self._parent._interpret_color() # If color is not visible, just quit now if isinstance(color, Color) and color.is_blank: return False 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 self._parent._changed['color'] = False self.shared_program['texture2D_LUT'] = cmap and cmap.texture_lut() self.update_gl_state(line_smooth=bool(self._parent._antialias)) px_scale = self.transforms.pixel_scale width = px_scale * self._parent._width self.update_gl_state(line_width=max(width, 1.0)) if self._parent._changed['connect']: self._connect = self._parent._interpret_connect() if isinstance(self._connect, np.ndarray): self._connect_ibo.set_data(self._connect) self._parent._changed['connect'] = False if self._connect is None: return False prof('prepare') # Draw if isinstance(self._connect, str) and \ self._connect == 'strip': self._draw_mode = 'line_strip' self._index_buffer = None elif isinstance(self._connect, str) and \ self._connect == 'segments': self._draw_mode = 'lines' self._index_buffer = None elif isinstance(self._connect, np.ndarray): self._draw_mode = 'lines' self._index_buffer = self._connect_ibo else: raise ValueError("Invalid line connect mode: %r" % self._connect) prof('draw') class _AggLineVisual(Visual): _agg_vtype = np.dtype([('a_position', np.float32, (2,)), ('a_tangents', np.float32, (4,)), ('a_segment', np.float32, (2,)), ('a_angles', np.float32, (2,)), ('a_texcoord', np.float32, (2,)), ('alength', np.float32), ('color', np.float32, (4,))]) _shaders = { 'vertex': glsl.get('lines/agg.vert'), 'fragment': glsl.get('lines/agg.frag'), } def __init__(self, parent): self._parent = parent self._vbo = gloo.VertexBuffer() self._pos = None self._color = None 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) Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) self._index_buffer = gloo.IndexBuffer() # The depth_test being disabled prevents z-ordering, but if # we turn it on the blending of the aa edges produces artifacts. self.set_gl_state('translucent', depth_test=False) self._draw_mode = 'triangles' def _prepare_transforms(self, view): data_doc = view.get_transform('visual', 'document') doc_px = view.get_transform('document', 'framebuffer') px_ndc = view.get_transform('framebuffer', 'render') vert = view.view_program.vert vert['transform'] = data_doc vert['doc_px_transform'] = doc_px vert['px_ndc_transform'] = px_ndc def _prepare_draw(self, view): bake = False if self._parent._changed['pos']: if self._parent._pos is None: return False self._pos = np.ascontiguousarray(self._parent._pos, dtype=np.float32) bake = True if self._parent._changed['color']: color, cmap = self._parent._interpret_color() self._color = 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, idxs = self._agg_bake(self._pos, self._color) self._vbo.set_data(V) self._index_buffer.set_data(idxs) # self._program.prepare() self.shared_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.shared_program[n] = v for n, v in self._U.items(): self.shared_program[n] = v self.shared_program['u_dash_atlas'] = self._dash_atlas @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) idxs = np.resize(np.array([0, 1, 2, 1, 2, 3], dtype=np.uint32), (n-1)*(2*3)) idxs += 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, idxs vispy-0.14.1/vispy/visuals/line/dash_atlas.py0000644000175000017510000000563714507104216020555 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from functools import lru_cache 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 def make_pattern(self, pattern, caps=(1, 1)): length = self._data.shape[1] return _make_pattern(length, pattern, caps) @lru_cache(maxsize=32) def _make_pattern(length, pattern, caps): """Make a concrete dash pattern of a given length.""" # 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 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.14.1/vispy/visuals/line/arrow.py0000644000175000017510000002364614507104216017604 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Arrows are a subclass of line visuals, which adds the ability to put several heads on a line. """ from __future__ import division import numpy as np from ... import glsl, gloo from ..transforms._util import as_vec4 from ..visual import Visual from .line import LineVisual ARROW_TYPES = ( 'stealth', 'curved', 'angle_30', 'angle_60', 'angle_90', 'triangle_30', 'triangle_60', 'triangle_90', 'inhibitor_round' ) class _ArrowHeadVisual(Visual): """Arrow head visual Several shapes to put on the end of a line. This visual differs from MarkersVisual in the sense that this visual calculates the orientation of the visual on the GPU, by calculating the tangent of the line between two given vertices. This is not really a visual you would use on your own, use :class:`ArrowVisual` instead. Parameters ---------- parent : ArrowVisual This actual ArrowVisual this arrow head is part of. """ ARROWHEAD_VERTEX_SHADER = glsl.get('arrowheads/arrowheads.vert') ARROWHEAD_FRAGMENT_SHADER = glsl.get('arrowheads/arrowheads.frag') _arrow_vtype = np.dtype([ ('v1', np.float32, (4,)), ('v2', np.float32, (4,)), ('size', np.float32), ('color', np.float32, (4,)), ('linewidth', np.float32) ]) def __init__(self, parent): Visual.__init__(self, self.ARROWHEAD_VERTEX_SHADER, self.ARROWHEAD_FRAGMENT_SHADER) self._parent = parent self.set_gl_state(depth_test=False, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self._draw_mode = 'points' self._arrow_vbo = gloo.VertexBuffer( np.array([], dtype=self._arrow_vtype)) def _prepare_transforms(self, view): xform = view.transforms.get_transform() view.view_program.vert['transform'] = xform def _prepare_draw(self, view=None): if self._parent._arrows_changed: self._prepare_vertex_data() self.shared_program.bind(self._arrow_vbo) self.shared_program['antialias'] = 1.0 self.shared_program.frag['arrow_type'] = self._parent.arrow_type self.shared_program.frag['fill_type'] = "filled" def _prepare_vertex_data(self): arrows = self._parent.arrows if arrows is None or arrows.size == 0: self._arrow_vbo = gloo.VertexBuffer( np.array([], dtype=self._arrow_vtype)) return v = np.zeros(len(arrows), dtype=self._arrow_vtype) # 2d // 3d v1 v2. sh = int(arrows.shape[1] / 2) v['v1'] = as_vec4(arrows[:, 0:sh]) v['v2'] = as_vec4(arrows[:, sh:int(2 * sh)]) v['size'][:] = self._parent.arrow_size color, cmap = self._parent._interpret_color(self._parent.arrow_color) v['color'][:] = color v['linewidth'][:] = self._parent.width self._arrow_vbo = gloo.VertexBuffer(v) class ArrowVisual(LineVisual): """Arrow visual A special line visual which can also draw optional arrow heads at the specified vertices. You add an arrow head by specifying two vertices `v1` and `v2` which represent the arrow body. This visual will draw an arrow head using `v2` as center point, and the orientation of the arrow head is automatically determined by calculating the direction vector between `v1` and `v2`. The arrow head can be detached from arrow body. Parameters ---------- pos : array Array of shape (..., 2) or (..., 3) specifying vertex coordinates of arrow body. 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. arrows : array A (N, 4) or (N, 6) matrix where each row contains the (x, y) or the (x, y, z) coordinates of the first and second vertex of the arrow head. Remember that the second vertex is used as center point for the arrow head, and the first vertex is only used for determining the arrow head orientation. arrow_type : string Specify the arrow head type, the currently available arrow head types are: * stealth * curved * triangle_30 * triangle_60 * triangle_90 * angle_30 * angle_60 * angle_90 * inhibitor_round arrow_size : float Specify the arrow size arrow_color : Color, tuple, or array The arrow head color. If an array is given, it must be of shape (..., 4) and provide one rgba color per arrow head. Can also be a colormap name, or appropriate `Function`. """ def __init__(self, pos=None, color=(0.5, 0.5, 0.5, 1), width=1, connect='strip', method='gl', antialias=False, arrows=None, arrow_type='stealth', arrow_size=None, arrow_color=(0.5, 0.5, 0.5, 1)): # Do not use the self._changed dictionary as it gets overwritten by # the LineVisual constructor. self._arrows_changed = False self._arrow_type = None self._arrow_size = None self._arrows = None self.arrow_type = arrow_type self.arrow_size = arrow_size self.arrow_color = arrow_color self.arrow_head = _ArrowHeadVisual(self) # TODO: `LineVisual.__init__` also calls its own `set_data` method, # which triggers an *update* event. This results in a redraw. After # that we call our own `set_data` method, which triggers another # redraw. This should be fixed. LineVisual.__init__(self, pos, color, width, connect, method, antialias) ArrowVisual.set_data(self, arrows=arrows) # Add marker visual for the arrow head self.add_subvisual(self.arrow_head) def set_data(self, pos=None, color=None, width=None, connect=None, arrows=None): """Set the data used for 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. 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. arrows : array A (N, 4) or (N, 6) matrix where each row contains the (x, y) or the (x, y, z) coordinate of the first and second vertex of the arrow body. Remember that the second vertex is used as center point for the arrow head, and the first vertex is only used for determining the arrow head orientation. """ if arrows is not None: self._arrows = arrows self._arrows_changed = True LineVisual.set_data(self, pos, color, width, connect) @property def arrow_type(self): return self._arrow_type @arrow_type.setter def arrow_type(self, value): if value not in ARROW_TYPES: raise ValueError( "Invalid arrow type '{}'. Should be one of {}".format( value, ", ".join(ARROW_TYPES) ) ) if value == self._arrow_type: return self._arrow_type = value self._arrows_changed = True @property def arrow_size(self): return self._arrow_size @arrow_size.setter def arrow_size(self, value): if value is None: self._arrow_size = 5.0 else: if value <= 0.0: raise ValueError("Arrow size should be greater than zero.") self._arrow_size = value self._arrows_changed = True @property def arrow_color(self): return self._arrow_color @arrow_color.setter def arrow_color(self, value): if value is not None: self._arrow_color = value self._arrows_changed = True @property def arrows(self): return self._arrows vispy-0.14.1/vispy/visuals/line/__init__.py0000644000175000017510000000035414507104216020200 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from .line import LineVisual # noqa from .arrow import ArrowVisual # noqa vispy-0.14.1/vispy/visuals/isosurface.py0000644000175000017510000000747414507104216017667 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 from ..color import Color 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*. vertex_colors : ndarray | None The vertex colors to use. face_colors : ndarray | None The face colors to use. color : ndarray | None The color to use. **kwargs : dict Keyword arguments to pass to the mesh construction. """ def __init__(self, data=None, level=None, vertex_colors=None, face_colors=None, color=(0.5, 0.5, 1, 1), **kwargs): self._data = None self._level = level self._vertex_colors = vertex_colors self._face_colors = face_colors self._color = Color(color) # We distinguish between recomputing and just changing the visual # properties - in the latter case we don't recompute the faces. self._vertices_cache = None self._faces_cache = None self._recompute = True self._update_meshvisual = True MeshVisual.__init__(self, **kwargs) if data is not None: self.set_data(data, vertex_colors=vertex_colors, face_colors=face_colors, color=color) @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=None, vertex_colors=None, face_colors=None, color=None): """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``. 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. """ # We only change the internal variables if they are provided if data is not None: self._data = data self._recompute = True if vertex_colors is not None: self._vertex_colors = vertex_colors self._update_meshvisual = True if face_colors is not None: self._face_colors = face_colors self._update_meshvisual = True if color is not None: self._color = Color(color) self._update_meshvisual = True self.update() def _prepare_draw(self, view): if self._data is None or self._level is None: return False if self._recompute: self._vertices_cache, self._faces_cache = isosurface(self._data, self._level) self._recompute = False self._update_meshvisual = True if self._update_meshvisual: MeshVisual.set_data(self, vertices=self._vertices_cache, faces=self._faces_cache, vertex_colors=self._vertex_colors, face_colors=self._face_colors, color=self._color) self._update_meshvisual = False return MeshVisual._prepare_draw(self, view) vispy-0.14.1/vispy/visuals/isoline.py0000644000175000017510000002047414507104216017161 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) 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 from .line import LineVisual from ..color import ColorArray 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 level_index = 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) level_index = np.append(level_index, np.array(len(point))) else: lines = point connects = np.arange(0, nbr*2).reshape((nbr, 2)) vertex_level = np.zeros(len(point)) + lev level_index = np.array(len(point)) vertex_level = vertex_level.reshape((vertex_level.size, 1)) return lines, connects, vertex_level, level_index 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._need_color_update = True self._need_recompute = True self._v = None self._c = None self._vl = None self._li = None self._lc = None self._cl = None self._update_color_lev = False 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._need_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._need_recompute = True if vertices is not None: self._vertices = vertices self._need_recompute = True if tris is not None: self._tris = tris self._need_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._need_color_update = True self.update() def _levels_to_colors(self): # computes ColorArrays for given levels # try _color_lev as colormap, except as everything else try: f_color_levs = get_colormap(self._color_lev) except (KeyError, TypeError): colors = ColorArray(self._color_lev).rgba else: lev = _normalize(self._levels, self._levels.min(), self._levels.max()) # map function expects (Nlev,1)! colors = f_color_levs.map(lev[:, np.newaxis]) if len(colors) == 1: colors = colors * np.ones((len(self._levels), 1)) # detect color/level mismatch and raise error if (len(colors) != len(self._levels)): raise TypeError("Color/level mismatch. Color must be of shape " "(Nlev, ...) and provide one color per level") self._lc = colors def _compute_iso_color(self): """Compute LineVisual color from level index and corresponding level color""" level_color = [] colors = self._lc for i, index in enumerate(self._li): level_color.append(np.zeros((index, 4)) + colors[i]) self._cl = np.vstack(level_color) def _prepare_draw(self, view): 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 False if self._need_recompute: self._v, self._c, self._vl, self._li = iso_mesh_line( self._vertices, self._tris, self._data, self._levels) self._levels_to_colors() self._compute_iso_color() LineVisual.set_data(self, pos=self._v, connect=self._c, color=self._cl) self._need_recompute = False if self._need_color_update: self._levels_to_colors() self._compute_iso_color() LineVisual.set_data(self, color=self._cl) self._update_color_lev = False return LineVisual._prepare_draw(self, view) vispy-0.14.1/vispy/visuals/isocurve.py0000644000175000017510000001636314507104216017360 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) 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 from .line import LineVisual from ..color import ColorArray from ..color.colormap import _normalize, get_colormap from ..geometry.isocurve import isocurve class IsocurveVisual(LineVisual): """Displays an isocurve of a 2D scalar array. Parameters ---------- data : ndarray | None 2D scalar array. levels : ndarray, shape (Nlev,) | None The levels at which the isocurve is constructed from "*data*". color_lev : Color, colormap name, tuple, list or array The color to use when drawing the line. If a list is given, it must be of shape (Nlev), if an array is given, it must be of shape (Nlev, ...). and provide one color per level (rgba, colorname). clim : tuple (min, max) limits to apply when mapping level values through a colormap. **kwargs : dict Keyword arguments to pass to `LineVisual`. """ def __init__(self, data=None, levels=None, color_lev=None, clim=None, **kwargs): self._data = None self._levels = levels self._color_lev = color_lev self._clim = clim self._need_color_update = True self._need_level_update = True self._need_recompute = True self._level_min = None self._data_is_uniform = False self._lc = None self._cl = None self._li = None self._connect = None self._verts = None kwargs['method'] = 'gl' kwargs['antialias'] = False LineVisual.__init__(self, **kwargs) if data is not None: self.set_data(data) @property def levels(self): """The threshold at which the isocurve is constructed from the 2D data. """ return self._levels @levels.setter def levels(self, levels): self._levels = levels self._need_level_update = True self._need_recompute = True self.update() @property def color(self): return self._color_lev @color.setter def color(self, color): self._color_lev = color self._need_level_update = True self._need_color_update = 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.levels``. """ self._data = data if self._clim is None: self._clim = (data.min(), data.max()) # sanity check, # should we raise an error here, since no isolines can be drawn? # for now, _prepare_draw returns False if no isoline can be drawn if self._data.min() != self._data.max(): self._data_is_uniform = False else: self._data_is_uniform = True self._need_recompute = True self.update() def _get_verts_and_connect(self, paths): """Retrieve vertices and connects from given paths-list""" verts = np.vstack(paths) gaps = np.add.accumulate(np.array([len(x) for x in paths])) - 1 connect = np.ones(gaps[-1], dtype=bool) connect[gaps[:-1]] = False return verts, connect def _compute_iso_line(self): """Compute LineVisual vertices, connects and color-index""" level_index = [] connects = [] verts = [] # calculate which level are within data range # this works for now and the existing examples, but should be tested # thoroughly also with the data-sanity check in set_data-function choice = np.nonzero((self.levels > self._data.min()) & (self.levels < self._data.max())) levels_to_calc = np.array(self.levels)[choice] # save minimum level index self._level_min = choice[0][0] try: from skimage.measure import find_contours except ImportError: find_contours = None for level in levels_to_calc: # if we use skimage isoline algorithm we need to add half a # pixel in both (x,y) dimensions because isolines are aligned to # pixel centers if find_contours is not None: contours = find_contours(self._data, level, positive_orientation='high') v, c = self._get_verts_and_connect(contours) # swap row, column to column, row (x, y) v[:, [0, 1]] = v[:, [1, 0]] v += np.array([0.5, 0.5]) else: paths = isocurve(self._data.astype(float).T, level, extend_to_edge=True, connected=True) v, c = self._get_verts_and_connect(paths) level_index.append(v.shape[0]) connects.append(np.hstack((c, [False]))) verts.append(v) self._li = np.hstack(level_index) self._connect = np.hstack(connects) self._verts = np.vstack(verts) def _compute_iso_color(self): """Compute LineVisual color from level index and corresponding color""" level_color = [] colors = self._lc for i, index in enumerate(self._li): level_color.append(np.zeros((index, 4)) + colors[i+self._level_min]) self._cl = np.vstack(level_color) def _levels_to_colors(self): # computes ColorArrays for given levels # try _color_lev as colormap, except as everything else try: f_color_levs = get_colormap(self._color_lev) except (KeyError, TypeError): colors = ColorArray(self._color_lev).rgba else: lev = _normalize(self._levels, self._clim[0], self._clim[1]) # map function expects (Nlev,1)! colors = f_color_levs.map(lev[:, np.newaxis]) # broadcast to (nlev, 4) array if len(colors) == 1: colors = colors * np.ones((len(self._levels), 1)) # detect color_lev/levels mismatch and raise error if (len(colors) != len(self._levels)): raise TypeError("Color/level mismatch. Color must be of shape " "(Nlev, ...) and provide one color per level") self._lc = colors def _prepare_draw(self, view): if (self._data is None or self._levels is None or self._color_lev is None or self._data_is_uniform): return False if self._need_level_update: self._levels_to_colors() self._need_level_update = False if self._need_recompute: self._compute_iso_line() self._compute_iso_color() LineVisual.set_data(self, pos=self._verts, connect=self._connect, color=self._cl) self._need_recompute = False if self._need_color_update: self._compute_iso_color() LineVisual.set_data(self, color=self._cl) self._need_color_update = False return LineVisual._prepare_draw(self, view) vispy-0.14.1/vispy/visuals/instanced_mesh.py0000644000175000017510000001307114507104216020476 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """An instanced version of MeshVisual with arbitrary shifts, transforms, and colors.""" from __future__ import division import numpy as np from ..gloo import VertexBuffer from ..gloo.texture import downcast_to_32bit_if_needed from ..color import ColorArray from .filters import InstancedShadingFilter from .shaders import Variable from .mesh import MeshVisual _VERTEX_SHADER = """ uniform bool use_instance_colors; // these attributes will be defined on an instance basis attribute vec3 shift; attribute vec3 transform_x; attribute vec3 transform_y; attribute vec3 transform_z; varying vec4 v_base_color; void main() { v_base_color = $color_transform($base_color); // transform is generated from column vectors (new basis vectors) // https://en.wikibooks.org/wiki/GLSL_Programming/Vector_and_Matrix_Operations#Constructors mat3 instance_transform = mat3(transform_x, transform_y, transform_z); vec3 pos_rotated = instance_transform * $to_vec4($position).xyz; vec4 pos_shifted = $to_vec4(pos_rotated + shift); gl_Position = $transform(pos_shifted); } """ class InstancedMeshVisual(MeshVisual): """Instanced Mesh visual. Mostly identical to MeshVisual, but additionally takes arrays of of positions and transforms (optionally colors) to create multiple instances of the mesh. Instancing is a rendering technique that re-uses the same mesh data by applying transformations to vertices and vertex data or textures, wich can drastically improve performance compared to having many simple MeshVisuals. Parameters ---------- instance_positions : (I, 3) array Coordinates for each instance of the mesh. instance_transforms : (I, 3, 3) array Matrices for the transforms to apply to each instance. instance_colors : ColorArray Matrices of colors for each instance. Colors *args : list Positional arguments to pass to :class:`~vispy.visuals.mesh.MeshVisual`. **kwargs : dict Keyword arguments to pass to :class:`~vispy.visuals.mesh.MeshVisual`. Examples -------- See example `scene/instanced_mesh_visual.py` in the gallery. """ _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': MeshVisual._shaders['fragment'], } _shading_filter_class = InstancedShadingFilter def __init__(self, *args, instance_positions, instance_transforms, instance_colors=None, **kwargs): self._instance_positions = None self._instance_positions_vbo = None self._instance_transforms = None self._instance_transforms_vbos = None self._instance_colors = None self._instance_colors_vbo = None super().__init__(*args, **kwargs) self.instance_positions = instance_positions self.instance_transforms = instance_transforms self.instance_colors = instance_colors @property def instance_positions(self): return self._instance_positions @instance_positions.setter def instance_positions(self, pos): pos = np.reshape(pos, (-1, 3)) if pos.ndim != 2 or pos.shape[-1] != 3: raise ValueError(f'positions must be 3D coordinates, but provided data has shape {pos.shape}') self._instance_positions = downcast_to_32bit_if_needed(pos, dtype=np.float32) self._instance_positions_vbo = VertexBuffer(self._instance_positions, divisor=1) self.mesh_data_changed() @property def instance_transforms(self): return self._instance_transforms @instance_transforms.setter def instance_transforms(self, matrix): matrix = np.reshape(matrix, (-1, 3, 3)) if matrix.ndim != 3 or matrix.shape[1:] != (3, 3): raise ValueError(f'transforms must be an array of 3x3 matrices, but provided data has shape {matrix.shape}') self._instance_transforms = downcast_to_32bit_if_needed(matrix, dtype=np.float32) # copy if not c contiguous self._instance_transforms_vbos = ( VertexBuffer(np.ascontiguousarray(self._instance_transforms[..., 0]), divisor=1), VertexBuffer(np.ascontiguousarray(self._instance_transforms[..., 1]), divisor=1), VertexBuffer(np.ascontiguousarray(self._instance_transforms[..., 2]), divisor=1), ) self.mesh_data_changed() @property def instance_colors(self): return self._instance_colors @instance_colors.setter def instance_colors(self, colors): if colors is not None: colors = ColorArray(colors) self._instance_colors_vbo = VertexBuffer(colors.rgba, divisor=1) else: self._instance_colors_vbo = Variable('base_color', self._color.rgba) self._instance_colors = colors self.mesh_data_changed() def _update_data(self): with self.events.data_updated.blocker(): super()._update_data() # set instance buffers self.shared_program.vert['base_color'] = self._instance_colors_vbo self.shared_program['transform_x'] = self._instance_transforms_vbos[0] self.shared_program['transform_y'] = self._instance_transforms_vbos[1] self.shared_program['transform_z'] = self._instance_transforms_vbos[2] self.shared_program['shift'] = self._instance_positions_vbo self.events.data_updated() vispy-0.14.1/vispy/visuals/infinite_line.py0000644000175000017510000001356614507104216020337 0ustar nileshnileshimport numpy as np from .. import gloo from .visual import Visual _VERTEX_SHADER = """ attribute vec2 a_pos; varying vec4 v_color; void main() { vec4 pos = vec4(a_pos, 0., 1.); if($is_vertical==1) { pos.y = $render_to_visual(pos).y; } else { pos.x = $render_to_visual(pos).x; } gl_Position = $transform(pos); gl_PointSize = 10.; v_color = $color; } """ _FRAGMENT_SHADER = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class InfiniteLineVisual(Visual): """Infinite horizontal or vertical line for 2D plots. Parameters ---------- pos : float Position of the line along the axis. color : list, tuple, or array The color to use when drawing the line. If an array is given, it must be of shape (1, 4) and provide one rgba color per vertex. line_width: float The width of the Infinite line, in pixels antialias: bool If the line is drawn with antialiasing vertical: True for drawing a vertical line, False for an horizontal line """ _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } def __init__(self, pos=None, color=(1.0, 1.0, 1.0, 1.0), line_width=1.0, antialias=False, vertical=True, **kwargs): """ """ Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) self._changed = {'pos': False, 'color': False} self.pos_buf = gloo.VertexBuffer() # The Visual superclass contains a MultiProgram, which is an object # that behaves like a normal shader program (you can assign shader # code, upload values, set template variables, etc.) but internally # manages multiple ModularProgram instances, one per view. # The MultiProgram is accessed via the `shared_program` property, so # the following modifications to the program will be applied to all # views: self.shared_program['a_pos'] = self.pos_buf self._program.vert['is_vertical'] = 1 if vertical else 0 self._need_upload = False self._is_vertical = bool(vertical) self._pos = np.zeros((2, 2), dtype=np.float32) self._color = np.ones(4, dtype=np.float32) self._line_width = line_width self._antialias = antialias # Visual keeps track of draw mode, index buffer, and GL state. These # are shared between all views. self._draw_mode = 'line_strip' self.set_gl_state('translucent', depth_test=False) self.set_data(pos=pos, color=color) def set_data(self, pos=None, color=None): """Set the data Parameters ---------- pos : float Position of the line along the axis. color : list, tuple, or array The color to use when drawing the line. If an array is given, it must be of shape (1, 4) and provide one rgba color per vertex. """ if pos is not None: pos = float(pos) xy = self._pos if self._is_vertical: xy[0, 0] = pos xy[0, 1] = -1 xy[1, 0] = pos xy[1, 1] = 1 else: xy[0, 0] = -1 xy[0, 1] = pos xy[1, 0] = 1 xy[1, 1] = pos self._changed['pos'] = True if color is not None: color = np.array(color, dtype=np.float32) if color.ndim != 1 or color.shape[0] != 4: raise ValueError('color must be a 4 element float rgba tuple,' ' list or array') self._color = color self._changed['color'] = True @property def color(self): return self._color @property def pos(self): if self._is_vertical: return self._pos[0, 0] else: return self._pos[0, 1] @property def line_width(self): return self._line_width @line_width.setter def line_width(self, val: float): self._line_width = val @property def antialias(self): return self._antialias @antialias.setter def antialias(self, val: float): self._antialias = val def _compute_bounds(self, axis, view): """Return the (min, max) bounding values of this visual along *axis* in the local coordinate system. """ is_vertical = self._is_vertical pos = self._pos if axis == 0 and is_vertical: return (pos[0, 0], pos[0, 0]) elif axis == 1 and not is_vertical: return (self._pos[0, 1], self._pos[0, 1]) return None @property def is_vertical(self): return self._is_vertical def _prepare_transforms(self, view=None): program = view.view_program transforms = view.transforms program.vert['render_to_visual'] = transforms.get_transform('render', 'visual') program.vert['transform'] = transforms.get_transform('visual', 'render') def _prepare_draw(self, view=None): """This method is called immediately before each draw. The *view* argument indicates which view is about to be drawn. """ self.update_gl_state(line_smooth=self._antialias) px_scale = self.transforms.pixel_scale width = px_scale * self._line_width self.update_gl_state(line_width=max(width, 1.0)) if self._changed['pos']: self.pos_buf.set_data(self._pos) self._changed['pos'] = False if self._changed['color']: self._program.vert['color'] = self._color self._changed['color'] = False vispy-0.14.1/vispy/visuals/image_complex.py0000644000175000017510000001223114507104216020320 0ustar nileshnileshfrom .image import ImageVisual, _APPLY_CLIM_FLOAT, _APPLY_GAMMA_FLOAT import numpy as np from .shaders import Function, FunctionChain # In a complex Image, the texture will be rg32f, where: # data.r contains the real component # data.g contains the imaginary component COMPLEX_TRANSFORMS = { "real": "float cplx2float(vec4 data) { return data.r; }", "imaginary": "float cplx2float(vec4 data) { return data.g; }", "magnitude": "float cplx2float(vec4 data) { return length(vec2(data)); }", "phase": "float cplx2float(vec4 data) { return atan(data.g, data.r); }", } CPU_COMPLEX_TRANSFORMS = { "magnitude": np.abs, "phase": np.angle, "real": np.real, "imaginary": np.imag, } class ComplexImageVisual(ImageVisual): """:class:`~vispy.visuals.ImageVisual` subclass displaying a complex-valued image. This class handles complex values by using an rg32f float texture behind the scenes, storing the real component in the "r" value and the imaginary in the "g" value. Parameters ---------- data : ndarray Complex valued ImageVisual data. Should be a two dimensional array with a dtype of np.complex64 or np.complex128. complex_mode : str The mode used to convert the complex value in each pixel into a scalar: * 'real': show only the real component. * 'imaginary': show only the imaginary component. * 'magnitude': show the magnitude (`np.abs`) of the complex value. * 'phase': show the phase (`np.angle`) of the complex value. """ COMPLEX_MODES = set(COMPLEX_TRANSFORMS) def __init__(self, data=None, complex_mode="magnitude", **kwargs): if complex_mode not in self.COMPLEX_MODES: raise ValueError( "complex_mode must be one of %s" % ", ".join(self.COMPLEX_MODES) ) self._data_is_complex = np.iscomplexobj(data) self._complex_mode = complex_mode if kwargs.get("clim", "auto") == "auto" and self._data_is_complex: kwargs["clim"] = self._calc_complex_clim(data) kwargs["texture_format"] = "r32f" if self._data_is_complex else "r32f" if self._data_is_complex: data = self._convert_complex_to_float_view(data) super().__init__(data=data, **kwargs) def _init_texture(self, data, texture_format, **texture_kwargs): texture_kwargs = {} if self._data_is_complex: texture_kwargs["format"] = "rg" return super()._init_texture(data, texture_format, **texture_kwargs) def set_data(self, image): data = np.asarray(image) if np.iscomplexobj(data): # Turn the texture into an rg32f texture # where r = 'real' and g = 'imag' self._data_is_complex = True # FUTURE: Add formal way of defining texture format from set_data self._texture._format = "rg" data = self._convert_complex_to_float_view(data) elif data.ndim == 3 and data.shape[-1] == 2: # data was complex but was already converted to 32-bit float # should really only occur from __init__ self._data_is_complex = True else: self._texture._format = None return super().set_data(data) @staticmethod def _convert_complex_to_float_view(complex_arr): # turn complex128 into complex64 if needed complex64_arr = complex_arr.astype(np.complex64, copy=False) float_view_arr = complex64_arr.view(dtype=np.float32).reshape((complex64_arr.shape + (2, ))) return float_view_arr @property def complex_mode(self): return self._data_is_complex and self._complex_mode @complex_mode.setter def complex_mode(self, value): if value not in self.COMPLEX_MODES: raise ValueError( "complex_mode must be one of %s" % ", ".join(self.COMPLEX_MODES) ) if self._complex_mode != value: self._complex_mode = value self._need_colortransform_update = True self.update() def _build_color_transform(self): if self.complex_mode: fclim = Function(_APPLY_CLIM_FLOAT) fgamma = Function(_APPLY_GAMMA_FLOAT) chain = [ Function(COMPLEX_TRANSFORMS[self.complex_mode]), fclim, fgamma, Function(self.cmap.glsl_map), ] fun = FunctionChain(None, chain) fclim["clim"] = self._texture.clim_normalized fgamma["gamma"] = self.gamma return fun return super()._build_color_transform() @ImageVisual.clim.setter def clim(self, clim): if clim == "auto" and self.complex_mode: clim = self._calc_complex_clim() super(ComplexImageVisual, type(self)).clim.fset(self, clim) def _calc_complex_clim(self, data=None): # it would be nice if this could be done in the scalable texture mixin, # but that would require the mixin knowing about the complex mode. func = CPU_COMPLEX_TRANSFORMS[self.complex_mode] _rendered = func(self._data if data is None else data) return (_rendered.min(), _rendered.max()) vispy-0.14.1/vispy/visuals/image.py0000644000175000017510000006376214507104216016610 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Primitive 2D image visual class.""" from __future__ import division import warnings import numpy as np from ..gloo import Texture2D, VertexBuffer from ..color import get_colormap from .shaders import Function, FunctionChain from .transforms import NullTransform from .visual import Visual from ..io import load_spatial_filters from ._scalable_textures import CPUScaledTexture2D, GPUScaledTexture2D _VERTEX_SHADER = """ uniform int method; // 0=subdivide, 1=impostor 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.)); } """ _FRAGMENT_SHADER = """ uniform vec2 image_size; uniform int method; // 0=subdivide, 1=impostor uniform sampler2D u_texture; varying vec2 v_texcoord; 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); } void main() { vec2 texcoord; if( method == 0 ) { texcoord = v_texcoord; } else { // vertex shader outputs clip coordinates; // fragment shader maps to texture coordinates texcoord = map_local_to_tex(vec4(v_texcoord, 0, 1)).xy; } gl_FragColor = $color_transform($get_data(texcoord)); } """ # noqa _INTERPOLATION_TEMPLATE = """ #include "misc/spatial-filters.frag" vec4 texture_lookup_filtered(vec2 texcoord) { if(texcoord.x < 0.0 || texcoord.x > 1.0 || texcoord.y < 0.0 || texcoord.y > 1.0) { discard; } return %s($texture, $shape, texcoord); }""" _TEXTURE_LOOKUP = """ vec4 texture_lookup(vec2 texcoord) { if(texcoord.x < 0.0 || texcoord.x > 1.0 || texcoord.y < 0.0 || texcoord.y > 1.0) { discard; } return texture2D($texture, texcoord); }""" _APPLY_CLIM_FLOAT = """ float apply_clim(float data) { // If data is NaN, don't draw it at all // http://stackoverflow.com/questions/11810158/how-to-deal-with-nan-or-inf-in-opengl-es-2-0-shaders if (!(data <= 0.0 || 0.0 <= data)) { discard; } data = clamp(data, min($clim.x, $clim.y), max($clim.x, $clim.y)); data = (data - $clim.x) / ($clim.y - $clim.x); return data; }""" _APPLY_CLIM = """ vec4 apply_clim(vec4 color) { // Handle NaN values // http://stackoverflow.com/questions/11810158/how-to-deal-with-nan-or-inf-in-opengl-es-2-0-shaders color.r = !(color.r <= 0.0 || 0.0 <= color.r) ? min($clim.x, $clim.y) : color.r; color.g = !(color.g <= 0.0 || 0.0 <= color.g) ? min($clim.x, $clim.y) : color.g; color.b = !(color.b <= 0.0 || 0.0 <= color.b) ? min($clim.x, $clim.y) : color.b; color.a = !(color.a <= 0.0 || 0.0 <= color.a) ? 0 : color.a; color.rgb = clamp(color.rgb, min($clim.x, $clim.y), max($clim.x, $clim.y)); color.rgb = (color.rgb - $clim.x) / ($clim.y - $clim.x); return max(color, 0.0); } """ _APPLY_GAMMA_FLOAT = """ float apply_gamma(float data) { return pow(data, $gamma); }""" _APPLY_GAMMA = """ vec4 apply_gamma(vec4 color) { color.rgb = pow(color.rgb, vec3($gamma)); return color; } """ _NULL_COLOR_TRANSFORM = 'vec4 pass(vec4 color) { return color; }' _C2L_RED = 'float cmap(vec4 color) { return color.r; }' _CUSTOM_FILTER = """ vec4 texture_lookup(vec2 texcoord) { // based on https://gist.github.com/kingbedjed/373c8811efcf1b3a155d29a13c1e5b61 vec2 tex_pixel = 1 / $shape; vec2 kernel_pixel = 1 / $kernel_shape; vec2 sampling_corner = texcoord - ($kernel_shape / 2 * tex_pixel); // loop over kernel pixels vec2 kernel_pos, tex_pos; vec4 color = vec4(0); float weight; // offset 0.5 to sample center of pixels for (float i = 0.5; i < $kernel_shape.x; i++) { for (float j = 0.5; j < $kernel_shape.y; j++) { kernel_pos = vec2(i, j) * kernel_pixel; tex_pos = sampling_corner + vec2(i, j) * tex_pixel; // TODO: allow other edge effects, like mirror or wrap if (tex_pos.x >= 0 && tex_pos.y >= 0 && tex_pos.x <= 1 && tex_pos.y <= 1) { weight = texture2D($kernel, kernel_pos).r; // make sure to clamp or we sample outside color += texture2D($texture, clamp(tex_pos, 0, 1)) * weight; } } } return color; } """ 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). If floating point data is provided and contains NaNs, they will be made transparent (discarded) for the single band data case when scaling is done on the GPU (see ``texture_format``). On the CPU, single band NaNs are mapped to 0 as they are sent to the GPU which result in them using the lowest ``clim`` value in the GPU. For RGB data, NaNs will be mapped to the lowest ``clim`` value. If the Alpha band is NaN it will be mapped to 0 (transparent). Note that NaN handling is not required by some OpenGL implementations and NaNs may be treated differently on some systems (ex. as 0s). 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. I.e. the values that map to black and white in a gray colormap. Can be 'auto' to auto-set bounds to the min and max of the data. If not given or None, 'auto' is used. gamma : float Gamma to use during colormap lookup. Final color will be cmap(val**gamma). by default: 1. interpolation : str Selects method of texture interpolation. Makes use of the two hardware interpolation methods and the available interpolation methods defined in vispy/gloo/glsl/misc/spatial_filters.frag * 'nearest': Default, uses 'nearest' with Texture interpolation. * 'linear': uses 'linear' with Texture interpolation. * 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', 'cubic', 'catrom', 'mitchell', 'spline16', 'spline36', 'gaussian', 'bessel', 'sinc', 'lanczos', 'blackman' * 'custom': uses the sampling kernel provided through 'custom_kernel'. texture_format: numpy.dtype | str | None How to store data on the GPU. OpenGL allows for many different storage formats and schemes for the low-level texture data stored in the GPU. Most common is unsigned integers or floating point numbers. Unsigned integers are the most widely supported while other formats may not be supported on older versions of OpenGL or with older GPUs. Default value is ``None`` which means data will be scaled on the CPU and the result stored in the GPU as an unsigned integer. If a numpy dtype object, an internal texture format will be chosen to support that dtype and data will *not* be scaled on the CPU. Not all dtypes are supported. If a string, then it must be one of the OpenGL internalformat strings described in the table on this page: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml The name should have `GL_` removed and be lowercase (ex. `GL_R32F` becomes ``'r32f'``). Lastly, this can also be the string ``'auto'`` which will use the data type of the provided image data to determine the internalformat of the texture. When this is specified (not ``None``) data is scaled on the GPU which allows for faster color limit changes. Additionally, when 32-bit float data is provided it won't be copied before being transferred to the GPU. custom_kernel: numpy.ndarray Kernel used for texture sampling when interpolation is set to 'custom'. **kwargs : dict Keyword arguments to pass to `Visual`. Notes ----- The colormap functionality through ``cmap`` and ``clim`` are only used if the data are 2D. """ _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } _func_templates = { 'texture_lookup_interpolated': _INTERPOLATION_TEMPLATE, 'texture_lookup_custom': _CUSTOM_FILTER, 'texture_lookup': _TEXTURE_LOOKUP, 'clim_float': _APPLY_CLIM_FLOAT, 'clim': _APPLY_CLIM, 'gamma_float': _APPLY_GAMMA_FLOAT, 'gamma': _APPLY_GAMMA, 'null_color_transform': _NULL_COLOR_TRANSFORM, 'red_to_luminance': _C2L_RED, } def __init__(self, data=None, method='auto', grid=(1, 1), cmap='viridis', clim='auto', gamma=1.0, interpolation='nearest', texture_format=None, custom_kernel=np.ones((1, 1)), **kwargs): """Initialize image properties, texture storage, and interpolation methods.""" self._data = None # load 'float packed rgba8' interpolation kernel # to load float interpolation kernel use # `load_spatial_filters(packed=False)` kernel, interpolation_names = load_spatial_filters() self._kerneltex = Texture2D(kernel, interpolation='nearest') # The unpacking can be debugged by changing "spatial-filters.frag" # to have the "unpack" function just return the .r component. That # combined with using the below as the _kerneltex allows debugging # of the pipeline # self._kerneltex = Texture2D(kernel, interpolation='linear', # internalformat='r32f') interpolation_names, interpolation_fun = self._init_interpolation( interpolation_names) self._interpolation_names = interpolation_names self._interpolation_fun = interpolation_fun self._interpolation = interpolation if self._interpolation not in self._interpolation_names: raise ValueError("interpolation must be one of %s" % ', '.join(self._interpolation_names)) self._method = method self._grid = grid self._need_texture_upload = True self._need_vertex_update = True self._need_colortransform_update = True self._need_interpolation_update = True self._texture = self._init_texture(data, texture_format) self._subdiv_position = VertexBuffer() self._subdiv_texcoord = VertexBuffer() # impostor quad covers entire viewport vertices = np.array([[-1, -1], [1, -1], [1, 1], [-1, -1], [1, 1], [-1, 1]], dtype=np.float32) self._impostor_coords = VertexBuffer(vertices) self._null_tr = NullTransform() self._init_view(self) Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) self.set_gl_state('translucent', cull_face=False) self._draw_mode = 'triangles' # define _data_lookup_fn as None, will be setup in # self._build_interpolation() self._data_lookup_fn = None self.clim = clim or "auto" # None -> "auto" self.cmap = cmap self.gamma = gamma self.custom_kernel = custom_kernel if data is not None: self.set_data(data) self.freeze() def _init_interpolation(self, interpolation_names): # create interpolation shader functions for available interpolations fun = [Function(self._func_templates['texture_lookup_interpolated'] % (n + '2D')) for n in interpolation_names] interpolation_names = [n.lower() for n in interpolation_names] # add custom filter fun.append(Function(self._func_templates['texture_lookup_custom'])) interpolation_names.append('custom') interpolation_fun = dict(zip(interpolation_names, fun)) interpolation_names = tuple(sorted(interpolation_names)) # overwrite "nearest" and "linear" spatial-filters # with "hardware" interpolation _data_lookup_fn hardware_lookup = Function(self._func_templates['texture_lookup']) interpolation_fun['nearest'] = hardware_lookup interpolation_fun['linear'] = hardware_lookup # alias bilinear to linear and bicubic to cubic (but deprecate) interpolation_names = interpolation_names + ('bilinear', 'bicubic') return interpolation_names, interpolation_fun def _init_texture(self, data, texture_format, **texture_kwargs): if self._interpolation == 'linear': texture_interpolation = 'linear' else: texture_interpolation = 'nearest' if texture_format is None: tex = CPUScaledTexture2D( data, interpolation=texture_interpolation, **texture_kwargs ) else: tex = GPUScaledTexture2D( data, internalformat=texture_format, interpolation=texture_interpolation, **texture_kwargs ) return tex def set_data(self, image, copy=False): """Set the image data. Parameters ---------- image : array-like The image data. texture_format : str or None """ data = np.array(image, copy=copy) if np.iscomplexobj(data): raise TypeError( "Complex data types not supported. Please use 'ComplexImage' instead" ) # can the texture handle this data? self._texture.check_data_format(data) if self._data is None or self._data.shape[:2] != data.shape[:2]: # Only rebuild if the size of the image changed self._need_vertex_update = True self._data = data self._need_texture_upload = True def view(self): """Get the :class:`vispy.visuals.visual.VisualView` for this visual.""" v = Visual.view(self) self._init_view(v) return v def _init_view(self, view): # Store some extra variables per-view view._need_method_update = True view._method_used = None @property def clim(self): """Get color limits used when rendering the image (cmin, cmax).""" return self._texture.clim @clim.setter def clim(self, clim): if self._texture.set_clim(clim): self._need_texture_upload = True self._update_colortransform_clim() self.update() def _update_colortransform_clim(self): if self._need_colortransform_update: # we are going to rebuild anyway so just do it later return try: norm_clims = self._texture.clim_normalized except RuntimeError: return else: # shortcut so we don't have to rebuild the whole color transform self.shared_program.frag['color_transform'][1]['clim'] = norm_clims @property def cmap(self): """Get the colormap object applied to luminance (single band) data.""" return self._cmap @cmap.setter def cmap(self, cmap): self._cmap = get_colormap(cmap) self._need_colortransform_update = True self.update() @property def gamma(self): """Get the gamma used when rendering the image.""" return self._gamma @gamma.setter def gamma(self, value): """Set gamma used when rendering the image.""" if value <= 0: raise ValueError("gamma must be > 0") self._gamma = float(value) # shortcut so we don't have to rebuild the color transform if not self._need_colortransform_update: self.shared_program.frag['color_transform'][2]['gamma'] = self._gamma self.update() @property def method(self): """Get rendering method name.""" 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): """Get size of the image (width, height).""" return self._data.shape[:2][::-1] @property def interpolation(self): """Get interpolation algorithm name.""" return self._interpolation @interpolation.setter def interpolation(self, i): if i not in self._interpolation_names: raise ValueError("interpolation must be one of %s" % ', '.join(self._interpolation_names)) if self._interpolation != i: self._interpolation = i self._need_interpolation_update = True self.update() @property def interpolation_functions(self): """Get names of possible interpolation methods.""" return self._interpolation_names @property def custom_kernel(self): """Kernel used by 'custom' interpolation for texture sampling""" return self._custom_kernel @custom_kernel.setter def custom_kernel(self, value): value = np.asarray(value, dtype=np.float32) if value.ndim != 2: raise ValueError(f'kernel must have 2 dimensions; got {value.ndim}') self._custom_kernel = value self._custom_kerneltex = Texture2D(value, interpolation='nearest', internalformat='r32f') if self._data_lookup_fn is not None and 'kernel' in self._data_lookup_fn: self._data_lookup_fn['kernel'] = self._custom_kerneltex self._data_lookup_fn['kernel_shape'] = value.shape[::-1] self.update() # The interpolation code could be transferred to a dedicated filter # function in visuals/filters as discussed in #1051 def _build_interpolation(self): """Rebuild the _data_lookup_fn for different interpolations.""" interpolation = self._interpolation # alias bilinear to linear if interpolation == 'bilinear': warnings.warn( "'bilinear' interpolation is Deprecated. Use 'linear' instead.", DeprecationWarning, stacklevel=2, ) interpolation = 'linear' # alias bicubic to cubic if interpolation == 'bicubic': warnings.warn( "'bicubic' interpolation is Deprecated. Use 'cubic' instead.", DeprecationWarning, stacklevel=2, ) interpolation = 'cubic' self._data_lookup_fn = self._interpolation_fun[interpolation] self.shared_program.frag['get_data'] = self._data_lookup_fn # only 'linear' and 'custom' use 'linear' texture interpolation if interpolation in ('linear', 'custom'): texture_interpolation = 'linear' else: texture_interpolation = 'nearest' # 'nearest' (and also 'linear') doesn't use spatial_filters.frag # so u_kernel and shape setting is skipped if interpolation not in ('nearest', 'linear'): self._data_lookup_fn['shape'] = self._data.shape[:2][::-1] if interpolation == 'custom': self._data_lookup_fn['kernel'] = self._custom_kerneltex self._data_lookup_fn['kernel_shape'] = self._custom_kernel.shape[::-1] else: self.shared_program['u_kernel'] = self._kerneltex if self._texture.interpolation != texture_interpolation: self._texture.interpolation = texture_interpolation self._data_lookup_fn['texture'] = self._texture self._need_interpolation_update = False def _build_vertex_data(self): """Rebuild the vertex buffers for the subdivide method.""" grid = self._grid 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 self._subdiv_position.set_data(vertices.astype('float32')) self._subdiv_texcoord.set_data(tex_coords.astype('float32')) self._need_vertex_update = False def _update_method(self, view): """Decide which method to use for *view* and configure it accordingly.""" method = self._method if method == 'auto': if view.transforms.get_transform().Linear: method = 'subdivide' else: method = 'impostor' view._method_used = method if method == 'subdivide': view.view_program['method'] = 0 view.view_program['a_position'] = self._subdiv_position view.view_program['a_texcoord'] = self._subdiv_texcoord elif method == 'impostor': view.view_program['method'] = 1 view.view_program['a_position'] = self._impostor_coords view.view_program['a_texcoord'] = self._impostor_coords else: raise ValueError("Unknown image draw method '%s'" % method) self.shared_program['image_size'] = self.size view._need_method_update = False self._prepare_transforms(view) def _build_texture(self): try: pre_clims = self._texture.clim_normalized except RuntimeError: pre_clims = "auto" pre_internalformat = self._texture.internalformat # copy was already made on `set_data` if requested self._texture.scale_and_set_data(self._data, copy=False) post_clims = self._texture.clim_normalized post_internalformat = self._texture.internalformat # color transform needs rebuilding if the internalformat was changed # new color limits need to be assigned if the normalized clims changed # otherwise, the original color transform should be fine new_if = post_internalformat != pre_internalformat new_cl = post_clims != pre_clims if new_if: self._need_colortransform_update = True elif new_cl and not self._need_colortransform_update: # shortcut so we don't have to rebuild the whole color transform self.shared_program.frag['color_transform'][1]['clim'] = self._texture.clim_normalized self._need_texture_upload = False def _compute_bounds(self, axis, view): if axis > 1: return 0, 0 else: return 0, self.size[axis] def _build_color_transform(self): if self._data.ndim == 2 or self._data.shape[2] == 1: # luminance data fclim = Function(self._func_templates['clim_float']) fgamma = Function(self._func_templates['gamma_float']) # NOTE: red_to_luminance only uses the red component, fancy internalformats # may need to use the other components or a different function chain fun = FunctionChain( None, [Function(self._func_templates['red_to_luminance']), fclim, fgamma, Function(self.cmap.glsl_map)] ) else: # RGB/A image data (no colormap) fclim = Function(self._func_templates['clim']) fgamma = Function(self._func_templates['gamma']) fun = FunctionChain(None, [Function(self._func_templates['null_color_transform']), fclim, fgamma]) fclim['clim'] = self._texture.clim_normalized fgamma['gamma'] = self.gamma return fun def _prepare_transforms(self, view): trs = view.transforms prg = view.view_program method = view._method_used if method == 'subdivide': prg.vert['transform'] = trs.get_transform() prg.frag['transform'] = self._null_tr else: prg.vert['transform'] = self._null_tr prg.frag['transform'] = trs.get_transform().inverse def _prepare_draw(self, view): if self._data is None: return False if self._need_interpolation_update: self._build_interpolation() if self._need_texture_upload: self._build_texture() if self._need_colortransform_update: prg = view.view_program self.shared_program.frag['color_transform'] = self._build_color_transform() self._need_colortransform_update = False prg['texture2D_LUT'] = self.cmap.texture_lut() if self._need_vertex_update: self._build_vertex_data() if view._need_method_update: self._update_method(view) vispy-0.14.1/vispy/visuals/histogram.py0000644000175000017510000000402014507104216017501 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 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, str) 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.14.1/vispy/visuals/gridmesh.py0000644000175000017510000000642214507104216017316 0ustar nileshnilesh from .mesh import MeshVisual from ..geometry import create_grid_mesh, MeshData class GridMeshVisual(MeshVisual): """Displays a mesh in a Cartesian grid about x,y,z coordinates. This makes it simple to generate a mesh from e.g. the output of numpy.meshgrid. All arguments are optional, though they can be changed individually later with the set_data method. Parameters ---------- xs : ndarray A 2d array of x coordinates for the vertices of the mesh. Must have the same dimensions as ys and zs. ys : ndarray A 2d array of y coordinates for the vertices of the mesh. Must have the same dimensions as xs and zs. zs : ndarray A 2d array of z coordinates for the vertices of the mesh. Must have the same dimensions as xs and ys. colors : ndarray | None The colors of the points of the mesh. Should be either a (width, height, 4) array of rgba colors at each grid point or a (width, height, 3) array of rgb colors at each grid point. Defaults to None, in which case the default color of a MeshVisual is used. shading : str | None Same as for the `MeshVisual` class. Defaults to 'smooth'. **kwargs : Other arguments are passed directly to MeshVisual. """ def __init__(self, xs, ys, zs, colors=None, shading='smooth', **kwargs): if xs is None or ys is None or zs is None: raise ValueError('All of xs, ys and zs must be initialised ' 'with arrays.') self._xs = None self._ys = None self._zs = None self.__vertices = None self.__meshdata = MeshData() MeshVisual.__init__(self, shading=shading, **kwargs) self.set_data(xs, ys, zs, colors) def set_data(self, xs=None, ys=None, zs=None, colors=None): """Update the mesh data. Parameters ---------- xs : ndarray | None A 2d array of x coordinates for the vertices of the mesh. ys : ndarray | None A 2d array of y coordinates for the vertices of the mesh. zs : ndarray | None A 2d array of z coordinates for the vertices of the mesh. colors : ndarray | None The color at each point of the mesh. Must have shape (width, height, 4) or (width, height, 3) for rgba or rgb color definitions respectively. """ if xs is None: xs = self._xs self.__vertices = None if ys is None: ys = self._ys self.__vertices = None if zs is None: zs = self._zs self.__vertices = None if self.__vertices is None: vertices, indices = create_grid_mesh(xs, ys, zs) self._xs = xs self._ys = ys self._zs = zs if self.__vertices is None: vertices, indices = create_grid_mesh(self._xs, self._ys, self._zs) self.__meshdata.set_vertices(vertices) self.__meshdata.set_faces(indices) if colors is not None: self.__meshdata.set_vertex_colors(colors.reshape( colors.shape[0] * colors.shape[1], colors.shape[2])) MeshVisual.set_data(self, meshdata=self.__meshdata) vispy-0.14.1/vispy/visuals/gridlines.py0000644000175000017510000000670114507104216017474 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from .image import ImageVisual from ..color import Color from .shaders import Function _GRID_COLOR = """ vec4 grid_color(vec2 pos) { vec4 px_pos = $map_to_doc(vec4(pos, 0, 1)); px_pos /= px_pos.w; // 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)); vec4 dy = $map_doc_to_local(px_pos + vec4(0, 1.0 / s, 0, 0)); local_pos /= local_pos.w; dx = dx / dx.w - local_pos; dy = dy / dy.w - 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; } return vec4($color.rgb, $color.a * alpha); } """ class GridLinesVisual(ImageVisual): """Displays regularly spaced grid lines in any coordinate system and at any scale. Parameters ---------- scale : tuple The scale factors to apply when determining the spacing of grid lines. color : Color The base color for grid lines. The final color may have its alpha channel modified. """ def __init__(self, scale=(1, 1), color='w'): # todo: PlaneVisual should support subdivide/impostor methods from # image and gridlines should inherit from plane instead. self._grid_color_fn = Function(_GRID_COLOR) self._grid_color_fn['color'] = Color(color).rgba self._grid_color_fn['scale'] = scale ImageVisual.__init__(self, method='impostor') self.set_gl_state('additive', cull_face=False) self.shared_program.frag['get_data'] = self._grid_color_fn cfun = Function('vec4 null(vec4 x) { return x; }') self.shared_program.frag['color_transform'] = cfun @property def size(self): return (1, 1) def _prepare_transforms(self, view): fn = self._grid_color_fn fn['map_to_doc'] = self.get_transform('visual', 'document') fn['map_doc_to_local'] = self.get_transform('document', 'visual') ImageVisual._prepare_transforms(self, view) def _prepare_draw(self, view): if self._need_vertex_update: self._build_vertex_data() if view._need_method_update: self._update_method(view) vispy-0.14.1/vispy/visuals/graphs/0000755000175000017510000000000014507104251016421 5ustar nileshnileshvispy-0.14.1/vispy/visuals/graphs/util.py0000644000175000017510000000620414507104216017753 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Graph utilities =============== A module containing several graph utility functions. """ import numpy as np try: from scipy.sparse import issparse from scipy import sparse except ImportError: def issparse(*args, **kwargs): return False def _get_edges(adjacency_mat): func = _sparse_get_edges if issparse(adjacency_mat) else _ndarray_get_edges return func(adjacency_mat) def _sparse_get_edges(adjacency_mat): return np.concatenate((adjacency_mat.row[:, np.newaxis], adjacency_mat.col[:, np.newaxis]), axis=-1) def _ndarray_get_edges(adjacency_mat): # Get indices of all non zero values i, j = np.where(adjacency_mat) return np.concatenate((i[:, np.newaxis], j[:, np.newaxis]), axis=-1) def _get_directed_edges(adjacency_mat): func = _sparse_get_edges if issparse(adjacency_mat) else _ndarray_get_edges if issparse(adjacency_mat): triu = sparse.triu tril = sparse.tril else: triu = np.triu tril = np.tril upper = triu(adjacency_mat) lower = tril(adjacency_mat) return np.concatenate((func(upper), func(lower))) def _straight_line_vertices(adjacency_mat, node_coords, directed=False): """ Generate the vertices for straight lines between nodes. If it is a directed graph, it also generates the vertices which can be passed to an :class:`ArrowVisual`. Parameters ---------- adjacency_mat : array The adjacency matrix of the graph node_coords : array The current coordinates of all nodes in the graph directed : bool Wether the graph is directed. If this is true it will also generate the vertices for arrows which can be passed to :class:`ArrowVisual`. Returns ------- vertices : tuple Returns a tuple containing containing (`line_vertices`, `arrow_vertices`) """ if not issparse(adjacency_mat): adjacency_mat = np.asarray(adjacency_mat, float) if (adjacency_mat.ndim != 2 or adjacency_mat.shape[0] != adjacency_mat.shape[1]): raise ValueError("Adjacency matrix should be square.") arrow_vertices = np.array([]) edges = _get_edges(adjacency_mat) line_vertices = node_coords[edges.ravel()] if directed: arrows = np.array(list(_get_directed_edges(adjacency_mat))) arrow_vertices = node_coords[arrows.ravel()] arrow_vertices = arrow_vertices.reshape((len(arrow_vertices)//2, 4)) return line_vertices, arrow_vertices def _rescale_layout(pos, scale=1): """ Normalize the given coordinate list to the range [0, `scale`]. Parameters ---------- pos : array Coordinate list scale : number The upperbound value for the coordinates range Returns ------- pos : array The rescaled (normalized) coordinates in the range [0, `scale`]. Notes ----- Changes `pos` in place. """ pos -= pos.min(axis=0) pos *= scale / pos.max() return pos vispy-0.14.1/vispy/visuals/graphs/tests/0000755000175000017510000000000014507104251017563 5ustar nileshnileshvispy-0.14.1/vispy/visuals/graphs/tests/test_networkx_layout.py0000644000175000017510000000275514507104216024464 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy import testing from vispy.visuals.graphs.layouts import get_layout from vispy.visuals.graphs.layouts.networkx_layout import NetworkxCoordinates import numpy as np # conditional import try: import networkx as nx except ModuleNotFoundError: nx = None def test_networkx_layout_with_graph(): """Testing the various inputs to the networkx layout.""" settings = dict(name="networkx_layout") if nx is None: return testing.SkipTest("'networkx' required") # empty input # testing.assert_raises(ValueError("Requires networkx input"), get_layout(**settings)) # define graph graph = nx.complete_graph(5) # define positions layout = np.random.rand(5, 2) settings['graph'] = graph settings['layout'] = layout # test numpy array input testing.assert_true(isinstance( get_layout(**settings), NetworkxCoordinates)) # testing string input settings['layout'] = 'circular' testing.assert_true(isinstance( get_layout(**settings), NetworkxCoordinates)) # testing dict input settings['layout'] = nx.circular_layout(graph) testing.assert_true(isinstance( get_layout(**settings), NetworkxCoordinates)) def test_networkx_layout_no_networkx(): settings = dict(name="networkx_layout") testing.assert_raises(ValueError, get_layout, **settings) vispy-0.14.1/vispy/visuals/graphs/tests/test_layouts.py0000644000175000017510000001212314507104216022674 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from numpy.testing import assert_allclose, assert_equal from vispy.visuals.graphs.layouts import get_layout from vispy.testing import (run_tests_if_main, assert_raises) adjacency_mat = np.array([ [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 1, 0, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 1, 0] ]) def test_get_layout(): from vispy.visuals.graphs.layouts.random import random # Simple retrieval assert_equal(random, get_layout('random')) # Pass arguments fruchterman_reingold = get_layout('force_directed', iterations=100) assert_equal(fruchterman_reingold.iterations, 100) # Check if layout exists assert_raises(KeyError, get_layout, 'fdgdfgs_non_existent') def test_random_layout(): layout = get_layout('random') expected_pos = np.array([ [0.22270932715095826, 0.7728936927702302], [0.6298054094517744, 0.21851589821484974], [0.75002099889163, 0.5592076821676369], [0.1786754307911973, 0.6442165368790972], [0.5979199081208609, 0.615159318836822], [0.46328431255222746, 0.3582897386994869], [0.9595461883180398, 0.2350580044144016], [0.094482942129406, 0.20584398882694932], [0.5758593091748346, 0.8158957494444451], [0.5908647616961652, 0.1584550825482285] ]) expected_vertices = np.array([ [0.22270932715095826, 0.7728936927702302], [0.6298054094517744, 0.21851589821484974], [0.6298054094517744, 0.21851589821484974], [0.22270932715095826, 0.7728936927702302], [0.6298054094517744, 0.21851589821484974], [0.5979199081208609, 0.615159318836822], [0.6298054094517744, 0.21851589821484974], [0.9595461883180398, 0.2350580044144016], [0.6298054094517744, 0.21851589821484974], [0.094482942129406, 0.20584398882694932], [0.1786754307911973, 0.6442165368790972], [0.5758593091748346, 0.8158957494444451], [0.5979199081208609, 0.615159318836822], [0.6298054094517744, 0.21851589821484974], [0.9595461883180398, 0.2350580044144016], [0.6298054094517744, 0.21851589821484974], [0.094482942129406, 0.20584398882694932], [0.6298054094517744, 0.21851589821484974], [0.5758593091748346, 0.8158957494444451], [0.1786754307911973, 0.6442165368790972], [0.5758593091748346, 0.8158957494444451], [0.5908647616961652, 0.1584550825482285], [0.5908647616961652, 0.1584550825482285], [0.5758593091748346, 0.8158957494444451] ]) pos, line_vertices, arrows = next(layout(adjacency_mat, random_state=0xDEADBEEF)) assert_allclose(pos, expected_pos, atol=1e-7) assert_allclose(line_vertices, expected_vertices, atol=1e-7) def test_circular_layout(): layout = get_layout('circular') expected_pos = np.array([ [1.0, 0.5], [0.9045084714889526, 0.7938926219940186], [0.6545084714889526, 0.9755282402038574], [0.3454914689064026, 0.9755282402038574], [0.09549146890640259, 0.7938926219940186], [0.0, 0.4999999701976776], [0.09549152851104736, 0.20610731840133667], [0.3454914689064026, 0.024471759796142578], [0.6545085906982422, 0.024471759796142578], [0.9045084714889526, 0.20610734820365906] ]) expected_vertices = np.array([ [1.0, 0.5], [0.9045084714889526, 0.7938926219940186], [0.9045084714889526, 0.7938926219940186], [1.0, 0.5], [0.9045084714889526, 0.7938926219940186], [0.09549146890640259, 0.7938926219940186], [0.9045084714889526, 0.7938926219940186], [0.09549152851104736, 0.20610731840133667], [0.9045084714889526, 0.7938926219940186], [0.3454914689064026, 0.024471759796142578], [0.3454914689064026, 0.9755282402038574], [0.6545085906982422, 0.024471759796142578], [0.09549146890640259, 0.7938926219940186], [0.9045084714889526, 0.7938926219940186], [0.09549152851104736, 0.20610731840133667], [0.9045084714889526, 0.7938926219940186], [0.3454914689064026, 0.024471759796142578], [0.9045084714889526, 0.7938926219940186], [0.6545085906982422, 0.024471759796142578], [0.3454914689064026, 0.9755282402038574], [0.6545085906982422, 0.024471759796142578], [0.9045084714889526, 0.20610734820365906], [0.9045084714889526, 0.20610734820365906], [0.6545085906982422, 0.024471759796142578] ]) pos, line_vertices, arrows = next(layout(adjacency_mat)) assert_allclose(pos, expected_pos, atol=1e-4) assert_allclose(line_vertices, expected_vertices, atol=1e-4) run_tests_if_main() vispy-0.14.1/vispy/visuals/graphs/tests/__init__.py0000644000175000017510000000002514507104216021672 0ustar nileshnilesh__author__ = 'lucas' vispy-0.14.1/vispy/visuals/graphs/layouts/0000755000175000017510000000000014507104251020121 5ustar nileshnileshvispy-0.14.1/vispy/visuals/graphs/layouts/random.py0000644000175000017510000000321214507104216021752 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Random Graph Layout ==================== This layout positions the nodes at random """ import numpy as np from ..util import _straight_line_vertices, issparse def random(adjacency_mat, directed=False, random_state=None): """ Place the graph nodes at random places. Parameters ---------- adjacency_mat : matrix or sparse The graph adjacency matrix directed : bool Whether the graph is directed. If this is True, is will also generate the vertices for arrows, which can be passed to an ArrowVisual. random_state : instance of RandomState | int | None Random state to use. Can be None to use ``np.random``. Yields ------ (node_vertices, line_vertices, arrow_vertices) : tuple Yields the node and line vertices in a tuple. This layout only yields a single time, and has no builtin animation """ if random_state is None: random_state = np.random elif not isinstance(random_state, np.random.RandomState): random_state = np.random.RandomState(random_state) if issparse(adjacency_mat): adjacency_mat = adjacency_mat.tocoo() # Randomly place nodes, visual coordinate system is between 0 and 1 num_nodes = adjacency_mat.shape[0] node_coords = random_state.rand(num_nodes, 2) line_vertices, arrows = _straight_line_vertices(adjacency_mat, node_coords, directed) yield node_coords, line_vertices, arrows vispy-0.14.1/vispy/visuals/graphs/layouts/networkx_layout.py0000644000175000017510000000677714507104216023773 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info.#!/usr/bin/env python3 from ..util import _straight_line_vertices, issparse import numpy as np try: import networkx as nx except ModuleNotFoundError: nx = None class NetworkxCoordinates: def __init__(self, graph=None, layout=None, **kwargs): """ Converts :graph: into a layout. Can be used in conjunction with networkx layouts or using raw 2D-numpy arrays. Parameters ---------- graph : a networkx graph. layout : str or dict or iterable-object of float32, optional - When :layout: is s string, a lookup will be performed in the networkx avaiable layouts. - When :layout: is a dict, it will be assumed that it takes the shape (key, value) = (node_id, 2D-coordinate). - When :layout: is numpy array it is assumed it takes the shape (number_of_nodes, 2). kwargs: dict, optional when layout is :str: :kwargs: will act as a setting dictionary for the layout function of networkx """ if nx is None: raise ValueError("networkx not found, please install networkx to use its layouts") if isinstance(graph, type(None)): raise ValueError("Requires networkx input") self.graph = graph self.positions = np.zeros((len(graph), 2), dtype=np.float32) # default random positions if isinstance(layout, type(None)): self.positions = np.random.rand(*self.positions.shape) # check for networkx elif isinstance(layout, str): if not layout.endswith("_layout"): layout += "_layout" # append for nx layout_function = getattr(nx, layout) if layout_function: self.positions = np.asarray( [i for i in dict(layout_function(graph, **kwargs)).values()]) else: raise ValueError("Check networkx for layouts") # assume dict from networkx; values are 2-array elif isinstance(layout, dict): self.positions = np.asarray([i for i in layout.values()]) # assume given values elif isinstance(layout, np.ndarray): assert layout.ndim == 2 assert layout.shape[0] == len(graph) self.positions = layout else: raise ValueError("Input not understood") # normalize coordinates self.positions = (self.positions - self.positions.min()) / \ (self.positions.max() - self.positions.min()) self.positions = self.positions.astype(np.float32) def __call__(self, adjacency_mat, directed=False): """ Parameters ---------- adjacency_mat : sparse adjacency matrix. directed : bool, default False Returns --------- (node_vertices, line_vertices, arrow_vertices) : tuple Yields the node and line vertices in a tuple. This layout only yields a single time, and has no builtin animation """ if issparse(adjacency_mat): adjacency_mat = adjacency_mat.tocoo() line_vertices, arrows = _straight_line_vertices( adjacency_mat, self.positions, directed) yield self.positions, line_vertices, arrows @property def adj(self): """Convenient storage and holder of the adjacency matrix for the :scene.visuals.Graph: function.""" return nx.adjacency_matrix(self.graph) vispy-0.14.1/vispy/visuals/graphs/layouts/force_directed.py0000644000175000017510000001651414507104216023444 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Force-Directed Graph Layout =========================== This module contains implementations for a force-directed layout, where the graph is modelled like a collection of springs or as a collection of particles attracting and repelling each other. The whole graph tries to reach a state which requires the minimum energy. """ import numpy as np try: from scipy.sparse import issparse except ImportError: def issparse(*args, **kwargs): return False from ..util import _straight_line_vertices, _rescale_layout class fruchterman_reingold(object): r"""Fruchterman-Reingold implementation adapted from NetworkX. In the Fruchterman-Reingold algorithm, the whole graph is modelled as a collection of particles, it runs a simplified particle simulation to find a nice layout for the graph. Parameters ---------- optimal : number Optimal distance between nodes. Defaults to :math:`1/\\sqrt{N}` where N is the number of nodes. iterations : int Number of iterations to perform for layout calculation. pos : array Initial positions of the nodes Notes ----- The algorithm is explained in more detail in the original paper [1]_. .. [1] Fruchterman, Thomas MJ, and Edward M. Reingold. "Graph drawing by force-directed placement." Softw., Pract. Exper. 21.11 (1991), 1129-1164. """ def __init__(self, optimal=None, iterations=50, pos=None): self.dim = 2 self.optimal = optimal self.iterations = iterations self.num_nodes = None self.pos = pos def __call__(self, adjacency_mat, directed=False): """ Starts the calculation of the graph layout. This is a generator, and after each iteration it yields the new positions for the nodes, together with the vertices for the edges and the arrows. There are two solvers here: one specially adapted for SciPy sparse matrices, and the other for larger networks. Parameters ---------- adjacency_mat : array The graph adjacency matrix. directed : bool Wether the graph is directed or not. If this is True, it will draw arrows for directed edges. Yields ------ layout : tuple For each iteration of the layout calculation it yields a tuple containing (node_vertices, line_vertices, arrow_vertices). These vertices can be passed to the `MarkersVisual` and `ArrowVisual`. """ if adjacency_mat.shape[0] != adjacency_mat.shape[1]: raise ValueError("Adjacency matrix should be square.") self.num_nodes = adjacency_mat.shape[0] if issparse(adjacency_mat): # Use the sparse solver solver = self._sparse_fruchterman_reingold else: solver = self._fruchterman_reingold for result in solver(adjacency_mat, directed): yield result def _fruchterman_reingold(self, adjacency_mat, directed=False): if self.optimal is None: self.optimal = 1 / np.sqrt(self.num_nodes) if self.pos is None: # Random initial positions pos = np.asarray( np.random.random((self.num_nodes, self.dim)), dtype=np.float32 ) else: pos = self.pos.astype(np.float32) # Yield initial positions line_vertices, arrows = _straight_line_vertices(adjacency_mat, pos, directed) yield pos, line_vertices, arrows # The initial "temperature" is about .1 of domain area (=1x1) # this is the largest step allowed in the dynamics. t = 0.1 # Simple cooling scheme. # Linearly step down by dt on each iteration so last iteration is # size dt. dt = t / float(self.iterations+1) # The inscrutable (but fast) version # This is still O(V^2) # Could use multilevel methods to speed this up significantly for iteration in range(self.iterations): delta_pos = _calculate_delta_pos(adjacency_mat, pos, t, self.optimal) pos += delta_pos _rescale_layout(pos) # cool temperature t -= dt # Calculate edge vertices and arrows line_vertices, arrows = _straight_line_vertices(adjacency_mat, pos, directed) yield pos, line_vertices, arrows def _sparse_fruchterman_reingold(self, adjacency_mat, directed=False): # Optimal distance between nodes if self.optimal is None: self.optimal = 1 / np.sqrt(self.num_nodes) # Change to list of list format # Also construct the matrix in COO format for easy edge construction adjacency_arr = adjacency_mat.toarray() adjacency_coo = adjacency_mat.tocoo() if self.pos is None: # Random initial positions pos = np.asarray( np.random.random((self.num_nodes, self.dim)), dtype=np.float32 ) else: pos = self.pos.astype(np.float32) # Yield initial positions line_vertices, arrows = _straight_line_vertices(adjacency_coo, pos, directed) yield pos, line_vertices, arrows # The initial "temperature" is about .1 of domain area (=1x1) # This is the largest step allowed in the dynamics. t = 0.1 # Simple cooling scheme. # Linearly step down by dt on each iteration so last iteration is # size dt. dt = t / float(self.iterations+1) for iteration in range(self.iterations): delta_pos = _calculate_delta_pos(adjacency_arr, pos, t, self.optimal) pos += delta_pos _rescale_layout(pos) # Cool temperature t -= dt # Calculate line vertices line_vertices, arrows = _straight_line_vertices(adjacency_coo, pos, directed) yield pos, line_vertices, arrows def _calculate_delta_pos(adjacency_arr, pos, t, optimal): """Helper to calculate the delta position""" # XXX eventually this should be refactored for the sparse case to only # do the necessary pairwise distances delta = pos[:, np.newaxis, :] - pos # Distance between points distance2 = (delta*delta).sum(axis=-1) # Enforce minimum distance of 0.01 distance2 = np.where(distance2 < 0.0001, 0.0001, distance2) distance = np.sqrt(distance2) # Displacement "force" displacement = np.zeros((len(delta), 2)) for ii in range(2): displacement[:, ii] = ( delta[:, :, ii] * ((optimal * optimal) / (distance*distance) - (adjacency_arr * distance) / optimal)).sum(axis=1) length = np.sqrt((displacement**2).sum(axis=1)) length = np.where(length < 0.01, 0.1, length) delta_pos = displacement * t / length[:, np.newaxis] return delta_pos vispy-0.14.1/vispy/visuals/graphs/layouts/circular.py0000644000175000017510000000302714507104216022302 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Circular Layout =============== This module contains several graph layouts which rely heavily on circles. """ import numpy as np from ..util import _straight_line_vertices, issparse def circular(adjacency_mat, directed=False): """Places all nodes on a single circle. Parameters ---------- adjacency_mat : matrix or sparse The graph adjacency matrix directed : bool Whether the graph is directed. If this is True, is will also generate the vertices for arrows, which can be passed to an ArrowVisual. Yields ------ (node_vertices, line_vertices, arrow_vertices) : tuple Yields the node and line vertices in a tuple. This layout only yields a single time, and has no builtin animation """ if issparse(adjacency_mat): adjacency_mat = adjacency_mat.tocoo() num_nodes = adjacency_mat.shape[0] t = np.linspace(0, 2 * np.pi, num_nodes, endpoint=False) # Visual coordinate system is between 0 and 1, so generate a circle with # radius 0.5 and center it at the point (0.5, 0.5). node_coords = (0.5 * np.array([np.cos(t), np.sin(t)]) + 0.5).T node_coords = node_coords.astype(np.float32) line_vertices, arrows = _straight_line_vertices(adjacency_mat, node_coords, directed) yield node_coords, line_vertices, arrows vispy-0.14.1/vispy/visuals/graphs/layouts/__init__.py0000644000175000017510000000267214507104216022242 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import inspect from .random import random from .circular import circular from .force_directed import fruchterman_reingold from .networkx_layout import NetworkxCoordinates _layout_map = { 'random': random, 'circular': circular, 'force_directed': fruchterman_reingold, 'spring_layout': fruchterman_reingold, "networkx_layout": NetworkxCoordinates, } AVAILABLE_LAYOUTS = tuple(_layout_map.keys()) def get_layout(name, *args, **kwargs): """ Retrieve a graph layout Some graph layouts accept extra options. Please refer to their documentation for more information. Parameters ---------- name : string The name of the layout. The variable `AVAILABLE_LAYOUTS` contains all available layouts. *args Positional arguments which are passed to the layout. **kwargs Keyword arguments which are passed to the layout. Returns ------- layout : callable The callable generator which will calculate the graph layout """ if name not in _layout_map: raise KeyError("Graph layout '%s' not found. Should be one of %s" % (name, AVAILABLE_LAYOUTS)) layout = _layout_map[name] if inspect.isclass(layout): layout = layout(*args, **kwargs) return layout vispy-0.14.1/vispy/visuals/graphs/graph.py0000644000175000017510000001726414507104216020107 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Graph Visual ============ This visual can be used to visualise graphs or networks. """ from ..visual import CompoundVisual from ..line import ArrowVisual from ..markers import MarkersVisual from . import layouts class GraphVisual(CompoundVisual): """Visual for displaying graphs or networks. Parameters ---------- adjacency_mat : array or sparse The adjacency matrix of the graph. directed : bool Whether the graph is directed or not. If True, then this visual will draw arrows for the directed edges. layout : str They layout to use. animate : bool Whether or not to animate. line_color : str or :class:`vispy.color.colormap.ColorMap` The color to use for the edges. line_width : number The edge thickness. arrow_type : str The kind of arrow head to use. See :class:`vispy.visuals.ArrowHead` for more information. arrow_size : number The size of the arrow head. node_symbol : string The marker to use for nodes. See :class:`vispy.visuals.MarkersVisual` for more information. node_size : number The size of the node border_color : str or :class:`vispy.color.colormap.ColorMap` The border color for nodes. face_color : str or :class:`vispy.color.colormap.ColorMap` The face color for nodes. border_width : number The border size for nodes. See Also -------- ArrowVisual, MarkersVisual """ _arrow_attributes = ('arrow_type', 'arrow_size') _arrow_kwargs = ('line_color', 'line_width') _node_kwargs = ('node_symbol', 'node_size', 'border_color', 'face_color', 'border_width') _arrow_kw_trans = dict(line_color='color', line_width='width') _node_kw_trans = dict(node_symbol='symbol', node_size='size', border_color='edge_color', border_width='edge_width') _node_properties_args = () def __init__(self, adjacency_mat=None, directed=False, layout=None, animate=False, line_color=None, line_width=None, arrow_type=None, arrow_size=None, node_symbol=None, node_size=None, border_color=None, face_color=None, border_width=None): self._edges = ArrowVisual(method='gl', connect='segments') self._nodes = MarkersVisual() self._arrow_data = {} self._node_data = {} self._node_properties = {} self._adjacency_mat = None self._layout = None self._layout_iter = None self.layout = layout self._directed = directed self.directed = directed self._animate = False self.animate = animate CompoundVisual.__init__(self, [self._edges, self._nodes]) self.set_data(adjacency_mat, line_color=line_color, line_width=line_width, arrow_type=arrow_type, arrow_size=arrow_size, node_symbol=node_symbol, node_size=node_size, border_color=border_color, face_color=face_color, border_width=border_width) @property def adjacency_matrix(self): return self._adjacency_mat @property def layout(self): return self._layout @layout.setter def layout(self, value): if isinstance(value, str): self._layout = layouts.get_layout(value) else: assert callable(value) self._layout = value self._layout_iter = None @property def directed(self): return self._directed @directed.setter def directed(self, value): self._directed = bool(value) @property def animate(self): return self._animate @animate.setter def animate(self, value): self._animate = bool(value) def animate_layout(self): if self._layout_iter is None: if self._adjacency_mat is None: raise ValueError("No adjacency matrix set yet. An adjacency " "matrix is required to calculate the layout.") self._layout_iter = iter(self._layout(self._adjacency_mat, self._directed)) try: node_vertices, line_vertices, arrows = next(self._layout_iter) except StopIteration: return True self._nodes.set_data(pos=node_vertices, **self._node_data) for k, v in self._node_properties.items(): setattr(self._nodes, k, v) self._edges.set_data(pos=line_vertices, arrows=arrows, **self._arrow_data) return False def set_final_layout(self): if self._layout_iter is None: if self._adjacency_mat is None: raise ValueError("No adjacency matrix set yet. An adjacency " "matrix is required to calculate the layout.") self._layout_iter = iter(self._layout(self._adjacency_mat, self._directed)) # Calculate the final position of the nodes and lines node_vertices = None line_vertices = None arrows = None for node_vertices, line_vertices, arrows in self._layout_iter: pass self._nodes.set_data(pos=node_vertices, **self._node_data) for k, v in self._node_properties.items(): setattr(self._nodes, k, v) self._edges.set_data(pos=line_vertices, arrows=arrows, **self._arrow_data) def reset_layout(self): self._layout_iter = None def set_data(self, adjacency_mat=None, **kwargs): """Set the data Parameters ---------- adjacency_mat : ndarray | None The adjacency matrix. **kwargs : dict Keyword arguments to pass to the arrows. """ if adjacency_mat is not None: if adjacency_mat.shape[0] != adjacency_mat.shape[1]: raise ValueError("Adjacency matrix should be square.") self._adjacency_mat = adjacency_mat for k in self._arrow_attributes: if k in kwargs: translated = (self._arrow_kw_trans[k] if k in self._arrow_kw_trans else k) setattr(self._edges, translated, kwargs.pop(k)) arrow_kwargs = {} for k in self._arrow_kwargs: if k in kwargs: translated = (self._arrow_kw_trans[k] if k in self._arrow_kw_trans else k) arrow_kwargs[translated] = kwargs.pop(k) node_kwargs = {} for k in self._node_kwargs: if k in kwargs: translated = (self._node_kw_trans[k] if k in self._node_kw_trans else k) node_kwargs[translated] = kwargs.pop(k) if len(kwargs) > 0: raise TypeError("%s.set_data() got invalid keyword arguments: %s" % (self.__class__.__name__, list(kwargs.keys()))) # some attributes should be set as properties node_properties = {} for k, v in list(node_kwargs.items()): if k in (self._node_properties_args): node_properties[k] = node_kwargs.pop(k) # The actual data is set in GraphVisual.animate_layout or # GraphVisual.set_final_layout self._arrow_data = arrow_kwargs self._node_data = node_kwargs self._node_properties = node_properties if not self._animate: self.set_final_layout() vispy-0.14.1/vispy/visuals/graphs/__init__.py0000644000175000017510000000004714507104216020534 0ustar nileshnileshfrom .graph import GraphVisual # noqa vispy-0.14.1/vispy/visuals/glsl/0000755000175000017510000000000014507104251016076 5ustar nileshnileshvispy-0.14.1/vispy/visuals/glsl/color.py0000644000175000017510000000320114507104216017563 0ustar nileshnilesh"""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.14.1/vispy/visuals/glsl/antialiasing.py0000644000175000017510000000617314507104216021123 0ustar nileshnilesh# 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.14.1/vispy/visuals/glsl/__init__.py0000644000175000017510000000005314507104216020206 0ustar nileshnilesh"""Repository of common GLSL functions.""" vispy-0.14.1/vispy/visuals/filters/0000755000175000017510000000000014507104251016605 5ustar nileshnileshvispy-0.14.1/vispy/visuals/filters/tests/0000755000175000017510000000000014507104251017747 5ustar nileshnileshvispy-0.14.1/vispy/visuals/filters/tests/test_wireframe_filter.py0000644000175000017510000000075314507104216024714 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy.visuals.mesh import MeshVisual from vispy.visuals.filters import WireframeFilter def test_empty_mesh_wireframe(): """Test that an empty mesh does not cause issues with wireframe filter""" mesh = MeshVisual() wf = WireframeFilter() mesh.attach(wf) # trigger update of filter wf.enabled = True vispy-0.14.1/vispy/visuals/filters/tests/test_primitive_picking_filters.py0000644000175000017510000000413614507104216026631 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np from vispy.geometry import create_plane from vispy.scene.visuals import Markers, Mesh from vispy.testing import requires_application, TestingCanvas from vispy.visuals.filters import FacePickingFilter, MarkerPickingFilter def test_empty_mesh_face_picking(): mesh = Mesh() filter = FacePickingFilter() mesh.attach(filter) filter.enabled = True @requires_application() def test_mesh_face_picking(): vertices, faces, _ = create_plane(125, 125) vertices = vertices["position"] vertices[:, :2] += 125 / 2 mesh = Mesh(vertices=vertices, faces=faces) filter = FacePickingFilter() mesh.attach(filter) with TestingCanvas(size=(125, 125)) as c: view = c.central_widget.add_view() view.add(mesh) filter.enabled = True mesh.update_gl_state(blend=False) picking_render = c.render(bgcolor=(0, 0, 0, 0), alpha=True) # unpack the IDs ids = picking_render.view(np.uint32) # the plane is made up of two triangles and nearly fills the view # pick one point on each triangle assert ids[125 // 2, 125 // 4] == 1 assert ids[125 // 2, 3 * 125 // 4] == 2 def test_empty_markers_picking(): markers = Markers() filter = MarkerPickingFilter() markers.attach(filter) filter.enabled = True @requires_application() def test_markers_picking(): markers = Markers( pos=np.array([[-0.5, -0.5], [0.5, 0.5]]), size=5, ) filter = MarkerPickingFilter() markers.attach(filter) with TestingCanvas(size=(125, 125)) as c: view = c.central_widget.add_view(camera="panzoom") view.camera.rect = (-1, -1, 2, 2) view.add(markers) filter.enabled = True markers.update_gl_state(blend=False) picking_render = c.render(bgcolor=(0, 0, 0, 0), alpha=True) ids = picking_render.view(np.uint32) assert ids[3 * 125 // 4, 125 // 4] == 1 assert ids[125 // 4, 3 * 125 // 4] == 2 vispy-0.14.1/vispy/visuals/filters/tests/__init__.py0000644000175000017510000000024014507104216022055 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. vispy-0.14.1/vispy/visuals/filters/picking.py0000644000175000017510000000300114507104216020576 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import struct from .base_filter import Filter class PickingFilter(Filter): """Filter used to color visuals by a picking ID. Note that the ID color uses the alpha channel, so this may not be used with blending enabled. """ FRAG_SHADER = """ void picking_filter() { if( $enabled == 0 ) return; if( gl_FragColor.a == 0.0 ) discard; gl_FragColor = $id_color; } """ def __init__(self, id_=None): super(PickingFilter, self).__init__(fcode=self.FRAG_SHADER, fpos=10) self.id = id_ self.enabled = False @property def id(self): return self._id @id.setter def id(self, id): if id < 1: raise ValueError('Picking ID must be integer > 0.') id_color = struct.unpack('<4B', struct.pack('`_ example script. """ def __init__(self, texture, texcoords, enabled=True): """Apply a texture on a mesh.""" vfunc = Function(""" void pass_coords() { $v_texcoords = $texcoords; } """) ffunc = Function(""" void apply_texture() { if ($enabled == 1) { gl_FragColor *= texture2D($u_texture, $texcoords); } } """) self._texcoord_varying = Varying('v_texcoord', 'vec2') vfunc['v_texcoords'] = self._texcoord_varying ffunc['texcoords'] = self._texcoord_varying self._texcoords_buffer = VertexBuffer( np.zeros((0, 2), dtype=np.float32) ) vfunc['texcoords'] = self._texcoords_buffer super().__init__(vcode=vfunc, vhook='pre', fcode=ffunc) self.enabled = enabled self.texture = texture self.texcoords = texcoords @property def enabled(self): """True to display the texture, False to disable.""" return self._enabled @enabled.setter def enabled(self, enabled): self._enabled = enabled self.fshader['enabled'] = 1 if enabled else 0 @property def texture(self): """The texture image.""" return self._texture @texture.setter def texture(self, texture): self._texture = texture self.fshader['u_texture'] = Texture2D(texture) @property def texcoords(self): """The texture coordinates as an (N, 2) array of floats.""" return self._texcoords @texcoords.setter def texcoords(self, texcoords): self._texcoords = texcoords self._update_texcoords_buffer(texcoords) def _update_texcoords_buffer(self, texcoords): if not self._attached or self._visual is None: return # FIXME: Indices for texture coordinates might be different than face # indices, although in some cases they are the same. Currently, # vispy.io.read_mesh assumes face indices and texture coordinates are # the same. # TODO: # 1. Add reading and returning of texture coordinate indices in # read_mesh. # 2. Add texture coordinate indices in MeshData from # vispy.geometry.meshdata # 3. Use mesh_data.get_texcoords_indices() here below. tc = texcoords[self._visual.mesh_data.get_faces()] self._texcoords_buffer.set_data(tc, convert=True) def _attach(self, visual): super()._attach(visual) self._update_texcoords_buffer(self._texcoords) shading_vertex_template = """ varying vec3 v_normal_vec; varying vec3 v_light_vec; varying vec3 v_eye_vec; varying vec4 v_pos_scene; void prepare_shading() { vec4 pos_scene = $render2scene(gl_Position); v_pos_scene = pos_scene; // Calculate normal vec4 normal_scene = $visual2scene(vec4($normal, 1.0)); vec4 origin_scene = $visual2scene(vec4(0.0, 0.0, 0.0, 1.0)); normal_scene /= normal_scene.w; origin_scene /= origin_scene.w; v_normal_vec = normalize(normal_scene.xyz - origin_scene.xyz); // Calculate eye vector per-vertex (to account for perspective) vec4 pos_doc = $scene2doc(pos_scene); pos_doc /= pos_doc.w; vec4 pos_front = pos_doc; vec4 pos_back = pos_doc; pos_front.z -= 1e-5; pos_back.z += 1e-5; pos_front = $doc2scene(pos_front); pos_back = $doc2scene(pos_back); // The eye vec eminates from the eye, pointing towards what is being viewed v_eye_vec = normalize(pos_back.xyz / pos_back.w - pos_front.xyz / pos_front.w); // Pass on light direction (the direction that the "photons" travel) v_light_vec = normalize($light_dir.xyz); } """ shading_fragment_template = """ varying vec3 v_normal_vec; varying vec3 v_light_vec; varying vec3 v_eye_vec; varying vec4 v_pos_scene; void shade() { if ($shading_enabled != 1) { return; } vec3 base_color = gl_FragColor.rgb; vec4 ambient_coeff = $ambient_coefficient; vec4 diffuse_coeff = $diffuse_coefficient; vec4 specular_coeff = $specular_coefficient; vec4 ambient_light = $ambient_light; vec4 diffuse_light = $diffuse_light; vec4 specular_light = $specular_light; float shininess = $shininess; vec3 normal = v_normal_vec; if ($flat_shading == 1) { vec3 u = dFdx(v_pos_scene.xyz); vec3 v = dFdy(v_pos_scene.xyz); normal = cross(u, v); } else { // Note(asnt): The normal calculated above always points in the // direction of the camera. Reintroduce the original orientation of the // face. normal = gl_FrontFacing ? normal : -normal; } normal = normalize(normal); vec3 light_vec = normalize(v_light_vec); vec3 eye_vec = normalize(v_eye_vec); // Ambient illumination. vec3 ambient = ambient_coeff.rgb * ambient_coeff.a * ambient_light.rgb * ambient_light.a; // Diffuse illumination (light vec points towards the surface) float diffuse_factor = dot(-light_vec, normal); diffuse_factor = max(diffuse_factor, 0.0); vec3 diffuse = diffuse_factor * diffuse_coeff.rgb * diffuse_coeff.a * diffuse_light.rgb * diffuse_light.a; // Specular illumination. Both light_vec and eye_vec point towards the surface. float specular_factor = 0.0; bool is_illuminated = diffuse_factor > 0.0; if (is_illuminated && shininess > 0.0) { // Phong reflection //vec3 reflection = reflect(-light_vec, normal); //float reflection_factor = max(dot(reflection, eye_vec), 0.0); // Blinn-Phong reflection vec3 halfway = -normalize(light_vec + eye_vec); float reflection_factor = clamp(dot(halfway, normal), 0.0, 1.0); specular_factor = pow(reflection_factor, shininess); } vec3 specular = specular_factor * specular_coeff.rgb * specular_coeff.a * specular_light.rgb * specular_light.a; // Blend the base color and combine the illuminations. vec3 color = base_color * (ambient + diffuse) + specular; gl_FragColor.rgb = color; } """ # noqa def _as_rgba(intensity_or_color, default_rgb=(1.0, 1.0, 1.0)): """Create an RGBA color from a color or a scalar intensity. Examples -------- >>> # Specify the full RGBA color. >>> _as_rgba((0.2, 0.3, 0.4, 0.25)) ... >>> # Specify an RGB color. (Default intensity `1.0` is used.) >>> _as_rgba((0.2, 0.3, 0.4)) ... >>> # Specify an intensity only. (Default color `(1.0, 1.0, 1.0)` is used.) >>> _as_rgba(0.25) ... """ if isinstance(intensity_or_color, numbers.Number): intensity = intensity_or_color return Color(default_rgb, alpha=intensity) color = intensity_or_color return Color(color) class ShadingFilter(Filter): """Apply shading to a :class:`~vispy.visuals.mesh.MeshVisual` using the Phong reflection model. For convenience, a :class:`~vispy.visuals.mesh.MeshVisual` creates and embeds a shading filter when constructed with an explicit `shading` parameter, e.g. `mesh = MeshVisual(..., shading='smooth')`. The filter is then accessible as `mesh.shading_filter`. When attached manually to a :class:`~vispy.visuals.mesh.MeshVisual`, the shading filter should come after any other filter that modifies the base color to be shaded. See the examples below. Parameters ---------- shading : str Shading mode: None, 'flat' or 'smooth'. If None, the shading is disabled. ambient_coefficient : str or tuple or Color Color and intensity of the ambient reflection coefficient (Ka). diffuse_coefficient : str or tuple or Color Color and intensity of the diffuse reflection coefficient (Kd). specular_coefficient : str or tuple or Color Color and intensity of the specular reflection coefficient (Ks). shininess : float The shininess controls the size of specular highlight. The higher, the more localized. Must be greater than or equal to zero. light_dir : array_like Direction of the light. Assuming a directional light. ambient_light : str or tuple or Color Color and intensity of the ambient light. diffuse_light : str or tuple or Color Color and intensity of the diffuse light. specular_light : str or tuple or Color Color and intensity of the specular light. enabled : bool, default=True Whether the filter is enabled at creation time. This can be changed at run time with :obj:`~enabled`. Notes ----- Under the Phong reflection model, the illumination `I` is computed as:: I = I_ambient + mesh_color * I_diffuse + I_specular for each color channel independently. `mesh_color` is the color of the :class:`~vispy.visuals.mesh.MeshVisual`, possibly modified by the filters applied before this one. The ambient, diffuse and specular terms are defined as:: I_ambient = Ka * Ia I_diffuse = Kd * Id * dot(L, N) I_specular = Ks * Is * dot(R, V) ** s with `L` the light direction, assuming a directional light, `N` the normal to the surface at the reflection point, `R` the direction of the reflection, `V` the direction to the viewer, `s` the shininess factor. The `Ka`, `Kd` and `Ks` coefficients are defined as an RGBA color. The RGB components define the color that the surface reflects, and the alpha component (A) defines the intensity/attenuation of the reflection. When applied in the per-channel illumation formulas above, the color component is multiplied by the intensity to obtain the final coefficient, e.g. `Kd = R * A` for the red channel. Similarly, the light intensities, `Ia`, `Id` and `Is`, are defined by RGBA colors, corresponding to the color of the light and its intensity. Examples -------- Define the mesh data for a :class:`vispy.visuals.mesh.MeshVisual`: >>> # A triangle. >>> vertices = np.array([(0, 0, 0), (1, 1, 1), (0, 1, 0)], dtype=float) >>> faces = np.array([(0, 1, 2)], dtype=int) Let the :class:`vispy.visuals.mesh.MeshVisual` create and embed a shading filter: >>> mesh = MeshVisual(vertices, faces, shading='smooth') >>> # Configure the filter afterwards. >>> mesh.shading_filter.shininess = 64 >>> mesh.shading_filter.specular_coefficient = 0.3 Create the shading filter manually and attach it to a :class:`vispy.visuals.mesh.MeshVisual`: >>> # With the default shading parameters. >>> shading_filter = ShadingFilter() >>> mesh = MeshVisual(vertices, faces) >>> mesh.attach(shading_filter) The filter can be configured at creation time and at run time: >>> # Configure at creation time. >>> shading_filter = ShadingFilter( ... # A shiny surface (small specular highlight). ... shininess=250, ... # A blue higlight, at half intensity. ... specular_coefficient=(0, 0, 1, 0.5), ... # Equivalent to `(0.7, 0.7, 0.7, 1.0)`. ... diffuse_coefficient=0.7, ... # Same as `(0.2, 0.3, 0.3, 1.0)`. ... ambient_coefficient=(0.2, 0.3, 0.3), ... ) >>> # Change the configuration at run time. >>> shading_filter.shininess = 64 >>> shading_filter.specular_coefficient = 0.3 Disable the filter temporarily: >>> # Turn off the shading. >>> shading_filter.enabled = False ... # Some time passes... >>> # Turn on the shading again. >>> shading_filter.enabled = True When using the :class:`WireframeFilter`, the wireframe is shaded only if the wireframe filter is attached before the shading filter: >>> shading_filter = ShadingFilter() >>> wireframe_filter = WireframeFilter() >>> # Option 1: Shade the wireframe. >>> mesh1 = MeshVisual(vertices, faces) >>> mesh1.attached(wireframe_filter) >>> mesh1.attached(shading_filter) >>> # Option 2: Do not shade the wireframe. >>> mesh2 = MeshVisual(vertices, faces) >>> mesh2.attached(shading_filter) >>> mesh2.attached(wireframe_filter) See also `examples/basics/scene/mesh_shading.py `_ example script. """ _shaders = { 'vertex': shading_vertex_template, 'fragment': shading_fragment_template, } def __init__(self, shading='flat', ambient_coefficient=(1, 1, 1, 1), diffuse_coefficient=(1, 1, 1, 1), specular_coefficient=(1, 1, 1, 1), shininess=100, light_dir=(10, 5, -5), ambient_light=(1, 1, 1, .25), diffuse_light=(1, 1, 1, 0.7), specular_light=(1, 1, 1, .25), enabled=True): self._shading = shading self._ambient_coefficient = _as_rgba(ambient_coefficient) self._diffuse_coefficient = _as_rgba(diffuse_coefficient) self._specular_coefficient = _as_rgba(specular_coefficient) self._shininess = shininess self._light_dir = light_dir self._ambient_light = _as_rgba(ambient_light) self._diffuse_light = _as_rgba(diffuse_light) self._specular_light = _as_rgba(specular_light) self._enabled = enabled vfunc = Function(self._shaders['vertex']) ffunc = Function(self._shaders['fragment']) self._normals = VertexBuffer(np.zeros((0, 3), dtype=np.float32)) vfunc['normal'] = self._normals super().__init__(vcode=vfunc, fcode=ffunc) @property def enabled(self): """True to enable the filter, False to disable.""" return self._enabled @enabled.setter def enabled(self, enabled): self._enabled = enabled self._update_data() @property def shading(self): """The shading method.""" return self._shading @shading.setter def shading(self, shading): assert shading in (None, 'flat', 'smooth') self._shading = shading self._update_data() @property def light_dir(self): """The light direction.""" return self._light_dir @light_dir.setter def light_dir(self, direction): direction = np.array(direction, float).ravel() if direction.size != 3 or not np.isfinite(direction).all(): raise ValueError('Invalid direction %s' % direction) self._light_dir = tuple(direction) self._update_data() @property def ambient_light(self): """The color and intensity of the ambient light.""" return self._ambient_light @ambient_light.setter def ambient_light(self, light_color): self._ambient_light = _as_rgba(light_color) self._update_data() @property def diffuse_light(self): """The color and intensity of the diffuse light.""" return self._diffuse_light @diffuse_light.setter def diffuse_light(self, light_color): self._diffuse_light = _as_rgba(light_color) self._update_data() @property def specular_light(self): """The color and intensity of the specular light.""" return self._specular_light @specular_light.setter def specular_light(self, light_color): self._specular_light = _as_rgba(light_color) self._update_data() @property def ambient_coefficient(self): """The ambient reflection coefficient.""" return self._ambient_coefficient @ambient_coefficient.setter def ambient_coefficient(self, color): self._ambient_coefficient = _as_rgba(color) self._update_data() @property def diffuse_coefficient(self): """The diffuse reflection coefficient.""" return self._diffuse_coefficient @diffuse_coefficient.setter def diffuse_coefficient(self, diffuse_coefficient): self._diffuse_coefficient = _as_rgba(diffuse_coefficient) self._update_data() @property def specular_coefficient(self): """The specular reflection coefficient.""" return self._specular_coefficient @specular_coefficient.setter def specular_coefficient(self, specular_coefficient): self._specular_coefficient = _as_rgba(specular_coefficient) self._update_data() @property def shininess(self): """The shininess controlling the spread of the specular highlight.""" return self._shininess @shininess.setter def shininess(self, shininess): self._shininess = float(shininess) self._update_data() def _update_data(self): if not self._attached: return self.vshader['light_dir'] = self._light_dir self.fshader['ambient_light'] = self._ambient_light.rgba self.fshader['diffuse_light'] = self._diffuse_light.rgba self.fshader['specular_light'] = self._specular_light.rgba self.fshader['ambient_coefficient'] = self._ambient_coefficient.rgba self.fshader['diffuse_coefficient'] = self._diffuse_coefficient.rgba self.fshader['specular_coefficient'] = self._specular_coefficient.rgba self.fshader['shininess'] = self._shininess self.fshader['flat_shading'] = 1 if self._shading == 'flat' else 0 self.fshader['shading_enabled'] = ( 1 if self._enabled and self._shading is not None else 0 ) normals = self._visual.mesh_data.get_vertex_normals(indexed='faces') self._normals.set_data(normals, convert=True) def on_mesh_data_updated(self, event): self._update_data() def _attach(self, visual): super()._attach(visual) render2scene = visual.transforms.get_transform('render', 'scene') visual2scene = visual.transforms.get_transform('visual', 'scene') scene2doc = visual.transforms.get_transform('scene', 'document') doc2scene = visual.transforms.get_transform('document', 'scene') self.vshader['render2scene'] = render2scene self.vshader['visual2scene'] = visual2scene self.vshader['scene2doc'] = scene2doc self.vshader['doc2scene'] = doc2scene if self._visual.mesh_data is not None: self._update_data() visual.events.data_updated.connect(self.on_mesh_data_updated) def _detach(self, visual): visual.events.data_updated.disconnect(self.on_mesh_data_updated) super()._detach(visual) instanced_shading_vertex_template = shading_vertex_template.replace( "$normal", "mat3($instance_transform_x, $instance_transform_y, $instance_transform_z) * $normal" ) class InstancedShadingFilter(ShadingFilter): """Shading filter modified for use with :class:`~vispy.visuals.InstancedMeshVisual`. See :class:`ShadingFilter` for details and usage. """ _shaders = { 'vertex': instanced_shading_vertex_template, 'fragment': ShadingFilter._shaders['fragment'], } def _attach(self, visual): super()._attach(visual) self.vshader['instance_transform_x'] = visual._instance_transforms_vbos[0] self.vshader['instance_transform_y'] = visual._instance_transforms_vbos[1] self.vshader['instance_transform_z'] = visual._instance_transforms_vbos[2] wireframe_vertex_template = """ varying vec3 v_bc; void prepare_wireframe() { v_bc = $bc; } """ # noqa wireframe_fragment_template = """ varying vec3 v_bc; void draw_wireframe() { if ($enabled != 1) { return; } vec3 d = fwidth(v_bc); // relative distance to edge vec3 fading3 = smoothstep(vec3(0.0), $width * d, v_bc); float opacity = 1.0 - min(min(fading3.x, fading3.y), fading3.z); if ($wireframe_only == 1) { if (opacity == 0.0) { // Inside a triangle. discard; } // On the edge. gl_FragColor = $color; gl_FragColor.a = opacity; } else if ($faces_only == 1) { if (opacity == 1.0) { // Inside an edge. discard; } // Inside a triangle. gl_FragColor.a = 1.0 - opacity; } else { gl_FragColor = mix(gl_FragColor, $color, opacity); } } """ # noqa class WireframeFilter(Filter): """Add wireframe to a mesh. The wireframe filter should be attached before the shading filter for the wireframe to be shaded. Parameters ---------- color : str or tuple or Color Line color of the wireframe width : float Line width of the wireframe enabled : bool Whether the wireframe is drawn or not Examples -------- See `examples/basics/scene/mesh_shading.py `_ example script. """ def __init__(self, enabled=True, color='black', width=1.0, wireframe_only=False, faces_only=False): self._attached = False self._color = Color(color) self._width = width self._enabled = enabled self._wireframe_only = wireframe_only self._faces_only = faces_only vfunc = Function(wireframe_vertex_template) ffunc = Function(wireframe_fragment_template) self._bc = VertexBuffer(np.zeros((0, 3), dtype=np.float32)) vfunc['bc'] = self._bc super().__init__(vcode=vfunc, fcode=ffunc) self.enabled = enabled @property def enabled(self): """True to enable the display of the wireframe, False to disable.""" return self._enabled @enabled.setter def enabled(self, enabled): self._enabled = enabled self.fshader['enabled'] = 1 if enabled else 0 self._update_data() @property def color(self): """The wireframe color.""" return self._color @color.setter def color(self, color): self._color = Color(color) self._update_data() @property def width(self): """The wireframe width.""" return self._width @width.setter def width(self, width): if width < 0: raise ValueError("width must be greater than zero") self._width = width self._update_data() @property def wireframe_only(self): """Draw only the wireframe and discard the interior of the faces.""" return self._wireframe_only @wireframe_only.setter def wireframe_only(self, wireframe_only): self._wireframe_only = wireframe_only self._update_data() @property def faces_only(self): """Make the wireframe transparent. Draw only the interior of the faces. """ return self._faces_only @faces_only.setter def faces_only(self, faces_only): self._faces_only = faces_only self._update_data() def _update_data(self): if not self.attached: return self.fshader['color'] = self._color.rgba self.fshader['width'] = self._width self.fshader['wireframe_only'] = 1 if self._wireframe_only else 0 self.fshader['faces_only'] = 1 if self._faces_only else 0 if self._visual.mesh_data.is_empty(): n_faces = 0 else: n_faces = len(self._visual.mesh_data.get_faces()) bc = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype='float') bc = np.tile(bc[None, ...], (n_faces, 1, 1)) self._bc.set_data(bc, convert=True) def on_data_updated(self, event): self._update_data() def _attach(self, visual): super()._attach(visual) visual.events.data_updated.connect(self.on_data_updated) def _detach(self, visual): visual.events.data_updated.disconnect(self.on_data_updated) super()._detach(visual) class FacePickingFilter(PrimitivePickingFilter): """Filter used to color mesh faces by a picking ID. Note that the ID color uses the alpha channel, so this may not be used with blending enabled. Examples -------- :ref:`sphx_glr_gallery_scene_face_picking.py` """ def _get_picking_ids(self): if self._visual.mesh_data.is_empty(): n_faces = 0 else: n_faces = len(self._visual.mesh_data.get_faces()) # we only care about the number of faces changing if self._n_primitives == n_faces: return None self._n_primitives = n_faces ids = np.arange(1, n_faces + 1, dtype=np.uint32) ids = np.repeat(ids, 3, axis=0) # repeat id for each vertex return ids vispy-0.14.1/vispy/visuals/filters/markers.py0000644000175000017510000000140314507104216020622 0ustar nileshnileshimport numpy as np from vispy.visuals.filters import PrimitivePickingFilter class MarkerPickingFilter(PrimitivePickingFilter): """Filter used to color markers by a picking ID. Note that the ID color uses the alpha channel, so this may not be used with blending enabled. Examples -------- :ref:`sphx_glr_gallery_scene_marker_picking.py` """ def _get_picking_ids(self): if self._visual._data is None: n_markers = 0 else: n_markers = len(self._visual._data['a_position']) # we only care about the number of markers changing if self._n_primitives == n_markers: return self._n_primitives = n_markers return np.arange(1, n_markers + 1, dtype=np.uint32) vispy-0.14.1/vispy/visuals/filters/color.py0000644000175000017510000001167114507104216020304 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from .base_filter import Filter from ..shaders import Function, Varying from ...color import colormap, Color class IsolineFilter(Filter): FRAG_SHADER = """ void isoline() { if ($isolevel <= 0. || $isowidth <= 0.) { return; } // function taken from glumpy/examples/isocurves.py // and extended to have level, width, color and antialiasing // as parameters // Extract data value // this accounts for perception, // have to decide, which one to use or make this a uniform const vec3 w = vec3(0.299, 0.587, 0.114); //const vec3 w = vec3(0.2126, 0.7152, 0.0722); float value = dot(gl_FragColor.rgb, w); // setup lw, aa float linewidth = $isowidth + $antialias; // "middle" contour(s) dividing upper and lower half // but only if isolevel is even if( mod($isolevel,2.0) == 0.0 ) { if( length(value - 0.5) < 0.5 / $isolevel) linewidth = linewidth * 2; } // Trace contour isoline float v = $isolevel * value - 0.5; float dv = linewidth/2.0 * fwidth(v); float f = abs(fract(v) - 0.5); float d = smoothstep(-dv, +dv, f); float t = linewidth/2.0 - $antialias; d = abs(d)*linewidth/2.0 - t; if( d < - linewidth ) { d = 1.0; } else { d /= $antialias; } // setup foreground vec4 fc = $isocolor; // mix with background if (d < 1.) { gl_FragColor = mix(gl_FragColor, fc, 1-d); } } """ def __init__(self, level=2., width=2.0, antialias=1.0, color='black', **kwargs): super(IsolineFilter, self).__init__(fcode=self.FRAG_SHADER, **kwargs) self.level = level self.width = width self.color = color self.antialias = antialias @property def level(self): return self._level @level.setter def level(self, lev): if lev <= 0: lev = 0 self._level = lev self.fshader['isolevel'] = float(lev) @property def width(self): return self._width @width.setter def width(self, w): self._width = w self.fshader['isowidth'] = float(w) @property def color(self): return self._color @color.setter def color(self, c): self._color = c self.fshader['isocolor'] = Color(c).rgba @property def antialias(self): return self._antialias @antialias.setter def antialias(self, a): self._antialias = a self.fshader['antialias'] = float(a) class Alpha(Filter): FRAG_SHADER = """ void apply_alpha() { gl_FragColor.a = gl_FragColor.a * $alpha; } """ def __init__(self, alpha=1.0, **kwargs): super(Alpha, self).__init__(fcode=self.FRAG_SHADER, **kwargs) self.alpha = alpha @property def alpha(self): return self._alpha @alpha.setter def alpha(self, a): self._alpha = a self.fshader['alpha'] = float(a) class ColorFilter(Filter): FRAG_SHADER = """ void apply_color_filter() { gl_FragColor = gl_FragColor * $filter; } """ def __init__(self, filter=(1., 1., 1., 1.), fpos=8, **kwargs): super(ColorFilter, self).__init__(fcode=self.FRAG_SHADER, fpos=fpos, **kwargs) self.filter = filter @property def filter(self): return self._filter @filter.setter def filter(self, f): self._filter = tuple(f) self.fshader['filter'] = self._filter class ZColormapFilter(Filter): FRAG_SHADER = """ void z_colormap_support() { $zval = $position.z; } """ VERT_SHADER = """ void apply_z_colormap() { gl_FragColor = $cmap(($zval - $zrange.x) / ($zrange.y - $zrange.x)); } """ def __init__(self, cmap, zrange=(0., 1.), fpos=3, vpos=9, **kwargs): super(ZColormapFilter, self).__init__(fcode=self.FRAG_SHADER, fpos=fpos, vcode=self.VERT_SHADER, vpos=vpos, **kwargs) if isinstance(cmap, str): cmap = colormap.get_colormap(cmap) self.cmap = Function(cmap.glsl_map) self.fshader['cmap'] = self.cmap self.fshader['zrange'] = zrange self.vshader['zval'] = Varying('v_zval', dtype='float') self.fshader['zval'] = self.vshader['zval'] def _attach(self, visual): super(ZColormapFilter, self)._attach(visual) self.vshader['position'] = visual.shared_program.vert['position'] vispy-0.14.1/vispy/visuals/filters/clipping_planes.py0000644000175000017510000001065714507104216022340 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import annotations from typing import Optional from functools import lru_cache import numpy as np from ..shaders import Function, Varying from .base_filter import Filter class PlanesClipper(Filter): """Clips visual output based on arbitrary clipping planes. Parameters ---------- cliping_planes : ArrayLike Each plane is defined by a position and a normal vector (magnitude is irrelevant). Shape: (n_planes, 2, 3) coord_system : str Coordinate system used by the clipping planes (see visuals.transforms.transform_system.py) """ VERT_CODE = """ void clip() { // pass position as varying for interpolation $v_position = gl_Position; } """ FRAG_CODE = """ void clip() { float distance_from_clip = $clip_with_planes($itransform($v_position).xyz); if (distance_from_clip < 0.) discard; } """ def __init__(self, clipping_planes: Optional[np.ndarray] = None, coord_system: str = 'scene'): tr = ['visual', 'scene', 'document', 'canvas', 'framebuffer', 'render'] if coord_system not in tr: raise ValueError(f'Invalid coordinate system {coord_system}. Must be one of {tr}.') self._coord_system = coord_system super().__init__( vcode=Function(self.VERT_CODE), vhook='post', vpos=1, fcode=Function(self.FRAG_CODE), fhook='pre', fpos=1, ) # initialize clipping planes self._clipping_planes = np.empty((0, 2, 3), dtype=np.float32) self._clipping_planes_func = Function(self._build_clipping_planes_glsl(0)) self.fshader['clip_with_planes'] = self._clipping_planes_func v_position = Varying('v_position', 'vec4') self.vshader['v_position'] = v_position self.fshader['v_position'] = v_position self.clipping_planes = clipping_planes @property def coord_system(self) -> str: """ Coordinate system used by the clipping planes (see visuals.transforms.transform_system.py) """ # unsettable cause we can't update the transform after being attached return self._coord_system def _attach(self, visual): super()._attach(visual) self.fshader['itransform'] = visual.get_transform('render', self._coord_system) @staticmethod @lru_cache(maxsize=10) def _build_clipping_planes_glsl(n_planes: int) -> str: """Build the code snippet used to clip the volume based on self.clipping_planes.""" func_template = ''' float clip_planes(vec3 loc) {{ float distance_from_clip = 3.4e38; // max float {clips}; return distance_from_clip; }} ''' # the vertex is considered clipped if on the "negative" side of the plane clip_template = ''' vec3 relative_vec{idx} = loc - $clipping_plane_pos{idx}; float distance_from_clip{idx} = dot(relative_vec{idx}, $clipping_plane_norm{idx}); distance_from_clip = min(distance_from_clip{idx}, distance_from_clip); ''' all_clips = [] for idx in range(n_planes): all_clips.append(clip_template.format(idx=idx)) formatted_code = func_template.format(clips=''.join(all_clips)) return formatted_code @property def clipping_planes(self) -> np.ndarray: """Get the set of planes used to clip the mesh. Each plane is defined by a position and a normal vector (magnitude is irrelevant). Shape: (n_planes, 2, 3) """ return self._clipping_planes @clipping_planes.setter def clipping_planes(self, value: Optional[np.ndarray]): if value is None: value = np.empty((0, 2, 3), dtype=np.float32) # only recreate function if amount of clipping planes changes if len(value) != len(self._clipping_planes): self._clipping_planes_func = Function(self._build_clipping_planes_glsl(len(value))) self.fshader['clip_with_planes'] = self._clipping_planes_func self._clipping_planes = value for idx, plane in enumerate(value): self._clipping_planes_func[f'clipping_plane_pos{idx}'] = tuple(plane[0]) self._clipping_planes_func[f'clipping_plane_norm{idx}'] = tuple(plane[1]) vispy-0.14.1/vispy/visuals/filters/clipper.py0000644000175000017510000000331514507104216020620 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from .base_filter import Filter from ..transforms import NullTransform from ...geometry import Rect class Clipper(Filter): """Clips visual output to a rectangular region.""" FRAG_SHADER = """ 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; } } """ def __init__(self, bounds=(0, 0, 1, 1), transform=None): super(Clipper, self).__init__(fcode=self.FRAG_SHADER, fhook='pre', fpos=1) self.bounds = bounds # (x, y, w, h) if transform is None: transform = NullTransform() self._transform = None self.transform = transform @property def bounds(self): """The clipping boundaries. This must be a tuple (x, y, w, h) in a clipping coordinate system that is defined by the `transform` property. """ return self._bounds @bounds.setter def bounds(self, b): self._bounds = Rect(b).normalized() b = self._bounds self.fshader['view'] = (b.left, b.right, b.bottom, b.top) @property def transform(self): """The transform that maps from framebuffer coordinates to clipping coordinates. """ return self._transform @transform.setter def transform(self, tr): if tr is self._transform: return self._transform = tr self.fshader['fb_to_clip'] = tr vispy-0.14.1/vispy/visuals/filters/base_filter.py0000644000175000017510000001654414507104216021451 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from abc import ABCMeta, abstractmethod import numpy as np from vispy.gloo import VertexBuffer from ..shaders import Function class BaseFilter(object): """Superclass for all filters.""" def _attach(self, visual): """Called when a filter should be attached to a visual. Parameters ---------- visual : instance of BaseVisual The visual that called this. """ raise NotImplementedError(self) def _detach(self, visual): """Called when a filter should be detached from a visual. Parameters ---------- visual : instance of BaseVisual The visual that called this. """ raise NotImplementedError(self) class Filter(BaseFilter): """Base class for all filters that use fragment and/or vertex shaders. Parameters ---------- vcode : str | Function | None Vertex shader code. If None, ``vhook`` and ``vpos`` will be ignored. vhook : {'pre', 'post'} Hook name to attach the vertex shader to. vpos : int Position in the hook to attach the vertex shader. fcode : str | Function | None Fragment shader code. If None, ``fhook`` and ``fpos`` will be ignored. fhook : {'pre', 'post'} Hook name to attach the fragment shader to. fpos : int Position in the hook to attach the fragment shader. Attributes ---------- vshader : Function | None Vertex shader. fshader : Function | None Fragment shader. """ def __init__(self, vcode=None, vhook='post', vpos=5, fcode=None, fhook='post', fpos=5): super(Filter, self).__init__() if vcode is not None: self.vshader = Function(vcode) if isinstance(vcode, str) else vcode self._vexpr = self.vshader() self._vhook = vhook self._vpos = vpos else: self.vshader = None if fcode is not None: self.fshader = Function(fcode) if isinstance(fcode, str) else fcode self._fexpr = self.fshader() self._fhook = fhook self._fpos = fpos else: self.fshader = None self._attached = False @property def attached(self): return self._attached def _attach(self, visual): """Called when a filter should be attached to a visual. Parameters ---------- visual : instance of Visual The visual that called this. """ if self.vshader: hook = visual._get_hook('vert', self._vhook) hook.add(self._vexpr, position=self._vpos) if self.fshader: hook = visual._get_hook('frag', self._fhook) hook.add(self._fexpr, position=self._fpos) self._attached = True self._visual = visual def _detach(self, visual): """Called when a filter should be detached from a visual. Parameters ---------- visual : instance of Visual The visual that called this. """ if self.vshader: hook = visual._get_hook('vert', self._vhook) hook.remove(self._vexpr) if self.fshader: hook = visual._get_hook('frag', self._fhook) hook.remove(self._fexpr) self._attached = False self._visual = None class PrimitivePickingFilter(Filter, metaclass=ABCMeta): """Abstract base class for Visual-specific filters to implement a primitive-picking mode. Subclasses must (and usually only need to) implement :py:meth:`_get_picking_ids`. """ def __init__(self, fpos=9, *, discard_transparent=False): # fpos is set to 9 by default to put it near the end, but before the # default PickingFilter vfunc = Function("""\ varying vec4 v_marker_picking_color; void prepare_marker_picking() { v_marker_picking_color = $ids; } """) ffunc = Function("""\ varying vec4 v_marker_picking_color; void marker_picking_filter() { if ( $enabled != 1 ) { return; } if ( $discard_transparent == 1 && gl_FragColor.a == 0.0 ) { discard; } gl_FragColor = v_marker_picking_color; } """) self._id_colors = VertexBuffer(np.zeros((0, 4), dtype=np.float32)) vfunc['ids'] = self._id_colors self._n_primitives = 0 super().__init__(vcode=vfunc, fcode=ffunc, fpos=fpos) self.enabled = False self.discard_transparent = discard_transparent @abstractmethod def _get_picking_ids(self): """Return a 1D array of picking IDs for the vertices in the visual. Generally, this method should be implemented to: 1. Calculate the number of primitives in the visual (may be persisted in `self._n_primitives`). 2. Calculate a range of picking ids for each primitive in the visual. IDs should start from 1, reserving 0 for the background. If primitives comprise multiple vertices (triangles), ids may need to be repeated. The return value should be an array of uint32 with shape (num_vertices,). If no change to the picking IDs is needed (for example, the number of primitives has not changed), this method should return `None`. """ raise NotImplementedError(self) def _update_id_colors(self): """Calculate the colors encoding the picking IDs for the visual. For performance, this method will not update the id colors VertexBuffer if :py:meth:`_get_picking_ids` returns `None`. """ # this should remain untouched ids = self._get_picking_ids() if ids is not None: id_colors = self._pack_ids_into_rgba(ids) self._id_colors.set_data(id_colors) @staticmethod def _pack_ids_into_rgba(ids): """Pack an array of uint32 primitive ids into float32 RGBA colors.""" if ids.dtype != np.uint32: raise ValueError(f"ids must be uint32, got {ids.dtype}") return np.divide( ids.view(np.uint8).reshape(-1, 4), 255, dtype=np.float32 ) def _on_data_updated(self, event=None): if not self.attached: return self._update_id_colors() @property def enabled(self): return self._enabled @enabled.setter def enabled(self, e): self._enabled = bool(e) self.fshader['enabled'] = int(self._enabled) self._on_data_updated() @property def discard_transparent(self): return self._discard_transparent @discard_transparent.setter def discard_transparent(self, d): self._discard_transparent = bool(d) self.fshader['discard_transparent'] = int(self._discard_transparent) def _attach(self, visual): super()._attach(visual) visual.events.data_updated.connect(self._on_data_updated) self._on_data_updated() def _detach(self, visual): visual.events.data_updated.disconnect(self._on_data_updated) super()._detach(visual) vispy-0.14.1/vispy/visuals/filters/__init__.py0000644000175000017510000000104214507104216020714 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from .base_filter import Filter, PrimitivePickingFilter # noqa from .clipper import Clipper # noqa from .color import Alpha, ColorFilter, IsolineFilter, ZColormapFilter # noqa from .picking import PickingFilter # noqa from .markers import MarkerPickingFilter # noqa from .mesh import TextureFilter, ShadingFilter, InstancedShadingFilter, WireframeFilter, FacePickingFilter # noqa vispy-0.14.1/vispy/visuals/ellipse.py0000644000175000017510000001174414507104216017154 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 .polygon import PolygonVisual class EllipseVisual(PolygonVisual): """ Displays a 2D ellipse Parameters ---------- center : array Center of the ellipse color : instance of Color The face color to use. border_color : instance of Color The border color to use. border_width: float The width of the border in pixels Line widths > 1px are only guaranteed to work when using `border_method='agg'` method. 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 360. num_segments : int Number of segments to be used to draw the ellipse Defaults to 100 **kwargs : dict Keyword arguments to pass to `PolygonVisual`. """ def __init__(self, center=None, color='black', border_color=None, border_width=1, radius=(0.1, 0.1), start_angle=0., span_angle=360., num_segments=100, **kwargs): self._center = center self._radius = radius self._start_angle = start_angle self._span_angle = span_angle self._num_segments = num_segments # triangulation can be very slow kwargs.setdefault('triangulate', False) PolygonVisual.__init__(self, pos=None, color=color, border_color=border_color, border_width=border_width, **kwargs) self._mesh.mode = "triangle_fan" self._regen_pos() self._update() @staticmethod def _generate_vertices(center, 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 start_angle = np.deg2rad(start_angle) vertices = np.empty([num_segments + 2, 2], dtype=np.float32) # split the total angle into num_segments instances theta = np.linspace(start_angle, start_angle + np.deg2rad(span_angle), num_segments + 1) # PolarProjection vertices[1:, 0] = center[0] + xr * np.cos(theta) vertices[1:, 1] = center[1] + yr * np.sin(theta) # specify center point (not used in border) vertices[0] = np.float32([center[0], center[1]]) return vertices @property def center(self): """The center of the ellipse""" return self._center @center.setter def center(self, center): """The center of the ellipse""" self._center = center self._regen_pos() self._update() @property def radius(self): """The start radii of the ellipse.""" return self._radius @radius.setter def radius(self, radius): self._radius = radius self._regen_pos() 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._regen_pos() 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._regen_pos() 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): num_segments = int(num_segments) if num_segments < 1: raise ValueError('EllipseVisual must consist of more than 1 ' 'segment') self._num_segments = num_segments self._regen_pos() self._update() def _regen_pos(self): vertices = self._generate_vertices(center=self._center, radius=self._radius, start_angle=self._start_angle, span_angle=self._span_angle, num_segments=self._num_segments) # don't use the center point self._pos = vertices[1:] vispy-0.14.1/vispy/visuals/cube.py0000644000175000017510000000327314507104216016433 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import warnings from .box import BoxVisual class CubeVisual(BoxVisual): """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, **kwargs): warnings.warn("The CubeVisual is deprecated in favor of BoxVisual", DeprecationWarning, stacklevel=2) if isinstance(size, tuple): width, height, depth = size else: width = height = depth = size BoxVisual.__init__(self, width=width, height=height, depth=depth, vertex_colors=vertex_colors, face_colors=face_colors, color=color, edge_color=edge_color, **kwargs) vispy-0.14.1/vispy/visuals/colorbar.py0000644000175000017510000005632014507104216017321 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Siddharth Bhat # ----------------------------------------------------------------------------- from functools import lru_cache import numpy as np from . import Visual, TextVisual, CompoundVisual, _BorderVisual # from .border import _BorderVisual from .shaders import Function from ..color import get_colormap _VERTEX_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)); } """ # noqa _FRAGMENT_SHADER = """ varying vec2 v_texcoord; void main() { // depending on orientation, we either use the x component or the inverted // y component for texcoords // (we get the texcoords inverted (with respect to the colormap) // so let's invert it to make sure that the colorbar renders correctly) vec4 mapped_color = $color_transform($orient_texcoord(v_texcoord)); gl_FragColor = mapped_color; } """ # noqa class _CoreColorBarVisual(Visual): """ Visual subclass that actually renders the ColorBar. Parameters ---------- pos : tuple (x, y) Position where the colorbar is to be placed with respect to the center of the colorbar halfdim : tuple (half_width, half_height) Half the dimensions of the colorbar measured from the center. That way, the total dimensions of the colorbar is (x - half_width) to (x + half_width) and (y - half_height) to (y + half_height) cmap : str | vispy.color.ColorMap Either the name of the ColorMap to be used from the standard set of names (refer to `vispy.color.get_colormap`), or a custom ColorMap object. The ColorMap is used to apply a gradient on the colorbar. orientation : {'left', 'right', 'top', 'bottom'} The orientation of the colorbar, used for rendering. The orientation can be thought of as the position of the label relative to the color bar. Note ---- This is purely internal. Externally, the ColorBarVisual must be used. This class was separated out to encapsulate rendering information That way, ColorBar simply becomes a CompoundVisual See Also -------- vispy.visuals.ColorBarVisual """ _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } def __init__(self, pos, halfdim, cmap, orientation, **kwargs): self._check_orientation(orientation) self._cmap = get_colormap(cmap) self._pos = pos self._halfdim = halfdim self._orientation = orientation Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) texcoord_func = self._get_texcoord_func(orientation) self.shared_program.frag['orient_texcoord'] = texcoord_func tex_coords = np.array([[0, 0], [1, 0], [1, 1], [0, 0], [1, 1], [0, 1]], dtype=np.float32) glsl_map_fn = Function(self._cmap.glsl_map) self.shared_program.frag['color_transform'] = glsl_map_fn self.shared_program['a_texcoord'] = tex_coords.astype(np.float32) self._update() def _update(self): """Rebuilds the shaders, and repositions the objects that are used internally by the ColorBarVisual""" x, y = self._pos halfw, halfh = self._halfdim # test that width and height are non-zero if halfw <= 0: raise ValueError("half-width must be positive and non-zero" ", not %s" % halfw) if halfh <= 0: raise ValueError("half-height must be positive and non-zero" ", not %s" % halfh) # test that the given width and height is consistent # with the orientation if (self._orientation == "bottom" or self._orientation == "top"): if halfw < halfh: raise ValueError("half-width(%s) < half-height(%s) for" "%s orientation," " expected half-width >= half-height" % (halfw, halfh, self._orientation, )) else: # orientation == left or orientation == right if halfw > halfh: raise ValueError("half-width(%s) > half-height(%s) for" "%s orientation," " expected half-width <= half-height" % (halfw, halfh, self._orientation, )) # Set up the attributes that the shaders require vertices = np.array([[x - halfw, y - halfh], [x + halfw, y - halfh], [x + halfw, y + halfh], # tri 2 [x - halfw, y - halfh], [x + halfw, y + halfh], [x - halfw, y + halfh]], dtype=np.float32) self.shared_program['a_position'] = vertices self.shared_program['texture2D_LUT'] = self._cmap.texture_lut() @staticmethod @lru_cache(maxsize=4) def _get_texcoord_func(orientation): if orientation == "top" or orientation == "bottom": func = Function(""" float orient_texcoord(vec2 texcoord) { return texcoord.x; } """) elif orientation == "left" or orientation == "right": func = Function(""" float orient_texcoord(vec2 texcoord) { return 1 - texcoord.y; } """) return func @staticmethod def _check_orientation(orientation): if orientation not in ('top', 'bottom', 'left', 'right'): raise ValueError("orientation must" " be one of 'top', 'bottom', " "'left', or 'right', " "not '%s'" % (orientation, )) @property def pos(self): return self._pos @pos.setter def pos(self, pos): self._pos = pos self._update() @property def halfdim(self): return self._halfdim @halfdim.setter def halfdim(self, halfdim): self._halfdim = halfdim @property def cmap(self): """The colormap of the Colorbar""" return self._cmap @cmap.setter def cmap(self, cmap): self._cmap = get_colormap(cmap) self._program.frag['color_transform'] = Function(self._cmap.glsl_map) @staticmethod def _prepare_transforms(view): # figure out padding by considering the entire transform # on the width and height program = view.view_program total_transform = view.transforms.get_transform() program.vert['transform'] = total_transform def _prepare_draw(self, view): self._draw_mode = "triangles" return True class ColorBarVisual(CompoundVisual): """Visual subclass displaying a colorbar Parameters ---------- cmap : str | vispy.color.ColorMap Either the name of the ColorMap to be used from the standard set of names (refer to `vispy.color.get_colormap`), or a custom ColorMap object. The ColorMap is used to apply a gradient on the colorbar. orientation : {'left', 'right', 'top', 'bottom'} The orientation of the colorbar, used for rendering. The orientation can be thought of as the position of the label relative to the color bar. When the orientation is 'left' or 'right', the colorbar is vertically placed. When it is 'top' or 'bottom', the colorbar is horizontally placed. * 'top': the colorbar is horizontal. Color is applied from left to right. Minimum corresponds to left and maximum to right. Label is to the top of the colorbar * 'bottom': Same as top, except that label is to the bottom of the colorbar * 'left': the colorbar is vertical. Color is applied from bottom to top. Minimum corresponds to bottom and maximum to top. Label is to the left of the colorbar * 'right': Same as left, except that the label is placed to the right of the colorbar size : (major_axis_length, minor_axis_length) lengths with respect to the major and minor axes. The minor axis is the shorter axis, while the major axis is the longer axis with respect to the orientation For orientations 'top' and 'bottom', the major axis is along the length. For orientations 'left' and 'right', the major axis is along the breadth pos : tuple (x, y) Position where the colorbar is to be placed with respect to the center of the colorbar label : str | vispy.visuals.TextVisual The label that is to be drawn with the colorbar that provides information about the colorbar. If a TextVisual object then 'label_color' is ignored. label_color : str | vispy.color.Color The color of the label and tick labels. This can either be a str as the color's name or an actual instace of a vipy.color.Color clim : tuple (min, max) the minimum and maximum values of the data that is given to the colorbar. This is used to draw the scale on the side of the colorbar. border_width : float (in px) The width of the border the colormap should have. This measurement is given in pixels border_color : str | vispy.color.Color The color of the border of the colormap. This can either be a str as the color's name or an actual instace of a vipy.color.Color .. versionchanged:: 0.7 Keyword argument ``label_str`` renamed to `label`. """ # The padding multiplier that's used to place the text # next to the Colorbar. Makes sure the text isn't # visually "sticking" to the Colorbar text_padding_factor = 1.05 def __init__(self, cmap, orientation, size, pos=[0, 0], label="", label_color='black', clim=(0.0, 1.0), border_width=1.0, border_color="black"): _CoreColorBarVisual._check_orientation(orientation) self._cmap = get_colormap(cmap) self._clim = clim self._pos = pos self._size = size self._orientation = orientation if not isinstance(label, TextVisual): label = TextVisual(label, color=label_color) self._label = label self._ticks = [] self._ticks.append(TextVisual(str(self._clim[0]), color=label_color)) self._ticks.append(TextVisual(str(self._clim[1]), color=label_color)) if orientation in ["top", "bottom"]: (width, height) = size elif orientation in ["left", "right"]: (height, width) = size self._halfdim = (width * 0.5, height * 0.5) self._colorbar = _CoreColorBarVisual(pos, self._halfdim, cmap, orientation) self._border = _BorderVisual(pos, self._halfdim, border_width, border_color) CompoundVisual.__init__(self, [self._colorbar, self._border, self._ticks[0], self._ticks[1], self._label, ]) self._update() def _update(self): """Rebuilds the shaders, and repositions the objects that are used internally by the ColorBarVisual""" self._colorbar.halfdim = self._halfdim self._border.halfdim = self._halfdim self._ticks[0].text = str(self._clim[0]) self._ticks[1].text = str(self._clim[1]) self._update_positions() self._colorbar._update() self._border._update() def _update_positions(self): """Updates the positions of the colorbars and labels""" self._colorbar.pos = self._pos self._border.pos = self._pos if self._orientation == "right" or self._orientation == "left": self._label.rotation = -90 x, y = self._pos halfw, halfh = self._halfdim label_anchors = \ ColorBarVisual._get_label_anchors(center=self._pos, halfdim=self._halfdim, orientation=self._orientation, transforms=self.label.transforms) self._label.anchors = label_anchors ticks_anchors = \ ColorBarVisual._get_ticks_anchors(center=self._pos, halfdim=self._halfdim, orientation=self._orientation, transforms=self.label.transforms) self._ticks[0].anchors = ticks_anchors self._ticks[1].anchors = ticks_anchors (label_pos, ticks_pos) = \ ColorBarVisual._calc_positions(center=self._pos, halfdim=self._halfdim, border_width=self.border_width, orientation=self._orientation, transforms=self.transforms) self._label.pos = label_pos self._ticks[0].pos = ticks_pos[0] self._ticks[1].pos = ticks_pos[1] @staticmethod def _get_label_anchors(center, halfdim, orientation, transforms): visual_to_doc = transforms.get_transform('visual', 'document') doc_x = visual_to_doc.map(np.array([1, 0, 0, 0], dtype=np.float32)) doc_y = visual_to_doc.map(np.array([0, 1, 0, 0], dtype=np.float32)) if doc_x[0] < 0: doc_x *= -1 if doc_y[1] < 0: doc_y *= -1 # NOTE: these are in document coordinates if orientation == "bottom": perp_direction = doc_y elif orientation == "top": perp_direction = -doc_y elif orientation == "left": perp_direction = -doc_x elif orientation == "right": perp_direction = doc_x perp_direction = np.array(perp_direction, dtype=np.float32) perp_direction /= np.linalg.norm(perp_direction) # rotate axes by -90 degrees to mimic label's rotation if orientation in ["left", "right"]: x = perp_direction[0] y = perp_direction[1] perp_direction[0] = -y perp_direction[1] = x # use the document (pixel) coord system to set text anchors anchors = [] if perp_direction[0] < 0: anchors.append('right') elif perp_direction[0] > 0: anchors.append('left') else: anchors.append('center') if perp_direction[1] < 0: anchors.append('bottom') elif perp_direction[1] > 0: anchors.append('top') else: anchors.append('middle') return anchors @staticmethod def _get_ticks_anchors(center, halfdim, orientation, transforms): visual_to_doc = transforms.get_transform('visual', 'document') doc_x = visual_to_doc.map(np.array([1, 0, 0, 0], dtype=np.float32)) doc_y = visual_to_doc.map(np.array([0, 1, 0, 0], dtype=np.float32)) if doc_x[0] < 0: doc_x *= -1 if doc_y[1] < 0: doc_y *= -1 # NOTE: these are in document coordinates if orientation == "bottom": perp_direction = doc_y elif orientation == "top": perp_direction = -doc_y elif orientation == "left": perp_direction = -doc_x elif orientation == "right": perp_direction = doc_x perp_direction = np.array(perp_direction, dtype=np.float32) perp_direction /= np.linalg.norm(perp_direction) # use the document (pixel) coord system to set text anchors anchors = [] if perp_direction[0] < 0: anchors.append('right') elif perp_direction[0] > 0: anchors.append('left') else: anchors.append('center') if perp_direction[1] < 0: anchors.append('bottom') elif perp_direction[1] > 0: anchors.append('top') else: anchors.append('middle') return anchors @staticmethod def _calc_positions(center, halfdim, border_width, orientation, transforms): """ Calculate the text centeritions given the ColorBar parameters. Note ---- This is static because in principle, this function does not need access to the state of the ColorBar at all. It's a computation function that computes coordinate transforms Parameters ---------- center: tuple (x, y) Center of the ColorBar halfdim: tuple (halfw, halfh) Half of the dimensions measured from the center border_width: float Width of the border of the ColorBar orientation: "top" | "bottom" | "left" | "right" Position of the label with respect to the ColorBar transforms: TransformSystem the transforms of the ColorBar """ (x, y) = center (halfw, halfh) = halfdim visual_to_doc = transforms.get_transform('visual', 'document') doc_to_visual = transforms.get_transform('document', 'visual') # doc_widths = visual_to_doc.map(np.array([halfw, halfh, 0, 0], # dtype=np.float32)) doc_x = visual_to_doc.map(np.array([halfw, 0, 0, 0], dtype=np.float32)) doc_y = visual_to_doc.map(np.array([0, halfh, 0, 0], dtype=np.float32)) if doc_x[0] < 0: doc_x *= -1 if doc_y[1] < 0: doc_y *= -1 # doc_halfw = np.abs(doc_widths[0]) # doc_halfh = np.abs(doc_widths[1]) if orientation == "top": doc_perp_vector = -doc_y elif orientation == "bottom": doc_perp_vector = doc_y elif orientation == "left": doc_perp_vector = -doc_x if orientation == "right": doc_perp_vector = doc_x perp_len = np.linalg.norm(doc_perp_vector) doc_perp_vector /= perp_len perp_len += border_width perp_len += 5 # pixels perp_len *= ColorBarVisual.text_padding_factor doc_perp_vector *= perp_len doc_center = visual_to_doc.map(np.array([x, y, 0, 0], dtype=np.float32)) doc_label_pos = doc_center + doc_perp_vector visual_label_pos = doc_to_visual.map(doc_label_pos)[:3] # next, calculate tick positions if orientation in ["top", "bottom"]: doc_ticks_pos = [doc_label_pos - doc_x, doc_label_pos + doc_x] else: doc_ticks_pos = [doc_label_pos + doc_y, doc_label_pos - doc_y] visual_ticks_pos = [] visual_ticks_pos.append(doc_to_visual.map(doc_ticks_pos[0])[:3]) visual_ticks_pos.append(doc_to_visual.map(doc_ticks_pos[1])[:3]) return (visual_label_pos, visual_ticks_pos) @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 = pos self._update_positions() @property def cmap(self): """The colormap of the Colorbar""" return self._colorbar._cmap @cmap.setter def cmap(self, cmap): self._colorbar.cmap = cmap @property def clim(self): """The data limits of the Colorbar Returns ------- clim: tuple(min, max) """ return self._clim @clim.setter def clim(self, clim): self._clim = clim self._update() @property def label(self): """The vispy.visuals.TextVisual associated with the label""" return self._label @label.setter def label(self, label): if isinstance(label, TextVisual): self._label = label else: self._label.text = label self._update() @property def ticks(self): """The vispy.visuals.TextVisual associated with the ticks Returns ------- ticks: [vispy.visual.TextVisual] The array is of length 2 """ return self._ticks @ticks.setter def ticks(self, ticks): self._ticks = ticks self._update() @property def border_width(self): """The width of the border around the ColorBar in pixels""" return self._border.border_width @border_width.setter def border_width(self, border_width): self._border.border_width = border_width self._update() @property def border_color(self): """The color of the border around the ColorBar in pixels""" return self._border.border_color @border_color.setter def border_color(self, border_color): self._border.border_color = border_color self._update() @property def orientation(self): """The orientation of the ColorBar""" return self._orientation @property def size(self): """The size of the ColorBar Returns ------- size: (major_axis_length, minor_axis_length) major and minor axis are defined by the orientation of the ColorBar """ (halfw, halfh) = self._halfdim if self.orientation in ["top", "bottom"]: return (halfw * 2., halfh * 2.) else: return (halfh * 2., halfw * 2.) @size.setter def size(self, size): if size[0] < size[1]: raise ValueError("Major axis must be greater than or equal to " "Minor axis. Given " "Major axis: (%s) < Minor axis (%s)" % (size[0], size[1])) if self.orientation in ["top", "bottom"]: (width, height) = size else: (height, width) = size if width < 0.: raise ValueError("width must be non-negative, not %s " % (width, )) elif width == 0.: raise ValueError("width must be non-zero, not %s" % (width, )) if height < 0.: raise ValueError("height must be non-negative, not %s " % (height, )) elif height == 0.: raise ValueError("height must be non-zero, not %s" % (height, )) self._halfdim = (width / 2., height / 2.) self._update() vispy-0.14.1/vispy/visuals/collections/0000755000175000017510000000000014507104251017453 5ustar nileshnileshvispy-0.14.1/vispy/visuals/collections/util.py0000644000175000017510000001214714507104216021010 0ustar nileshnilesh#!/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. Examples -------- >>> 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 result:: ['color,vertex,normal,', 10, 'float32'] level 1 result:: [['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(): dtype_list = dtype_reduce(value[0], level, depth + 1) if isinstance(dtype_list[0], str): items.append([key, dtype_list[1], dtype_list[2]]) else: items.append(dtype_list) 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 not isinstance(item[0], 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. Parameters ---------- 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.14.1/vispy/visuals/collections/triangle_collection.py0000644000175000017510000000116114507104216024045 0ustar nileshnilesh# -*- 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.14.1/vispy/visuals/collections/segment_collection.py0000644000175000017510000000143514507104216023706 0ustar nileshnilesh# -*- 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.14.1/vispy/visuals/collections/raw_triangle_collection.py0000644000175000017510000000505414507104216024723 0ustar nileshnilesh# -*- 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 ... 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 Collection.append(self, vertices=V, uniforms=U, indices=np.array(indices).ravel(), itemsize=itemsize) vispy-0.14.1/vispy/visuals/collections/raw_segment_collection.py0000644000175000017510000000666214507104216024566 0ustar nileshnilesh# -*- 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 ... 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.14.1/vispy/visuals/collections/raw_polygon_collection.py0000644000175000017510000000516014507104216024603 0ustar nileshnilesh# -*- 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 ... 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 Collection.append(self, vertices=V, uniforms=U, indices=np.array(indices).ravel(), itemsize=itemsize) vispy-0.14.1/vispy/visuals/collections/raw_point_collection.py0000644000175000017510000000677614507104216024263 0ustar nileshnilesh# -*- 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. """ from __future__ import division import numpy as np from ... 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.14.1/vispy/visuals/collections/raw_path_collection.py0000644000175000017510000001003614507104216024046 0ustar nileshnilesh# -*- 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 ... 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.14.1/vispy/visuals/collections/polygon_collection.py0000644000175000017510000000203214507104216023725 0ustar nileshnilesh# -*- 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.14.1/vispy/visuals/collections/point_collection.py0000644000175000017510000000134414507104216023374 0ustar nileshnilesh# -*- 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.14.1/vispy/visuals/collections/path_collection.py0000644000175000017510000000174014507104216023177 0ustar nileshnilesh# -*- 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.14.1/vispy/visuals/collections/collection.py0000644000175000017510000002133414507104216022164 0ustar nileshnilesh# -*- 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, program=None, **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.pop(name, scope) defaults[name] = default gtype = Collection._gtypes[(basetype, count)] if scope == "local": # numpy dtypes with size 1 are ambiguous, only add size if it is greater than 1 vtype.append((name, basetype, count) if count != 1 else (name, basetype)) declarations[ "attributes"] += "attribute %s %s;\n" % (gtype, name) elif scope == "shared": # numpy dtypes with size 1 are ambiguous, only add size if it is greater than 1 utype.append((name, basetype, count) if count != 1 else (name, basetype)) declarations["varyings"] += "varying %s %s;\n" % (gtype, name) else: declarations["uniforms"] += "uniform %s %s;\n" % (gtype, name) self._uniforms[name] = None if len(kwargs) > 0: raise NameError("Invalid keyword argument(s): %s" % list(kwargs.keys())) 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 if program is None: program = ModularProgram(vertex, fragment) else: program.vert = vertex program.frag = fragment if hasattr(program, 'changed'): 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): try: BaseCollection.__setitem__(self, key, value) except IndexError: for program in self._programs: program[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.14.1/vispy/visuals/collections/base_collection.py0000644000175000017510000004054314507104216023161 0ustar nileshnilesh# -*- 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. """ from __future__ import division import math import numpy as np from ...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', np.float32)) vtype = np.dtype(vtype) # Check utype is made of float32 only utype = eval(str(np.dtype(utype))) r_utype = dtype_reduce(utype) if not isinstance(r_utype[0], 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__', np.float32, 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 successive 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: idxs = np.array(indices) + vsize elif isinstance(itemsize, int): idxs = 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(idxs, 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 idxs = None U = None if self._indices_list is not None: idxs = 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 idxs is not None: istart, iend = self._indices_list._items[key] indices = idxs[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 = 4 * linesize // int(count) 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.14.1/vispy/visuals/collections/array_list.py0000644000175000017510000003342214507104216022203 0ustar nileshnilesh# -*- 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 successive 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(sublist) for sublist 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 successive 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(sublist) for sublist 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 items = self._items[istart:self._count] + size self._items[istart + _count:self._count + _count] = items # 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 successive 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.14.1/vispy/visuals/collections/agg_segment_collection.py0000644000175000017510000001114614507104216024524 0ustar nileshnilesh# -*- 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 ... 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) idxs = np.ones((itemcount, 6), dtype=int) idxs[:] = 0, 1, 2, 0, 2, 3 idxs[:] += 4 * np.arange(itemcount)[:, np.newaxis] idxs = idxs.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=idxs, itemsize=4 * itemcount) vispy-0.14.1/vispy/visuals/collections/agg_point_collection.py0000644000175000017510000000326214507104216024213 0ustar nileshnilesh# -*- 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 ... 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.14.1/vispy/visuals/collections/agg_path_collection.py0000644000175000017510000001505614507104216024022 0ustar nileshnilesh# -*- 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 ... import glsl from ... import gloo 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 = int(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 idxs = np.resize( np.array([0, 1, 2, 1, 2, 3], dtype=np.uint32), n * 2 * 3) idxs += np.repeat(4 * np.arange(n, dtype=np.uint32), 6) idxs[-6:] = 4 * n - 6, 4 * n - 5, 0, 4 * n - 5, 0, 1 else: idxs = np.resize( np.array([0, 1, 2, 1, 2, 3], dtype=np.uint32), (n - 1) * 2 * 3) idxs += np.repeat(4 * np.arange(n - 1, dtype=np.uint32), 6) idxs = idxs.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=idxs, itemsize=itemsize * 4 - 4) def draw(self, mode="triangles"): """Draw collection""" gloo.set_depth_mask(0) Collection.draw(self, mode) gloo.set_depth_mask(1) vispy-0.14.1/vispy/visuals/collections/agg_fast_path_collection.py0000644000175000017510000001673414507104216025043 0ustar nileshnilesh# -*- 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 ... import glsl from ... import gloo 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 = int(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. Examples -------- >>> 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: idxs = np.arange(n + 3) if key == 'prev': idxs -= 2 idxs[0], idxs[1], idxs[-1] = n - 1, n - 1, n - 1 elif key == 'next': idxs[0], idxs[-3], idxs[-2], idxs[-1] = 1, 0, 1, 1 else: idxs -= 1 idxs[0], idxs[-1], idxs[n + 1] = 0, 0, 0 else: idxs = np.arange(n + 2) if key == 'prev': idxs -= 2 idxs[0], idxs[1], idxs[-1] = 0, 0, n - 2 elif key == 'next': idxs[0], idxs[-1], idxs[-2] = 1, n - 1, n - 1 else: idxs -= 1 idxs[0], idxs[-1] = 0, n - 1 idxs = np.repeat(idxs, 2) return P[idxs] def draw(self, mode="triangle_strip"): """Draw collection""" gloo.set_depth_mask(0) Collection.draw(self, mode) gloo.set_depth_mask(1) vispy-0.14.1/vispy/visuals/collections/__init__.py0000644000175000017510000000214714507104216021571 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/visuals/box.py0000644000175000017510000000541314507104216016303 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from ..geometry import create_box from .mesh import MeshVisual from .visual import CompoundVisual class BoxVisual(CompoundVisual): """Visual that displays a box. Parameters ---------- width : float Box width. height : float Box height. depth : float Box depth. width_segments : int Box segments count along the width. height_segments : float Box segments count along the height. depth_segments : float Box segments count along the depth. planes: array_like Any combination of ``{'-x', '+x', '-y', '+y', '-z', '+z'}`` Included planes in the box construction. vertex_colors : ndarray Same as for `MeshVisual` class. See `create_plane` for vertex ordering. face_colors : ndarray Same as for `MeshVisual` class. See `create_plane` 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, width=1, height=1, depth=1, width_segments=1, height_segments=1, depth_segments=1, planes=None, vertex_colors=None, face_colors=None, color=(0.5, 0.5, 1, 1), edge_color=None, **kwargs): vertices, filled_indices, outline_indices = create_box( width, height, depth, width_segments, height_segments, depth_segments, planes) self._mesh = MeshVisual(vertices['position'], filled_indices, vertex_colors, face_colors, color) if edge_color: self._border = MeshVisual(vertices['position'], outline_indices, color=edge_color, mode='lines') else: self._border = MeshVisual() CompoundVisual.__init__(self, [self._mesh, self._border], **kwargs) self.mesh.set_gl_state(polygon_offset_fill=True, polygon_offset=(1, 1), depth_test=True) @property def mesh(self): """The vispy.visuals.MeshVisual that used to fill in.""" return self._mesh @mesh.setter def mesh(self, mesh): self._mesh = mesh @property def border(self): """The vispy.visuals.MeshVisual that used to draw the border.""" return self._border @border.setter def border(self, border): self._border = border vispy-0.14.1/vispy/visuals/border.py0000644000175000017510000001533514507104216016774 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Siddharth Bhat # ----------------------------------------------------------------------------- import numpy as np from . import Visual from ..color import Color _VERTEX_SHADER = """ attribute vec2 a_position; attribute vec2 a_adjust_dir; void main() { // First map the vertex to document coordinates vec4 doc_pos = $visual_to_doc(vec4(a_position, 0, 1)); // Also need to map the adjustment direction vector, but this is tricky! // We need to adjust separately for each component of the vector: vec4 adjusted; if ( a_adjust_dir.x == 0. ) { // If this is an outer vertex, no adjustment for line weight is needed. // (In fact, trying to make the adjustment would result in no // triangles being drawn, hence the if/else block) adjusted = doc_pos; } else { // Inner vertexes must be adjusted for line width, but this is // surprisingly tricky given that the rectangle may have been scaled // and rotated! vec4 doc_x = $visual_to_doc(vec4(a_adjust_dir.x, 0., 0., 0.)) - $visual_to_doc(vec4(0., 0., 0., 0.)); vec4 doc_y = $visual_to_doc(vec4(0, a_adjust_dir.y, 0., 0.)) - $visual_to_doc(vec4(0., 0., 0., 0.)); doc_x = normalize(doc_x); doc_y = normalize(doc_y); // Now doc_x + doc_y points in the direction we need in order to // correct the line weight of _both_ segments, but the magnitude of // that correction is wrong. To correct it we first need to // measure the width that would result from using doc_x + doc_y: vec4 proj_y_x = dot(doc_x, doc_y) * doc_x; // project y onto x float cur_width = length(doc_y - proj_y_x); // measure current weight // And now we can adjust vertex position for line width: adjusted = doc_pos + ($border_width / cur_width) * (doc_x + doc_y); } // Finally map the remainder of the way to render coordinates gl_Position = $doc_to_render(adjusted); } """ _FRAGMENT_SHADER = """ void main() { gl_FragColor = $border_color; } """ # noqa class _BorderVisual(Visual): """ Visual subclass to display 2D pixel-width borders. Parameters ---------- pos : tuple (x, y) Position where the colorbar is to be placed with respect to the center of the colorbar halfdim : tuple (half_width, half_height) Half the dimensions of the colorbar measured from the center. That way, the total dimensions of the colorbar is (x - half_width) to (x + half_width) and (y - half_height) to (y + half_height) border_width : float (in px) The width of the border the colormap should have. This measurement is given in pixels border_color : str | vispy.color.Color The color of the border of the colormap. This can either be a str as the color's name or an actual instace of a vipy.color.Color """ _shaders = { 'vertex': _VERTEX_SHADER, 'fragment': _FRAGMENT_SHADER, } def __init__(self, pos, halfdim, border_width=1.0, border_color=None, **kwargs): self._pos = pos self._halfdim = halfdim self._border_width = border_width self._border_color = Color(border_color) Visual.__init__(self, vcode=self._shaders['vertex'], fcode=self._shaders['fragment']) @staticmethod def _prepare_transforms(view): program = view.shared_program program.vert['visual_to_doc'] = \ view.transforms.get_transform('visual', 'document') program.vert['doc_to_render'] = \ view.transforms.get_transform('document', 'render') @property def visual_border_width(self): """The border width in visual coordinates""" render_to_doc = \ self.transforms.get_transform('document', 'visual') vec = render_to_doc.map([self.border_width, self.border_width, 0]) origin = render_to_doc.map([0, 0, 0]) visual_border_width = [vec[0] - origin[0], vec[1] - origin[1]] # we need to flip the y axis because coordinate systems are inverted visual_border_width[1] *= -1 return visual_border_width def _update(self): x, y = self._pos halfw, halfh = self._halfdim border_vertices = np.array([ [x - halfw, y - halfh], [x - halfw, y - halfh], [x + halfw, y - halfh], [x + halfw, y - halfh], [x + halfw, y + halfh], [x + halfw, y + halfh], [x - halfw, y + halfh], [x - halfw, y + halfh], [x - halfw, y - halfh], [x - halfw, y - halfh], ], dtype=np.float32) # Direction each vertex should move to correct for line width adjust_dir = np.array([ [0, 0], [-1, -1], [0, 0], [1, -1], [0, 0], [1, 1], [0, 0], [-1, 1], [0, 0], [-1, -1], ], dtype=np.float32) self.shared_program['a_position'] = border_vertices self.shared_program['a_adjust_dir'] = adjust_dir self.shared_program.vert['border_width'] = float(self._border_width) self.shared_program.frag['border_color'] = self._border_color.rgba def _prepare_draw(self, view=None): self._update() self._draw_mode = "triangle_strip" return True @property def border_width(self): """The width of the border""" return self._border_width @border_width.setter def border_width(self, border_width): self._border_width = border_width # positions of text need to be changed accordingly self._update() @property def border_color(self): """The color of the border in pixels""" return self._border_color @border_color.setter def border_color(self, border_color): self._border_color = Color(border_color) self.shared_program.frag['border_color'] = self._border_color.rgba @property def pos(self): """The center of the BorderVisual""" return self._pos @pos.setter def pos(self, pos): self._pos = pos self._update() @property def halfdim(self): """The half-dimensions measured from the center of the BorderVisual""" return self._halfdim @halfdim.setter def halfdim(self, halfdim): self._halfdim = halfdim self._update() vispy-0.14.1/vispy/visuals/axis.py0000644000175000017510000005602414507104216016463 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import math import numpy as np from .visual import CompoundVisual, updating_property from .line import LineVisual from .text import TextVisual # XXX TODO list (see code, plus): # 1. Automated tick direction? # 2. Expand to 3D (only 2D supported currently) # 3. Input validation # 4. Property support # 5. Reactivity to resizing (current tick lengths grow/shrink w/zoom) # 6. Improve tick label naming (str(x) is not good) and tick selection class AxisVisual(CompoundVisual): """Axis visual Parameters ---------- pos : array Co-ordinates of start and end of the axis. domain : tuple The data values at the beginning and end of the axis, used for tick labels. i.e. (5, 10) means the axis starts at 5 and ends at 10. Default is (0, 1). tick_direction : array The tick direction to use (in document coordinates). scale_type : str The type of scale. For now only 'linear' is supported. axis_color : tuple RGBA values for the axis colour. Default is black. tick_color : tuple RGBA values for the tick colours. The colour for the major and minor ticks is currently fixed to be the same. Default is a dark grey. text_color : Color The color to use for drawing tick and axis labels minor_tick_length : float The length of minor ticks, in pixels major_tick_length : float The length of major ticks, in pixels tick_width : float Line width for the ticks tick_label_margin : float Margin between ticks and tick labels tick_font_size : float The font size to use for rendering tick labels. axis_width : float Line width for the axis axis_label : str Text to use for the axis label axis_label_margin : float Margin between ticks and axis labels axis_font_size : float The font size to use for rendering axis labels. font_size : float Font size for both the tick and axis labels. If this is set, tick_font_size and axis_font_size are ignored. anchors : iterable A 2-element iterable (tuple, list, etc.) giving the horizontal and vertical alignment of the tick labels. The first element should be one of 'left', 'center', or 'right', and the second element should be one of 'bottom', 'middle', or 'top'. If this is not specified, it is determined automatically. """ def __init__(self, pos=None, domain=(0., 1.), tick_direction=(-1., 0.), scale_type="linear", axis_color=(1, 1, 1), tick_color=(0.7, 0.7, 0.7), text_color='w', minor_tick_length=5, major_tick_length=10, tick_width=2, tick_label_margin=12, tick_font_size=8, axis_width=3, axis_label=None, axis_label_margin=35, axis_font_size=10, font_size=None, anchors=None): if scale_type != 'linear': raise NotImplementedError('only linear scaling is currently ' 'supported') if font_size is not None: tick_font_size = font_size axis_font_size = font_size self._pos = None self._domain = None # If True, then axis stops at the first / last major tick. # If False, then axis extends to edge of *pos* # (private until we come up with a better name for this) self._stop_at_major = (False, False) self.ticker = Ticker(self, anchors=anchors) self.tick_direction = np.array(tick_direction, float) self.scale_type = scale_type self._minor_tick_length = minor_tick_length # px self._major_tick_length = major_tick_length # px self._tick_label_margin = tick_label_margin # px self._axis_label_margin = axis_label_margin # px self._axis_label = axis_label self._need_update = True self._line = LineVisual(method='gl', width=axis_width, antialias=True, color=axis_color) self._ticks = LineVisual(method='gl', width=tick_width, connect='segments', antialias=True, color=tick_color) self._text = TextVisual(font_size=tick_font_size, color=text_color) self._axis_label_vis = TextVisual(font_size=axis_font_size, color=text_color) CompoundVisual.__init__(self, [self._line, self._text, self._ticks, self._axis_label_vis]) if pos is not None: self.pos = pos self.domain = domain @property def text_color(self): return self._text.color @text_color.setter def text_color(self, value): self._text.color = value self._axis_label_vis.color = value @property def axis_color(self): return self._line.color @axis_color.setter def axis_color(self, value): self._line.set_data(color=value) @property def axis_width(self): return self._line.width @axis_width.setter def axis_width(self, value): self._line.set_data(width=value) @property def tick_color(self): return self._ticks.color @tick_color.setter def tick_color(self, value): self._ticks.set_data(color=value) @property def tick_width(self): return self._ticks.width @tick_width.setter def tick_width(self, value): self._ticks.set_data(width=value) @property def tick_font_size(self): return self._text.font_size @tick_font_size.setter def tick_font_size(self, value): self._text.font_size = value @updating_property def tick_direction(self): """The tick direction to use (in document coordinates).""" @tick_direction.setter def tick_direction(self, tick_direction): self._tick_direction = np.array(tick_direction, float) @property def axis_font_size(self): return self._axis_label_vis.font_size @axis_font_size.setter def axis_font_size(self, value): self._axis_label_vis.font_size = value @updating_property def domain(self): """The data values at the beginning and end of the axis, used for tick labels.""" @updating_property def axis_label(self): """Text to use for the axis label.""" @updating_property def pos(self): """Co-ordinates of start and end of the axis.""" @pos.setter def pos(self, pos): self._pos = np.array(pos, float) @updating_property def minor_tick_length(self): """The length of minor ticks, in pixels""" @updating_property def major_tick_length(self): """The length of major ticks, in pixels""" @updating_property def tick_label_margin(self): """Margin between ticks and tick labels""" @updating_property def axis_label_margin(self): """Margin between ticks and axis labels""" @property def _vec(self): """Vector in the direction of the axis line""" return self.pos[1] - self.pos[0] def _update_subvisuals(self): tick_pos, labels, tick_label_pos, anchors, axis_label_pos = \ self.ticker.get_update() self._line.set_data(pos=self.pos, color=self.axis_color) self._ticks.set_data(pos=tick_pos, color=self.tick_color) self._text.text = list(labels) self._text.pos = tick_label_pos self._text.anchors = anchors if self.axis_label is not None: self._axis_label_vis.text = self.axis_label self._axis_label_vis.pos = axis_label_pos self._need_update = False def _prepare_draw(self, view): if self._pos is None: return False if self.axis_label is not None: self._axis_label_vis.rotation = self._rotation_angle if self._need_update: self._update_subvisuals() @property def _rotation_angle(self): """Determine the rotation angle of the axis as projected onto the canvas.""" # TODO: make sure we only call get_transform if the transform for # the line is updated tr = self._line.get_transform(map_from='visual', map_to='canvas') trpos = tr.map(self.pos) # Normalize homogeneous coordinates # trpos /= trpos[:, 3:] x1, y1, x2, y2 = trpos[:, :2].ravel() if x1 > x2: x1, y1, x2, y2 = x2, y2, x1, y1 return math.degrees(math.atan2(y2-y1, x2-x1)) def _compute_bounds(self, axis, view): if axis == 2: return (0., 0.) # now axis in (0, 1) return self.pos[:, axis].min(), self.pos[:, axis].max() class Ticker(object): """Class to determine tick marks Parameters ---------- axis : instance of AxisVisual The AxisVisual to generate ticks for. """ def __init__(self, axis, anchors=None): self.axis = axis self._anchors = anchors def get_update(self): major_tick_fractions, minor_tick_fractions, tick_labels = \ self._get_tick_frac_labels() tick_pos, tick_label_pos, axis_label_pos, anchors = \ self._get_tick_positions(major_tick_fractions, minor_tick_fractions) return tick_pos, tick_labels, tick_label_pos, anchors, axis_label_pos def _get_tick_positions(self, major_tick_fractions, minor_tick_fractions): # tick direction is defined in visual coords, but use document # coords to determine the tick length trs = self.axis.transforms visual_to_document = trs.get_transform('visual', 'document') direction = np.array(self.axis.tick_direction) direction /= np.linalg.norm(direction) if self._anchors is None: # use the document (pixel) coord system to set text anchors anchors = [] if direction[0] < 0: anchors.append('right') elif direction[0] > 0: anchors.append('left') else: anchors.append('center') if direction[1] < 0: anchors.append('bottom') elif direction[1] > 0: anchors.append('top') else: anchors.append('middle') else: anchors = self._anchors # now figure out the tick positions in visual (data) coords doc_unit = visual_to_document.map([[0, 0], direction[:2]]) doc_unit = doc_unit[1] - doc_unit[0] doc_len = np.linalg.norm(doc_unit) vectors = np.array([[0., 0.], direction * self.axis.minor_tick_length / doc_len, direction * self.axis.major_tick_length / doc_len, direction * (self.axis.major_tick_length + self.axis.tick_label_margin) / doc_len ], dtype=float) minor_vector = vectors[1] - vectors[0] major_vector = vectors[2] - vectors[0] label_vector = vectors[3] - vectors[0] axislabel_vector = direction * (self.axis.major_tick_length + self.axis.axis_label_margin) / doc_len major_origins, major_endpoints = self._tile_ticks( major_tick_fractions, major_vector) minor_origins, minor_endpoints = self._tile_ticks( minor_tick_fractions, minor_vector) tick_label_pos = major_origins + label_vector axis_label_pos = 0.5 * (self.axis.pos[0] + self.axis.pos[1]) + axislabel_vector num_major = len(major_tick_fractions) num_minor = len(minor_tick_fractions) c = np.empty([(num_major + num_minor) * 2, 2]) c[0:(num_major-1)*2+1:2] = major_origins c[1:(num_major-1)*2+2:2] = major_endpoints c[(num_major-1)*2+2::2] = minor_origins c[(num_major-1)*2+3::2] = minor_endpoints return c, tick_label_pos, axis_label_pos, anchors def _tile_ticks(self, frac, tickvec): """Tiles tick marks along the axis.""" origins = np.tile(self.axis._vec, (len(frac), 1)) origins = self.axis.pos[0].T + (origins.T*frac).T endpoints = tickvec + origins return origins, endpoints def _get_tick_frac_labels(self): """Get the major ticks, minor ticks, and major labels""" minor_num = 4 # number of minor ticks per major division if (self.axis.scale_type == 'linear'): domain = self.axis.domain if domain[1] < domain[0]: flip = True domain = domain[::-1] else: flip = False offset = domain[0] scale = domain[1] - domain[0] transforms = self.axis.transforms length = self.axis.pos[1] - self.axis.pos[0] # in logical coords n_inches = np.sqrt(np.sum(length ** 2)) / transforms.dpi major = _get_ticks_talbot(domain[0], domain[1], n_inches, 2) labels = ['%g' % x for x in major] majstep = major[1] - major[0] minor = [] minstep = majstep / (minor_num + 1) minstart = 0 if self.axis._stop_at_major[0] else -1 minstop = -1 if self.axis._stop_at_major[1] else 0 for i in range(minstart, len(major) + minstop): maj = major[0] + i * majstep minor.extend(np.linspace(maj + minstep, maj + majstep - minstep, minor_num)) major_frac = major - offset minor_frac = np.array(minor) - offset if scale != 0: # maybe something better to do here? major_frac /= scale minor_frac /= scale use_mask = (major_frac > -0.0001) & (major_frac < 1.0001) major_frac = major_frac[use_mask] labels = [l for li, l in enumerate(labels) if use_mask[li]] minor_frac = minor_frac[(minor_frac > -0.0001) & (minor_frac < 1.0001)] # Flip ticks coordinates if necessary : if flip: major_frac = 1 - major_frac minor_frac = 1 - minor_frac elif self.axis.scale_type == 'logarithmic': return NotImplementedError elif self.axis.scale_type == 'power': return NotImplementedError return major_frac, minor_frac, labels # ############################################################################# # Translated from matplotlib class MaxNLocator(object): """Select no more than N intervals at nice locations.""" def __init__(self, nbins=10, steps=None, trim=True, integer=False, symmetric=False, prune=None): """ Keyword args: *nbins* Maximum number of intervals; one less than max number of ticks. *steps* Sequence of nice numbers starting with 1 and ending with 10; e.g., [1, 2, 4, 5, 10] *integer* If True, ticks will take only integer values. *symmetric* If True, autoscaling will result in a range symmetric about zero. *prune* ['lower' | 'upper' | 'both' | None] Remove edge ticks -- useful for stacked or ganged plots where the upper tick of one axes overlaps with the lower tick of the axes above it. If prune=='lower', the smallest tick will be removed. If prune=='upper', the largest tick will be removed. If prune=='both', the largest and smallest ticks will be removed. If prune==None, no ticks will be removed. """ self._nbins = int(nbins) self._trim = trim self._integer = integer self._symmetric = symmetric if prune is not None and prune not in ['upper', 'lower', 'both']: raise ValueError( "prune must be 'upper', 'lower', 'both', or None") self._prune = prune if steps is None: steps = [1, 2, 2.5, 3, 4, 5, 6, 8, 10] else: if int(steps[-1]) != 10: steps = list(steps) steps.append(10) self._steps = steps self._integer = integer if self._integer: self._steps = [n for n in self._steps if divmod(n, 1)[1] < 0.001] def bin_boundaries(self, vmin, vmax): nbins = self._nbins scale, offset = scale_range(vmin, vmax, nbins) if self._integer: scale = max(1, scale) vmin = vmin - offset vmax = vmax - offset raw_step = (vmax - vmin) / nbins scaled_raw_step = raw_step / scale best_vmax = vmax best_vmin = vmin for step in self._steps: if step < scaled_raw_step: continue step *= scale best_vmin = step * divmod(vmin, step)[0] best_vmax = best_vmin + step * nbins if (best_vmax >= vmax): break if self._trim: extra_bins = int(divmod((best_vmax - vmax), step)[0]) nbins -= extra_bins return (np.arange(nbins + 1) * step + best_vmin + offset) def __call__(self): vmin, vmax = self.axis.get_view_interval() return self.tick_values(vmin, vmax) def tick_values(self, vmin, vmax): locs = self.bin_boundaries(vmin, vmax) prune = self._prune if prune == 'lower': locs = locs[1:] elif prune == 'upper': locs = locs[:-1] elif prune == 'both': locs = locs[1:-1] return locs def view_limits(self, dmin, dmax): if self._symmetric: maxabs = max(abs(dmin), abs(dmax)) dmin = -maxabs dmax = maxabs return np.take(self.bin_boundaries(dmin, dmax), [0, -1]) def scale_range(vmin, vmax, n=1, threshold=100): dv = abs(vmax - vmin) if dv == 0: # maxabsv == 0 is a special case of this. return 1.0, 0.0 # Note: this should never occur because # vmin, vmax should have been checked by nonsingular(), # and spread apart if necessary. meanv = 0.5 * (vmax + vmin) if abs(meanv) / dv < threshold: offset = 0 elif meanv > 0: ex = divmod(np.log10(meanv), 1)[0] offset = 10 ** ex else: ex = divmod(np.log10(-meanv), 1)[0] offset = -10 ** ex ex = divmod(np.log10(dv / n), 1)[0] scale = 10 ** ex return scale, offset # ############################################################################# # Tranlated from http://www.justintalbot.com/research/axis-labeling/ # See "An Extension of Wilkinson's Algorithm for Positioning Tick Labels # on Axes" # by Justin Talbot, Sharon Lin, and Pat Hanrahan, InfoVis 2010. def _coverage(dmin, dmax, lmin, lmax): return 1 - 0.5 * ((dmax - lmax) ** 2 + (dmin - lmin) ** 2) / (0.1 * (dmax - dmin)) ** 2 def _coverage_max(dmin, dmax, span): range_ = dmax - dmin if span <= range_: return 1. else: half = (span - range_) / 2.0 return 1 - half ** 2 / (0.1 * range_) ** 2 def _density(k, m, dmin, dmax, lmin, lmax): r = (k-1.0) / (lmax-lmin) rt = (m-1.0) / (max(lmax, dmax) - min(lmin, dmin)) return 2 - max(r / rt, rt / r) def _density_max(k, m): return 2 - (k-1.0) / (m-1.0) if k >= m else 1. def _simplicity(q, Q, j, lmin, lmax, lstep): eps = 1e-10 n = len(Q) i = Q.index(q) + 1 if ((lmin % lstep) < eps or (lstep - lmin % lstep) < eps) and lmin <= 0 and lmax >= 0: v = 1 else: v = 0 return (n - i) / (n - 1.0) + v - j def _simplicity_max(q, Q, j): n = len(Q) i = Q.index(q) + 1 return (n - i)/(n - 1.0) + 1. - j def _get_ticks_talbot(dmin, dmax, n_inches, density=1.): # density * size gives target number of intervals, # density * size + 1 gives target number of tick marks, # the density function converts this back to a density in data units # (not inches) n_inches = max(n_inches, 2.0) # Set minimum otherwise code can crash :( if dmin == dmax: return np.array([dmin, dmax]) m = density * n_inches + 1.0 only_inside = False # we cull values outside ourselves Q = [1, 5, 2, 2.5, 4, 3] w = [0.25, 0.2, 0.5, 0.05] best_score = -2.0 best = None j = 1.0 n_max = 1000 while j < n_max: for q in Q: sm = _simplicity_max(q, Q, j) if w[0] * sm + w[1] + w[2] + w[3] < best_score: j = n_max break k = 2.0 while k < n_max: dm = _density_max(k, n_inches) if w[0] * sm + w[1] + w[2] * dm + w[3] < best_score: break delta = (dmax-dmin)/(k+1.0)/j/q z = np.ceil(np.log10(delta)) while z < float('infinity'): step = j * q * 10 ** z cm = _coverage_max(dmin, dmax, step*(k-1.0)) if (w[0] * sm + w[1] * cm + w[2] * dm + w[3] < best_score): break min_start = np.floor(dmax/step)*j - (k-1.0)*j max_start = np.ceil(dmin/step)*j if min_start > max_start: z = z+1 break for start in range(int(min_start), int(max_start)+1): lmin = start * (step/j) lmax = lmin + step*(k-1.0) lstep = step s = _simplicity(q, Q, j, lmin, lmax, lstep) c = _coverage(dmin, dmax, lmin, lmax) d = _density(k, m, dmin, dmax, lmin, lmax) leg = 1. # _legibility(lmin, lmax, lstep) score = w[0] * s + w[1] * c + w[2] * d + w[3] * leg if (score > best_score and (not only_inside or (lmin >= dmin and lmax <= dmax))): best_score = score best = (lmin, lmax, lstep, q, k) z += 1 k += 1 if k == n_max: raise RuntimeError('could not converge on ticks') j += 1 if j == n_max: raise RuntimeError('could not converge on ticks') if best is None: raise RuntimeError('could not converge on ticks') return np.arange(best[4]) * best[2] + best[0] vispy-0.14.1/vispy/visuals/_scalable_textures.py0000644000175000017510000004630314507104216021366 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import warnings import numpy as np from vispy.gloo.texture import Texture2D, Texture3D, convert_dtype_and_clip def get_default_clim_from_dtype(dtype): """Get min and max color limits based on the range of the dtype.""" # assume floating point data is pre-normalized to 0 and 1 if np.issubdtype(dtype, np.floating): return 0, 1 # assume integer RGBs fill the whole data space dtype_info = np.iinfo(dtype) dmin = dtype_info.min dmax = dtype_info.max return dmin, dmax def get_default_clim_from_data(data): """Compute a reasonable clim from the min and max, taking nans into account. If there are no non-finite values (nan, inf, -inf) this is as fast as it can be. Otherwise, this functions is about 3x slower. """ # Fast min_value = data.min() max_value = data.max() # Need more work? The nan-functions are slower min_finite = np.isfinite(min_value) max_finite = np.isfinite(max_value) if not (min_finite and max_finite): finite_data = data[np.isfinite(data)] if finite_data.size: min_value = finite_data.min() max_value = finite_data.max() else: min_value = max_value = 0 # no finite values in the data return min_value, max_value class _ScaledTextureMixin: """Mixin class to make a texture aware of color limits. This class contains the shared functionality for the CPU and GPU mixin classes below. In some cases this class provides a "generic" implementation of a specific method and is then overridden by one of the subclasses. Parameters ---------- data : ndarray | tuple | None Texture data in the form of a numpy array. A tuple of the shape of the texture can also be given. However, some subclasses may benefit from or even require a numpy array to make decisions based on shape **and** dtype. **texture_kwargs Any other keyword arguments to pass to the parent TextureXD class. """ def __init__(self, data=None, **texture_kwargs): self._clim = None self._data_dtype = None data, texture_kwargs = self.init_scaling_texture(data, **texture_kwargs) # Call the __init__ of the TextureXD class super().__init__(data, **texture_kwargs) def init_scaling_texture(self, data=None, internalformat=None, **texture_kwargs): """Initialize scaling properties and create a representative array.""" self._data_dtype = getattr(data, 'dtype', None) data = self._create_rep_array(data) internalformat = self._get_texture_format_for_data( data, internalformat) texture_kwargs['internalformat'] = internalformat return data, texture_kwargs def _get_texture_format_for_data(self, data, internalformat): return internalformat @property def clim(self): """Color limits of the texture's data.""" return self._clim def set_clim(self, clim): """Set clim and return if a texture update is needed. In this default implementation, it is assumed changing the color limit never requires re-uploading the data to the texture (always return ``False``). """ need_texture_upload = False if isinstance(clim, str): if clim != 'auto': raise ValueError('clim must be "auto" if a string') self._clim = clim else: try: cmin, cmax = clim except (ValueError, TypeError): raise ValueError('clim must have two elements') self._clim = (cmin, cmax) return need_texture_upload @property def clim_normalized(self): """Normalize current clims to match texture data inside the shader. If data is scaled on the CPU then the texture data will be in the range 0-1 in the _build_texture() method. Inside the fragment shader the final contrast adjustment will be applied based on this normalized ``clim``. """ if isinstance(self.clim, str) and self.clim == "auto": raise RuntimeError("Can't return 'auto' normalized color limits " "until data has been set. Call " "'scale_and_set_data' first.") if self._data_dtype is None: raise RuntimeError("Can't return normalized color limits until " "data has been set. Call " "'scale_and_set_data' first.") if self.clim[0] == self.clim[1]: return self.clim[0], np.inf # if the internalformat of the texture is normalized we need to # also normalize the clims so they match in-shader clim_min = self.normalize_value(self.clim[0], self._data_dtype) clim_max = self.normalize_value(self.clim[1], self._data_dtype) return clim_min, clim_max @property def is_normalized(self): """Whether the in-shader representation of this texture is normalized or not. Formats ending in 'f' (float), 'ui' (unsigned integer), or 'i' (signed integer) are not normalized in the GPU. Formats ending in "_snorm" are normalized on the range [-1, 1] based on the data type of the input data (ex. 0-255 for uint8). Formats with no data type suffix are normalized on the range [0, 1]. See https://www.khronos.org/opengl/wiki/Image_Format for more information. This property can be used to determine if input shader variables (uniforms, template variables) need to also be normalized. See :meth:`~BaseTexture.normalize_value` below. """ if self.internalformat is None: return True return self.internalformat[-1] not in ('f', 'i') def normalize_value(self, val, input_data_dtype): """Normalize values to match in-shader representation of this shader. Parameters ---------- val : int | float | ndarray Value(s) to normalize. input_data_dtype : numpy.dtype Data type of input data. The assumption is that the provided values to be normalized are in the same range as the input texture data and must be normalized in the same way. """ if not self.is_normalized: return val dtype_info = np.iinfo(input_data_dtype) dmin = dtype_info.min dmax = dtype_info.max val = (val - dmin) / (dmax - dmin) # XXX: Do we need to handle _snorm differently? # Not currently supported in vispy. return val def _data_num_channels(self, data): # if format == 'luminance': # num_channels = 1 if data is not None: # array or shape tuple ndim = getattr(data, 'ndim', len(data)) # Ex. (M, N, 3) in Texture2D (ndim=2) -> 3 channels num_channels = data.shape[-1] if ndim == self._ndim + 1 else 1 else: num_channels = 4 return num_channels def _create_rep_array(self, data): """Get a representative array with an initial shape. Data will be filled in and the texture resized later. """ dtype = getattr(data, 'dtype', np.float32) num_channels = self._data_num_channels(data) init_shape = (10,) * self._ndim + (num_channels,) return np.zeros(init_shape).astype(dtype) def check_data_format(self, data): """Check if provided data will cause issues if set later.""" # this texture type has no limitations return def scale_and_set_data(self, data, offset=None, copy=False): """Upload new data to the GPU.""" # we need to call super here or we get infinite recursion return super().set_data(data, offset=offset, copy=copy) def set_data(self, data, offset=None, copy=False): self.scale_and_set_data(data, offset=offset, copy=copy) class CPUScaledTextureMixin(_ScaledTextureMixin): """Texture mixin class for smarter scaling decisions. This class wraps the logic to normalize data on the CPU before sending it to the GPU (the texture). Pre-scaling on the CPU can be helpful in cases where OpenGL 2/ES requirements limit the texture storage to an 8-bit normalized integer internally. This class includes optimizations where image data is not re-normalized if the previous normalization can still be used to visualize the data with the new color limits. This class should only be used internally. For similar features where scaling occurs on the GPU see :class:`vispy.visuals._scalable_textures.GPUScaledTextureMixin`. To use this mixin, a subclass should be created to combine this mixin with the texture class being used. Existing subclasses already exist in this module. Note that this class **must** appear first in the subclass's parent classes so that its ``__init__`` method is called instead of the parent Texture class. """ def __init__(self, data=None, **texture_kwargs): self._data_limits = None # Call the __init__ of the mixin base class super().__init__(data, **texture_kwargs) def _clim_outside_data_limits(self, cmin, cmax): if self._data_limits is None: return False return cmin < self._data_limits[0] or cmax > self._data_limits[1] def set_clim(self, clim): """Set clim and return if a texture update is needed.""" need_texture_upload = False # NOTE: Color limits are not checked against data type limits if isinstance(clim, str): if clim != 'auto': raise ValueError('clim must be "auto" if a string') need_texture_upload = True self._clim = clim else: try: cmin, cmax = clim except (ValueError, TypeError): raise ValueError('clim must have two elements') if self._clim_outside_data_limits(cmin, cmax): need_texture_upload = True self._clim = (cmin, cmax) return need_texture_upload @property def clim_normalized(self): """Normalize current clims to match texture data inside the shader. If data is scaled on the CPU then the texture data will be in the range 0-1 in the _build_texture() method. Inside the fragment shader the final contrast adjustment will be applied based on this normalized ``clim``. """ if isinstance(self.clim, str) and self.clim == "auto": raise RuntimeError("Can't return 'auto' normalized color limits " "until data has been set. Call " "'scale_and_set_data' first.") if self._data_limits is None: raise RuntimeError("Can't return normalized color limits until " "data has been set. Call " "'scale_and_set_data' first.") range_min, range_max = self._data_limits clim_min, clim_max = self.clim if clim_min == clim_max: return 0, np.inf clim_min = (clim_min - range_min) / (range_max - range_min) clim_max = (clim_max - range_min) / (range_max - range_min) return clim_min, clim_max @staticmethod def _scale_data_on_cpu(data, clim, copy=True): data = np.array(data, dtype=np.float32, copy=copy) if clim[0] != clim[1]: # we always must copy the data if we change it here, otherwise it might change # unexpectedly the data held outside of here if not copy: data = data.copy() data -= clim[0] data *= 1 / (clim[1] - clim[0]) return data def scale_and_set_data(self, data, offset=None, copy=True): """Upload new data to the GPU, scaling if necessary.""" if self._data_dtype is None: data.dtype == self._data_dtype # ensure dtype is the same as it was before, or funny things happen # no copy is performed unless asked for or necessary data = convert_dtype_and_clip(data, self._data_dtype, copy=copy) clim = self._clim is_auto = isinstance(clim, str) and clim == 'auto' if data.ndim == self._ndim or data.shape[self._ndim] == 1: if is_auto: clim = get_default_clim_from_data(data) data = self._scale_data_on_cpu(data, clim, copy=False) data_limits = clim else: data_limits = get_default_clim_from_dtype(data.dtype) if is_auto: clim = data_limits self._clim = float(clim[0]), float(clim[1]) self._data_limits = data_limits return super().scale_and_set_data(data, offset=offset, copy=False) class GPUScaledTextureMixin(_ScaledTextureMixin): """Texture class for smarter scaling and internalformat decisions. This texture class uses internal formats that are not supported by strict OpenGL 2/ES drivers without additional extensions. By using this texture we upload data to the GPU in a format as close to the original data type as possible (32-bit floats on the CPU are 32-bit floats on the GPU). No normalization/scaling happens on the CPU and all of it happens on the GPU. This should avoid unnecessary data copies as well as provide the highest precision for the final visualization. The texture format may either be a GL enum string (ex. 'r32f'), a numpy dtype object (ex. np.float32), or 'auto' which means the texture will try to pick the best format for the provided data. By using 'auto' you also give the texture permission to change formats in the future if new data is provided with a different data type. This class should only be used internally. For similar features where scaling occurs on the CPU see :class:`vispy.visuals._scalable_textures.CPUScaledTextureMixin`. To use this mixin, a subclass should be created to combine this mixin with the texture class being used. Existing subclasses already exist in this module. Note that this class **must** appear first in the subclass's parent classes so that its ``__init__`` method is called instead of the parent Texture class. """ # dtype -> internalformat # 'r' will be replaced (if needed) with rgb or rgba depending on number of bands _texture_dtype_format = { np.float32: 'r32f', np.float64: 'r32f', np.uint8: 'r8', # uint8 normalized np.uint16: 'r16', # uint16 normalized # np.int8: 'r8', # not supported, there are no signed-integer norm formats # np.int16: 'r16', # np.uint32: 'r32ui', # not supported, no normal formats for 32bit ints # np.int32: 'r32i', } # instance variable that will be used later on _auto_texture_format = False def _handle_auto_texture_format(self, texture_format, data): if isinstance(texture_format, str) and texture_format == 'auto': if data is None: warnings.warn("'texture_format' set to 'auto' but no data " "provided. Falling back to CPU scaling.") texture_format = None else: texture_format = data.dtype.type self._auto_texture_format = True return texture_format def _get_gl_tex_format(self, texture_format, num_channels): if texture_format and not isinstance(texture_format, str): texture_format = np.dtype(texture_format).type if texture_format not in self._texture_dtype_format: raise ValueError("Can't determine internal texture format for '{}'".format(texture_format)) texture_format = self._texture_dtype_format[texture_format] # adjust internalformat for format of data (RGBA vs L) texture_format = texture_format.replace('r', 'rgba'[:num_channels]) return texture_format def _get_texture_format_for_data(self, data, internalformat): if internalformat is None: raise ValueError("'internalformat' must be provided for GPU scaled textures.") num_channels = self._data_num_channels(data) texture_format = self._handle_auto_texture_format(internalformat, data) texture_format = self._get_gl_tex_format(texture_format, num_channels) return texture_format def _compute_clim(self, data): clim = self._clim is_auto = isinstance(clim, str) and clim == 'auto' if data.ndim == self._ndim or data.shape[2] == 1: if is_auto: clim = get_default_clim_from_data(data) elif is_auto: # assume that RGB data is already scaled (0, 1) clim = get_default_clim_from_dtype(data.dtype) return float(clim[0]), float(clim[1]) def _internalformat_will_change(self, data): shape_repr = self._create_rep_array(data) new_if = self._get_gl_tex_format(data.dtype, shape_repr.shape[-1]) return new_if != self.internalformat def check_data_format(self, data): """Check if provided data will cause issues if set later.""" if self._internalformat_will_change(data) and not self._auto_texture_format: raise ValueError("Data being set would cause a format change " "in the texture. This is only allowed when " "'texture_format' is set to 'auto'.") def _reformat_if_necessary(self, data): if not self._internalformat_will_change(data): return if self._auto_texture_format: shape_repr = self._create_rep_array(data) internalformat = self._get_gl_tex_format(data.dtype, shape_repr.shape[-1]) self._resize(data.shape, internalformat=internalformat) else: raise RuntimeError("'internalformat' needs to change but " "'texture_format' was not 'auto'.") def scale_and_set_data(self, data, offset=None, copy=False): """Upload new data to the GPU, scaling if necessary.""" self._reformat_if_necessary(data) self._data_dtype = np.dtype(data.dtype) self._clim = self._compute_clim(data) return super().scale_and_set_data(data, offset=offset, copy=copy) class CPUScaledTexture2D(CPUScaledTextureMixin, Texture2D): """Texture class with clim scaling handling builtin. See :class:`vispy.visuals._scalable_textures.CPUScaledTextureMixin` for more information. """ class GPUScaledTexture2D(GPUScaledTextureMixin, Texture2D): """Texture class with clim scaling handling builtin. See :class:`vispy.visuals._scalable_textures.GPUScaledTextureMixin` for more information. """ class CPUScaledTexture3D(CPUScaledTextureMixin, Texture3D): """Texture class with clim scaling handling builtin. See :class:`vispy.visuals._scalable_textures.CPUScaledTextureMixin` for more information. """ class GPUScaledTextured3D(GPUScaledTextureMixin, Texture3D): """Texture class with clim scaling handling builtin. See :class:`vispy.visuals._scalable_textures.GPUScaledTextureMixin` for more information. """ vispy-0.14.1/vispy/visuals/__init__.py0000644000175000017510000000426414507104216017255 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 .axis import AxisVisual # noqa from .box import BoxVisual # noqa from .cube import CubeVisual # noqa from .ellipse import EllipseVisual # noqa from .gridlines import GridLinesVisual # noqa from .image import ImageVisual # noqa from .image_complex import ComplexImageVisual # noqa from .gridmesh import GridMeshVisual # noqa from .histogram import HistogramVisual # noqa from .infinite_line import InfiniteLineVisual # noqa from .instanced_mesh import InstancedMeshVisual # noqa from .isocurve import IsocurveVisual # noqa from .isoline import IsolineVisual # noqa from .isosurface import IsosurfaceVisual # noqa from .line import LineVisual, ArrowVisual # noqa from .linear_region import LinearRegionVisual # noqa from .line_plot import LinePlotVisual # noqa from .markers import MarkersVisual # noqa from .mesh import MeshVisual # noqa from .mesh_normals import MeshNormalsVisual # noqa from .plane import PlaneVisual # noqa from .polygon import PolygonVisual # noqa from .rectangle import RectangleVisual # noqa from .regular_polygon import RegularPolygonVisual # noqa from .scrolling_lines import ScrollingLinesVisual # noqa from .spectrogram import SpectrogramVisual # noqa from .sphere import SphereVisual # noqa from .surface_plot import SurfacePlotVisual # noqa from .text import TextVisual # noqa from .tube import TubeVisual # noqa from .visual import BaseVisual, Visual, CompoundVisual # noqa from .volume import VolumeVisual # noqa from .xyz_axis import XYZAxisVisual # noqa from .border import _BorderVisual # noqa from .colorbar import ColorBarVisual # noqa from .graphs import GraphVisual # noqa from .windbarb import WindbarbVisual # noqa vispy-0.14.1/vispy/version.py0000644000175000017510000000024214507104250015503 0ustar nileshnilesh# file generated by setuptools_scm # don't change, don't track in version control __version__ = version = '0.14.1' __version_tuple__ = version_tuple = (0, 14, 1) vispy-0.14.1/vispy/util/0000755000175000017510000000000014507104251014424 5ustar nileshnileshvispy-0.14.1/vispy/util/wrappers.py0000644000175000017510000001305714507104216016650 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 specific 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 from .config import _get_args def use(app=None, gl=None): """Set the usage options for vispy Specify what app backend and GL backend to use. 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. * 'PyQt6': use Qt widget toolkit via PyQt6. * 'PySide': use Qt widget toolkit via PySide. * 'PySide2': use Qt widget toolkit via PySide2. * 'PySide6': use Qt widget toolkit via PySide6. * 'PyGlet': use Pyglet backend. * 'Glfw': use Glfw backend (successor of Glut). Widely available on Linux. * 'SDL2': use SDL v2 backend. * 'osmesa': Use OSMesa backend Additional backends: * 'jupyter_rfb': show vispy canvases in Jupyter lab/notebook (depends on the jupyter_rfb library). 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. See Also -------- vispy.app.use_app vispy.gloo.gl.use_gl """ if app is None and gl is None: raise TypeError('Must specify at least one of "app" or "gl".') if app == 'osmesa': from ..util.osmesa_gl import fix_osmesa_gl_lib fix_osmesa_gl_lib() if gl is not None: raise ValueError("Do not specify gl when using osmesa") # Apply now if gl: from .. import gloo, config config['gl_backend'] = gl gloo.gl.use_gl(gl) if app: from ..app import use_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 _get_args(err_fun): raise subprocess.CalledProcessError(p.returncode, command, output) else: raise subprocess.CalledProcessError(p.returncode, command) if return_code: output = output + (p.returncode,) return output def test(*args, **kwargs): """Proxy function to delay `.testing` import""" from vispy.testing import test as _test # noqa return _test(*args, **kwargs) test.__test__ = False # no discover test function as test vispy-0.14.1/vispy/util/transforms.py0000644000175000017510000001230214507104216017173 0ustar nileshnilesh#!/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 4x4 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. Returns ------- M : ndarray Transformation matrix describing the rotation. """ 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.14.1/vispy/util/tests/0000755000175000017510000000000014507104251015566 5ustar nileshnileshvispy-0.14.1/vispy/util/tests/test_vispy.py0000644000175000017510000000272614507104216020361 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/tests/test_transforms.py0000644000175000017510000000275014507104216021402 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/tests/test_run.py0000644000175000017510000000065014507104216020005 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/tests/test_logging.py0000644000175000017510000000277114507104216020635 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 emit_list: logger.debug('Selected foo') assert_equal(len(emit_list), 1) assert_in('test_logging', emit_list[0]) # can't really parse this location with use_log_level('debug', record=True, print_msg=False) as emit_list: logger.debug('foo') assert_equal(len(emit_list), 1) assert_in('test_logging', emit_list[0]) with use_log_level('debug', 'foo', True, False) as emit_list: logger.debug('bar') assert_equal(len(emit_list), 0) with use_log_level('info', record=True, print_msg=False) as emit_list: logger.debug('foo') logger.info('bar') assert_equal(len(emit_list), 1) assert_not_in('unknown', emit_list[0]) run_tests_if_main() vispy-0.14.1/vispy/util/tests/test_key.py0000644000175000017510000000121314507104216017765 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/tests/test_import.py0000644000175000017510000001115314507104216020513 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.version'] 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: # pkg_resources from vispy/__init__.py shows up as vispy.pkg_resources in python 2.7 if m.startswith('vispy') and '__future__' not in m and 'pkg_resources' 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('PySide2', allmodnames) assert_not_in('PySide6', allmodnames) assert_not_in('PyQt4', allmodnames) assert_not_in('PyQt5', allmodnames) assert_not_in('PyQt6', 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.14.1/vispy/util/tests/test_gallery_scraper.py0000644000175000017510000000730414507104216022362 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Test the sphinx-gallery custom scraper.""" import os from vispy.testing import TestingCanvas, requires_application import pytest try: from sphinx_gallery.gen_gallery import DEFAULT_GALLERY_CONF except ImportError: pytest.skip("Skipping sphinx-gallery tests", allow_module_level=True) from ..gallery_scraper import VisPyGalleryScraper pytest.importorskip("PyQt5", reason="Gallery scraper only supports PyQt5") def _create_fake_block_vars(canvas): block_vars = { "example_globals": { "canvas": canvas, }, "src_file": "example.py", "image_path_iterator": (f"{x}.png" for x in range(10)), } return block_vars def _create_fake_gallery_conf(src_dir): gallery_conf = {} gallery_conf.update(DEFAULT_GALLERY_CONF) gallery_conf.update({ "compress_images": "images", "compress_images_args": [], "src_dir": src_dir, "gallery_dirs": src_dir, }) return gallery_conf @requires_application() @pytest.mark.parametrize("include_gallery_comment", [False, True]) def test_single_frame(include_gallery_comment, tmpdir): canvas = TestingCanvas() block_vars = _create_fake_block_vars(canvas) gallery_conf = _create_fake_gallery_conf(str(tmpdir)) script = "\n# vispy: gallery 30\n" if include_gallery_comment else "" with tmpdir.as_cwd(): with open("example.py", "w") as example_file: example_file.write(script) scraper = VisPyGalleryScraper() rst = scraper(None, block_vars, gallery_conf) if include_gallery_comment: assert "0.png" in rst assert os.path.isfile("0.png") else: assert "0.png" not in rst assert not os.path.isfile("0.png") assert not os.path.isfile("1.png") # only one file created @requires_application() def test_single_animation(tmpdir): canvas = TestingCanvas() block_vars = _create_fake_block_vars(canvas) gallery_conf = _create_fake_gallery_conf(str(tmpdir)) with tmpdir.as_cwd(): with open("example.py", "w") as example_file: example_file.write("""# vispy: gallery 10:50:5 """) scraper = VisPyGalleryScraper() rst = scraper(None, block_vars, gallery_conf) assert "0.gif" in rst assert os.path.isfile("0.gif") assert not os.path.isfile("0.png") # only gif file created assert not os.path.isfile("1.png") # only one file created @requires_application() @pytest.mark.parametrize( "exported_files", [ ("example.png",), ("example.gif",), ("example1.png", "example2.png"), ]) def test_single_export(exported_files, tmpdir): canvas = TestingCanvas() block_vars = _create_fake_block_vars(canvas) gallery_conf = _create_fake_gallery_conf(str(tmpdir)) with tmpdir.as_cwd(): # create the files that the example should have created for fn in exported_files: open(fn, "w").close() with open("example.py", "w") as example_file: example_file.write("""# vispy: gallery-exports {} """.format(" ".join(exported_files))) scraper = VisPyGalleryScraper() rst = scraper(None, block_vars, gallery_conf) for idx, fn in enumerate(exported_files): # the original file should have been moved assert not os.path.isfile(fn) # the new name should from the sphinx-gallery iterator new_name = str(idx) + os.path.splitext(fn)[1] assert os.path.isfile(new_name) assert new_name in rst vispy-0.14.1/vispy/util/tests/test_fourier.py0000644000175000017510000000237114507104216020656 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/tests/test_event_emitter.py0000644000175000017510000004677714507104216022077 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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): """The 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) def test_emitter_reentrance_allowed_when_blocked1(): # Minimal re-entrance example e = EventEmitter(source=None, type='test') count = 0 @e.connect def foo(x): nonlocal count count += 1 with e.blocker(): e() e() assert count == 1 def test_emitter_reentrance_allowed_when_blocked2(): # More realistic re-entrance example # The real world case for this might be a longer chain of events # where event1's callback knows that what it's doing will trigger # the same event so it blocks it: # event1 -> foo -> event2 -> bar -> event1 -> (ignored bc blocked). e1 = EventEmitter(source=None, type='test1') e2 = EventEmitter(source=None, type='test2') count = 0 @e1.connect def foo(x): nonlocal count count += 1 with e1.blocker(): e2() @e2.connect def bar(x): nonlocal count count += 10 e1() e1() assert count == 11 def test_emitter_reentrance_allowed_when_blocked3(): # Like the previous, but blocking callbacks instead of the event itself. # Allows more fine-grained control. To some extent anyway - all callbacks # of the event must be blocked to prevent raising the emitter loop error. e1 = EventEmitter(source=None, type='test1') e2 = EventEmitter(source=None, type='test2') count = 0 @e1.connect def foo(x): nonlocal count count += 1 with e1.blocker(foo): e2() @e2.connect def bar(x): nonlocal count count += 10 e1() @e2.connect def eggs(x): nonlocal count count += 100 e1() e1() assert count == 111 run_tests_if_main() vispy-0.14.1/vispy/util/tests/test_emitter_group.py0000644000175000017510000001737214507104216022077 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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): """The 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): """The 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 emit_list: grp.em1() assert_true(len(emit_list) >= 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 emit_list: grp.em1() assert_true(len(emit_list) >= 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): """The 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.14.1/vispy/util/tests/test_docstring_parameters.py0000644000175000017510000001033014507104216023414 0ustar nileshnilesh# TODO inspect for Cython (see sagenb.misc.sageinspect) import re import inspect import pytest import vispy.scene.cameras.magnify from vispy.testing import run_tests_if_main, requires_numpydoc public_modules = [ # the list of modules users need to access for all functionality 'vispy', 'vispy.color', 'vispy.geometry', 'vispy.gloo', 'vispy.io', 'vispy.plot', 'vispy.scene', 'vispy.util', 'vispy.visuals', ] def _func_name(func, cls=None): """Get the name.""" parts = [] if cls is not None: module = inspect.getmodule(cls) else: module = inspect.getmodule(func) if module: parts.append(module.__name__) if cls is not None: parts.append(cls.__name__) parts.append(func.__name__) return '.'.join(parts) # functions to ignore docstring_ignores = [ 'vispy.scene.visuals', # not parsed properly by this func, copies anyway ] error_ignores = { # These we do not live by: 'GL01', # Docstring should start in the line immediately after the quotes 'EX01', 'EX02', # examples failed (we test them separately) 'ES01', # no extended summary 'SA01', # no see also 'YD01', # no yields section 'SA04', # no description in See Also 'PR04', # Parameter "shape (n_channels" has no type 'RT02', # The first line of the Returns section should contain only the type, unless multiple values are being returned # noqa # XXX should also verify that | is used rather than , to separate params # XXX should maybe also restore the parameter-desc-length < 800 char check } error_ignores_specific = {} subclass_name_ignores = ( (vispy.scene.cameras.magnify.MagnifyCamera, {'MagnifyTransform', 'Magnify1DTransform'}), ) def check_parameters_match(func, cls=None): """Check docstring, return list of incorrect results.""" from numpydoc.validate import validate name = _func_name(func, cls) skip = (not name.startswith('vispy.') or any(re.match(d, name) for d in docstring_ignores) or 'deprecation_wrapped' in getattr( getattr(func, '__code__', None), 'co_name', '')) if skip: return list() if cls is not None: for subclass, ignores in subclass_name_ignores: if issubclass(cls, subclass) and name.split('.')[-1] in ignores: return list() incorrect = ['%s : %s : %s' % (name, err[0], err[1]) for err in validate(name)['errors'] if err[0] not in error_ignores and (name.split('.')[-1], err[0]) not in error_ignores_specific] return incorrect @pytest.mark.xfail @requires_numpydoc() def test_docstring_parameters(): """Test module docstring formatting.""" from numpydoc import docscrape incorrect = [] for name in public_modules: # Assert that by default we import all public names with `import vispy` # if name not in ('vispy'): # extra = name.split('.')[1] # assert hasattr(vispy, extra) with pytest.warns(None): # traits warnings 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 incorrect += check_parameters_match(cls) cdoc = docscrape.ClassDoc(cls) for method_name in cdoc.methods: method = getattr(cls, method_name) incorrect += check_parameters_match(method, cls=cls) if hasattr(cls, '__call__') and \ 'of type object' not in str(cls.__call__): incorrect += check_parameters_match(cls.__call__, cls) functions = inspect.getmembers(module, inspect.isfunction) for fname, func in functions: if fname.startswith('_'): continue incorrect += check_parameters_match(func) incorrect = sorted(list(set(incorrect))) msg = '\n' + '\n'.join(incorrect) msg += '\n%d error%s' % (len(incorrect), 's' if len(incorrect) == 1 else '') if len(incorrect) > 0: raise AssertionError(msg) run_tests_if_main() vispy-0.14.1/vispy/util/tests/test_config.py0000644000175000017510000000422214507104216020445 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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, requires_ssl) 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) @requires_ssl() 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.14.1/vispy/util/tests/__init__.py0000644000175000017510000000000014507104216017666 0ustar nileshnileshvispy-0.14.1/vispy/util/svg/0000755000175000017510000000000014507104251015223 5ustar nileshnileshvispy-0.14.1/vispy/util/svg/viewport.py0000644000175000017510000000374114507104216017462 0ustar nileshnilesh# -*- 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.14.1/vispy/util/svg/transformable.py0000644000175000017510000000177114507104216020443 0ustar nileshnilesh# -*- 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.14.1/vispy/util/svg/transform.py0000644000175000017510000001541314507104216017615 0ustar nileshnilesh# -*- 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(object): """ 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 = r"(?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.14.1/vispy/util/svg/svg.py0000644000175000017510000000206614507104216016401 0ustar nileshnilesh# -*- 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 = "[MLVHCSQTAZmlvhcsqtaz])" r"(?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.14.1/vispy/util/svg/number.py���������������������������������������������������������������0000644�0001750�0001751�00000001244�14507104216�017067� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- class Number(object): def __init__(self, content): if not content: self._value = 0 else: content = content.strip() self._value = float(content) def __float__(self): return self._value @property def value(self): return self._value def __repr__(self): return repr(self._value) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/svg/length.py���������������������������������������������������������������0000644�0001750�0001751�00000004407�14507104216�017064� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- 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.14.1/vispy/util/svg/group.py����������������������������������������������������������������0000644�0001750�0001751�00000003546�14507104216�016742� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import copy from ...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.14.1/vispy/util/svg/geometry.py�������������������������������������������������������������0000644�0001750�0001751�00000036611�14507104216�017440� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Anti-Grain Geometry - Version 2.4 # Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) # # 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. # # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. # ---------------------------------------------------------------------------- # # 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.14.1/vispy/util/svg/element.py��������������������������������������������������������������0000644�0001750�0001751�00000002604�14507104216�017231� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- 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.14.1/vispy/util/svg/color.py����������������������������������������������������������������0000644�0001750�0001751�00000015417�14507104216�016724� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from itertools import product from string import hexdigits # See _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)} _HEXDEC = {} for x, y in product(hexdigits, repeat=2): v = x + y _HEXDEC[v] = int(v, 16) 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.14.1/vispy/util/svg/base.py�����������������������������������������������������������������0000644�0001750�0001751�00000001440�14507104216�016507� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- 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.14.1/vispy/util/svg/__init__.py�������������������������������������������������������������0000644�0001750�0001751�00000001241�14507104216�017333� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- 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.14.1/vispy/util/quaternion.py���������������������������������������������������������������0000644�0001750�0001751�00000015170�14507104216�017170� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/ptime.py��������������������������������������������������������������������0000644�0001750�0001751�00000000755�14507104216�016124� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ ptime.py - Precision time function made os-independent """ import time as systime # get a reference starting time - initial performance counter START_TIME = systime.time() - systime.perf_counter() def time(): # return reference starting time + delta of performance counters return START_TIME + systime.perf_counter() �������������������vispy-0.14.1/vispy/util/profiler.py�����������������������������������������������������������������0000644�0001750�0001751�00000011127�14507104216�016623� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/osmesa_gl.py����������������������������������������������������������������0000644�0001750�0001751�00000001125�14507104216�016747� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os from . import logger def fix_osmesa_gl_lib(): """ When using OSMesa, the gl functions (from libGL) are included in libOSMesa.so. This function modifies the VISPY_GL_LIB env variable so gl2 picks up libOSMesa.so as the OpenGL library. This modification must be done before vispy.gloo is imported for the first time. """ if 'VISPY_GL_LIB' in os.environ: logger.warning('VISPY_GL_LIB is ignored when using OSMesa. Use ' 'OSMESA_LIBRARY instead.') os.environ['VISPY_GL_LIB'] = os.getenv('OSMESA_LIBRARY', 'libOSMesa.so') �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/logs.py���������������������������������������������������������������������0000644�0001750�0001751�00000031011�14507104216�015737� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import base64 import logging import sys import inspect import re import traceback import json from functools import partial import numpy as np ############################################################################### # 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, str): 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, str): 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 == (2 ** int(np.log2(ii))): 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': return buffer.ravel().tobytes() 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.14.1/vispy/util/keys.py���������������������������������������������������������������������0000644�0001750�0001751�00000004613�14507104216�015756� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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. """ 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, str): 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.14.1/vispy/util/gallery_scraper.py����������������������������������������������������������0000644�0001750�0001751�00000023500�14507104216�020155� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Scraper for sphinx-gallery. This is used to collect screenshots from the examples when executed via sphinx-gallery. This can be included in any project wanting to take advantage of this by adding the following to your sphinx ``conf.py``: .. code-block:: python sphinx_gallery_conf = { ... 'image_scrapers': ('vispy',) } The scraper is provided to sphinx-gallery via the ``vispy._get_sg_image_scraper()`` function. """ from __future__ import annotations import os import time import shutil from vispy.io import imsave from vispy.gloo.util import _screenshot from vispy.scene import SceneCanvas from sphinx_gallery.scrapers import optipng, figure_rst class VisPyGalleryScraper: """Custom sphinx-gallery scraper to save the current Canvas to an image.""" def __repr__(self): return self.__class__.__name__ def __call__(self, block, block_vars, gallery_conf): """Scrape VisPy Canvases and applications. Parameters ---------- block : tuple A tuple containing the (label, content, line_number) of the block. block_vars : dict Dict of block variables. gallery_conf : dict Contains the configuration of Sphinx-Gallery Returns ------- rst : str The ReSTructuredText that will be rendered to HTML containing the images. This is often produced by :func:`sphinx_gallery.scrapers.figure_rst`. """ example_fn = block_vars["src_file"] frame_num_list = self._get_frame_list_from_source(example_fn) image_path_iterator = block_vars['image_path_iterator'] canvas_or_widget = get_canvaslike_from_globals(block_vars["example_globals"]) if not frame_num_list: image_paths = [] elif isinstance(frame_num_list[0], str): # example produces an image/animation as output image_paths = [] for frame_image, image_path in zip(frame_num_list, image_path_iterator): image_path = os.path.splitext(image_path)[0] + os.path.splitext(frame_image)[1] shutil.move(frame_image, image_path) image_paths.append(image_path) else: image_paths = self._save_example_to_files( canvas_or_widget, frame_num_list, gallery_conf, image_path_iterator) fig_titles = "" # alt text # FUTURE: Handle non-images (ex. MP4s) with raw HTML return figure_rst(image_paths, gallery_conf['src_dir'], fig_titles) def _save_example_to_files(self, canvas_or_widget, frame_num_list, gallery_conf, image_path_iterator): image_path = next(image_path_iterator) frame_grabber = FrameGrabber(canvas_or_widget, frame_num_list) frame_grabber.collect_frames() if len(frame_num_list) > 1: # let's make an animation # FUTURE: mp4 with imageio? image_path = os.path.splitext(image_path)[0] + ".gif" frame_grabber.save_animation(image_path) else: frame_grabber.save_frame(image_path) frame_grabber.cleanup() if 'images' in gallery_conf['compress_images']: optipng(image_path, gallery_conf['compress_images_args']) return [image_path] def _get_frame_list_from_source(self, filename): lines = open(filename, 'rb').read().decode('utf-8').splitlines() for line in lines[:10]: if not line.startswith("# vispy:"): continue if "gallery-exports" in line: _frames = line.split('gallery-exports')[1].split(',')[0].strip() frames = self._frame_exports_to_list(_frames) break if "gallery " in line: # Get what frames to grab _frames = line.split('gallery')[1].split(',')[0].strip() frames = self._frame_specifier_to_list(_frames) break else: # no frame number hint - don't grab any frames frames = [] return frames def _frame_specifier_to_list(self, frame_specifier): _frames = frame_specifier or '0' frames = [int(i) for i in _frames.split(':')] if not frames: frames = [5] if len(frames) > 1: frames = list(range(*frames)) return frames def _frame_exports_to_list(self, frame_specifier): frames = frame_specifier.split(" ") frame_paths = [] for frame_fn in frames: # existing image file created by the example if not os.path.isfile(frame_fn): raise FileNotFoundError( "Example gallery frame specifier must be a frame number, " "frame range, or relative filename produced by the example.") frame_paths.append(frame_fn) return frame_paths def get_canvaslike_from_globals(globals_dict): qt_widget = _get_qt_top_parent(globals_dict) if qt_widget is not None: return qt_widget # Get canvas if "canvas" in globals_dict: return globals_dict["canvas"] if "Canvas" in globals_dict: return globals_dict["Canvas"]() if "fig" in globals_dict: return globals_dict["fig"] return None def _get_qt_top_parent(globals_dict): if "QWidget" not in globals_dict and "QMainWindow" not in globals_dict and "QtWidgets" not in globals_dict: return None qtwidgets = globals_dict.get("QtWidgets") qmainwindow = globals_dict.get("QMainWindow", getattr(qtwidgets, "QMainWindow", None)) qwidget = globals_dict.get("QWidget", getattr(qtwidgets, "QWidget", qmainwindow)) all_qt_widgets = [widget for widget in globals_dict.values() if isinstance(widget, qwidget) and widget is not None] all_qt_mains = [widget for widget in all_qt_widgets if isinstance(widget, qmainwindow)] if all_qt_mains: return all_qt_mains[0] if all_qt_widgets: return all_qt_widgets[0] return None class FrameGrabber: """Helper to grab a series of screenshots from the current Canvas-like object.""" def __init__(self, canvas_obj, frame_grab_list: list[int]): self._canvas = canvas_obj self._done = False self._current_frame = -1 self._collected_images = [] self._frames_to_grab = frame_grab_list[:] # copy so original list is preserved def cleanup(self): from PyQt5.QtWidgets import QApplication for child_widget in QApplication.allWidgets(): if hasattr(child_widget, 'close'): child_widget.close() QApplication.processEvents() def on_draw(self, _): if self._done: return # Grab only once self._current_frame += 1 if self._current_frame in self._frames_to_grab: self._frames_to_grab.remove(self._current_frame) if isinstance(self._canvas, SceneCanvas): im = self._canvas.render(alpha=True) else: im = _screenshot() self._collected_images.append(im) if not self._frames_to_grab or self._current_frame > self._frames_to_grab[0]: self._done = True def collect_frames(self): """Show current Canvas and render and collect all frames requested.""" if self._is_qt_widget(): self._grab_qt_screenshot() else: self._grab_vispy_screenshots() def _is_qt_widget(self): try: from PyQt5.QtWidgets import QWidget except ImportError: return False return isinstance(self._canvas, QWidget) def _grab_qt_screenshot(self): from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import QTimer self._canvas.show() # Qt is going to grab from the screen so we need the window on top self._canvas.raise_() # We need to give the GUI event loop and OS time to draw everything time.sleep(1.5) QApplication.processEvents() QTimer.singleShot(1000, self._grab_widget_screenshot) time.sleep(1.5) QApplication.processEvents() def _grab_widget_screenshot(self): from PyQt5.QtWidgets import QApplication screen = QApplication.screenAt(self._canvas.pos()) screenshot = screen.grabWindow(int(self._canvas.windowHandle().winId())) arr = self._qpixmap_to_ndarray(screenshot) self._collected_images.append(arr) @staticmethod def _qpixmap_to_ndarray(pixmap): from PyQt5 import QtGui import numpy as np im = pixmap.toImage().convertToFormat(QtGui.QImage.Format.Format_RGB32) size = pixmap.size() width = size.width() height = size.height() im_bits = im.constBits() im_bits.setsize(height * width * 4) # Convert 0xffRRGGBB buffer -> (B, G, R, 0xff) -> (R, G, B) return np.array(im_bits).reshape((height, width, 4))[:, :, 2::-1] def _grab_vispy_screenshots(self): os.environ['VISPY_IGNORE_OLD_VERSION'] = 'true' self._canvas.events.draw.connect(self.on_draw, position='last') with self._canvas as c: self._collect_frames(c) def _collect_frames(self, canvas, limit=10000): n = 0 while not self._done and n < limit: canvas.update() canvas.app.process_events() n += 1 if n >= limit or len(self._frames_to_grab) > 0: raise RuntimeError("Could not collect any images") def save_frame(self, filename, frame_index=0): imsave(filename, self._collected_images[frame_index]) def save_animation(self, filename): import imageio # multiple gif not properly supported yet imageio.mimsave(filename, self._collected_images) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/frozen.py�������������������������������������������������������������������0000644�0001750�0001751�00000001573�14507104216�016310� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # Class adapted from: # http://stackoverflow.com/questions/3603502/ class Frozen(object): __isfrozen = False def __setattr__(self, key, value): if self.__isfrozen and not hasattr(self, key): raise AttributeError('%r is not an attribute of class %s. Call ' '"unfreeze()" to allow addition of new ' 'attributes' % (key, self)) object.__setattr__(self, key, value) def freeze(self): """Freeze the object so that only existing properties can be set""" self.__isfrozen = True def unfreeze(self): """Unfreeze the object so that additional properties can be added""" self.__isfrozen = False �������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fourier.py������������������������������������������������������������������0000644�0001750�0001751�00000003657�14507104216�016465� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/fonts/����������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�015555� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fonts/tests/����������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�016717� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fonts/tests/test_font.py����������������������������������������������������0000644�0001750�0001751�00000002575�14507104216�021310� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np import warnings from vispy.testing import assert_in, run_tests_if_main from vispy.util.fonts import list_fonts, _load_glyph, _vispy_fonts import pytest known_bad_fonts = set([ 'Noto Color Emoji', # https://github.com/vispy/vispy/issues/1771 'Bahnschrift', # https://github.com/vispy/vispy/pull/1974 ]) # try both a vispy and system font <--- what does this mean??? sys_fonts = set(list_fonts()) - set(_vispy_fonts) def test_font_list(): """Test font listing""" f = list_fonts() assert len(f) > 0 for font in _vispy_fonts: assert_in(font, f) @pytest.mark.parametrize('face', ['OpenSans'] + sorted(sys_fonts)) def test_font_glyph(face): """Test loading glyphs""" if face in known_bad_fonts or face.split(" ")[0] in known_bad_fonts: pytest.xfail() font_dict = dict(face=face, size=12, bold=False, italic=False) glyphs_dict = dict() chars = 'foobar^C&#' for char in chars: # Warning that Arial might not exist with warnings.catch_warnings(record=True): warnings.simplefilter('always') _load_glyph(font_dict, char, glyphs_dict) assert len(glyphs_dict) == np.unique([c for c in chars]).size run_tests_if_main() �����������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fonts/tests/__init__.py�����������������������������������������������������0000644�0001750�0001751�00000000000�14507104216�021017� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fonts/data/�����������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�016466� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fonts/data/OpenSans-Regular.ttf���������������������������������������������0000644�0001750�0001751�00000650420�14507104216�022342� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0DSIGžD�;œ��tGDEF�&¯�7|���GPOS 77�7œ���8GSUB+=·�7Ô��ÆOS/2¡>žÉ��¸���`cmap)«/h��´��cvt M¤�����¢fpgm~a¶��Ð��´gasp��#�7l���glyft8™K��%Œ�/´head÷vâ¦��<���6hhea Ì s��t���$hmtxè5<Ý����škernT+ ~�U@�¶6loca)Üñ��4��VmaxpC ��˜��� names°ˆ…� x��ÇpostCïl�@��&+prepC·–¤��„�� ����š!Çõ__<õ� �����É51‹����ÉèLLûšýÕ ¢b��� ����������ý¨�� ¬ûšþ{ ¢���������������£���ª�Š��V����/�\���ø���¶��š3��š3��Ñ�fñ à�ï@� [���(����1ASC�@� ÿýþ�„X �Ÿ����H¶��� �Í�Á��������#�˜5�…+�3“�ƒ–�h×�qÅ�…^�R^�=j�V“�hö�?“�T!�˜ð�“�f“�¼“�d“�^“�+“�…“�u“�^“�h“�j!�˜!�?“�h“�w“�ho�1�y��/�É �}Õ�És�É!�ÉÓ�}ç�ɪ�T#ÿ`é�É'�É9�É�É;�}Ñ�É;�}ò�Éd�jm�Ó�ºÃ��h�ž�{��‘�R¢�¦ð�¢�3V�1–ÿüž‰s�^ç�°Ï�sç�s}�s¶�b�'é�°�¢ÿ‘3�°�°q�°é�°Õ�sç�°ç�sD�°Ñ�jÓ�é�¤��9�1�'�¾�R�=hî�H“�h��#�˜“�¾“�?“�{“�hî!�{ž5¨�dÕ�Fú�R“�h“�T¨�d�ÿúm�“�hÇ�1Ç�!ž‰ô�°=�q!�˜Ñ�%Ç�L��Bú�P=�K=�.=�o�3������������üÿþ �}s�És�És�És�ɪ�<ª�Tªÿÿª�<Ç�/�É;�};�};�};�};�}“�…;�}Ó�ºÓ�ºÓ�ºÓ�º{��ã�Éú�°s�^s�^s�^s�^s�^s�^Ý�^Ï�s}�s}�s}�s}�sÿÚ�©ÿ³ÿìÅ�qé�°Õ�sÕ�sÕ�sÕ�sÕ�s“�hÕ�sé�¤é�¤é�¤é�¤�ç�°���s�^��s�^��s�^ �}Ï�s �}Ï�s �}Ï�s �}Ï�sÕ�Éç�sÇ�/ç�ss�É}�ss�É}�ss�É}�ss�É}�ss�É}�sÓ�}b�'Ó�}b�'Ó�}b�'Ó�}b�'ç�Éé�°ç��é�ªÿâÿª�*ÿÚª�ÿ̪�T�5ª�T�°Í�T �¢#ÿ`ÿ‘é�É3�°%�°'�É�£'�É�Y'�É�°'�Ƀ�°/�ÿü�Éé�°�Éé�°�Éé�°s��Éé�°;�}Õ�s;�}Õ�s;�}Õ�sb�}‰�qò�ÉD�°ò�ÉD�`ò�ÉD�‚d�jÑ�jd�jÑ�jd�jÑ�jd�jÑ�jm�Ó�m�Ó�m�Ó�Ó�ºé�¤Ó�ºé�¤Ó�ºé�¤Ó�ºé�¤Ó�ºé�¤Ó�ºé�¤h�9�{���{��‘�R¾�R‘�R¾�R‘�R¾�R�°ž�Ã��s�^üÿþÝ�^;�}Õ�sd�jÑ�j¼ ¼ ²-¼%�¢žo“�%¼ž�çžüž��!�˜òÿÔ}ÿÔ˜ÿäÿä…ÿÔÿä¶ÿé��/�É)�É“�'s�É‘�Rç�É;�}ª�Té�ÉÓ��9�É�Ém�H;�}Õ�ÉÑ�ɉ�Jm�{��b�jž�^�mB�Pª�<{��ã�sÍ�Zé�°¶�¨ß�¤ã�s�°� ¤�qÍ�ZÝ�sé�°¼�s¶�¨%�°Fÿòô�°V��Í�qÕ�s3�Õ�¦Û�sç�sÉ�ß�¤¾�s^ÿì�¤/�s¶� ß�¤Õ�sß�¤/�ss�Éß�)�É�}d�jª�Tª�<#ÿ`o�� �Éß�å�Éø�Õ�É��ç�É/�É)�Éw�s�ÉÁ�¦�J�Ë�Ëå�É¢��9�Éç�É;�}Õ�ÉÑ�É �}m�ø�b�jž�å�É�ªB�ÉD�É�Ó�É%�É �=f�É�3s�^Å�w�°m�°“�)}�sã�Ý�D�°�°'�°‘�á�°�°Õ�sø�°ç�°Ï�s¼�)�¸�q1�'�°Ý�œ�°-�°�))�°¼�°ð�9¦�°q�%}�sé�m�°ð�sÑ�j�¢ÿìÿ‘²��°é�'�°�ø�°7�Ém�°h�9�h�9�h�9�{�����R��R��RJÿü\�\�ö�?\�Í�Í�=��{�{�¤F�˜ ž�dÅ�…%�…o�Ro�Pã�˜ þy'�m“�b“�D�š¸�?˜�)�w'�É5�%B�Pô�f=�G=� =�G=�j¦�f“�'é�É �L“�hd�%¤�w� “�b“�h“�h“�hª�o¼�¼�ž�Ûÿ‘�‰�q�Ç�'Ç�Ç�;Ç�)Ç�9Ç�3Ç�#������������ª�����V��y��!��š���Í������������T��Tÿ‘\�ú� …��¸�9�Éq�°��s�^Rþߪ�u3�˜u�u�=�}ß�s%�ºR�¤��üS��ý ��ü��ý��ý;s�É�Ë}�s�°�…��f��Z�Éã�°m��ƒ� ^�É!�°Å�#� Ë�ÉÅ�°¨�?Ý�^�m�¤=�}Õ�s�� ���� �� ¬�}}�s�}B�sþ�}w�sß�^���}ç�sß�ju�Ëž�øžßžáé�)¦�))�É%�°ç�/¼�ã�Éç�°7�/m�#�É3�°�=�¦�JÝ�DJ�É\�°é�ÉD�°é�/#�ƒ�ì�)ø�É/�°�Éã�°‰�Éì�°;�}�s �}Ï�sm�¼�){����{����ô�V�'×�¼�)‰�ªß�œ�ªÍ�œ�É®�°´�=F�3´�=F�3ª�TÁ�ã�ƒ�Éd�°¦��“�Ñ�Éî�°ö�É9�°�ªÝ�œ;�Éã�°ª�T��s�^��s�^üÿþÝ�^s�É}�s×�uy�f×�uy�fÁ�ã�¦�JÝ�Dª�Jé��Ë�°�Ë�°;�}Õ�s=�}Õ�s=�}Õ�s �=ð�9ø��ø��ø���ªÝ�œ7�Ém�°Ó�É)�°7�/m�ø�R�'ž�1�'ç�ƒç�s1�ƒ+�s;�Nj�P��N/�PÙ��Ï��ÉN�° �}�s®�-�)ª�oÍ�Zš��‘���s�^��s�^��s�^��s�-��s�^��s�^��s�^��s�^��s�^��s�^��s�^��s�^s�É}�ss�É}�ss�É}�ss�É}�ss�]}�Js�É}�ss�É}�ss�É}�sª�T�{ª�T�;�}Õ�s;�}Õ�s;�}Õ�s;�}Õ�a;�}Õ�s;�}Õ�s;�}Õ�s=�}ß�s=�}ß�s=�}ß�s=�}ß�s=�}ß�sÓ�ºé�¤Ó�ºé�¤%�ºR�¤%�ºR�¤%�ºR�¤%�ºR�¤%�ºR�¤{���{���{���ç�s��ûå��üq��ûš��üq��üh��üy��üy��üy��üh¤�1¤�¤�-�4‰�sô�-�)“�^�“�…“�u“�^“�h“�jm�Z�\m�Ó�ç�qç�qç�qç�qç�q;�É;�;�³;ÿÇ;�;ÿ«;ÿó;ÿç;�V;�»^�Éåÿä;�É��É��É�É�™�¸���������� ����°�€��0�H�I�~�Ë�Ï'2a’¡°ðÿ7¼ÇÉÝó #ŠŒ¡ªÎÒÖ O_†‘¿Ï?…ÇÊñùM   " & 0 3 : < D p y  ¤ § ¬!!!! !"!&!.!^"""""""+"H"`"e%Êûþÿÿýÿÿ��� �I�J� �Ì�Ð(3b’ ¯ðú7¼ÆÉØó� #„ŒŽ£«ÑÖ�P`ˆ’ÀÐ�>€ ÈËòM �   & 0 2 9 < D p t  £ § «!!!! !"!&!.!["""""""+"H"`"d%Êû�þÿÿüÿÿÿã��ÿãÿÂ��ÿÂ��ÿÂ��ÿ°�¿�²�aÿI����ÿ–þ…þ„þvÿhÿcÿbÿ]�gÿD��ýÏ����ýÍþ‚þ��ýš��þ ��þ ��þ äXäãzä}��ä}��ã âBáïáîáíáêáááàáÛáÚáÓáËáÈá™ávát��áá á ânàþàûàôàÈà%à"àààààßçßÐßÍÜi��OS����®�����ª���®���À�����������ð�����������������������à���ê��������0��L��\����������p��r��������������������������������`��������������������������������������<������–—˜™š›�ëœ�í�ïž�ñŸ�ó &'()*+,-./0123456789:;<=>?@AIJ$%TUVWXY¡\]^_`abcdef¢hijklmnopqrstuv£hœžŸ ¤¥£¤¥¦§ijêëìíîïðñòóôõkö÷“”•–—˜™šøù¦ÊËÌÍÎÏÐÑÒÓÔÕÖ×§¨F©�opqrstu45]^��@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! �, °`E°% Fa#E#aH-, EhD-,E#F`° a °F`°&#HH-,E#F#a° ` °&a° a°&#HH-,E#F`°@a °f`°&#HH-,E#F#a°@` °&a°@a°&#HH-, <�<-, E# °ÍD# ¸ZQX# °D#Y °íQX# °MD#Y °&QX# ° D#Y!!-, EhD °` E°FvhŠE`D-,± C#Ce -,�± C#C -,�°(#p±(>°(#p±(E:±� -, E°%Ead°PQXED!!Y-,I°#D-, E°�C`D-,°C°Ce -, i°@a°�‹ ±,ÀŠŒ¸�b`+ d#da\X°aY-,ŠEŠŠ‡°+°)#D°)zä-,Ee°,#DE°+#D-,KRXED!!Y-,KQXED!!Y-,°%# Šõ�°`#íì-,°%# Šõ�°a#íì-,°%õ�íì-,°C°RX!!!!!F#F`ŠŠF# FŠ`Ša¸ÿ€b# #б ŠpE` °�PX°a¸ÿº‹°FŒY°`h:Y-, E°%FRK°Q[X°%F ha°%°%?#!8!Y-, E°%FPX°%F ha°%°%?#!8!Y-,�°C°C -,!! d#d‹¸@�b-,!°€QX d#d‹¸ �b²�@/+Y°`-,!°ÀQX d#d‹¸Ub²�€/+Y°`-, d#d‹¸@�b`#!-,KSXа%Id#Ei°@‹a°€b° aj°#D#°ö!#Š 9/Y-,KSX °%Idi °&°%Id#a°€b° aj°#D°&°öа#D°ö°#D°íа& 9# 9//Y-,E#E`#E`#E`#vh°€b -,°H+-, E°�TX°@D E°@aD!!Y-,E±0/E#Ea`°`iD-,KQX°/#p°#B!!Y-,KQX °%EiSXD!!Y!!Y-,E°C°�`c°`iD-,°/ED-,E# EŠ`D-,E#E`D-,K#QX¹�3ÿà±4 ³3�4�YDD-,°CX°&EŠXdf°`d° `f X!°@Y°aY#XeY°)#D#°)à!!!!!Y-,°CTXKS#KQZX8!!Y!!!!Y-,°CX°%Ed° `f X!°@Y°a#XeY°)#D°%°% XY°%°% F°%#B<°%°%°%°% F°%°`#B< X�Y°%°%°)à°) EeD°%°%°)à°%°% XY°%°%CH°%°%°%°%°`CH!Y!!!!!!!-,°% F°%#B°%°%EH!!!!-,°% °%°%CH!!!-,E# E °�P X#e#Y#h °@PX!°@Y#XeYŠ`D-,KS#KQZX EŠ`D!!Y-,KTX EŠ`D!!Y-,KS#KQZX8!!Y-,°�!KTX8!!Y-,°CTX°F+!!!!Y-,°CTX°G+!!!Y-,°CTX°H+!!!!Y-,°CTX°I+!!!Y-, Š#KSŠKQZX#8!!Y-,�°%I°�SX °@8!Y-,F#F`#Fa#  FŠa¸ÿ€bб@@ŠpE`h:-, Š#IdŠ#SX<!Y-,KRX}zY-,°�KKTB-,±�B±#ˆQ±@ˆSZX¹�� ˆTX²C`BY±$ˆQX¹ ��@ˆTX²C`B±$ˆTX² C`B�KKRX²C`BY¹@��€ˆTX²C`BY¹@��€c¸�ˆTX²C`BY¹@��c¸�ˆTX²C`BY±&ˆQX¹@��c¸�ˆTX²@C`BY¹@��c¸�ˆTX²€C`BYYYYYY±�CTX@ @@ @  ±CTX²@º�� �³  ±€CRX²@¸€± @²@º€� @Y¹@��€ˆU¹@��c¸�ˆUZX³ � ³ � YYYBBBBB-,Eh#KQX# E d°@PX|YhŠ`YD-,°�°%°%°#>�°#>± ° #eB° #B°#?�°#?± °#eB°#B°-,°€°CP°°CT[X!#° ÉŠíY-,°Y+-,Šå-@™ !H U UHU?¯MK&LK3KF%&4U%3$UÿÿÿJI3IF%3UU3U?¯GFëF#3"U3U3UU3UOÏÿ�U3�Uo��¯�ï��€¸±TS++K¸ÿRK° P[°ˆ°%S°ˆ°@QZ°ˆ°�UZ[X±ŽY…�BK°2SX° YK°dSX°±�BYss++^stu+++++t+st+++++++++++++st+++^�����N¶��u¶Í��������������H��‘��ÿì����ÿì����ÿì��þÿì��¶�ü”ÿíþ…ÿêþ©ÿì�þ¼������������������������������������������������������������‹��Ý�˜��Ž�™�ˆ��Š���������Q�w�ÿ{ìjƒ®ÙA_t–¯ñ[¹ûF£Å4‘ÇûDd»A€Û  U Š ¸  9 l ” à á  V œ Ù , y Ì ð $ K ¿ æ6Or“©È$y´T”(f”Ò'¹úO£Ö(h¥ÌG€¬îK……¶S¡õ•ËG”Ïíõ•ÍÙc‚ÁñEl¥Ýó{Œ®¿ÑÝ+7HYj|ž¯Á*;L]n€®  * ; L ^ o ±!!(!8!H!X!i!z"""!"1"A"R"c"t"…"—"ÿ###/#?#O#`#¦$ $$,$<$M$]$´$Å$Ö$æ$÷%%%%0%@%Q%a%r%ƒ%”%¤%µ%Æ%Î&:&K&[&l&|&&ž&ª&¶&Ç&×&è&ø' ''*';'G'W'h'y'É("(3(D(U(f(w(ˆ(“(ž(¯(Æ(Ò(Þ(ï)�) ))L)])n)y)…)–)¦)²)¾)ø*-*>*N*Z*e*v*†*—*Þ+'+8+H+Y+i+{+Œ+ï,i,z,Š,•,¡,²,Ã,Ô,ä,õ----.->-I-T-e-u-²...%.6.F.W.g.y.Š.œ.­.¹.Å.Ö.ç.ø///+/;/L/]/n/~/¥/ø0w11'181I1Y1d1o1˜1Á1×1ÿ22T2{2´2æ33N3_3g3x3Š3œ3­3¿3Ð3ã3ë3ó444"4*424‹4“4›4Á4É4Ñ55525:5q5y55è5ð6<66¢6´6Ä6Ô6ä6õ77k7Ð88g8Å99L9¦9Ò9Ú:,:4:_:Ê:Ò;;\;¨;í<%<]<º==_=¹=Ë=Ü=ì=ü> >>o>€>Ê>Ò>Ú>ì>ô?S?¦?å?ö@@7@?@†@Ž@–@ß@çA,A‰AÁAÒBBVcV—V®VÒW2WbWãX,X>XPX}X‰X•X¼XãYY!Y@YuY·YüZMZnZÓ['['['['['['['['['['['['['['\q\Ì\Ý\å]l]§^ ^^-^9^E^W^Œ^Ã^Ó^ã_@_—_à`1`:`C`L`z`™`ª`»`Ë`ÛaNa™aíb;b›bþc?c€cÖd,ddôeieàfŒg0g8g@ggöh/hghyh‹ii i€iójk;kÑl:l}l¿mm3m`m†m¬noooßp1p‚p×qCq{q´rrUr¨rûsssPsŒsÍttXt¬tæuu]u¢uÝvvsvÆwBw¹wÅwÑxx4x‹P‹b‹t‹‰‹‹¯‹Á‹Ó‹å‹÷Œ ŒŒ-ŒBŒVŒbŒnŒŒŒ¡Œ±ŒÃŒÕŒçŒù /AVj{Œ˜¤°¼ÍÞðŽŽŽ&Ž8ŽJŽ\ŽnŽƒŽ—Ž¨Ž¸ŽÉŽÙŽêŽû (4@L]n °ÁÒãóÿ #4EVfr¦á‘‘j‘‘ú’2’{’Í’õ““;“D“ƒ“­“î”N”“”Þ”æ• ••n•z•ö–––q––‘–¢–²–Ç–Ø–é–ú— ——.—?—J—[—g—y——“—›—­—µ—½—ΗÚ����Á�� ¶���·��/2/3/3/310!!7!!ÁIü·hyý‡¶úJhæ��˜ÿ㉶���+@   OY �??+�9/933310#3432#"&Fi3Ïáx:?@94D“#ú´ˆFB@G?���…¦°¶���@ � �?3Í2993310#!#?(i)+)h)¶ýðýð���3��ö¶���™@U    � !  ! NY �NYO O   �/3?399//]]33+�3333+�339939939939223910!!#!#!5!!5!3!3!!!ÕBþÍT‰TþÑRˆPþúDþë+R‹R1T†Tüå/Bþуþ¬þR®þR®T´þL´þLþ¬T��ƒÿ‰ � �&�-�f@5'%* !�� ./%  MY$*LY*+MY*�//99//92+�33+�33+�3933333333310#5"&'53&&546753&'4&'6 Ì·pÒCSÙYͥ˧¸«4•šœJªY€ÙýÝZocfÁˆ±èß#œ%/¸A¬ˆƒ¨¶´Eƒ; þN2_{eHY,þ{L\)ƒ]���hÿì-Ë� ��!�-�1�E@$� ("".( 023 + + +010%�?3?3??99//33933331032#"#"&5463232654&#"#"&54632#òJS¤¤SJÊ™”Œ›•’‘œ¦JTTPPTTJË™”Ž™•’ŽŸþþüÕ“+ªªTR¨ªäéîßãæîüÛ«©§­«¥¥«ãéîÞãæë úJ¶���qÿìÓÍ� ��5�Q@0�#*+.+-#& 673 IY3'-0/&** / JY �?+�?9/99?+93333106654&#"27%467.546326673#'#"&žHWegVYo›ñŸþKo\,›þ¹‹´U=$į¢ºˆ—8C¨D‰+å¹vô–×í“E}XKSMa`ûš¨DYfAu‰ú‚Èf_bj9–¨§•kµ]þy>§cþâ”þݲj\Ô���…¦?¶��·��?Í9310#?(i)¶ýð���Rþ¼!¶� �@ � � '�??99331073#&R›’¢‘”‹ “š1 ήÁþ2ôðþ6½ªÆ���=þ¼ ¶� �@  � � '�??993310#654'3 ›’ ‹”‘¢“š1þùþ:¨¼ËðôÎÁ¯þ1��V��0@    ��?Ä29333910%'%7‘+Žþƒø¬° °òþ‡‡+þuo¶þº^jþ–^F¶o‹���h�ã)Ã� �(@�  PY��/]3+�3933310!!#!5!3œþd‹þfš‹ŠþVªŠ¬��?þøm�î��µ� ��/Í9910%#67^b5}A îdþ÷rh2\��TÙ?q��µ���/399105!TëÙ˜˜��˜ÿã‰�ò� �@ �� OY �?+931074632#"&˜=9:AB93CjCEECAF?�����Û¶��·��??9910#Ûýߦ!¶úJ¶��fÿì-Í� ��(@� � KY KY�?+�?+993310#"3232#"-ïöìöîôî÷üá–¤¦••¦¤–Ýþ…þŠr~rþ~þ’þÁþÝ';;%þß��¼��˶� �$@ �   �??9/9993310!#47'3Ë¢4ÔXƒŒ‚t.¬r+��d��%Ë��+@� KYLY�?+�?+93310!!5>54&#"'632!%ü?°p8Ž~[£dXÊîÎêœÖþÀðƒ²˜Su‰+8% =1*"% BC55FY;GY "***GY*(?GY(.GY�?+�?+�?+�99//99++�993333310#"'332!"&5467&&5467&&5463232654&##"3254#"1Ë,ÜÀ1+jJZ²¿þÜþè×é€t*9@EUkØÆVEþ–ŒÑÉn˜Çq~Z‚tóöu~Hi#qG¡À8U-+–¶¿ ’d’P59UlK†›H‡DJA,>85G��ÿì¨F��4@  GY@�FY�?+�?Í+�393310%267# #5773!!,Ri*þÂF`>þÂ^u  OŒPEêþý{cj���¤ÿì9H��4@   FY �??+�?393993331032653#'##"&5Lz‚¬Ÿ¦‰ 3µtÈÇHý9†„¼Õ@û¸“QV¾ÑÍ������H� �@   ��??399910!3363 þ`²ìP u̲þ`HýväD5M0û¸����#H��,@    ��?3?339933339910!&'##3366733663/É4(ÏÀþÕ®jo 1É´Ä8#¿¬þу;ѯ_ýHþcþPK9µ5uý‹¬u$–Üû¸���'��H� �"@�  �?3?39991033##¸þƒ½! »þƒ‘¼þÍþʼ1þ\¤ýéýϼþD��þH��$@ � � FY ��?2?+�991033663#"'53277²ðO Sæ²þ)F»ˆLJ7D«I=HýÖ_3÷|û ¹›… Àœ���R��mH� �+@� GYGY�?+�?+93310!!5!5!!müåVýÏçý²]qVüº��=þ¼Á¶��,@ �� '�??933333310%&&54ᘡ463Ûuq¾Ð~x‚tضæßß f\Œªš/hY\`2›¬‹ÁþÙ×' '×�îþ{��@ ���??93103#î÷ü��Hþ¼Ë¶��,@ �'�??933333310&54'52"5665467 ßã¸Óv‚z~;otnq?'×'Á‹®™þÎa[YhþÑ™«Œ\f)rx���hP)T��$@ PY�PY�/+�Ä/Ä+9910"56323267#"&'&&R56dDqYBb/6€6fŽH~HKZÉC6—m&@9–n! ���˜þ‹‰^���+@ �� OY "�??+�9/9333103##"&54632Ûi3Ïáy<@  �    �??99//333393333310%#5&5%53&#"327Ëi“…ËÁŒ‡KŽ11…m¬¢Ÿ§Žð6ÈÎ úü>¬¤!Œ3ÓÙÔË;��?��DÉ��H@&   NY �LY�KY��?+�?+�9/3+�393333102&#"!!!!5655#5346ª¾ª=š{}¦þZAJûûÍÆÆàÉT…M|ŒþÙÝdˆ,š/ôß<²Í���{ ��'� @ �"�()%�/33/399331047'76327'#"''7&732654&#"¸J‡^‡h‚f‰_†JJƒ\‰f†d‡\…Jttž rtÓzkŒ\…II…\Šqvƒg‡\…GI…\ˆk|p Ÿqr¢¤�����q¶��V@.      �  �??399//]9223333933333103!!!!#!5!5!5!3H{®þ`þÃ=þäþÄ<þÄ�þe²ß×üþªþô ª�îþ{���$@ ���??99//9333103#3#îüøþ ü÷��{ÿø–�1�=�C@&2�*8 �#>?;6-! !'GY! GY ��?+�?+�99333310467&&54632&&#"#"'532654&&'.7654&'‹VNJTÏÅ^Ÿa5b‡Ltt{šº–RJ™êÔÚ€NÂR†0lsކB’„§1‰“¹DU)V‰%(oUy‹'ƒ';@54&#"'632!ý¤ìYR!P?4bEBƒ˜„“Y“®¸JhæVaL6DE&2Xo‚pP—Š¥��!9É�#�9@"� $%]mL ! !�?3?39/]]]39310#"'53254##532654&#"'6632sRD°¸¨˜t“{ÓçuwgcPCBp8E?Œ^ˆçPg/¢€8{D¢‘kOD=D+#Z-6w�‰Ù!� �¶  € �/Í99106673#‰0o Ê,®@oò>°AA¾4��°þDH��5@ � FY  �??+�??399333331032653#'##"'##3Vþ«Ÿ¦ˆ oå–X ¦¦}þú½Ô@û¸“§\T þÀ4��qþü`��'@�� �/3?39/93310####"&563!`rÕs>TØËÚè-þü°ùP3úûþ��˜L‰Z� �@ ��  OY�/+93104632#"&˜>8:AB93CÓBEEBAF?���%þ´����$@ ���/Ì29/393310#"'532654&'73´™–3--;OQOmXn7´þßaj j(6+5²s'��LJá¶� � @�  ��?2?9/9933103#47'R…6‡C¶ü”C[Z-_`��B¾Ç� ��%@ ��� �?3Ä]2993310#"&5463232654&#"¾«–’©¨—˜¥ýþ[hi\\ig\o¤·º¡£µ¶¢zzzz{vv��P�u¨¾�� �#@ �  �/3/393310'7'7¨þ¨uþáuXþuþ¨uþáuX þiG_^EþiþiG_^Eþiÿÿ�K��Ѷ�'ƒ���&�{ÿ�<ý·� ³�?55�ÿÿ�.��Û¶�'?���&�{â��tNý·�²�?5�ÿÿ���!É�&�uù��'ß��<mý·� ³+�?55���3þwT^��(�A@"##)*� && OY& IY#�?+�?+�9/_^]9333103267#"&54>76655#"&54632NKay=„zP–b;ÅÆ¾Ø#@Y6eA´y;>B73F¬3z”TjKM8dq&0‡`ºªFiYR/Xt]+‡EB@G@ÿÿ����s&�$���CÿÂR�³&�+5ÿÿ����s&�$���v�…R�³&�+5ÿÿ����s&�$��K�#R�³&�+5ÿÿ����/&�$��R�R�³&�+5ÿÿ����%&�$���j�7R� ´$&�+55ÿÿ����&�$���P�9��ÿþ��¶���N@, �  IYIY IY  IY�?+�??99//+++�33933310!!!#!!!!!!#ýýþã°ºÉý¼ýãDûT¾vÑþ/¶—þ)–ýæÒµ�ÿÿ�}þÏË&�&����z��ÿÿ�É��øs&�(���Cÿ·R�³ &�+5ÿÿ�É��øs&�(���v�?R�³&�+5ÿÿ�É��øs&�(��KÿûR�³&�+5ÿÿ�É��ø%&�(���j�R� ´!&�+55ÿÿ�<��Vs&�,���Cþ³R�³ &�+5ÿÿ�T��ss&�,���vÿaR�³&�+5ÿÿÿÿ��¡s&�,��KþóR�³&�+5ÿÿ�<��o%&�,���jÿR� ´!&�+55��/��H¶� ��W@2 ��IY?¯Ïß  JY JY�?+�?+�9/_^]3+�39333310�!!#53! �!#!!3 Hþwþþ{šš²Q|µýÇç{þ…¾béþ–þ‰–—þ‰þ¤@ýü–þ ÿÿ�É��?/&�1��R�“R�³&�+5ÿÿ�}ÿì¾s&�2���C�yR�³&�+5ÿÿ�}ÿì¾s&�2���v R�³!&�+5ÿÿ�}ÿì¾s&�2��K�´R�³&&�+5ÿÿ�}ÿì¾/&�2��R�šR�³!&�+5ÿÿ�}ÿì¾%&�2���j�ÕR� ´-&�+55��… ˜� �@    �/993310'7¬`þ ^`þžþ¤e^þ da˜cþžþ c_þ¡c``eþ���}ÿþö���#�N@, �� $%! !IY IY�?Æ+�?Æ9+�99933910�!"''7&�!27'32&#"¾þþÄë”exl²`DÑaxjÀ´ný`s°óøü'ej¨óýÝþ¡þndOšÆme‰^‡P”Êþ•šüLR2*þúš¯IþÍ�ÿÿ�ºÿìs&�8���C�FR�³&�+5ÿÿ�ºÿìs&�8���v�ÏR�³&�+5ÿÿ�ºÿìs&�8��K�}R�³ &�+5ÿÿ�ºÿì%&�8���j�˜R� ´'&�+55ÿÿ����{s&�<���v�1R�³&�+5��É��y¶� ��6@ �� JY JY �??99//++99333310!##33 32654&##yþÑþḪª×üú¨âʾÊÌãîþÁ¶ÿ�ÏýꤕŠ���°ÿìœ�0�A@")*#� �*12*..&FY.�*FY�?+�??+�9/9333310#"'53254&'&&54676654&# #4632X8GNŒf³¼k?œH×Sn`EGK@ˆþì¦ÜÞÎáò‡sFC! *93_e «Eš'/¶KkFR{T?j59Z5PUßûL²²»ÿÿ�^ÿìÍ!&�D���CŽ��³&&�+5ÿÿ�^ÿìÍ!&�D���v+��³.&�+5ÿÿ�^ÿìÍ!&�D��KØ��³3&�+5ÿÿ�^ÿìÍÝ&�D��R½��³.&�+5ÿÿ�^ÿìÍÓ&�D���jâ�� ´:&�+55ÿÿ�^ÿìÍ…&�D��P÷�� ´(&�+55��^ÿìs\�)�4�;�a@3*�$08090 �<=-'-FY11GY8$'"'5FY�?3+�3?39/993+�3+�39333399310467754&#"'66326632!!267# '#"&732655"!4&^øþ¸tw£4JÇb‚¥)5«nÀèýC:[TV•eþß}Qņ£¹®kX‘¨žº¤½y‹ €/¡³D{T)5W_X`þõÞkþu#'”&!éjª—_Y©šcm2¦žœ¨�ÿÿ�sþ‹\&�F����zF��ÿÿ�sÿì!&�H���Cµ��³&�+5ÿÿ�sÿì!&�H���vN��³$&�+5ÿÿ�sÿì!&�H��K÷��³)&�+5ÿÿ�sÿìÓ&�H���j �� ´0&�+55ÿÿÿÚ��c!&�ó���CþQ���³&�+5ÿÿ�©��2!&�ó���vÿ ���³ &�+5ÿÿÿ³��U!&�ó��Kþ§���³&�+5ÿÿÿì��Ó&�ó���jþ·��� ´&�+55��qÿìb!��&�J@+! �� '( FY  $FY�?+�?39/99+933310�#"�54�327&''7&'774&# 326bþû÷ÞþéÜâd9ÍþñIé\^EœfîLϘ¥¨´œþ¯¯¢¯¡3þçþÒ âæyÖ¿›l…>1uIKŠkwþrþ蓪þ˜§·É�ÿÿ�°��DÝ&�Q��R��³&�+5ÿÿ�sÿìb!&�R���CÔ��³&�+5ÿÿ�sÿìb!&�R���vV��³"&�+5ÿÿ�sÿìb!&�R��K��³'&�+5ÿÿ�sÿìbÝ&�R��Rñ��³"&�+5ÿÿ�sÿìbÓ&�R���j�� ´.&�+55��h�ü)¨����3@  �PY�/+�3/33/39333105!4632#"&4632#"&hÁý®;64:;34=;64:;34=ŠŠþè<=?:9@?ô<=?:9@?��sÿ¼b‡���#�K@) �� $%! FY !FY�?Æ+�?Æ9+�999339910�#"''7&�327&#"4'326bþòîšpTr^ îštTuaü½5ÑKr£¦—3þ/Gq£©%þôþÓEuNƒ˜� +LwL…˜ù«f†5ÖÔ¤dý}3Û�ÿÿ�¤ÿì9!&�X���CÄ��³&�+5ÿÿ�¤ÿì9!&�X���vq��³&�+5ÿÿ�¤ÿì9!&�X��K��³#&�+5ÿÿ�¤ÿì9Ó&�X���j!�� ´*&�+55ÿÿ�þ!&�\���v��³&�+5��°þu��"�>@ $#�   FY FY�?+�?+�99??993333106632#"'##3%"3 4&XBªj×ðñÖÞz ¦¦H¨˜šª/”´YOþÔþõþôþÓ¡"M?þ5�þ.4Z¸É)çǰ×Ñÿÿ�þÓ&�\���jµ�� ´+&�+55ÿÿ����´&�$��M�?R�³&�+5ÿÿ�^ÿìÍb&�D��Mõ��³(&�+5ÿÿ����7&�$��N�+R�³&�+5ÿÿ�^ÿìÍå&�D��Nä��³%&�+5ÿÿ��þB¼&�$���Q ��ÿÿ�^þB�Z&�D���Q��ÿÿ�}ÿìÏs&�&���vR�³ &�+5ÿÿ�sÿì‹!&�F���vD��³ &�+5ÿÿ�}ÿìÏs&�&��K�¬R�³%&�+5ÿÿ�sÿì‹!&�F��KÔ��³%&�+5ÿÿ�}ÿìÏ1&�&��OR�³ &�+5ÿÿ�sÿì‹ß&�F��OP���³ &�+5ÿÿ�}ÿìÏs&�&��L�ÁR�³"&�+5ÿÿ�sÿì¡!&�F��Ló��³"&�+5ÿÿ�É��Xs&�'��L�XR�³&�+5ÿÿ�sÿì&�G��8 ���²#��?5�ÿÿ�/��H¶�’����sÿìÓ��'�d@7%�()GY/  �  "FY FY�?+�?+�99?9/_^]3+�3?933333310%##"323&55!5!533##%26554&#"š så×ïðÖßw þ@À¦œœ‡þžª™›ª’›š“§&,¢SI…¸¸û%w¹Î#éÇãÏÒÖÿÿ�É��ø´&�(��M�R�³&�+5ÿÿ�sÿìb&�H��M ��³&�+5ÿÿ�É��ø7&�(��N�R�³ &�+5ÿÿ�sÿìå&�H��Nû��³&�+5ÿÿ�É��ø&�(��Oo5�³&�+5ÿÿ�sÿìß&�H��OT���³$&�+5ÿÿ�ÉþBø¶&�(���Qs��ÿÿ�sþa\&�H���Qf�ÿÿ�É��øs&�(��L�R�³&�+5ÿÿ�sÿì!&�H��Lû��³&&�+5ÿÿ�}ÿì=s&�*��K�éR�³*&�+5ÿÿ�'þ1!&�J��KÊ��³P&�+5ÿÿ�}ÿì=7&�*��N�R�³&�+5ÿÿ�'þ1å&�J��NÎ��³B&�+5ÿÿ�}ÿì=1&�*��OdR�³%&�+5ÿÿ�'þ1ß&�J��O���³K&�+5ÿÿ�}þ;=Ë&�*���9'��ÿÿ�'þ1!&�J��:D��³F&�+5ÿÿ�É��s&�+��K�–R�³&�+5ÿÿ�°��Dª&�K��K�‰�³%&�+5�����ç¶���T@,�   IY JY �?3?399//33+�33+9333333331053!533##!##55!ɪªÈȪüþªÉuüþ¾øøøøûϰýP1þŠéé����D��Y@2 �  FY GY   /   �� �?3?9///]3+�3+�3933333310!4&#"##5353!!36632žz‚®ž¦œœ¦Áþ? 1µtÉÉž†„ºÕýçÛººÄT8O[¿Òý\ÿÿÿâ��Ê/&�,��RþÚR�³&�+5ÿÿÿ��xÝ&�ó��Rþˆ���³ &�+5ÿÿ�*��‚´&�,��MþýR�³&�+5ÿÿÿÚ��2b&�ó��Mþ­���³&�+5ÿÿ���Š7&�,��NþùR�³ &�+5ÿÿÿÌ��8å&�ó��Nþ§���³&�+5ÿÿ�TþBV¶&�,���Qh�ÿÿ�5þBß&�L���Q�ÿÿ�T��V1&�,��O�PR�³&�+5��°��VH��@ ��??9310!#3V¦¦Hÿÿ�Tþ¶�&�,����-¨��ÿÿ�¢þlß�&�L����M��ÿÿÿ`þes&�-��Kþ·R�³&�+5ÿÿÿ‘þO!&7��Kþ¡���³&�+5ÿÿ�Éþ;é¶&�.���9�‰��ÿÿ�°þ;&�N���9+���°��F� �/@  � �?3?399333103##3/Ïþb»Éþ—‡²² FþýœøqþyFþå¦qÿÿ�É��øs&�/���vÿcR�³&�+5ÿÿ�£��,¬&�O���vÿ‹�³ &�+5ÿÿ�Éþ;ø¶&�/���91�ÿÿ�Yþ;W&�O���9þè��ÿÿ�É��ø·&�/��8ÿ£�² �?5�ÿÿ�°�� &�O��8+��²��?5�ÿÿ�É��ø¶&�/���Oýgÿÿ�°��¨�&�O���OBý8����ø¶� �=@! � �  �� IY��?+�?99//99333103'73%!ÉiC¬ª)Cþ”…ü;reýF®yÓþ<š�ÿü��'� �7@�    �  ��??99//9339333107#'73V‰HѦnF´¦`^pý?THqw �ÿÿ�É��?s&�1���vR�³&�+5ÿÿ�°��D!&�Q���vy��³&�+5ÿÿ�Éþ;?¶&�1���9�Í��ÿÿ�°þ;D\&�Q���9V�ÿÿ�É��?s&�1��L�¦R�³&�+5ÿÿ�°��D!&�Q��L��³ &�+5ÿÿ���˶�'�Q�‡��è��²�?5���Éþ?¶��8@  �IY�"�?+�??3999333310"'53265##33&53Éb6GSijüÀÀŸÁþ‘zoËþøžüÛ¶ûN•à=úXÃÌ��°þD\��8@ FY�FY��?+�??9?+933310"'53254&#"#336632%V7<>Œz‚¬ ¦‡ 4´nËÇŒþ‡¬y†„ºÖýÁH–RX¿Òüšªÿÿ�}ÿì¾´&�2��M�ÇR�³&�+5ÿÿ�sÿìbb&�R��M��³&�+5ÿÿ�}ÿì¾7&�2��N�ÁR�³&�+5ÿÿ�sÿìbå&�R��N��³&�+5ÿÿ�}ÿì¾s&�2��SR� ´+&�+55ÿÿ�sÿìb!&�R��SZ�� ´,&�+55��}ÿìçÍ���S@.�  !IY� IY  IY IY�IY��?+�?+�?+�?+�9/+933310!!# ��!2!!!!!"��327&çý�f\þ¹þŸ\@fZý³'ýÙMüDùþÿ÷pWW‰jh†—þ)–ýæþÏþÙþ×þÍ!u��qÿìZ��*�1�U@-%/%23+( (FY.FY .. ""FY��?3+�3?39/99++�393399310 '#"��326632!!26732654&#"%"!4&–þÛ}>щßþôëƒÍ>:À~Éîý'J^¡WX˜û!˜§£™›¥¦•G‘  „ëtw1 ,wrpyþ÷âiþw#'”' 9ÓÛÕÑÝÕØØ¤žž¤ÿÿ�É��Ïs&�5���v�yR�³&�+5ÿÿ�°��'!&�U���vÜ��³&�+5ÿÿ�Éþ;϶&�5���9}�ÿÿ�`þ;'\&�U���9þï��ÿÿ�É��Ïs&�5��L�R�³!&�+5ÿÿ�‚��'!&�U��Lÿv���³&�+5ÿÿ�jÿìs&�6���v�PR�³.&�+5ÿÿ�jÿìs!&�V���vê��³.&�+5ÿÿ�jÿìs&�6��KÿêR�³3&�+5ÿÿ�jÿìs!&�V��K—��³3&�+5ÿÿ�jþË&�6����z'��ÿÿ�jþs\&�V����z�Õ��ÿÿ�jÿìs&�6��LÿäR�³0&�+5ÿÿ�jÿìs!&�V��L™��³0&�+5ÿÿ�þ;Z¶&�7���9�ÿÿ�þ;¨F&�W���9‚�ÿÿ���Zs&�7��LÿÜR�³&�+5ÿÿ�ÿì×&�W��8b��²��?5�����Z¶��?@! �   JY IY�?+�3?9/3+�3933310!5!!!!#!5áþ1Hþ16þʪþÇ/ð——þý^¢��ÿì¨F��L@)  GY GY @�FY�?+�?Í9/3+�3+�39333310%27# 5#53#5773!!!!U< j*þÈF`>þÂ-þÓu\þ�PEêþÿ�ôÝ�ÿÿ�ºÿì/&�8��R�oR�³&�+5ÿÿ�¤ÿì9Ý&�X��R÷��³&�+5ÿÿ�ºÿì´&�8��M�‘R�³&�+5ÿÿ�¤ÿì9b&�X��M��³&�+5ÿÿ�ºÿì7&�8��N�‹R�³&�+5ÿÿ�¤ÿì9å&�X��N��³&�+5ÿÿ�ºÿì×&�8��P�œR� ´&�+55ÿÿ�¤ÿì9…&�X��P#�� ´&�+55ÿÿ�ºÿìs&�8��S�áR� ´%&�+55ÿÿ�¤ÿì9!&�X��Sh�� ´(&�+55ÿÿ�ºþB¶&�8���Q!��ÿÿ�¤þBeH&�X���Qô��ÿÿ���Ls&�:��KTR�³(&�+5ÿÿ���#!&�Z��K�Á���³+&�+5ÿÿ����{s&�<��KÿàR�³&�+5ÿÿ�þ!&�\��K­��³$&�+5ÿÿ����{%&�<���jÿñR� ´&�+55ÿÿ�R��?s&�=���v�BR�³&�+5ÿÿ�R��m!&�]���vè��³&�+5ÿÿ�R��?1&�=��ODR�³&�+5ÿÿ�R��mß&�]��O�ß���³&�+5ÿÿ�R��?s&�=��LÿíR�³&�+5ÿÿ�R��m!&�]��L†��³&�+5��°��Û� �@�  FY��??+39310!#!2&#"V¦g`d+WIaYœƒ%…{z���ÃþË� �D@$  !" FY �FY�FY��?+�?+�9/3+�3933310"'53265#5754632&#"!!HE@F=_MÞÞ¢¶Uxfy‰���çÙ¶!� ��@   € �/3Í29106673#%6673#ç$nº%«:ae1eº%«:`ò0ºE?Ä0D±:?Ä0��üÙs� �¶� € �/Í99106673#ü5 ¸m1döHãRJíL�ƒ´��� �+@  !"   �/3Ì99//393310673#'4632#"&%4632#"&�A½!y3På4&)17#&4´4&)17#&4…©†C³=4.4.21124.4.211ÿÿ���� &�$��Tþ ÿ—�²��?5�ÿÿ�˜L‰Z�y��ÿÿÿÔ��u �&�(}�TýØÿ—�²��?5�ÿÿÿÔ��µ �'�+�–��TýØÿ—�²��?5�ÿÿÿä��D �'�,�î��Týèÿ—�²��?5�ÿÿÿäÿì �&�2D�Týèÿ—�²��?5�ÿÿÿÔ��… �'�< ��TýØÿ—�² ��?5�ÿÿÿä��3 �&v?�Týèÿ—�²#��?5�ÿÿÿéÿì“´&†��UþÎ��� µ.&�+555ÿÿ����¼�$��ÿÿ�É��¾¶�%����É��ø¶��@�IY�??+99310!#øý{ª¶™úã¶�ÿÿ�'��m¶(��ÿÿ�É��ø¶�(��ÿÿ�R��?¶�=��ÿÿ�É��¶�+����}ÿì¾Í����?@   �IY�� IY IY�?+�?+�9/+99339910!!%�! ��! �32#"ãuý‹ÛþþÄþ½þ¡`D;bûsúôóø÷òõû3•?þ¡þn‹he‰þpþ þØþÌ0,*.þÎ�ÿÿ�T��V¶�,��ÿÿ�É��é¶�.�������Ó¶� �@ �  �?3?99910!#&'#3Ó¶þ¶W!Gþ¸¶± üZ‹Éü^¶ÿÿ�É��q¶�0��ÿÿ�É��?¶�1����H��%¶��� �4@  �IY��  IY IY�?+�?+�9/+910!!!!!5ÃçýR‹üu´ü#H–—ûy˜˜ÿÿ�}ÿì¾Í�2����É�� ¶��#@�� IY�?3?+993310!#!#! ªýªCúá¶�ÿÿ�É��h¶�3����J��\¶� �5@ �  � IY� IY��?+�?+�3933310355!!'!Jáþ+Ëý\`ÌþTo+™ýßýš˜�ÿÿ���Z¶�7��ÿÿ����{¶�<����jÿìøË��"�+�P@)' +,- **JY"$$JY��??99//3+�3+�339333333310332###5#"$54663332654&+"33Û¬F«û…•þý°)¬-°þþ’‡ü«C¬Éßι:¬9¶ÑÞÊË´ˆøŸ¦þý‚áá„¡žø‹üEÛùÒÔ·ÅÙ�ÿÿ���–¶�;����m��ò¶��>@ �� IY �??339/3+�393333310!##"&&5333332653##ƒª-°ÿ®ÏÔªÓϰþý¯-¾z÷¤ãþ!¼ÉdüœÆ»ãþ¥÷{���P��ôÍ��9@    !�IY  IY�?3+�333?+93310"!5!&5�! �!!5654!îú­´ý¶l— b:;bž—ký¶·©ù5þÿýáþ³„…˜v^Ë6`þ¥þÇÏþ¦x˜…†NÞüÿÿ�<��o%&�,���jÿR� ´!&�+55ÿÿ����{%&�<���jÿïR� ´&�+55ÿÿ�sÿìÇs&~��T��³4&�+5ÿÿ�Zÿì‡s&‚��TÈ��³/&�+5ÿÿ�°þDs&„��T;��³&�+5ÿÿ�¨ÿì“s&†��TþÄ���³&�+5ÿÿ�¤ÿìq´&’��U;�� µ4&�+555��sÿìÇ\� �*�G@$ '"+,'(( FY� �FY$ �?3+�3?+�993?9333310%26554&# "323673327#"&'#P©–˜©þÑ“…Öîôáy¡6 )T!.AQY ;§wÃÚåÇþPÔÔ‹) )TT\8BötþIr wQVVQ��°þ¨��)�L@('"*+#"FY## � FY �FY���?+�?+�99//+�?93333102#"&'#46"32654&##532654&“ÜùþÇyþøîm O¦ýäž]¡V«­¾±p\›¢œзþÚ3*þ‘Ñá&ýã4áöŒ¬¥ü‰1%–¤Ž“‰{…�� þH��!@ �??39/3910#4733>3´@+þ?¬ð^)+ê¬þk05þ`&r<ý¸ëgŽmûÓ|þÜ��qÿì`��*�;@ % �+,�"(FY FY��?+�?+�9933310&&54632&&#"�#"$544&'326!Œt¤g½~HpŸQUak§Ò±þðìãþðâa{ﲓ¢®¨NŸc‚˜-?‡>,OBGo[sñ¤ëþøøÒ±þs€·J5Ù «º���Zÿì‡\�%�M@+# &'%%FY%% %% !FY FY �?+�?+�9/_^]+�993310# 3267#"&54675&&54632&&#"!Ë”þÉ“’T¦d‰ÝÒñn‚bkàÀa¥d?^‚Oú=ÃZb'/”K©”bƒ) \…ž!-…*¢¬���sþo � �0@�!"#��FY��?+�33?9333105!�#654&'&&54>7!°ð×þàŠ;}¬•ˆ¦}o˼;pÉò(þñ‡´þ½þߦbvI%m[•¤¡k8=$ÛÂrÐÃåÚ���°þD\��/@�   FY  ��???9?+99333104&#"#336632žz‚¬ ¦‡3¸qÆÈþ±†„ºÖýÁH–QY¿ÒûI��sÿìJ+� ���I@'��FY¿  FY  FY�?+�?+�9/_^]+99333310#"322!"!Jôúðùõôôúþ¤œýy–§¡– … ˜ þjþv“—ˆþkûá13þÐþÌ)þáþç��¨ÿì“H��@ FY �?+�?993103267#"&5NIW%ei2 ‘Hüúhe  ¨© ÿÿ�°��F�ú���ÿòÿìF!�"�3@$��#FY� FY ��??+�93?+3910#'.#"5632327#"&'&'#Ù:2C1:9D?[yX6k*#!0=JSœT Xþ7¢UF$ …<‚˜ü 13 yLS´ð`tÑý¶�ÿÿ�°þDH�w�������H��@ � ��?2?9993103363#¬ÛS±Ÿ¦ÏáºHý²Cî>¯½Qþ•þá��qþo �1�I@'- (�%2300GY00&)%&%FY&�#�??+�39/+�99333310#"#6654&'&&54675&5467##5!#"33V²°Õ2_‡Tއ6Cœ5BsÈÇž€Ù‹¦€sDº3‚৯ªò²ŽPb=$nZA•cG“47="ȰŒÒ' @Ùuž2 ƒP_slÿÿ�sÿìb\�R����ÿìôH��6@    FY �FY�?+�??+�3393310%27#"5!##57!#}&0+TÛþ#¦ÝLÕ3uƒýÑüFºJDŽýާþ÷ÂÑŶºÐ���ÿç“H��,@� FY FY �?+�?+�39310!3267#"&5!57“þPÍ/b#o0µªþ×”HŽý–ß }ªªJD���¤ÿìqH��%@ � FY��?+�?3993310"&332654&'3�sç覞™§¡"¦$þþú Xý°ÀÃîû‚àˆÖŒþÂþÔ���sþL\��"�A@#   ��#$FY  FY��??3+�3??+93333310$�746324&#"66ƒþüþôσYQh¦•´Úˆø¥y|fIN³ÆþÚ #(ýZuà|þu#l»¾þÛú²þûþ&'¹Ûxrý’ì�ÿìþPN� �9@!"! FY�FY��?+�??+�?9391023327#"&'#&&#"56²6N>,‘>´þT¾0R?--<;s;–þ–²Ь&F+%1N+[pþaüüþzJvŸƒýhD¼cP ��¤þ‡��=@ �FY �??3+�3?3?933333106654&'3�#$�3Z¼Ë%¦?þãþð¤þøþö¦´¸úiçÌxë¨þðôþìþÎþ&Ú "ýÛÃÚ ™��sÿì¼H�'�=@ & ()&� � FY#��?2+�3?39/99339310"54733265332654'3#"'#ô¶Ë7D¬D9xk^i¡j]kx7E¬A9˶ÜD A(þœ™œÿÁØ}7þÉ€ŒØÁ—’þùüþÖ¶¶ÿÿ� ÿì“Ó&†���jþÔ��� ´%&�+55ÿÿ�¤ÿìqÓ&’���j9�� ´+&�+55ÿÿ�sÿìbs&�R��T!��³"&�+5ÿÿ�¤ÿìqs&’��T'��³&�+5ÿÿ�sÿì¼s&–��T�É���³1&�+5ÿÿ�É��ø%&�(���j�'R� ´!&�+55��ÿìB¶��F@& IYIY�IY��?+�??+�39/+933310"'5326554&#!#!5!!!2Ï`67[ehƒŒþƒªþ°·þCŒÍÝÄ–|pƒ€qý——þ^¿²¾Óÿÿ�É��øs&a���v�ZR�³&�+5��}ÿìãÍ��8@ IY�IY IY�?+�?+�9/+93310"!!�327# ��!2&BâþóÓý)  ù¢É¡âþ´þ¢yNí²G©3úñ–þîþã7•9„m_‘X”Rÿÿ�jÿìË�6��ÿÿ�T��V¶�,��ÿÿ�<��o%&�,���jÿR� ´!&�+55ÿÿÿ`þh¶�-�����ÿé#¶��#�G@&�� $%#IY IY JY JY�?+�?+�?+�9/+933310!!!#"'532>!3 32654&###þíþüþ¹þ“9TP‹kE@2?0A+7DA¦z:ýL…Æ·ÀÜfªÎÜþHýöûy>gú¾âýýM‹ŒŠ|��É��T¶���J@&  �� IY  JY�?+�??39/3+�3933333310!!!#3!33 32654&##Tþðþûþ·ý}ªªƒ¬y9ýN…ĹÁÛfªÎܰýP¶ý’nýýM‹Œ‰}�����B¶��:@�  IY� IY��  �?3?9/++�3933310!2#4&#!#!5!! ÍÙª}Œþ}ªþ°öþ}¼µýôö~qý——ÿÿ�É��ås&´���v�¢R�³&�+5ÿÿ�ÿìø^&½��6�DR�³&�+5��Éþƒ ¶� �0@ �� IY"�??3+�?3933310!!#!3!3 þ/°þ>ªïªþƒ}¶úä�ÿÿ����¼�$����É��}¶� ��=@ � � IY IYJY�?+�?+�9/+933310!!!!3232654&##}þýþûþT^ýLãÁòtüöï¾­°ÛϪÚж—þ'Y®þT‚•Žx�ÿÿ�É��¾¶�%��ÿÿ�É��ø¶a����þƒJ¶� ��C@$  ��  IY " IY�?+�33?3?+93333310#!#3!3!!J¢ü¢qšÛ ‘¹þþ³Ήþƒ}þƒæ3úäƒòýYê�ÿÿ�É��ø¶�(������¼¶��<@  � �� �?33?33933333933310333###VýÁ¾9¤:¾ýÀRÄýº¤ý»ÇðÆý<Äý<Äý<ýåýåý��Jÿì5Ë�(�C@$��# )*JY &&JY& JY �?+�?+�9/+�993310!"'532654&##532654&#"'6632·¡·½þÎþéÿ£`ßgÆËáßÚÑÍᢉn²uTeû‡áÿ`´´‘ÍåOž.2–†Š“„k€2JrKMÅ��Ë��R¶��4@   ��?2?3993399333310333#47##ËŸ4º  ü˺¶üÓá¶ÄúJ%ÉÝû5�ÿÿ�Ë��R^&²��6�áR�³&�+5��É��å¶� �-@�  �?3?399393310!##33åÎý\ªª“Ãýyåý¶ý<Äý:���ÿçÙ¶��-@�� IY JY�??+�?+93310!#!'"'53266!Ùªþ%=]˜~J;6;5O=]8ðþ!þE®W×Y¸ÿÿ�É��q¶�0��ÿÿ�É��¶�+��ÿÿ�}ÿì¾Í�2��ÿÿ�É�� ¶n��ÿÿ�É��h¶�3��ÿÿ�}ÿìÏË�&��ÿÿ���Z¶�7����ÿìø¶��*@   � �IY��?+�?3993910"'5326733673%oT]`n…BýǼ° g´þ-T‡©¦+e‹AüÁ1/T5û껪Oÿÿ�jÿìøËs��ÿÿ���–¶�;����Éþƒ¸¶� �2@ �� �IY"�??+�3?3933310%3#!3!3 ¬¡û²ª漢ýé}¶úä���ª��Ƕ��-@ ��IY �??39/+9933310!##"&5332673Ǫ•ÆjÏߪa±©ª\5'¾³EýÏyt7Ê��É��y¶� �1@ �� IY�?+�3?33933310!!3!3!3yùPªXªX¬¶úäúä��Éþƒ¶��;@�  �"  �IY��?+�33?33?933331033!3!33#ɪG¬Hª¬¢¶úäúäúäýé}�����¶� ��=@ �� IY IY JY�?+�?+�9/+933310#!!5!3 32654&##þýùþGþ°úôüõüµ©¯ËàªÎÜ—ýÍþ‹Œˆ~���É�� ¶� ���?@  ��IY�� JY��?+�?39/+�?9333310333 #%32654&###3ɪïþýùþö÷µª³ÈÛ—ªª¶ýÍÏÎÜ‘Œ‰{ýR¶��É��º¶� ��2@ ��IY JY�?+�?9/+9933310#!3! ! 4&#!ºþñûþª# ü¹+l»ÎþòªËß¶ýÓþ ‡��=ÿì‰Ë��:@ IY  IY �IY�?+�?+�9/+93310"'632�!"'53 �!5!&�Ó¬¢H¬ìÙ9¢þ”þªãœS¬cý1Íþñ3LT°þºÝþˆþl9•"!˜å��ÉÿìçÍ���G@&   ��  IY IY  IY�?+�??9/+�?+93333310�! �!#3!�! �32#"çþ«þÐþÓþ« þžªªdQ3Vû îçêíëèéðÝþžþqoUýP¶ý’7Nþoþ¡þØþÌ2**.þÏ��3��N¶� ��=@   �JY �� JY  �?3?+�9/9+933310#&&54$!!##"!3{þÉš¡’’ªã·¾{Ýbýž3ÏžÄÓúJbÁ~ŽþÝÿÿ�^ÿìÍZ�D����wÿìT!��"�;@ ��$# FY FY�??+�9/9+�39333107$736632�#"� !"wÔæÚþ¥•‘‘ >ÄkÊâþúêçþúü1þëLu ¦‘h“2=&’:"!öÔT`þúèþÿþßb×…s?h7þùþí��°��LH����I@& ��  !FY FY  FY �?+�?+�9/+�99333310#!! 4&#!! 4&#!!26){oŒáØþᘃ‡œþÓ1{}þÇš~5ko ~o™¦HýYQþ—šPCþËL���°��DH��@�FY�??+99310!#!Dþ¦”ºüFH��)þ…hH� ��C@$  ��  GY " FY�?+�33?3?+93333310#!#36!3!#h¡ý V†˜+þÃö ‘lþ…{þ… ¶êüG6Þþ9‘�ÿÿ�sÿì\�H������ßF��<@      �  �?33?3393333393331033###3¤™Ŷþ6ñÀþ™þ¿ðþ7¶ÃFýíýíýÍ+ýÕ+ýÕ3ýí��Dÿì\�"�M@+  !#$"!"!FY"" "" FY FY �?+�?+�9/_^]+�993310 54#"'632#"'532654!#57üM~f;ªÉ½ÚÍ~tõØí·»“þɘ¬¢*‡L›†¸9%‰g˜©G˜Vc]¿��°��bH� �4@      �????999933333103#77#LQÏ›ý°ÏHýI¶9¦û¸ž„‚ü\H�ÿÿ�°��b &Ò��6=��³&�+5��°�� H� �-@    ��?3?3993933103##3/¶þ'�Âþ ¦¦HýïýÉ+ýÕHýë��ÿòáH��-@� �FY GY�??+�?+93310!#!#"'532!á¨þ·`™v6 sˆ#ºþœþ^ {æï��°��/F��5@�  �??3?399399333310%773##&'#3é+)Ó“:þå‹þå5”Ë+ ]vÓûº‰:™ýJ¸†KüwFýIn��°��bH� �9@  FY/?   �?3?39/]+99333310!3#!#Vf¦¦ýš¦Hþ5Ëû¸îþHÿÿ�sÿìb\�R����°��HH��#@� FY�?3?+993310!#!#!V¦˜¨ý¶Hû¸¸�ÿÿ�°þu\�S��ÿÿ�sÿì‹\�F����)��“H��$@� FY�??+�39310!#!5!“þœ¦þ jºüFºŽÿÿ�þH�\����qþF����L@'  ��  � FY FY�??3+�3?3+�3?9333333310�#&�54�73�%66Fþåþ¤øþàÿžûûÙ°À¹·{þ“¾¯%ùþÙþ$Ü.ôù&¼þDþÔðÀÚTÏÈ'ü®Úÿÿ�'��H�[����°þ…ÝH� �2@ ��   FY"�??+�3?3933310#!3!33ݦüy¦F¦›þ…{HüG¹üG��œ��-H��-@  FY  �??39/+993331032673##"&5BÛ[¦i¦¦i³q¤ºHþpÀ8CÕû¸ðH;¬“œ��°��oH� �1@�   �FY�?+�3?33933310%!3!3!3áæ¨úA¦å¦¹û¸HüG¹���°þ‡ F��;@ �   � FY "�??+�33?339333310%!33#!3!3á榨úN¦å¦·üIýøyFüI·���)��H� ��=@ �  �FY�� FY FY�?+�?+�9/+933310!2#!!5!4&#!! -9à×ßÜþ%þ¢L|þÍ9ƒš›¦¨ºŽüü]Sþ—���°��yH� ���?@ �  �FY�� FY�?+�?39/+�?9333310!2#!3#3! 54&#V+ÑÉÕÏþ9¦#¦¦ûÝz“ƒ›š¥©Hû¸Hý¬þ—¹\T��°��LH� ��2@� � FY�� FY�?+�?9/+9933310! #!3!2654&#VR¤ÛÓþ¦@„Œ”ƒþË¢¬Hý¬þ—\][U��9ÿì}\��D@&   FY    �FY�FY��?+�?+�9/_^]+93310"'53267!5!&&#"'663 ��V§v<Œ[®½ ýÕ)©¡g—/7¤P� þß9“$º¹¬ 6Œ#þÛþìþóþÖ��°ÿì3\���Q@-   ��  FY FY     FY�?+�??9/_^]+�?+93333310�#"'!#3!6632�32654&#"3þÿàÕúþᦦ!üÏÜüî’¡ž•’¡¡’%þóþÔ ÷þHþ5äûþÏþúÓÛÕÙÒØØ��%��ÁH� ��=@    FY FY �?3?+�9/9+9333103#&&5463!#!!!!"çÂ;‡Êµè¦þëþö þÓòÏ¡z–¬û¸¶N¾rÿÿ�sÿìÓ&�H���j�� ´0&�+55��þD�'�f@:%%()! FYGY/ !!��FY��?+�??9///_^]3+�3+�3933333310"'53254&#"##5353!!36632/O4:7z‚­¨œœ¦‘þo 1µtÉɉþ‰ªR†„¼ÓýçÛººÄT8O[¿Òü¶œªÿÿ�°��D!&Í���vñ��³&�+5��sÿìª\��D@& FY � FY�FY��?+�?+�9/_^]+93310"��32&#"!!327yøþòûRž91m¤ª)ýÕ ª§Œ—t#* 3£©¾µ;“9ÿÿ�jÿìs\�V��ÿÿ�¢��fß�L��ÿÿÿì��Ó&�ó���jþ·��� ´&�+55ÿÿÿ‘þfß�M����ÿòBH���L@) ��FY��  FY GY FY�?+�?+�?+�9/+933331032!!!#"'532!4&##3 °ôÓËþKþeþþ(µ«8 sˆ#Pì}žç탛šþ²ºýúþ> {æïüü[Uþ—��°��¤F���J@&    FY  FY�?+�??39/3+�3933333310!2!!!#3!3 54&#��ÙËþNþ`þ ¬¬ú¦ð€™Fþ;™šþ²îþFþ7Éý®þ—¹\T�ÿÿ���D�é��ÿÿ�°�� !&Ô���v3��³&�+5ÿÿ�þ &�\��6·��³&�+5��°þ‡FF� �2@   " FY�?+�3?3?933310!!3!3!#/þ¦J¦þ¦FüI·ûºþ‡���É��ã��#@� IY�??Æ+9933103!#f¢ýkª¶-þ:úã¶���°��D‰��'@�� GY�??+�Æ993310!#!3Dþ¦î¦Çü9HA�ÿÿ���Ls&�:���CR�³&�+5ÿÿ���#!&�Z���Cs��³&�+5ÿÿ���Ls&�:���v°R�³#&�+5ÿÿ���#!&�Z���v���³&&�+5ÿÿ���L%&�:���jdR� ´/&�+55ÿÿ���#Ó&�Z���j�Ï��� ´2&�+55ÿÿ����{s&�<���Cÿ”R�³ &�+5ÿÿ�þ!&�\���Cÿa���³&�+5��RÙ®q��µ���/399105!R\Ù˜˜��RÙ®q��µ���/399105!R\Ù˜˜ÿÿ�RÙ®q���ÿüþ1NÿÓ���@ � �/3/3333210!5!5!5!Nü®Rü®Rþ1‹Œ‹���ÁD¶��¶ ��?Í9910'673% b8{B%ÁZ yþ÷���ÁD¶��¶ �?Æ9910#75b5zF ¶dþ÷rØ�ÿÿ�?þøm�î�����ÁF¶��¶ �?Í9910#&'7ß%B{-m¶ûú^e���Á´¶���@  � �?3Í2910'63!'673–8z{; ý× b8{B%Á×sþßaZ yþ÷���Á´¶���@  �?3Æ2910#7!#675b5zF '`8}B ¶dþ÷rØ[þözd4]�ÿÿ�þù´�î ��û8� ·�@ H¸ÿÀ³ H¸ÿÀ³ H�+++55��{��‰� �C@!  � � � �?.333?9/333933333310%#53%‰þ 1Ä1þ´L1Ä1`çûúª¡þ_��{��š��u@:  � �       ��??99//9922333333333393333333333333310%%#553%%9aþŸ1Æ1þ¦Z++þ¦Z1Æ1aþŸ+ç¨þ…{¨+¨|þ„¨þå��¤ô^ã� �¶�� �/Í93104632#"&¤qlitsjkrìy~|{wƒ�ÿÿ�˜ÿã®�ò�&����'�����%����dÿì ;Ë� ���$�/�;�F�[@0� 0B6<+%%+<B GH33(? "99-D D D�?3??99//3333?33393333331032#"#"&5!2%#32654&#"#"&5!232654&#"#"&5!2ìS]´´]Sí¡œ•£8˜¥iüÕ”+ S][YY[]Sí¢›”£7–§û8Q][YY[]Q뢛•£8–§ªªTR¨ªæçîßÉðÛúJ¶ü«©§­«¥¥«ææïÝÉìÝ«©§­«¥¥«ææîÞÉì�ÿÿ�…¦?¶� ��ÿÿ�…¦°¶������R�u¾��@ �//993310RVwþß!wþª'—Eþ¢þ¡G—��P�u¾��@ ���//993310'7þ¨uþáuX þiG_^Eþi�ÿÿ�˜ÿãJ¶�&�����Á���þy��¶��·��??3310#üy‡¶úJ¶��m!ÃÇ��&@�   � �?Í2?399333104&#"#3363 LNPr[t` K‘!¤TGizþ¤™XeúþT��b��#¶��K@(�  NY�LY LY �??+�99//+�3+�39333310!!##53!!!!¸4þ̦°°ý•Dý¼‹þö +—ýé—��D��HÉ�%�p@@ "  &'NY  ! NY !!!?!O! !!�LY�KY��?+�?+�99//_^]3+�33+�39333333102&#"!!!!!!5655#535#53546°Éž<˜“z~¤þ\¤þ\AJûüÎÈÈÈÈàÉPƒG‡º¦!dˆ,š0ó#¦Ï²Í���šÿìѶ��!�*�`@7"&   +,"KYNY  *KY�MY�?+�??+�9/////++933333310%267#"&5#57733#!##! 32654&##N"V bÝÝ4þ‘þëþö@¥�þý¡4ȹ¬·Ru}ˆŠÏPE¿ÓþGMR—ãêýÁ¶Óýî‘¢‘Ž���?ÿì‰Ë�&�q@? $ '( NYNY/ ""�LY"LY�?+�?+�99//_^]3+�33+�3933333310 !!!!327#"�#53'57#53�32&þÁOþýôÏþA%˪œ™’«íþß.¦˜˜¤'$íÉ¥G¦5þm9@-´ÅB–A *,P$a‹V��ÿø Á����+�E@$% * � *,-# '  �??99//33?3?39333310##"&546323254#"%"&54632&#"327üÕ”+©”‹ª§”ªþ²°°²ýʦ¶¼«hX!QPàÜbZN¶úJ¶û˜Ÿ·¹ž¸ºœîîëÛ±¡¨³#gîë!e%��wÿìœË��$�=@#  %&#     ��/3/399//99333310%273#"&5556746324#"$}®_™Ž– ``Nr–‡u‡Î¯R®C>�oÕ¦²µ©ó#q&òŠŸ¡Š¹þÐJþåh{+ÂVlþK‰���É��ö���'�+�_@1  �"+(�,-% ((()JY(�?3?3?+�99//9933933333310!###33&53#"&5463232654&#"5!Ç»ýL—ª˜ü¡“‹¢¡“‹¢þ"Q][OO[\RV�ËþàlüÁ¶û:õŠGü·£¸» £µ»rvussppý ‡‡��%å…¶���O@'�    �?Ä229/33333333393333310##5!###33#7#q{ÑÓXÉw»ÄË´Óågjjý™/þRÑýÑ/ý/¤‰ýÓ�ÿÿ�P��ôÍv����fÿÝ‹H���4@  ! /?��/2/39/]93933310"&546632!3267&&#"yñ…Šô•˜ó‡üÅ1¦Rƒ·QHbÙ“2£X­z#“«ÿŒŽþý¥þœ5Fi)›|‹5Buþéÿÿ�Gÿìó¶�'\���&�{û�@`ý³� ´�?555�ÿÿ� ÿìÉ�'¢���'@uý³�uÿ�� ´�?555�ÿÿ�Gÿì¶�'œ���&= �@qý³� ´,�?555�ÿÿ�jÿì�¶�'F���'@mý³?1�� ´�?555���fÿì5Ç��(�A@"&��)* "GY FYFY�?+�?+�9/9+933310#"&546327!"56632267&&#"5§þì­¬»ˆè—a’+þæ>0/›JÒØý¢_¦x€Pe¥ee¦þúþ5éÉÀ©3¡]KZ•,!Ÿ%þìûÆ–al„ú€v‚��'��m¶�� �(@    IY�??+�999331073!!&'ϦÑûº!=(þüÑþþDhNú°fôáyüþùÊ���Éþ!¶��#@� IY��?3?+993310!#!wüüªXþ øó¦øZ��Lþݶ� �1@ � � IY� IY��?+�?+933331055!!!Lwý™@ü°Cý¤ªþkœ3l—üüü˜��h)��@ ��PY�/+99105!hÁŠŠ��%ÿò¼˜��@   �//9/933310##5!3oþé´!뉇ýT½���w“- ��!�-�3@ +��% ./" ( �/333/3993393310#"&'#"&54632632267&&#""32654&-§€]™A<™Xƒ¨¨ƒµz|¹…¢ü}Bm62mHLda¡Bm73nGLdeσ¹jthq­Ž†³Ûׯþ»[da]iWSjy\ba^kTUi�� þø��@   ��/2/393102&#"#"'5325}O,1>°¥£J;=:¶‰óúá°»‡ój���b‡-��/�p@@(10'PY/ *@*$PY*@�PY/ @ PY� �/]Ä+�Í_^]+�ÄÞÄ+�Í_^]+�Ä993310"56323267#"&'&&"56323267#"&'&&P69l”CpXM[-5€6e™CoXI[19€5j–EtRE_173dšEvOTU�@9–n%!B9—m%–D5•m "B7–n !"���h�¦)��F@& � PY  PY�/3Ä+�3/_^]Æ3+�393310!5!!5!!!!!'}þëTþ-‡}mþª×ýéƒ}Á‰‰9æ‰þð‰þå7ÿÿ�h�)Ù&���+��ýt� ³��?55�ÿÿ�h�)Ù&�!��+��ýt� ³��?55���o��=Ã�� � @ �� �//999933103# oÂHÄþh{Œ›ƒ"&SYNX) hæ ���)9¢Ç��#�6@! ��%$ �!�?3?39/]93933310632&#"36632#"&2654&#")ÛÛJ14S– qU}”¦™­DQcXVUpjÃÿr™¦+;”~¤Òc]cO[Z;Y|���9J¶��@ �� �??39310!5!¢^þ9Vþ Jøt^üò��39“Ç��"�-�?@" &+ ./ )))) !#��?2?39/]39993333102#"&5467&&54632654&''"654&d|—”°¥Š’ŸIUJ95TVZT]QHF¬DKDQŒNÇvh‚LJžq‰€tEt..]Df~ýfar���TþÁî�� �����#�'�+�/�3�7�;�?�C�G�S�[�k�t�|�‰�ø@‡A@=<10� TNXHvkp`zg…†ED)(%$ †;g`87/k4,H# N Š‹ �*BZQ†\t\)AF>duulE=‚}VKkvk&2%1 �BA>\=l 12k \lkkl\-, 9854! �/333333333/3333333339///9999993333Ä2Ä2339333ÄÄ233933333333333333333333333310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3#"&546323254#"%32##32654&##32654#"'53253T/ÀÎ0mù�oÀÃmýIûáþò·mmmmûÂü0ooÀwú¨ooooþmmûŸ‡‡‡~ˆþs‡‡‡‡á¬mp.,=.m^Ï{B.$*/;J1%Z^4+V}i¾0oÁÁoþÐÁù/ÂmmÂþÑmmmmþooú¨ú;mm¦Jooooü/yýhI‘œœ‘’›š“ÅÅÄaCS1BD5QYb" "ãš+%Jþú fV’þr_c���TþÁª���*�.@ % +,(""��//9///33910 54676654&#"63232654&#"þ¬üTüVë,AgI»¥OºGR Z?>1HT;GFBIHCHEüVüW©û/2A1R~X‡š8*²P:/5K6DpJ;þí?HI>@IHÿÿÿ‘þW!&7��Lþ©���³&�+5ÿÿ�ÁD¶���� ÿìß+�-�6�f@9 4%.+-% 78GY�!.!GY+... ..((1FY(FY�?+�?+�99//_^]3+�3+933333310�! 47654&#"'6323 4'&$&54632�3%&#"Vþàþýþw$ 6!S_X]éwßþÉ ¶¨Ð�*þÇ·{]aN.AþŸþnX9{z/# v']]#ƒ„:Ïp?,i¼ƒ£þÍþ×Ó�_Kš�����{Ã��(@� JY�?+�??993106632&#"#39zM\:0((;V|e¬þ#ºÍ#7l0‡8¡üìUýã/‡���ÿìwH��)�L@'!!' *+� FY$�FY��?2+�3?+�339/99339310"&54!57!##"'#32655332654')ºÇ‡þãŽ×úuȹÝDDþÏ?Blu]l¢k]umoçððJDŽüûðç¶¶΄þþg®¨}¼¼z’©­þï�ÿÿ�É��qu&�0���vœT�³&�+5ÿÿ�°��Ë!&�P���vÍ���³-&�+5ÿÿ��ýÕ¼&�$���[5��ÿÿ�^ýÕÍZ&�D���[�Ç��ÿÿþßÿìÒÍ�&�2�\þG��� ³�?55���uýÕ5ÿƒ� ��@  �� �/3Ì2993310#"&546324&#"3265}fexxee~nB33B<95@þ®axubbuva9<<98==��˜hÏÅ���@    �/ÄÜÆ9310673#%47#"&°F½)w1Nþèíy%]7C‡µzN¬9v£=H)5JD�ÿÿ���Ó�'�I°���&�I����Lm��ÿÿ���Ã�'�I°���&�I����Om����}ÿìd��!�<@�� "#  IY IY�?+�?Æ+�999333310�! ��! >5332#"¼þþÆþ½þ¡aCE³2:¶ƒh`ûuúôóöõòóýÝþžþq‰jh†× Cfi›­'°þþþÖþÎ1+'1þÑ���sÿìð��"�<@�� #$  FY FY�?+�?Æ+�999333310�#"&5�32>5332654&#"bþòî“ä| îÙ‰3:´yfGü½ž­¯Ÿ¯­œ%þôþÓŠ­ +Acnœ¯&йÓÛÛÓÒØØ��ºÿì{��3@   IY�?+�?Æ39999333310>53�! �533265:Fµ!¬•þáþøþôþÔªÌÆ¸Á¶Æ>pn¶¸ýþþþêý®üF·ÄÁ¼¸���¤ÿì–ò��D@"    FY�??+�?3Æ939933333310326536653#'##"&5Lz‚¬Ÿ¦RJ² °‰ 4µoËÈFý;†„¼Õ>y €šº¿ü¬“RU¾ÑË�ÿÿüSÙýÜ!��CúÊ��ÿÿý Ùþ–!��vû„��ÿÿüÙÿÝ�Rû���ý¸þs��@  �� �/Ì23339310#'6654&#"563 þs¦ i VNCI> &E�׌"q°2++)d �ý;þ þÿ}� �µ�� �/Í33104632#"&ý;;*(::(*;ò9669777�ÿÿ�É��øs&�(���CÿØR�³ &�+5ÿÿ�Ë��Rs&²���C�hR�³&�+5ÿÿ�sÿì!&�H���C·��³&�+5ÿÿ�°��b!&Ò���CÜ��³&�+5��…ÿì‘É�1�E@$"*'/ '23�IY((,%%IY �?3+�3?39/9+�3933310"'632��#"&'## �32&&#"327332¤<^-E~–äþåÿl¬SP©kÿ�þåÿä™|F-]<“¥Ï»‹fªfŽ»Î¥/)’Pþˆþ­þþa-32.›wSxP’)þ×þöþÓþ²LÉþ7LK0 (�����H��(@�  � �??339?910#33663�363#&' ³Õþ¬ö .Jެ² - ­™¦ÃÛ¶}!É3þ„HýI]½5£$Õüÿ,¸³Rþ–þåZ\����ü���L@( ��IY�IY���JY�?+�?99//+�3+�393333310!3!!3 !!!32654&##?¬¢þ^É1þ÷þûþhþÁëÕÀµºÚ¶úþæ”þàþdÐÚfü+‰Šz�����œ'���G@&�    FYFY � FY �?+�?3Æ9/++�393333310!!! #!#5353! 54&#¨Xþ¨?µßÜþ!ëë¦1‡œHŒþÅþͦ¨¼ŒßüÍþ—¹\T��Éÿì!Ë� �J@) !"�IYIY IY�?+�??9/3+�3?+933310"!!�327# �!#3!�%2&&ãþü¿ý= ÷šÂ˜ÞþÁþ¥þ¢ªªdq0Õ¶Hd3úñ–þïþâ7•9pTýP¶ý’3N\’0&���°ÿìœ\�!�Y@2   "# FY  FY    �FY��?+�??9/_^]3+�3?+93333310"�'!#3!6$32&#"!!3267wëþô þᦦ! ßQš62Še£§ýæ ©¤=wbn øþHþ3ëö 3¤ª¼µ%“9�����m¶� ��4@   IY  �?33?9/9+�39310####3#!'&'˜”œ•þß²hžg·ý\LR8@ªýVªýV¶úJ?Ïdb¤��� ��yH� ��5@   FY  �?33?9/9+�39310#####!&&'#¨ѬÏq—sͬÑ!+8" Hû¸éþéþHþ-lŠj\���É��^¶���F@%    IY    �?333?39/93+�33933310####!#3!3#!&'…š“þãº"þ_ªªážf¼ýf>v #°ýP°ýP°ýP¶ý’núJH5V/Ch��°��H���M@+    FY/? �?333??9/]93+�33933310#####!#3!#!FΪÐq˜nѬÑþߦ¦^Åh Y Hû¸îþîþîþHþ3Ís"_Ù�����®¶��"�K@( !$#!!IYJY"�?33?9/33+�3+�99933310#.####"#>75!)þZvšd2…®‰#DeYª[cA ‡¹ˆ/c•vþe¾ý {¶…þH‹¤þ;Éo`&ýB¾'_oþ7ÅŸŽIï…™þ9��� ��H� �#�N@*!"%$" "FYGY# �?33?9/33+�33+�99933310#.####"#>75!‹þ®WoI1›¬…":TL ™ KR8'‡ªƒ0InWþ± ý´%Hiþ 0PiþqPWGýö @^þ®P=iO2`iŒþÁ���É��Ŷ�$�'�a@5!&#'%'"# )(#$&$&IY!IY'!!$�?333??9/33+�33+�99933333310#.####"#67!#3!5!=þ]x™e-ˆ¨ŠFi_¬^dB!‡²‡78þRªª×þhÁý {¶…þHœþ;Éhc(ýD¼(_lþ7¾¸:ýP¶ý’é…™þ7���°��ºH�$�'�g@:!&#'%'"# )(#$&$&FY!FY'/!?!!!$�?333??9/]33+�33+�99933333310#.####"#67!#3!5!1þ®XoI0›¬…":VJ š KT7&‡ªƒ/%þͦ¦5þ°!ý´%Hiþž1NiþrPVFýø?\þ®Px(þHþ5biŒþÇ��?þN5Ñ�K�„@M�!?FF ?7C<*-( LMIJYI941../. .*@CJY<**$JY* IY IY# IY "�?+�?+�Æ+�?+�39/+�9Ý_^]9Ä2?+933310327632&#"#"&5467665!#532654&#"'67&''536632&#"ðWYaxxF›GP Diii³¸Ùè̵þ@ÚÑÍᢉj»nV¨¾9u1{\ƒ\ƒ@20+,o0²Á¿ªºËþåþ抆‰72'¦3}…~ Š “„k€7ErrBy4;ˆsVq RG½Œ¸²ÐÕ 7���þ{N�F�ƒ@N)6 . >2@<) GHD>A�GY�AA/A A>&FY#FY3232FY!#& >>8FY> ",GY�?+�??3+�9///+�9++�Ô_^]Ä+�99333102&#"32772&&##"&5467$54&##53 54#"'67&'5366ø3-)/g-zŒÓøòá]m0KYVz¯}'T7³‚\Ÿ¾´NœŸ”w7üJX;|~\g{KŒX†Np O>Šk¸9GÊ”¨*,1+'wpt}¾aZ¬¢"$‡7ub4‰nUÿÿ�m��ò¶u��ÿÿ�¤þ‡•����}ÿì¾Í� ���G@%��IY  IY  IY�?+�?+�9/_^]+99333310�! ��! �2!"!&¾þþÄþ½þ¡`D;býaå÷ ü+ ùèàûÓôÝþ¡þn‹he‰þpüD þõþî´þþÿ�þ���sÿìb\� ���I@'��FY  FY  FY�?+�?+�9/_^]+99333310�#"&5�32�267!"!&&bþòî“ä| îæþž¤ ýi   œž “¡%þôþÓŠ­ +þÎýM¸¿º½X­§¨¬������HÃ�� @�JY �??9?+3910"#367>32&á;N9þ¸Åýî´RH# F¢;TnY*O87gµûå¶üVÇß¿˜A�����=R��@ GY ��??9?+3910!336>32&#"–þj®ádR`%G[T-&/:øHý›þôdv 5z{4 T\üßÿÿ����Hs&€��v×R� ´!&�+55ÿÿ����=!&��vd��� ´"&�+55��}þ ¢Í� ��.�D@& �!.'�/0%*JY%  IY IY�?+�?+�?393?+93310�! ��! �32#"%33663#"'532677Tþ¹þÜþ×þ½C,#EûÝßÙÚÝÜØÚáo°öN Sä°þ+E¼ˆLJ7B^u#=Ýþ þo‹hfˆþpþ þ×þÍ1+)/þÒAý‹Ïf,ûƒû ¶ž… gYœÿÿ�sþ{\�&�R����\u����}ÿ‡-��(�Q@* & ""�� )*$"& &IY IY�?33+�33?33+�339333333310�#"'$��%632�6632654'#"'þÑþøw|þôþÑ+|y -û!ʽI6n½Êʽnq½ÊÝþÒþs,oo)Š61…,ll,þsþÕôþÏ)0&V)1ôô/'XV'þÓ���sÿ“Ï´��-�P@*  +%#��  ./(%++FY  FY �?33+�33?33+�33933333310#"&'&54766326632665%#"&'ÏàÌ @89= ËåàÐ>98@ ÊâüP}‰ <5g†|þü =35< ‰}%éþß%6-+8$&åé $8*+9&þÜá±Ò*"JÒ¯`>* ,Ñ���}ÿì;��E�T�U@.C7++&FKPH< 7 UV R@H:"@:@IY(:4IY.4�?3+�3?3+�3ÖÜÔÍ29/393310#".#"#54632326732#"'632��!"&'# ��32&&#"5654.5432¢TŽxf+/<}tp:pw…Ný(X«=7«]¼Ò¥“<_+FyšäþàþýhªLK§nþüþãäšyF+^<”¥Ò€íx$\8CÇy$+$43gn$,$øºB?9HN- (+’Rþˆþ­þŒþb(0-+uUvR’+þÙþôþÑþ´h¢=H)5ID���sÿì�*�?�N�\@3((,"@E JB6 OP2:?--6LB @ FYFY% FY��?3+�3?39++�3ÞÜÔ23Í293310"'#"32&#"!273 4&#"'6632#".#"#5463235654.5432+”^\áúϺ>w(9YGtm1{p>oC-nsGY9(w>»Î÷QTxe+k}sp:qvƒNþðîw$\8CAA#( ‹3ÖÖþ^P*&¢ÖÖ3‹ þ×þêþõþÚ¥x$*$fdo%+%Ý¡>H(8JD���^ÿì� �@�_@40$96>6) $AB-'-IY77!' @ H  @';3!3IY!�?3+�3?3Þ22Í+239/9+�3933310#'##'##'5"'632�!"&'## ��32&&#"3267332‹P 2º1!1¼/!PC<]-F|™äÿþâþýt¬L N¬pþüþãå–~F-]<“¥Ò¾A‚3ªf‘¼Ô¥¬gggg¬þ+)’Pþˆþ­þ‹þc001/ rUvP’)þ×þöþÑþ´&&Éþ7LJ1 (������¤� �*�?@$$ +,( @ H  #�??33Þ22Í+239?910#'##'##'5#33663�363#&¶R2¼11¼2P¬'ªÕþ¬ö') #º¬² - ­™¦ÃÛ¶}!¤¬gggg¬ü%_þ–HýIo«#QˆÕüÿ,¸³Rþ–þåZ\���}þãË��-@  �IY IY  �??+�?+93310"��!27## �4$32&Hõþà o9ªþµþŸ¯HØíªG«3þÀþèþÚþÔýtØ„màV¸T’N��sþ¢\��/@  FY�FY��?+�?+�?93310"��32&#"3267#uþþüûO¤01Žh±«««5P9¦+"3ÍÝÜÈýnØ���jÿüu��/@!�   � �?Í9910'%7%7%¶y¶þáB!ÍþßC!¹v¸!DþáÌA9þÃCB¦s¨d¦u¨=CþÀ¦s¦þž¨s��Ë‘¬´��@ � � �/332993310#"&5463!6632#‡*03)*6Á+/3-,6ð-2255).0138(��øåÛ×��@ � € �/Ì233991027632#54#"##5x–•Qot}j+fyŽTb;:odf$+$y�ß×Í5��@ � � �/Ì93104632&ßC8\$wî¸8EL6(J@�á×Ï5��@ ��  �/Ì93105654.5432Ïîw$\8C¸¡@J(6LE��)þÁÁ‘� ��(�6�D�R�_�m�€@I_(DZ"> R6mL0gno�:HHAOED>LVcc\jf_Zm,,%3/"(6OLjm3663mjLO  �/3/39////////333333333333333333910&&#"#632&&#"#6632&&#"#6632!&&#"#6632&&#"#6632!&&#"#6632&&#"#632!&&#"#6632o�4@%% >:),25 ?@;+.6/'$3 �//9910#67'66737&'&&'57667'67'&'7&&'7 F$a5; Ia4#GÈAÝûhB¿OݦC¾CE±xüꛩE±x+REC{Lj'ZC‚&#B¿OݘGÈAÜ‚þ Ia5; F$a5ª'XDnXü•Y?DnXÞŒ·FÆcüéE¨—þf��=ÿì?Í� �'�Q@*�$%�()IY$ IY !IY �?+�?+�99//3+�3933333310473337�! �!�3267# �"&"!&=‘q"M)(ûÜ÷eÊrÝ‚þÆþ£Ž›¯ÑðnˇI62þ•²þõù û��3þ‡ÝZ�!�(�X@/  !&! )*!" FY%  �"FY�FY��?+�?+�99//3+�3??93333310&'$54733376632!3267#"!4&Õ¿Óþöj"ú·Ïñý ¬­eŸbŽ¥¦D†—=Œ àÝE2/;g#Êàþ÷âiÆÃ *”Aþ™H¤ž¥�ÿÿ�T��V¶�,��ÿÿ���¼`&°��6T�³&�+5ÿÿ���ß &Ð��6�¤���³&�+5��Éþ�¶��B@% IYIY �JY �??39/9++�?+933310"#337 ��!"&'53254$^Œ_ªª‰Íý…ObþÙþõR|Fz˜»Èþë{ý¤¶ý<ÄýTþ»þÏþÆþ¤˜1 ñèý���°þ !H��B@%��  FYGYFY�??39/9++�?+933310!#33#"'532654&#"T¤¤ã·þ7�ünÌ…ˆ_.lG‡˜»¾R\Hýúþþäþõ±ü„<‘&ÙÈÓÏ���þƒ‘¶��9@�IY JY �IY"�??+�?+�?+933310%3##!'"'53266!ٸŜªþ%=]˜~J;6;5O=]8šýé}ðþ!þE®W×Y¸���þ‡F��9@� FY GY �FY"�??+�?+�?+933310%3##!#"'532!ß°¬}¦þµ^˜v:q‰"ýøy¸þ˜þdÀ Ùö���Éþ�¶��=@  �� IY IY�?+�??39/+9333310%�!"&'53 !#3!3þæþûRzM{‡Œüþªªª–þÂþ¨–1÷#ýP¶ý’n��°þ bH��G@'   FY    �FY��?+�??39/_^]+9333310"'53265!#3!3Ó„]of}výœ¦¦d¨Ïþ :•=ÆÏ½þHþ5Ëûëþôþã��Éþƒ×¶��D@$  �  IY   �IY"�??+�??39/+933333310%3##!#3!3¸‘Åžªüþªªªšýé}°ýP¶ý’n���°þ‡F��D@$    FY  FY "�??+�??39/+933333310!33##!#Vf¦°¬}¦ýš¦Fþ7ÉüIýøyîþF���ªþƒÇ¶��=@  ��  IY "IY�?+�??39/+9333310!##3#"&5332673Ǫ¢¢•ÆjÏߪa±©ªþƒÂ5'¾³EýÏyt7Ê��œþ…-H��=@   FY  " FY �?+�??39/+933331032673##3#"&5BÛ[¦i¦•¦•i³q¤ºHþpÀ8CÕû¸þ… aH;¬“œ��Éþƒ)¶��H@%   IY" ��?3?3??+�9933933333310!##!333##47#Pþ�ÑÑþ¸Çžªþ À/ü^¶ûJ¶úäýé}®„Üúò���°þ‡ßF��?@    � FY "�??3+�??39939333310%7733###&'#3é+)Ó°¬}“:þå‹þå5”Ë)- ]vÓüIýøy‰:™ýJ¸†KüwFý-nÿÿ�T��V¶�,��ÿÿ����^&�$��6�9R�³&�+5ÿÿ�^ÿìÍ &�D��6è��³%&�+5ÿÿ����%&�$���j�=R� ´$&�+55ÿÿ�^ÿìÍÓ&�D���jó�� ´:&�+55ÿÿÿþ��¶�ˆ��ÿÿ�^ÿìs\�¨��ÿÿ�É��ø^&�(��6�R�³ &�+5ÿÿ�sÿì &�H��6 ��³&�+5��uÿìXÍ���=@  IY  IY �IY�?+�?+�9/+933310"5663 ��! 5!�27!˜ãâsÒ†Koþ¦þËý¬/þùÃÒùü‡Ì5Lž& þqþ›þ¢þqëF ûN ÷þøü���fÿì\���;@    FY �FY�FY��?+�?+�9/+9333102��#"55!&&#"566267!úõþýÚÐóô³¦b¥_Y¢š…š ýÃ\þÔþûþøþÉ áiÌ»!)“("ü¥œ¤�ÿÿ�uÿìX%&á���j�“R� ´/&�+55ÿÿ�fÿìÓ&â���jê�� ´1&�+55ÿÿ���¼%&°���jR� ´'&�+55ÿÿ���ßÓ&Ð���j�¢��� ´'&�+55ÿÿ�Jÿì5%&±���jÿóR� ´>&�+55ÿÿ�DÿìÓ&Ñ���j”�� ´8&�+55��Jÿì7¶��@@#�IY�JY�� JY�?+�?9/++�3933310! '532654&##5!5!ü$þÍþêþÿ£`ÞjÇÊáߌîýN‡? ÓÁÎèOž.2™†ŠÞ™‹���þ¦H��@@#� FY�GY�� FY�?+�?9/++�3933310�#"'532654&##5!5!¬•æþØïꊷȡÅÖÊyÅý‰8ÏrʈÞþîFšV¾ ¤ªrþŽ{�ÿÿ�Ë��R´&²��M�´R�³&�+5ÿÿ�°��bb&Ò��M1��³&�+5ÿÿ�Ë��R%&²���j�¾R� ´%&�+55ÿÿ�°��bÓ&Ò���j=�� ´#&�+55ÿÿ�}ÿì¾%&�2���j�ÑR� ´-&�+55ÿÿ�sÿìbÓ&�R���j�� ´.&�+55ÿÿ�}ÿì¾Í~��ÿÿ�sÿìb\��ÿÿ�}ÿì¾%&~���j�ÑR� ´/&�+55ÿÿ�sÿìbÓ&���j�� ´0&�+55ÿÿ�=ÿì‰%&Ç���jÿíR� ´0&�+55ÿÿ�9ÿì}Ó&ç���jŽ�� ´0&�+55ÿÿ�ÿìø´&½��M�/R�³&�+5ÿÿ�þb&�\��M­��³&�+5ÿÿ�ÿìø%&½���j�;R� ´,&�+55ÿÿ�þÓ&�\���j·�� ´+&�+55ÿÿ�ÿìøs&½��S�R� ´*&�+55ÿÿ�þ!&�\��S�� ´)&�+55ÿÿ�ª��Ç%&Á���j�jR� ´)&�+55ÿÿ�œ��-Ó&á���j�� ´(&�+55��Éþƒ¶� �-@   IY "�IY��?+�??+93310!!3##É?ýk¡¡ª¶™û}ýé}��°þ‡BF� �-@   FY "�FY��?+�??+93310!!3##°’þ–¦–FŒüÕýøyÿÿ�É�� %&Å���jR� ´-&�+55ÿÿ�°��yÓ&å���j�Å��� ´,&�+55ÿÿ�/þu¶&›���€�“��ÿÿ�þuBH&œ���u�ÿÿ�þuɶ�&�;���€X��ÿÿ�'þu4H�&�[���Ã������–¶��;@"     �IY �� �?3?39/993+�3910!33!!##!3þw¼kl·þp<þº½Áþwþp¶¿þºTbý»Eýž˜ýDƒý}¼���'��H��;@"     �GY �� �?3?39/993+�3910!33!!##!uþ´½! »þ²þâh¼þÍþʼfþèwÑþ\¤þ/þ ¼þDö���ƒ��7¶� ��4@�� IYJY�??+�9/+99333104$!33! $#"33ƒ$ ƪþcþõþô ºÞ¶ËÙ¤ÔÎpúJÕÛ|Ž„ÿÿ�sÿì7�G����ƒÿìw¶��#�F@$ ##$%IY� � JY��?2+�3?99//9+93339310"&54$!3332653#"&'#"!265Nâé*"‘ªædyªÏ¸vŸ3q)—ÔÂ!ÑÐÙÞpû·ì{næþ®ÎRZªÀ‹–þôwp���sÿì‡�"�.�Q@), &&/0� *FY�##FY �?3+�3?+�9/99?933339310%2653#"&'##"323&&53!26554&# þvk¨È½ž+K¹ÐèçÏjŸ? ¦mý¹¢’”¢þâ‹w„ˆ9þ½ÈÅ[qq[)  /MUp¾ûŒ ‰¹Î#çÉþNÖÒ��NÿìË�*�K@(("" +,JY  %%IY% JY�?+�?+�99//+�9933310#532654&#"'663232653#"&'&&®ÉÁÀÕš€g±gT]ö‚Öõ²œbl|wp¨Ò½ÊÐͬ“„l7ErHPħ·3þÑ–y‡Íþ)ÆÇÑÈ–‘��PÿìÅ\�%�K@( $$ &'!FYFY�FY�?+�?+�99//+�9933310%23# &&##53 54#"'6632Bݦ»Äþ†”Œo!òK‡M9U£h¸ÓÀc{ w 9þ½ÊÃMcX¬¢$"‡($›†¸9zjÓ��NþƒÑË�#�J@(#! #$%JY##IY#!" JY�?+�??+�9/+�99333104&##532654&#"'66323##ƒåâÙÑÍᤇiÃiTaþ„Üý½£¸Ã¬¢¬œ…‹“„k€:BrJNħŒ·³”þþýé}���Pþ‡Z��J@(  FYFY" FY�?+�??+�9/+�99333104!#53 54&#"'6323##ÕþË–u9…w™–=¡Ë¿ÕË~p¦•-ǬRPF‡Jš‡¶9 %‰fœýøy����ÿé!¶�#�:@## $%IY  JY �?3+�3?+�9/93310!#"'53266!32653#"&5 þH+LS‚dE@2?1@,8J7ïospq¨Í¼ÄÈðþ®þDÒf>hé®ûωyy‡Íþ)ÁÌÌÅ���ÿì)F��:@�FYGY �?3+�3?+�9/93310323#"&5!#"'532!ÏhwÕ¦»¾¼ËþÅ^˜v:q‰"qƒ‰ƒ ;þ½ÊÃÄË=þ˜þdÀ Ùö���Éÿì^¶��C@#�  IY IY �?+�??399//+9333331032653#"&5!#3!3önspq¦È¿ÃÈý'ªªÙª…‰yy‡Íþ)¿ÎËÆ3ýP¶ý’n���°ÿì¨H��M@*  FY   FY�?+�??399//_^]+93333310!3323#"&55!#VP¦jwÕ¦»ÀºÍý°¦Hþ5Ëý=‰… 9þ½ÊÃÆÉsþH���}ÿìšË��:@�IY�� IY IY�?+�?+�9/+93310!�! �4$32&&# ��3 !f4þÌþÉþ»þ“³UêxíSBZÖWþõþÞ ÷´þðVþ¡þ±‘`åTµ1'”&.þÅþãþãþÃ×���sÿì°\��:@ �FY�� FY FY�?+�?+�9/+93310!! ��!2&#"3265!²þýþþîþ×C!Ô¯;¨¦ÍåÌÅ©¯þª?Cýð'+PƒJÞÒÏß ���ÿìô¶��9@ � IY��IY�?+�3?+�9/933105!!323#"&5<þ/wrè¨Ó½ÆÍ™™üh‰{�Ïþ)ÀÍÎà���)ÿì‡F��6@ �FY  FY �?+�?9/+�393310!323#"&5!5!þ¦mvצ½ÀÀÉþ¨XºýɉƒAþ½ÊÃËÄ?Œ��oÿìXË�&�G@& �$##� '(#JYJY JY�?+�?+�9/+�99333104$3 &&#"33#"327! $54675&&œáÑ^iµeŒŸÑÈÙÕÞèÊ·éǯþûþôþÛϼª´\©ÆxD4{r€“ŽŠŽ\žMÜÅ—À²ÿÿ�Zÿì‡\‚��ÿÿ��þuk¶�&µ���€ú��ÿÿ�þusH&Õ�����ÿÿ��þ ¼&�$���gé��ÿÿ�^þ ÍZ&�D���gy��ÿÿ����á&�$��füR�³&�+5ÿÿ�^ÿìÍ&�D��f¦���³)&�+5ÿÿ����Ñ&�$��wåR� ´&�+55ÿÿ�^ÿìA&�D��w“��� ´+&�+55ÿÿ����Ñ&�$��xÝR� ´&�+55ÿÿ�-ÿìÍ&�D��x“��� ´+&�+55ÿÿ����J&�$��yÙR� ´&�+55ÿÿ�^ÿìø&�D��yœ��� ´+&�+55ÿÿ����b&�$��zåR� ´-&�+55ÿÿ�^ÿìÍ&�D��z‘��� ´C&�+55ÿÿ��þ s&�$���'gé��K�+R�³)&�+5ÿÿ�^þ Í!&�D���'gy��KÔ��³>&�+5ÿÿ����&�$��{ìR� ´&�+55ÿÿ�^ÿìÍÁ&�D��{š��� ´-&�+55ÿÿ����&�$��|éR� ´&�+55ÿÿ�^ÿìÍÁ&�D��|˜��� ´-&�+55ÿÿ����X&�$��}éR� ´!&�+55ÿÿ�^ÿìÍ&�D��} ��� ´7&�+55ÿÿ����^&�$��~ãR� ´'&�+55ÿÿ�^ÿìÍ &�D��~˜��� ´=&�+55ÿÿ��þ I&�$���'N�-dgé���³&�+5ÿÿ�^þ Íå&�D���&NØ�gy���³%&�+5ÿÿ�Éþ ø¶&�(���gÁ��ÿÿ�sþ \&�H���g¸��ÿÿ�É��øá&�(��fÑR�³&�+5ÿÿ�sÿì&�H��fÉ���³&�+5ÿÿ�É��ø/&�(��RÿäR�³&�+5ÿÿ�sÿìÝ&�H��RÐ��³$&�+5ÿÿ�É��oÑ&�(��wÁR� ´&�+55ÿÿ�sÿì\&�H��w®��� ´!&�+55ÿÿ�]��øÑ&�(��xÃR� ´&�+55ÿÿ�Jÿì&�H��x°��� ´!&�+55ÿÿ�É��9J&�(��y¾R� ´&�+55ÿÿ�sÿìø&�H��y¢��� ´!&�+55ÿÿ�É��øb&�(��z¸R� ´*&�+55ÿÿ�sÿì&�H��z¢��� ´9&�+55ÿÿ�Éþ øs&�(���'g¾��K�R�³%&�+5ÿÿ�sþ !&�H���'g°��Kñ��³4&�+5ÿÿ�T��Vá&�,��fÉR�³&�+5ÿÿ�{��æ&�ó��fs���³&�+5ÿÿ�Tþ V¶&�,���g´��ÿÿ�þ fß&�L���gb��ÿÿ�}þ ¾Í&�2���g��ÿÿ�sþ b\&�R���gÉ��ÿÿ�}ÿì¾á&�2��fR�³&�+5ÿÿ�sÿìb&�R��fÙ���³&�+5ÿÿ�}ÿì¾Ñ&�2��w}R� ´&�+55ÿÿ�sÿìu&�R��wÇ��� ´&�+55ÿÿ�}ÿì¾Ñ&�2��x}R� ´&�+55ÿÿ�aÿìb&�R��xÇ��� ´&�+55ÿÿ�}ÿì¾J&�2��y{R� ´&�+55ÿÿ�sÿìbø&�R��yÇ��� ´&�+55ÿÿ�}ÿì¾b&�2��zyR� ´6&�+55ÿÿ�sÿìb&�R��zÅ��� ´7&�+55ÿÿ�}þ ¾s&�2���'g��K�ÁR�³1&�+5ÿÿ�sþ b!&�R���'gÍ��K��³2&�+5ÿÿ�}ÿìds&_���v+R�³+&�+5ÿÿ�sÿì!&`���vm��³+&�+5ÿÿ�}ÿìds&_���C�‡R�³#&�+5ÿÿ�sÿì!&`���CÔ��³$&�+5ÿÿ�}ÿìdá&_��fR�³&&�+5ÿÿ�sÿì&`��fÙ���³'&�+5ÿÿ�}ÿìd/&_��R� R�³+&�+5ÿÿ�sÿìÝ&`��Rõ��³#&�+5ÿÿ�}þ d&_���g{��ÿÿ�sþ ð&`���gÉ��ÿÿ�ºþ ¶&�8���gJ��ÿÿ�¤þ 9H&�X���g¸��ÿÿ�ºÿìá&�8��fTR�³&�+5ÿÿ�¤ÿì9&�X��fÕ���³&�+5ÿÿ�ºÿì{s&a���v�îR�³%&�+5ÿÿ�¤ÿì–!&b���vy��³&&�+5ÿÿ�ºÿì{s&a���C�ZR�³&�+5ÿÿ�¤ÿì–!&b���C»��³&�+5ÿÿ�ºÿì{á&a��f`R�³ &�+5ÿÿ�¤ÿì–&b��fÛ���³"&�+5ÿÿ�ºÿì{/&a��R�R�³%&�+5ÿÿ�¤ÿì–Ý&b��Rÿ��³&�+5ÿÿ�ºþ {&a���gL��ÿÿ�¤þ –ò&b���g²��ÿÿ��þ {¶&�<���gœ��ÿÿ�þH&�\���gžÿýÿÿ����{á&�<��fªR�³ &�+5ÿÿ�þ&�\��fj���³&�+5ÿÿ����{/&�<��RÿÂR�³&�+5ÿÿ�þÝ&�\��RŠ��³&�+5ÿÿ�sþÅÓ&�Ó����B�´���ûåÙþ´!� ��@  ��€ �/3Í2339910#&&'53#&&'53þ´`4±%ºc1þœ`8®%»c1Ù*Ê?=®D,È?=®D��üqÙÿ®� ��(@�   À�/3Ì99//9339910#&'#57673'673#þÓ^pcra^5p4°B—PI6¬Sx`ÙK[eA<{M^¦Â[pn`��ûšÙþ×� ��*@��   À�/3Ì99//9339910#&'#57673%#&'53þ×^arji^5p4°B—ýî_xT¬4KÙAe`F<{M^¦¬^pla�üqÙÿ{ø� ��4@�!   À�/3Ì299//93339310#&'#57673#'6654&#"5632þÓ^pcra^5p4°B—¨P 9?9+.7ÃÙK[eA<{M^¦{gQƒ &%P�ühÙþç��%�:@ '" � "� �"À�/Ì9///3Ä339339910".#"#663232673#&'#57673þ-%GC?(*[ eK%IC>(* Z c^^arji^5p4°B—5%12jq$11hsþ¤Ae`F<{M^¦�üyÙþÇÁ���$@ @ €�/3Ý2ÔÍ339910673# 332673ý^P1¬Vw`>þìf LjbVi •ôher]þüH9A@xŒ�üyÙþÇÁ���$@ @ €�/3Ý2ÔÍ339910#&'53 332673ýÑ^wV¬4K5þìf LjbVi •Ý]rlaþåH9A@xŒ�üyÙþÇ���.@��  €�/Í239/Ä2339310#'6654&#"5632 332673þ1R 9B9,%$>À•þìf LjbVi •yd)Z %%NýÓH9A@xŒ�ühÙþç ��$�0@" & @ �!€�/Ý2ÖÄ3Í39/3329910".#"#663232673 332673þ-%GC?(*[ dL%IC>(* Z cÝþìf LjbVi •3$02hq$11grþ¦H9A@xŒ��1þBm����@ �  �/Ì293104'3#"'5326ß‹{žfcA2 6%3îg‡x„[gl 0���þuq�š� �@ �� ��/Ì299310%#"'5325qä8<)=^šþßþüŒd0���þuq�� �@ �� ��/Ì299310%#"'5325qä8<)=^þêþüŒd%�ÿÿ�4��C¶��ÿx����sÿìs� ��(@ �� KY &MY�?+�?+993310#"3232654&#"÷ÞÙöùÚØùý›Žžžš/þõþÈ55þËþñÐèêÎÌìé���-��7^� �&@ ��  �??99//993310!#47'37¡C>–Z‹1ïŒC0pr#��)��×s��,@� KY&LY�?+�3?+9310!!5>54&#"'632!×üR‘q,‹wXœ\ZÀòÆÚ‚ºþ¹¾…/whSAWg=Jm¨¨–s»€ç���^þ•t�'�G@&��" ()KY %%KY%& KY %�?+�?+�9/+�9933310!"&'53 !#532654&#"'6632î°ªþÞþõtÁ[_×`{þ^’«È“~`ªmTZë‚Õ쌲´’Ñá#,ž/1) —†kz4FpGQÃ���þ¨f^� ��B@!  �MY $�??9/933+�393333310%##!533!47#fÙ¨ý2¾¸Ùþ† )Dþ9þs}ÆüD\ÚÞV\ýž���…þ•_��:@�KY��LY KY%�?+�?+�9/+933102�#"'53265!"'!!6-ç þßþ÷‚FÐe°Ãþ‰^ V7×ý·%s&åÇãþþO -3¦27¬™þI�ÿÿ�uÿì/Ë�����^þ©+_��@�LY�$�??+9310!5!^üãÍýªþ©™…úÏÿÿ�hÿì)Ë�����jþ•%t��%�A@"" ��&'MY KY&MY%�?+�?+�9/9+933310!"'532##"&5432"326654&&%ýhtDPfðõ 7¶rÂäÿЕßxþœ“[™XR“ïü¦)3SWèÐä™þÛ0¸¤¥J€Fi²f�ÿÿ���Ä�'�I¶����I����\ݪÁ�"�3�Z@.,00.*&&( ��(.54+1$-/-)/##( ())�?3/3Í2/3993339933333310#"'53254&&'&&54632&#"##33#7#H•|‘Jjw”6UxQŽn}\"dSÿ®�7@ÿ®�7Cÿq�7Dÿ\�7Fÿ\�7Gÿ×�7Hÿq�7Jÿ…�7ûÿ×�7ýÿ×�7ÿ®�7ÿ®�7ÿ®�7ÿ…�7 ÿ…�7Wÿš�7Xÿq�7Yÿ\�7_ÿ×�7`ÿq�7bÿš�7ÿq�7ÿ\�7ÿq�7 ÿ\�7!ÿq�7"ÿ\�7#ÿq�7%ÿq�7&ÿ\�7'ÿq�7(ÿ\�7)ÿq�7*ÿ\�7+ÿq�7,ÿ\�7-ÿq�7.ÿ\�7/ÿq�70ÿ\�71ÿq�72ÿ\�73ÿq�74ÿ\�76ÿq�78ÿq�7:ÿq�7<ÿq�7@ÿq�7Bÿq�7Dÿq�7Iÿ×�7Jÿq�7Kÿ×�7Lÿq�7Mÿ×�7Nÿq�7Oÿ×�7Qÿ×�7Rÿq�7Sÿ×�7Tÿq�7Uÿ×�7Vÿq�7Wÿ×�7Xÿq�7Yÿ×�7Zÿq�7[ÿ×�7\ÿq�7]ÿ×�7^ÿq�7_ÿ×�7`ÿq�7bÿš�7dÿš�7fÿš�7hÿš�7jÿš�7lÿš�7nÿš�7pÿ×�7�)�8�ÿ×�8�ÿ×�8�$ÿì�8�‚ÿì�8�ƒÿì�8�„ÿì�8�…ÿì�8�†ÿì�8�‡ÿì�8�Âÿì�8�Äÿì�8�Æÿì�8Cÿì�8ÿ×�8 ÿ×�8Xÿì�8ÿì�8ÿì�8!ÿì�8#ÿì�8%ÿì�8'ÿì�8)ÿì�8+ÿì�8-ÿì�8/ÿì�81ÿì�83ÿì�9�ÿš�9�ÿš�9�"�)�9�$ÿ®�9�&ÿì�9�*ÿì�9�2ÿì�9�4ÿì�9�Dÿ×�9�Fÿ×�9�Gÿ×�9�Hÿ×�9�Jÿì�9�Pÿì�9�Qÿì�9�Rÿ×�9�Sÿì�9�Tÿ×�9�Uÿì�9�Vÿì�9�Xÿì�9�‚ÿ®�9�ƒÿ®�9�„ÿ®�9�…ÿ®�9�†ÿ®�9�‡ÿ®�9�‰ÿì�9�”ÿì�9�•ÿì�9�–ÿì�9�—ÿì�9�˜ÿì�9�šÿì�9�¢ÿ×�9�£ÿ×�9�¤ÿ×�9�¥ÿ×�9�¦ÿ×�9�§ÿ×�9�¨ÿ×�9�©ÿ×�9�ªÿ×�9�«ÿ×�9�¬ÿ×�9�­ÿ×�9�´ÿ×�9�µÿ×�9�¶ÿ×�9�·ÿ×�9�¸ÿ×�9�ºÿ×�9�»ÿì�9�¼ÿì�9�½ÿì�9�¾ÿì�9�Âÿ®�9�Ãÿ×�9�Äÿ®�9�Åÿ×�9�Æÿ®�9�Çÿ×�9�Èÿì�9�Éÿ×�9�Êÿì�9�Ëÿ×�9�Ìÿì�9�Íÿ×�9�Îÿì�9�Ïÿ×�9�Ñÿ×�9�Óÿ×�9�Õÿ×�9�×ÿ×�9�Ùÿ×�9�Ûÿ×�9�Ýÿ×�9�Þÿì�9�ßÿì�9�àÿì�9�áÿì�9�âÿì�9�ãÿì�9�äÿì�9�åÿì�9�úÿì�9ÿì�9ÿì�9 ÿì�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿì�9ÿì�9!ÿì�9+ÿì�9-ÿì�9/ÿì�91ÿì�93ÿì�95ÿì�9Cÿ®�9Dÿ×�9Fÿ×�9Gÿì�9Hÿ×�9Jÿì�9ÿš�9 ÿš�9Wÿì�9Xÿ®�9Yÿ×�9_ÿì�9`ÿ×�9bÿì�9ÿ®�9ÿ×�9ÿ®�9 ÿ×�9!ÿ®�9"ÿ×�9#ÿ®�9%ÿ®�9&ÿ×�9'ÿ®�9(ÿ×�9)ÿ®�9*ÿ×�9+ÿ®�9,ÿ×�9-ÿ®�9.ÿ×�9/ÿ®�90ÿ×�91ÿ®�92ÿ×�93ÿ®�94ÿ×�96ÿ×�98ÿ×�9:ÿ×�9<ÿ×�9@ÿ×�9Bÿ×�9Dÿ×�9Iÿì�9Jÿ×�9Kÿì�9Lÿ×�9Mÿì�9Nÿ×�9Oÿì�9Qÿì�9Rÿ×�9Sÿì�9Tÿ×�9Uÿì�9Vÿ×�9Wÿì�9Xÿ×�9Yÿì�9Zÿ×�9[ÿì�9\ÿ×�9]ÿì�9^ÿ×�9_ÿì�9`ÿ×�9bÿì�9dÿì�9fÿì�9hÿì�9jÿì�9lÿì�9nÿì�:�ÿš�:�ÿš�:�"�)�:�$ÿ®�:�&ÿì�:�*ÿì�:�2ÿì�:�4ÿì�:�Dÿ×�:�Fÿ×�:�Gÿ×�:�Hÿ×�:�Jÿì�:�Pÿì�:�Qÿì�:�Rÿ×�:�Sÿì�:�Tÿ×�:�Uÿì�:�Vÿì�:�Xÿì�:�‚ÿ®�:�ƒÿ®�:�„ÿ®�:�…ÿ®�:�†ÿ®�:�‡ÿ®�:�‰ÿì�:�”ÿì�:�•ÿì�:�–ÿì�:�—ÿì�:�˜ÿì�:�šÿì�:�¢ÿ×�:�£ÿ×�:�¤ÿ×�:�¥ÿ×�:�¦ÿ×�:�§ÿ×�:�¨ÿ×�:�©ÿ×�:�ªÿ×�:�«ÿ×�:�¬ÿ×�:�­ÿ×�:�´ÿ×�:�µÿ×�:�¶ÿ×�:�·ÿ×�:�¸ÿ×�:�ºÿ×�:�»ÿì�:�¼ÿì�:�½ÿì�:�¾ÿì�:�Âÿ®�:�Ãÿ×�:�Äÿ®�:�Åÿ×�:�Æÿ®�:�Çÿ×�:�Èÿì�:�Éÿ×�:�Êÿì�:�Ëÿ×�:�Ìÿì�:�Íÿ×�:�Îÿì�:�Ïÿ×�:�Ñÿ×�:�Óÿ×�:�Õÿ×�:�×ÿ×�:�Ùÿ×�:�Ûÿ×�:�Ýÿ×�:�Þÿì�:�ßÿì�:�àÿì�:�áÿì�:�âÿì�:�ãÿì�:�äÿì�:�åÿì�:�úÿì�:ÿì�:ÿì�: ÿì�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿì�:ÿì�:!ÿì�:+ÿì�:-ÿì�:/ÿì�:1ÿì�:3ÿì�:5ÿì�:Cÿ®�:Dÿ×�:Fÿ×�:Gÿì�:Hÿ×�:Jÿì�:ÿš�: ÿš�:Wÿì�:Xÿ®�:Yÿ×�:_ÿì�:`ÿ×�:bÿì�:ÿ®�:ÿ×�:ÿ®�: ÿ×�:!ÿ®�:"ÿ×�:#ÿ®�:%ÿ®�:&ÿ×�:'ÿ®�:(ÿ×�:)ÿ®�:*ÿ×�:+ÿ®�:,ÿ×�:-ÿ®�:.ÿ×�:/ÿ®�:0ÿ×�:1ÿ®�:2ÿ×�:3ÿ®�:4ÿ×�:6ÿ×�:8ÿ×�::ÿ×�:<ÿ×�:@ÿ×�:Bÿ×�:Dÿ×�:Iÿì�:Jÿ×�:Kÿì�:Lÿ×�:Mÿì�:Nÿ×�:Oÿì�:Qÿì�:Rÿ×�:Sÿì�:Tÿ×�:Uÿì�:Vÿ×�:Wÿì�:Xÿ×�:Yÿì�:Zÿ×�:[ÿì�:\ÿ×�:]ÿì�:^ÿ×�:_ÿì�:`ÿ×�:bÿì�:dÿì�:fÿì�:hÿì�:jÿì�:lÿì�:nÿì�;�&ÿ×�;�*ÿ×�;�2ÿ×�;�4ÿ×�;�‰ÿ×�;�”ÿ×�;�•ÿ×�;�–ÿ×�;�—ÿ×�;�˜ÿ×�;�šÿ×�;�Èÿ×�;�Êÿ×�;�Ìÿ×�;�Îÿ×�;�Þÿ×�;�àÿ×�;�âÿ×�;�äÿ×�;ÿ×�;ÿ×�;ÿ×�;ÿ×�;Gÿ×�;_ÿ×�;Iÿ×�;Kÿ×�;Mÿ×�;Oÿ×�;Qÿ×�;Sÿ×�;Uÿ×�;Wÿ×�;Yÿ×�;[ÿ×�;]ÿ×�;_ÿ×�<�ÿ…�<�ÿ…�<�"�)�<�$ÿ…�<�&ÿ×�<�*ÿ×�<�2ÿ×�<�4ÿ×�<�Dÿš�<�Fÿš�<�Gÿš�<�Hÿš�<�Jÿ×�<�PÿÃ�<�QÿÃ�<�Rÿš�<�SÿÃ�<�Tÿš�<�UÿÃ�<�Vÿ®�<�XÿÃ�<�]ÿ×�<�‚ÿ…�<�ƒÿ…�<�„ÿ…�<�…ÿ…�<�†ÿ…�<�‡ÿ…�<�‰ÿ×�<�”ÿ×�<�•ÿ×�<�–ÿ×�<�—ÿ×�<�˜ÿ×�<�šÿ×�<�¢ÿš�<�£ÿš�<�¤ÿš�<�¥ÿš�<�¦ÿš�<�§ÿš�<�¨ÿš�<�©ÿš�<�ªÿš�<�«ÿš�<�¬ÿš�<�­ÿš�<�´ÿš�<�µÿš�<�¶ÿš�<�·ÿš�<�¸ÿš�<�ºÿš�<�»ÿÃ�<�¼ÿÃ�<�½ÿÃ�<�¾ÿÃ�<�Âÿ…�<�Ãÿš�<�Äÿ…�<�Åÿš�<�Æÿ…�<�Çÿš�<�Èÿ×�<�Éÿš�<�Êÿ×�<�Ëÿš�<�Ìÿ×�<�Íÿš�<�Îÿ×�<�Ïÿš�<�Ñÿš�<�Óÿš�<�Õÿš�<�×ÿš�<�Ùÿš�<�Ûÿš�<�Ýÿš�<�Þÿ×�<�ßÿ×�<�àÿ×�<�áÿ×�<�âÿ×�<�ãÿ×�<�äÿ×�<�åÿ×�<�úÿÃ�<ÿÃ�<ÿÃ�< ÿÃ�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿÃ�<ÿÃ�<ÿ®�<!ÿ®�<+ÿÃ�<-ÿÃ�</ÿÃ�<1ÿÃ�<3ÿÃ�<5ÿÃ�<<ÿ×�<>ÿ×�<@ÿ×�<Cÿ…�<Dÿš�<Fÿš�<Gÿ×�<Hÿš�<Jÿ®�<ÿ…�< ÿ…�<WÿÃ�<Xÿ…�<Yÿš�<_ÿ×�<`ÿš�<bÿÃ�<ÿ…�<ÿš�<ÿ…�< ÿš�<!ÿ…�<"ÿš�<#ÿ…�<%ÿ…�<&ÿš�<'ÿ…�<(ÿš�<)ÿ…�<*ÿš�<+ÿ…�<,ÿš�<-ÿ…�<.ÿš�</ÿ…�<0ÿš�<1ÿ…�<2ÿš�<3ÿ…�<4ÿš�<6ÿš�<8ÿš�<:ÿš�<<ÿš�<@ÿš�<Bÿš�<Dÿš�<Iÿ×�<Jÿš�<Kÿ×�<Lÿš�<Mÿ×�<Nÿš�<Oÿ×�<Qÿ×�<Rÿš�<Sÿ×�<Tÿš�<Uÿ×�<Vÿš�<Wÿ×�<Xÿš�<Yÿ×�<Zÿš�<[ÿ×�<\ÿš�<]ÿ×�<^ÿš�<_ÿ×�<`ÿš�<bÿÃ�<dÿÃ�<fÿÃ�<hÿÃ�<jÿÃ�<lÿÃ�<nÿÃ�=�&ÿì�=�*ÿì�=�2ÿì�=�4ÿì�=�‰ÿì�=�”ÿì�=�•ÿì�=�–ÿì�=�—ÿì�=�˜ÿì�=�šÿì�=�Èÿì�=�Êÿì�=�Ìÿì�=�Îÿì�=�Þÿì�=�àÿì�=�âÿì�=�äÿì�=ÿì�=ÿì�=ÿì�=ÿì�=Gÿì�=_ÿì�=Iÿì�=Kÿì�=Mÿì�=Oÿì�=Qÿì�=Sÿì�=Uÿì�=Wÿì�=Yÿì�=[ÿì�=]ÿì�=_ÿì�>�-�¸�D�ÿì�D� ÿì�Dÿì�D ÿì�E�ÿì�E� ÿì�E�Yÿ×�E�Zÿ×�E�[ÿ×�E�\ÿ×�E�]ÿì�E�¿ÿ×�E7ÿ×�E<ÿì�E>ÿì�E@ÿì�Eûÿ×�Eýÿ×�Eÿì�E ÿì�Epÿ×�F��)�F� �)�F�)�F �)�H�ÿì�H� ÿì�H�Yÿ×�H�Zÿ×�H�[ÿ×�H�\ÿ×�H�]ÿì�H�¿ÿ×�H7ÿ×�H<ÿì�H>ÿì�H@ÿì�Hûÿ×�Hýÿ×�Hÿì�H ÿì�Hpÿ×�I��{�I� �{�I�{�I �{�K�ÿì�K� ÿì�Kÿì�K ÿì�N�Fÿ×�N�Gÿ×�N�Hÿ×�N�Rÿ×�N�Tÿ×�N�¢ÿ×�N�©ÿ×�N�ªÿ×�N�«ÿ×�N�¬ÿ×�N�­ÿ×�N�´ÿ×�N�µÿ×�N�¶ÿ×�N�·ÿ×�N�¸ÿ×�N�ºÿ×�N�Éÿ×�N�Ëÿ×�N�Íÿ×�N�Ïÿ×�N�Ñÿ×�N�Óÿ×�N�Õÿ×�N�×ÿ×�N�Ùÿ×�N�Ûÿ×�N�Ýÿ×�Nÿ×�Nÿ×�Nÿ×�Nÿ×�NHÿ×�N`ÿ×�N6ÿ×�N8ÿ×�N:ÿ×�N<ÿ×�N@ÿ×�NBÿ×�NDÿ×�NJÿ×�NLÿ×�NNÿ×�NRÿ×�NTÿ×�NVÿ×�NXÿ×�NZÿ×�N\ÿ×�N^ÿ×�N`ÿ×�P�ÿì�P� ÿì�Pÿì�P ÿì�Q�ÿì�Q� ÿì�Qÿì�Q ÿì�R�ÿì�R� ÿì�R�Yÿ×�R�Zÿ×�R�[ÿ×�R�\ÿ×�R�]ÿì�R�¿ÿ×�R7ÿ×�R<ÿì�R>ÿì�R@ÿì�Rûÿ×�Rýÿ×�Rÿì�R ÿì�Rpÿ×�S�ÿì�S� ÿì�S�Yÿ×�S�Zÿ×�S�[ÿ×�S�\ÿ×�S�]ÿì�S�¿ÿ×�S7ÿ×�S<ÿì�S>ÿì�S@ÿì�Sûÿ×�Sýÿ×�Sÿì�S ÿì�Spÿ×�U��R�U� �R�U�Dÿ×�U�Fÿ×�U�Gÿ×�U�Hÿ×�U�Jÿì�U�Rÿ×�U�Tÿ×�U�¢ÿ×�U�£ÿ×�U�¤ÿ×�U�¥ÿ×�U�¦ÿ×�U�§ÿ×�U�¨ÿ×�U�©ÿ×�U�ªÿ×�U�«ÿ×�U�¬ÿ×�U�­ÿ×�U�´ÿ×�U�µÿ×�U�¶ÿ×�U�·ÿ×�U�¸ÿ×�U�ºÿ×�U�Ãÿ×�U�Åÿ×�U�Çÿ×�U�Éÿ×�U�Ëÿ×�U�Íÿ×�U�Ïÿ×�U�Ñÿ×�U�Óÿ×�U�Õÿ×�U�×ÿ×�U�Ùÿ×�U�Ûÿ×�U�Ýÿ×�U�ßÿì�U�áÿì�U�ãÿì�U�åÿì�Uÿ×�Uÿ×�Uÿ×�Uÿ×�UDÿ×�UFÿ×�UHÿ×�U�R�U �R�UYÿ×�U`ÿ×�Uÿ×�U ÿ×�U"ÿ×�U&ÿ×�U(ÿ×�U*ÿ×�U,ÿ×�U.ÿ×�U0ÿ×�U2ÿ×�U4ÿ×�U6ÿ×�U8ÿ×�U:ÿ×�U<ÿ×�U@ÿ×�UBÿ×�UDÿ×�UJÿ×�ULÿ×�UNÿ×�URÿ×�UTÿ×�UVÿ×�UXÿ×�UZÿ×�U\ÿ×�U^ÿ×�U`ÿ×�W��)�W� �)�W�)�W �)�Y��R�Y� �R�Y�ÿ®�Y�ÿ®�Y�"�)�Y�R�Yÿ®�Y �R�Y ÿ®�Z��R�Z� �R�Z�ÿ®�Z�ÿ®�Z�"�)�Z�R�Zÿ®�Z �R�Z ÿ®�[�Fÿ×�[�Gÿ×�[�Hÿ×�[�Rÿ×�[�Tÿ×�[�¢ÿ×�[�©ÿ×�[�ªÿ×�[�«ÿ×�[�¬ÿ×�[�­ÿ×�[�´ÿ×�[�µÿ×�[�¶ÿ×�[�·ÿ×�[�¸ÿ×�[�ºÿ×�[�Éÿ×�[�Ëÿ×�[�Íÿ×�[�Ïÿ×�[�Ñÿ×�[�Óÿ×�[�Õÿ×�[�×ÿ×�[�Ùÿ×�[�Ûÿ×�[�Ýÿ×�[ÿ×�[ÿ×�[ÿ×�[ÿ×�[Hÿ×�[`ÿ×�[6ÿ×�[8ÿ×�[:ÿ×�[<ÿ×�[@ÿ×�[Bÿ×�[Dÿ×�[Jÿ×�[Lÿ×�[Nÿ×�[Rÿ×�[Tÿ×�[Vÿ×�[Xÿ×�[Zÿ×�[\ÿ×�[^ÿ×�[`ÿ×�\��R�\� �R�\�ÿ®�\�ÿ®�\�"�)�\�R�\ÿ®�\ �R�\ ÿ®�^�-�¸�‚�ÿq�‚� ÿq�‚�&ÿ×�‚�*ÿ×�‚�- �‚�2ÿ×�‚�4ÿ×�‚�7ÿq�‚�9ÿ®�‚�:ÿ®�‚�<ÿ…�‚�‰ÿ×�‚�”ÿ×�‚�•ÿ×�‚�–ÿ×�‚�—ÿ×�‚�˜ÿ×�‚�šÿ×�‚�Ÿÿ…�‚�Èÿ×�‚�Êÿ×�‚�Ìÿ×�‚�Îÿ×�‚�Þÿ×�‚�àÿ×�‚�âÿ×�‚�äÿ×�‚ÿ×�‚ÿ×�‚ÿ×�‚ÿ×�‚$ÿq�‚&ÿq�‚6ÿ®�‚8ÿ…�‚:ÿ…�‚Gÿ×�‚úÿ®�‚üÿ®�‚þÿ®�‚�ÿ…�‚ÿq�‚ ÿq�‚_ÿ×�‚Iÿ×�‚Kÿ×�‚Mÿ×�‚Oÿ×�‚Qÿ×�‚Sÿ×�‚Uÿ×�‚Wÿ×�‚Yÿ×�‚[ÿ×�‚]ÿ×�‚_ÿ×�‚oÿ…�‚qÿ…�‚sÿ…�‚ÿq�ƒ�ÿq�ƒ� ÿq�ƒ�&ÿ×�ƒ�*ÿ×�ƒ�- �ƒ�2ÿ×�ƒ�4ÿ×�ƒ�7ÿq�ƒ�9ÿ®�ƒ�:ÿ®�ƒ�<ÿ…�ƒ�‰ÿ×�ƒ�”ÿ×�ƒ�•ÿ×�ƒ�–ÿ×�ƒ�—ÿ×�ƒ�˜ÿ×�ƒ�šÿ×�ƒ�Ÿÿ…�ƒ�Èÿ×�ƒ�Êÿ×�ƒ�Ìÿ×�ƒ�Îÿ×�ƒ�Þÿ×�ƒ�àÿ×�ƒ�âÿ×�ƒ�äÿ×�ƒÿ×�ƒÿ×�ƒÿ×�ƒÿ×�ƒ$ÿq�ƒ&ÿq�ƒ6ÿ®�ƒ8ÿ…�ƒ:ÿ…�ƒGÿ×�ƒúÿ®�ƒüÿ®�ƒþÿ®�ƒ�ÿ…�ƒÿq�ƒ ÿq�ƒ_ÿ×�ƒIÿ×�ƒKÿ×�ƒMÿ×�ƒOÿ×�ƒQÿ×�ƒSÿ×�ƒUÿ×�ƒWÿ×�ƒYÿ×�ƒ[ÿ×�ƒ]ÿ×�ƒ_ÿ×�ƒoÿ…�ƒqÿ…�ƒsÿ…�ƒÿq�„�ÿq�„� ÿq�„�&ÿ×�„�*ÿ×�„�- �„�2ÿ×�„�4ÿ×�„�7ÿq�„�9ÿ®�„�:ÿ®�„�<ÿ…�„�‰ÿ×�„�”ÿ×�„�•ÿ×�„�–ÿ×�„�—ÿ×�„�˜ÿ×�„�šÿ×�„�Ÿÿ…�„�Èÿ×�„�Êÿ×�„�Ìÿ×�„�Îÿ×�„�Þÿ×�„�àÿ×�„�âÿ×�„�äÿ×�„ÿ×�„ÿ×�„ÿ×�„ÿ×�„$ÿq�„&ÿq�„6ÿ®�„8ÿ…�„:ÿ…�„Gÿ×�„úÿ®�„üÿ®�„þÿ®�„�ÿ…�„ÿq�„ ÿq�„_ÿ×�„Iÿ×�„Kÿ×�„Mÿ×�„Oÿ×�„Qÿ×�„Sÿ×�„Uÿ×�„Wÿ×�„Yÿ×�„[ÿ×�„]ÿ×�„_ÿ×�„oÿ…�„qÿ…�„sÿ…�„ÿq�…�ÿq�…� ÿq�…�&ÿ×�…�*ÿ×�…�- �…�2ÿ×�…�4ÿ×�…�7ÿq�…�9ÿ®�…�:ÿ®�…�<ÿ…�…�‰ÿ×�…�”ÿ×�…�•ÿ×�…�–ÿ×�…�—ÿ×�…�˜ÿ×�…�šÿ×�…�Ÿÿ…�…�Èÿ×�…�Êÿ×�…�Ìÿ×�…�Îÿ×�…�Þÿ×�…�àÿ×�…�âÿ×�…�äÿ×�…ÿ×�…ÿ×�…ÿ×�…ÿ×�…$ÿq�…&ÿq�…6ÿ®�…8ÿ…�…:ÿ…�…Gÿ×�…úÿ®�…üÿ®�…þÿ®�…�ÿ…�…ÿq�… ÿq�…_ÿ×�…Iÿ×�…Kÿ×�…Mÿ×�…Oÿ×�…Qÿ×�…Sÿ×�…Uÿ×�…Wÿ×�…Yÿ×�…[ÿ×�…]ÿ×�…_ÿ×�…oÿ…�…qÿ…�…sÿ…�…ÿq�†�ÿq�†� ÿq�†�&ÿ×�†�*ÿ×�†�- �†�2ÿ×�†�4ÿ×�†�7ÿq�†�9ÿ®�†�:ÿ®�†�<ÿ…�†�‰ÿ×�†�”ÿ×�†�•ÿ×�†�–ÿ×�†�—ÿ×�†�˜ÿ×�†�šÿ×�†�Ÿÿ…�†�Èÿ×�†�Êÿ×�†�Ìÿ×�†�Îÿ×�†�Þÿ×�†�àÿ×�†�âÿ×�†�äÿ×�†ÿ×�†ÿ×�†ÿ×�†ÿ×�†$ÿq�†&ÿq�†6ÿ®�†8ÿ…�†:ÿ…�†Gÿ×�†úÿ®�†üÿ®�†þÿ®�†�ÿ…�†ÿq�† ÿq�†_ÿ×�†Iÿ×�†Kÿ×�†Mÿ×�†Oÿ×�†Qÿ×�†Sÿ×�†Uÿ×�†Wÿ×�†Yÿ×�†[ÿ×�†]ÿ×�†_ÿ×�†oÿ…�†qÿ…�†sÿ…�†ÿq�‡�ÿq�‡� ÿq�‡�&ÿ×�‡�*ÿ×�‡�- �‡�2ÿ×�‡�4ÿ×�‡�7ÿq�‡�9ÿ®�‡�:ÿ®�‡�<ÿ…�‡�‰ÿ×�‡�”ÿ×�‡�•ÿ×�‡�–ÿ×�‡�—ÿ×�‡�˜ÿ×�‡�šÿ×�‡�Ÿÿ…�‡�Èÿ×�‡�Êÿ×�‡�Ìÿ×�‡�Îÿ×�‡�Þÿ×�‡�àÿ×�‡�âÿ×�‡�äÿ×�‡ÿ×�‡ÿ×�‡ÿ×�‡ÿ×�‡$ÿq�‡&ÿq�‡6ÿ®�‡8ÿ…�‡:ÿ…�‡Gÿ×�‡úÿ®�‡üÿ®�‡þÿ®�‡�ÿ…�‡ÿq�‡ ÿq�‡_ÿ×�‡Iÿ×�‡Kÿ×�‡Mÿ×�‡Oÿ×�‡Qÿ×�‡Sÿ×�‡Uÿ×�‡Wÿ×�‡Yÿ×�‡[ÿ×�‡]ÿ×�‡_ÿ×�‡oÿ…�‡qÿ…�‡sÿ…�‡ÿq�ˆ�-�{�‰�&ÿ×�‰�*ÿ×�‰�2ÿ×�‰�4ÿ×�‰�‰ÿ×�‰�”ÿ×�‰�•ÿ×�‰�–ÿ×�‰�—ÿ×�‰�˜ÿ×�‰�šÿ×�‰�Èÿ×�‰�Êÿ×�‰�Ìÿ×�‰�Îÿ×�‰�Þÿ×�‰�àÿ×�‰�âÿ×�‰�äÿ×�‰ÿ×�‰ÿ×�‰ÿ×�‰ÿ×�‰Gÿ×�‰_ÿ×�‰Iÿ×�‰Kÿ×�‰Mÿ×�‰Oÿ×�‰Qÿ×�‰Sÿ×�‰Uÿ×�‰Wÿ×�‰Yÿ×�‰[ÿ×�‰]ÿ×�‰_ÿ×�Š�-�{�‹�-�{�Œ�-�{��-�{�’�ÿ®�’�ÿ®�’�$ÿ×�’�7ÿÃ�’�9ÿì�’�:ÿì�’�;ÿ×�’�<ÿì�’�=ÿì�’�‚ÿ×�’�ƒÿ×�’�„ÿ×�’�…ÿ×�’�†ÿ×�’�‡ÿ×�’�Ÿÿì�’�Âÿ×�’�Äÿ×�’�Æÿ×�’$ÿÃ�’&ÿÃ�’6ÿì�’8ÿì�’:ÿì�’;ÿì�’=ÿì�’?ÿì�’Cÿ×�’ ÿì�’úÿì�’üÿì�’þÿì�’�ÿì�’ÿ®�’ ÿ®�’Xÿ×�’ÿ×�’ÿ×�’!ÿ×�’#ÿ×�’%ÿ×�’'ÿ×�’)ÿ×�’+ÿ×�’-ÿ×�’/ÿ×�’1ÿ×�’3ÿ×�’oÿì�’qÿì�’sÿì�’ÿÃ�”�ÿ®�”�ÿ®�”�$ÿ×�”�7ÿÃ�”�9ÿì�”�:ÿì�”�;ÿ×�”�<ÿì�”�=ÿì�”�‚ÿ×�”�ƒÿ×�”�„ÿ×�”�…ÿ×�”�†ÿ×�”�‡ÿ×�”�Ÿÿì�”�Âÿ×�”�Äÿ×�”�Æÿ×�”$ÿÃ�”&ÿÃ�”6ÿì�”8ÿì�”:ÿì�”;ÿì�”=ÿì�”?ÿì�”Cÿ×�” ÿì�”úÿì�”üÿì�”þÿì�”�ÿì�”ÿ®�” ÿ®�”Xÿ×�”ÿ×�”ÿ×�”!ÿ×�”#ÿ×�”%ÿ×�”'ÿ×�”)ÿ×�”+ÿ×�”-ÿ×�”/ÿ×�”1ÿ×�”3ÿ×�”oÿì�”qÿì�”sÿì�”ÿÃ�•�ÿ®�•�ÿ®�•�$ÿ×�•�7ÿÃ�•�9ÿì�•�:ÿì�•�;ÿ×�•�<ÿì�•�=ÿì�•�‚ÿ×�•�ƒÿ×�•�„ÿ×�•�…ÿ×�•�†ÿ×�•�‡ÿ×�•�Ÿÿì�•�Âÿ×�•�Äÿ×�•�Æÿ×�•$ÿÃ�•&ÿÃ�•6ÿì�•8ÿì�•:ÿì�•;ÿì�•=ÿì�•?ÿì�•Cÿ×�• ÿì�•úÿì�•üÿì�•þÿì�•�ÿì�•ÿ®�• ÿ®�•Xÿ×�•ÿ×�•ÿ×�•!ÿ×�•#ÿ×�•%ÿ×�•'ÿ×�•)ÿ×�•+ÿ×�•-ÿ×�•/ÿ×�•1ÿ×�•3ÿ×�•oÿì�•qÿì�•sÿì�•ÿÃ�–�ÿ®�–�ÿ®�–�$ÿ×�–�7ÿÃ�–�9ÿì�–�:ÿì�–�;ÿ×�–�<ÿì�–�=ÿì�–�‚ÿ×�–�ƒÿ×�–�„ÿ×�–�…ÿ×�–�†ÿ×�–�‡ÿ×�–�Ÿÿì�–�Âÿ×�–�Äÿ×�–�Æÿ×�–$ÿÃ�–&ÿÃ�–6ÿì�–8ÿì�–:ÿì�–;ÿì�–=ÿì�–?ÿì�–Cÿ×�– ÿì�–úÿì�–üÿì�–þÿì�–�ÿì�–ÿ®�– ÿ®�–Xÿ×�–ÿ×�–ÿ×�–!ÿ×�–#ÿ×�–%ÿ×�–'ÿ×�–)ÿ×�–+ÿ×�–-ÿ×�–/ÿ×�–1ÿ×�–3ÿ×�–oÿì�–qÿì�–sÿì�–ÿÃ�—�ÿ®�—�ÿ®�—�$ÿ×�—�7ÿÃ�—�9ÿì�—�:ÿì�—�;ÿ×�—�<ÿì�—�=ÿì�—�‚ÿ×�—�ƒÿ×�—�„ÿ×�—�…ÿ×�—�†ÿ×�—�‡ÿ×�—�Ÿÿì�—�Âÿ×�—�Äÿ×�—�Æÿ×�—$ÿÃ�—&ÿÃ�—6ÿì�—8ÿì�—:ÿì�—;ÿì�—=ÿì�—?ÿì�—Cÿ×�— ÿì�—úÿì�—üÿì�—þÿì�—�ÿì�—ÿ®�— ÿ®�—Xÿ×�—ÿ×�—ÿ×�—!ÿ×�—#ÿ×�—%ÿ×�—'ÿ×�—)ÿ×�—+ÿ×�—-ÿ×�—/ÿ×�—1ÿ×�—3ÿ×�—oÿì�—qÿì�—sÿì�—ÿÃ�˜�ÿ®�˜�ÿ®�˜�$ÿ×�˜�7ÿÃ�˜�9ÿì�˜�:ÿì�˜�;ÿ×�˜�<ÿì�˜�=ÿì�˜�‚ÿ×�˜�ƒÿ×�˜�„ÿ×�˜�…ÿ×�˜�†ÿ×�˜�‡ÿ×�˜�Ÿÿì�˜�Âÿ×�˜�Äÿ×�˜�Æÿ×�˜$ÿÃ�˜&ÿÃ�˜6ÿì�˜8ÿì�˜:ÿì�˜;ÿì�˜=ÿì�˜?ÿì�˜Cÿ×�˜ ÿì�˜úÿì�˜üÿì�˜þÿì�˜�ÿì�˜ÿ®�˜ ÿ®�˜Xÿ×�˜ÿ×�˜ÿ×�˜!ÿ×�˜#ÿ×�˜%ÿ×�˜'ÿ×�˜)ÿ×�˜+ÿ×�˜-ÿ×�˜/ÿ×�˜1ÿ×�˜3ÿ×�˜oÿì�˜qÿì�˜sÿì�˜ÿÃ�š�ÿ®�š�ÿ®�š�$ÿ×�š�7ÿÃ�š�9ÿì�š�:ÿì�š�;ÿ×�š�<ÿì�š�=ÿì�š�‚ÿ×�š�ƒÿ×�š�„ÿ×�š�…ÿ×�š�†ÿ×�š�‡ÿ×�š�Ÿÿì�š�Âÿ×�š�Äÿ×�š�Æÿ×�š$ÿÃ�š&ÿÃ�š6ÿì�š8ÿì�š:ÿì�š;ÿì�š=ÿì�š?ÿì�šCÿ×�š ÿì�šúÿì�šüÿì�šþÿì�š�ÿì�šÿ®�š ÿ®�šXÿ×�šÿ×�šÿ×�š!ÿ×�š#ÿ×�š%ÿ×�š'ÿ×�š)ÿ×�š+ÿ×�š-ÿ×�š/ÿ×�š1ÿ×�š3ÿ×�šoÿì�šqÿì�šsÿì�šÿÃ�›�ÿ×�›�ÿ×�›�$ÿì�›�‚ÿì�›�ƒÿì�›�„ÿì�›�…ÿì�›�†ÿì�›�‡ÿì�›�Âÿì�›�Äÿì�›�Æÿì�›Cÿì�›ÿ×�› ÿ×�›Xÿì�›ÿì�›ÿì�›!ÿì�›#ÿì�›%ÿì�›'ÿì�›)ÿì�›+ÿì�›-ÿì�›/ÿì�›1ÿì�›3ÿì�œ�ÿ×�œ�ÿ×�œ�$ÿì�œ�‚ÿì�œ�ƒÿì�œ�„ÿì�œ�…ÿì�œ�†ÿì�œ�‡ÿì�œ�Âÿì�œ�Äÿì�œ�Æÿì�œCÿì�œÿ×�œ ÿ×�œXÿì�œÿì�œÿì�œ!ÿì�œ#ÿì�œ%ÿì�œ'ÿì�œ)ÿì�œ+ÿì�œ-ÿì�œ/ÿì�œ1ÿì�œ3ÿì��ÿ×��ÿ×��$ÿì��‚ÿì��ƒÿì��„ÿì��…ÿì��†ÿì��‡ÿì��Âÿì��Äÿì��Æÿì�Cÿì�ÿ×� ÿ×�Xÿì�ÿì�ÿì�!ÿì�#ÿì�%ÿì�'ÿì�)ÿì�+ÿì�-ÿì�/ÿì�1ÿì�3ÿì�ž�ÿ×�ž�ÿ×�ž�$ÿì�ž�‚ÿì�ž�ƒÿì�ž�„ÿì�ž�…ÿì�ž�†ÿì�ž�‡ÿì�ž�Âÿì�ž�Äÿì�ž�Æÿì�žCÿì�žÿ×�ž ÿ×�žXÿì�žÿì�žÿì�ž!ÿì�ž#ÿì�ž%ÿì�ž'ÿì�ž)ÿì�ž+ÿì�ž-ÿì�ž/ÿì�ž1ÿì�ž3ÿì�Ÿ�ÿ…�Ÿ�ÿ…�Ÿ�"�)�Ÿ�$ÿ…�Ÿ�&ÿ×�Ÿ�*ÿ×�Ÿ�2ÿ×�Ÿ�4ÿ×�Ÿ�Dÿš�Ÿ�Fÿš�Ÿ�Gÿš�Ÿ�Hÿš�Ÿ�Jÿ×�Ÿ�PÿÃ�Ÿ�QÿÃ�Ÿ�Rÿš�Ÿ�SÿÃ�Ÿ�Tÿš�Ÿ�UÿÃ�Ÿ�Vÿ®�Ÿ�XÿÃ�Ÿ�]ÿ×�Ÿ�‚ÿ…�Ÿ�ƒÿ…�Ÿ�„ÿ…�Ÿ�…ÿ…�Ÿ�†ÿ…�Ÿ�‡ÿ…�Ÿ�‰ÿ×�Ÿ�”ÿ×�Ÿ�•ÿ×�Ÿ�–ÿ×�Ÿ�—ÿ×�Ÿ�˜ÿ×�Ÿ�šÿ×�Ÿ�¢ÿš�Ÿ�£ÿš�Ÿ�¤ÿš�Ÿ�¥ÿš�Ÿ�¦ÿš�Ÿ�§ÿš�Ÿ�¨ÿš�Ÿ�©ÿš�Ÿ�ªÿš�Ÿ�«ÿš�Ÿ�¬ÿš�Ÿ�­ÿš�Ÿ�´ÿš�Ÿ�µÿš�Ÿ�¶ÿš�Ÿ�·ÿš�Ÿ�¸ÿš�Ÿ�ºÿš�Ÿ�»ÿÃ�Ÿ�¼ÿÃ�Ÿ�½ÿÃ�Ÿ�¾ÿÃ�Ÿ�Âÿ…�Ÿ�Ãÿš�Ÿ�Äÿ…�Ÿ�Åÿš�Ÿ�Æÿ…�Ÿ�Çÿš�Ÿ�Èÿ×�Ÿ�Éÿš�Ÿ�Êÿ×�Ÿ�Ëÿš�Ÿ�Ìÿ×�Ÿ�Íÿš�Ÿ�Îÿ×�Ÿ�Ïÿš�Ÿ�Ñÿš�Ÿ�Óÿš�Ÿ�Õÿš�Ÿ�×ÿš�Ÿ�Ùÿš�Ÿ�Ûÿš�Ÿ�Ýÿš�Ÿ�Þÿ×�Ÿ�ßÿ×�Ÿ�àÿ×�Ÿ�áÿ×�Ÿ�âÿ×�Ÿ�ãÿ×�Ÿ�äÿ×�Ÿ�åÿ×�Ÿ�úÿÃ�ŸÿÃ�ŸÿÃ�Ÿ ÿÃ�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�ŸÿÃ�ŸÿÃ�Ÿÿ®�Ÿ!ÿ®�Ÿ+ÿÃ�Ÿ-ÿÃ�Ÿ/ÿÃ�Ÿ1ÿÃ�Ÿ3ÿÃ�Ÿ5ÿÃ�Ÿ<ÿ×�Ÿ>ÿ×�Ÿ@ÿ×�ŸCÿ…�ŸDÿš�ŸFÿš�ŸGÿ×�ŸHÿš�ŸJÿ®�Ÿÿ…�Ÿ ÿ…�ŸWÿÃ�ŸXÿ…�ŸYÿš�Ÿ_ÿ×�Ÿ`ÿš�ŸbÿÃ�Ÿÿ…�Ÿÿš�Ÿÿ…�Ÿ ÿš�Ÿ!ÿ…�Ÿ"ÿš�Ÿ#ÿ…�Ÿ%ÿ…�Ÿ&ÿš�Ÿ'ÿ…�Ÿ(ÿš�Ÿ)ÿ…�Ÿ*ÿš�Ÿ+ÿ…�Ÿ,ÿš�Ÿ-ÿ…�Ÿ.ÿš�Ÿ/ÿ…�Ÿ0ÿš�Ÿ1ÿ…�Ÿ2ÿš�Ÿ3ÿ…�Ÿ4ÿš�Ÿ6ÿš�Ÿ8ÿš�Ÿ:ÿš�Ÿ<ÿš�Ÿ@ÿš�ŸBÿš�ŸDÿš�ŸIÿ×�ŸJÿš�ŸKÿ×�ŸLÿš�ŸMÿ×�ŸNÿš�ŸOÿ×�ŸQÿ×�ŸRÿš�ŸSÿ×�ŸTÿš�ŸUÿ×�ŸVÿš�ŸWÿ×�ŸXÿš�ŸYÿ×�ŸZÿš�Ÿ[ÿ×�Ÿ\ÿš�Ÿ]ÿ×�Ÿ^ÿš�Ÿ_ÿ×�Ÿ`ÿš�ŸbÿÃ�ŸdÿÃ�ŸfÿÃ�ŸhÿÃ�ŸjÿÃ�ŸlÿÃ�ŸnÿÃ� �þö� �þö� �$ÿš� �;ÿ×� �=ÿì� �‚ÿš� �ƒÿš� �„ÿš� �…ÿš� �†ÿš� �‡ÿš� �Âÿš� �Äÿš� �Æÿš� ;ÿì� =ÿì� ?ÿì� Cÿš� þö�  þö� Xÿš� ÿš� ÿš� !ÿš� #ÿš� %ÿš� 'ÿš� )ÿš� +ÿš� -ÿš� /ÿš� 1ÿš� 3ÿš�¢�ÿì�¢� ÿì�¢ÿì�¢ ÿì�£�ÿì�£� ÿì�£ÿì�£ ÿì�¤�ÿì�¤� ÿì�¤ÿì�¤ ÿì�¥�ÿì�¥� ÿì�¥ÿì�¥ ÿì�¦�ÿì�¦� ÿì�¦ÿì�¦ ÿì�§�ÿì�§� ÿì�§ÿì�§ ÿì�ª�ÿì�ª� ÿì�ª�Yÿ×�ª�Zÿ×�ª�[ÿ×�ª�\ÿ×�ª�]ÿì�ª�¿ÿ×�ª7ÿ×�ª<ÿì�ª>ÿì�ª@ÿì�ªûÿ×�ªýÿ×�ªÿì�ª ÿì�ªpÿ×�«�ÿì�«� ÿì�«�Yÿ×�«�Zÿ×�«�[ÿ×�«�\ÿ×�«�]ÿì�«�¿ÿ×�«7ÿ×�«<ÿì�«>ÿì�«@ÿì�«ûÿ×�«ýÿ×�«ÿì�« ÿì�«pÿ×�¬�ÿì�¬� ÿì�¬�Yÿ×�¬�Zÿ×�¬�[ÿ×�¬�\ÿ×�¬�]ÿì�¬�¿ÿ×�¬7ÿ×�¬<ÿì�¬>ÿì�¬@ÿì�¬ûÿ×�¬ýÿ×�¬ÿì�¬ ÿì�¬pÿ×�­�ÿì�­� ÿì�­�Yÿ×�­�Zÿ×�­�[ÿ×�­�\ÿ×�­�]ÿì�­�¿ÿ×�­7ÿ×�­<ÿì�­>ÿì�­@ÿì�­ûÿ×�­ýÿ×�­ÿì�­ ÿì�­pÿ×�²�ÿì�²� ÿì�²�Yÿ×�²�Zÿ×�²�[ÿ×�²�\ÿ×�²�]ÿì�²�¿ÿ×�²7ÿ×�²<ÿì�²>ÿì�²@ÿì�²ûÿ×�²ýÿ×�²ÿì�² ÿì�²pÿ×�´�ÿì�´� ÿì�´�Yÿ×�´�Zÿ×�´�[ÿ×�´�\ÿ×�´�]ÿì�´�¿ÿ×�´7ÿ×�´<ÿì�´>ÿì�´@ÿì�´ûÿ×�´ýÿ×�´ÿì�´ ÿì�´pÿ×�µ�ÿì�µ� ÿì�µ�Yÿ×�µ�Zÿ×�µ�[ÿ×�µ�\ÿ×�µ�]ÿì�µ�¿ÿ×�µ7ÿ×�µ<ÿì�µ>ÿì�µ@ÿì�µûÿ×�µýÿ×�µÿì�µ ÿì�µpÿ×�¶�ÿì�¶� ÿì�¶�Yÿ×�¶�Zÿ×�¶�[ÿ×�¶�\ÿ×�¶�]ÿì�¶�¿ÿ×�¶7ÿ×�¶<ÿì�¶>ÿì�¶@ÿì�¶ûÿ×�¶ýÿ×�¶ÿì�¶ ÿì�¶pÿ×�¸�ÿ×�¸� ÿ×�¸ÿ×�¸ ÿ×�º�ÿì�º� ÿì�º�Yÿ×�º�Zÿ×�º�[ÿ×�º�\ÿ×�º�]ÿì�º�¿ÿ×�º7ÿ×�º<ÿì�º>ÿì�º@ÿì�ºûÿ×�ºýÿ×�ºÿì�º ÿì�ºpÿ×�¿��R�¿� �R�¿�ÿ®�¿�ÿ®�¿�"�)�¿�R�¿ÿ®�¿ �R�¿ ÿ®�À�ÿì�À� ÿì�À�Yÿ×�À�Zÿ×�À�[ÿ×�À�\ÿ×�À�]ÿì�À�¿ÿ×�À7ÿ×�À<ÿì�À>ÿì�À@ÿì�Àûÿ×�Àýÿ×�Àÿì�À ÿì�Àpÿ×�Á��R�Á� �R�Á�ÿ®�Á�ÿ®�Á�"�)�Á�R�Áÿ®�Á �R�Á ÿ®�Â�ÿq�Â� ÿq�Â�&ÿ×�Â�*ÿ×�Â�- �Â�2ÿ×�Â�4ÿ×�Â�7ÿq�Â�9ÿ®�Â�:ÿ®�Â�<ÿ…�Â�‰ÿ×�Â�”ÿ×�Â�•ÿ×�Â�–ÿ×�Â�—ÿ×�Â�˜ÿ×�Â�šÿ×�Â�Ÿÿ…�Â�Èÿ×�Â�Êÿ×�Â�Ìÿ×�Â�Îÿ×�Â�Þÿ×�Â�àÿ×�Â�âÿ×�Â�äÿ×�Âÿ×�Âÿ×�Âÿ×�Âÿ×�Â$ÿq�Â&ÿq�Â6ÿ®�Â8ÿ…�Â:ÿ…�ÂGÿ×�Âúÿ®�Âüÿ®�Âþÿ®�Â�ÿ…�Âÿq� ÿq�Â_ÿ×�ÂIÿ×�ÂKÿ×�ÂMÿ×�ÂOÿ×�ÂQÿ×�ÂSÿ×�ÂUÿ×�ÂWÿ×�ÂYÿ×�Â[ÿ×�Â]ÿ×�Â_ÿ×�Âoÿ…�Âqÿ…�Âsÿ…�Âÿq�Ã�ÿì�Ã� ÿì�Ãÿì�à ÿì�Ä�ÿq�Ä� ÿq�Ä�&ÿ×�Ä�*ÿ×�Ä�- �Ä�2ÿ×�Ä�4ÿ×�Ä�7ÿq�Ä�9ÿ®�Ä�:ÿ®�Ä�<ÿ…�Ä�‰ÿ×�Ä�”ÿ×�Ä�•ÿ×�Ä�–ÿ×�Ä�—ÿ×�Ä�˜ÿ×�Ä�šÿ×�Ä�Ÿÿ…�Ä�Èÿ×�Ä�Êÿ×�Ä�Ìÿ×�Ä�Îÿ×�Ä�Þÿ×�Ä�àÿ×�Ä�âÿ×�Ä�äÿ×�Äÿ×�Äÿ×�Äÿ×�Äÿ×�Ä$ÿq�Ä&ÿq�Ä6ÿ®�Ä8ÿ…�Ä:ÿ…�ÄGÿ×�Äúÿ®�Äüÿ®�Äþÿ®�Ä�ÿ…�Äÿq�Ä ÿq�Ä_ÿ×�ÄIÿ×�ÄKÿ×�ÄMÿ×�ÄOÿ×�ÄQÿ×�ÄSÿ×�ÄUÿ×�ÄWÿ×�ÄYÿ×�Ä[ÿ×�Ä]ÿ×�Ä_ÿ×�Äoÿ…�Äqÿ…�Äsÿ…�Äÿq�Å�ÿì�Å� ÿì�Åÿì�Å ÿì�Æ�ÿq�Æ� ÿq�Æ�&ÿ×�Æ�*ÿ×�Æ�- �Æ�2ÿ×�Æ�4ÿ×�Æ�7ÿq�Æ�9ÿ®�Æ�:ÿ®�Æ�<ÿ…�Æ�‰ÿ×�Æ�”ÿ×�Æ�•ÿ×�Æ�–ÿ×�Æ�—ÿ×�Æ�˜ÿ×�Æ�šÿ×�Æ�Ÿÿ…�Æ�Èÿ×�Æ�Êÿ×�Æ�Ìÿ×�Æ�Îÿ×�Æ�Þÿ×�Æ�àÿ×�Æ�âÿ×�Æ�äÿ×�Æÿ×�Æÿ×�Æÿ×�Æÿ×�Æ$ÿq�Æ&ÿq�Æ6ÿ®�Æ8ÿ…�Æ:ÿ…�ÆGÿ×�Æúÿ®�Æüÿ®�Æþÿ®�Æ�ÿ…�Æÿq�Æ ÿq�Æ_ÿ×�ÆIÿ×�ÆKÿ×�ÆMÿ×�ÆOÿ×�ÆQÿ×�ÆSÿ×�ÆUÿ×�ÆWÿ×�ÆYÿ×�Æ[ÿ×�Æ]ÿ×�Æ_ÿ×�Æoÿ…�Æqÿ…�Æsÿ…�Æÿq�Ç�ÿì�Ç� ÿì�Çÿì�Ç ÿì�È�&ÿ×�È�*ÿ×�È�2ÿ×�È�4ÿ×�È�‰ÿ×�È�”ÿ×�È�•ÿ×�È�–ÿ×�È�—ÿ×�È�˜ÿ×�È�šÿ×�È�Èÿ×�È�Êÿ×�È�Ìÿ×�È�Îÿ×�È�Þÿ×�È�àÿ×�È�âÿ×�È�äÿ×�Èÿ×�Èÿ×�Èÿ×�Èÿ×�ÈGÿ×�È_ÿ×�ÈIÿ×�ÈKÿ×�ÈMÿ×�ÈOÿ×�ÈQÿ×�ÈSÿ×�ÈUÿ×�ÈWÿ×�ÈYÿ×�È[ÿ×�È]ÿ×�È_ÿ×�Ê�&ÿ×�Ê�*ÿ×�Ê�2ÿ×�Ê�4ÿ×�Ê�‰ÿ×�Ê�”ÿ×�Ê�•ÿ×�Ê�–ÿ×�Ê�—ÿ×�Ê�˜ÿ×�Ê�šÿ×�Ê�Èÿ×�Ê�Êÿ×�Ê�Ìÿ×�Ê�Îÿ×�Ê�Þÿ×�Ê�àÿ×�Ê�âÿ×�Ê�äÿ×�Êÿ×�Êÿ×�Êÿ×�Êÿ×�ÊGÿ×�Ê_ÿ×�ÊIÿ×�ÊKÿ×�ÊMÿ×�ÊOÿ×�ÊQÿ×�ÊSÿ×�ÊUÿ×�ÊWÿ×�ÊYÿ×�Ê[ÿ×�Ê]ÿ×�Ê_ÿ×�Ì�&ÿ×�Ì�*ÿ×�Ì�2ÿ×�Ì�4ÿ×�Ì�‰ÿ×�Ì�”ÿ×�Ì�•ÿ×�Ì�–ÿ×�Ì�—ÿ×�Ì�˜ÿ×�Ì�šÿ×�Ì�Èÿ×�Ì�Êÿ×�Ì�Ìÿ×�Ì�Îÿ×�Ì�Þÿ×�Ì�àÿ×�Ì�âÿ×�Ì�äÿ×�Ìÿ×�Ìÿ×�Ìÿ×�Ìÿ×�ÌGÿ×�Ì_ÿ×�ÌIÿ×�ÌKÿ×�ÌMÿ×�ÌOÿ×�ÌQÿ×�ÌSÿ×�ÌUÿ×�ÌWÿ×�ÌYÿ×�Ì[ÿ×�Ì]ÿ×�Ì_ÿ×�Î�&ÿ×�Î�*ÿ×�Î�2ÿ×�Î�4ÿ×�Î�‰ÿ×�Î�”ÿ×�Î�•ÿ×�Î�–ÿ×�Î�—ÿ×�Î�˜ÿ×�Î�šÿ×�Î�Èÿ×�Î�Êÿ×�Î�Ìÿ×�Î�Îÿ×�Î�Þÿ×�Î�àÿ×�Î�âÿ×�Î�äÿ×�Îÿ×�Îÿ×�Îÿ×�Îÿ×�ÎGÿ×�Î_ÿ×�ÎIÿ×�ÎKÿ×�ÎMÿ×�ÎOÿ×�ÎQÿ×�ÎSÿ×�ÎUÿ×�ÎWÿ×�ÎYÿ×�Î[ÿ×�Î]ÿ×�Î_ÿ×�Ð�ÿ®�Ð�ÿ®�Ð�$ÿ×�Ð�7ÿÃ�Ð�9ÿì�Ð�:ÿì�Ð�;ÿ×�Ð�<ÿì�Ð�=ÿì�Ð�‚ÿ×�Ð�ƒÿ×�Ð�„ÿ×�Ð�…ÿ×�Ð�†ÿ×�Ð�‡ÿ×�Ð�Ÿÿì�Ð�Âÿ×�Ð�Äÿ×�Ð�Æÿ×�Ð$ÿÃ�Ð&ÿÃ�Ð6ÿì�Ð8ÿì�Ð:ÿì�Ð;ÿì�Ð=ÿì�Ð?ÿì�ÐCÿ×�Рÿì�Ðúÿì�Ðüÿì�Ðþÿì�Ð�ÿì�Ðÿ®�Ð ÿ®�ÐXÿ×�Ðÿ×�Ðÿ×�Ð!ÿ×�Ð#ÿ×�Ð%ÿ×�Ð'ÿ×�Ð)ÿ×�Ð+ÿ×�Ð-ÿ×�Ð/ÿ×�Ð1ÿ×�Ð3ÿ×�Ðoÿì�Ðqÿì�Ðsÿì�ÐÿÃ�Ñ��R�Ñ� �R�Ñ� ��Ñ�"�¤�Ñ�@��Ñ�E�=�Ñ�K�=�Ñ�N�=�Ñ�O�=�Ñ�`��Ñ�ç�=�Ñ�é�{�Ñ�R�Ñ �R�Ò�ÿ®�Ò�ÿ®�Ò�$ÿ×�Ò�7ÿÃ�Ò�9ÿì�Ò�:ÿì�Ò�;ÿ×�Ò�<ÿì�Ò�=ÿì�Ò�‚ÿ×�Ò�ƒÿ×�Ò�„ÿ×�Ò�…ÿ×�Ò�†ÿ×�Ò�‡ÿ×�Ò�Ÿÿì�Ò�Âÿ×�Ò�Äÿ×�Ò�Æÿ×�Ò$ÿÃ�Ò&ÿÃ�Ò6ÿì�Ò8ÿì�Ò:ÿì�Ò;ÿì�Ò=ÿì�Ò?ÿì�ÒCÿ×�Ò ÿì�Òúÿì�Òüÿì�Òþÿì�Ò�ÿì�Òÿ®�Ò ÿ®�ÒXÿ×�Òÿ×�Òÿ×�Ò!ÿ×�Ò#ÿ×�Ò%ÿ×�Ò'ÿ×�Ò)ÿ×�Ò+ÿ×�Ò-ÿ×�Ò/ÿ×�Ò1ÿ×�Ò3ÿ×�Òoÿì�Òqÿì�Òsÿì�ÒÿÃ�Ô�-�{�Õ�ÿì�Õ� ÿì�Õ�Yÿ×�Õ�Zÿ×�Õ�[ÿ×�Õ�\ÿ×�Õ�]ÿì�Õ�¿ÿ×�Õ7ÿ×�Õ<ÿì�Õ>ÿì�Õ@ÿì�Õûÿ×�Õýÿ×�Õÿì�Õ ÿì�Õpÿ×�Ö�-�{�×�ÿì�×� ÿì�×�Yÿ×�×�Zÿ×�×�[ÿ×�×�\ÿ×�×�]ÿì�×�¿ÿ×�×7ÿ×�×<ÿì�×>ÿì�×@ÿì�×ûÿ×�×ýÿ×�×ÿì�× ÿì�×pÿ×�Ø�-�{�Ù�ÿì�Ù� ÿì�Ù�Yÿ×�Ù�Zÿ×�Ù�[ÿ×�Ù�\ÿ×�Ù�]ÿì�Ù�¿ÿ×�Ù7ÿ×�Ù<ÿì�Ù>ÿì�Ù@ÿì�Ùûÿ×�Ùýÿ×�Ùÿì�Ù ÿì�Ùpÿ×�Ú�-�{�Û�ÿì�Û� ÿì�Û�Yÿ×�Û�Zÿ×�Û�[ÿ×�Û�\ÿ×�Û�]ÿì�Û�¿ÿ×�Û7ÿ×�Û<ÿì�Û>ÿì�Û@ÿì�Ûûÿ×�Ûýÿ×�Ûÿì�Û ÿì�Ûpÿ×�Ü�-�{�Ý�ÿì�Ý� ÿì�Ý�Yÿ×�Ý�Zÿ×�Ý�[ÿ×�Ý�\ÿ×�Ý�]ÿì�Ý�¿ÿ×�Ý7ÿ×�Ý<ÿì�Ý>ÿì�Ý@ÿì�Ýûÿ×�Ýýÿ×�Ýÿì�Ý ÿì�Ýpÿ×�ç�ÿì�ç� ÿì�çÿì�ç ÿì�ø�&ÿ×�ø�*ÿ×�ø�2ÿ×�ø�4ÿ×�ø�‰ÿ×�ø�”ÿ×�ø�•ÿ×�ø�–ÿ×�ø�—ÿ×�ø�˜ÿ×�ø�šÿ×�ø�Èÿ×�ø�Êÿ×�ø�Ìÿ×�ø�Îÿ×�ø�Þÿ×�ø�àÿ×�ø�âÿ×�ø�äÿ×�øÿ×�øÿ×�øÿ×�øÿ×�øGÿ×�ø_ÿ×�øIÿ×�øKÿ×�øMÿ×�øOÿ×�øQÿ×�øSÿ×�øUÿ×�øWÿ×�øYÿ×�ø[ÿ×�ø]ÿ×�ø_ÿ×�ù�Fÿ×�ù�Gÿ×�ù�Hÿ×�ù�Rÿ×�ù�Tÿ×�ù�¢ÿ×�ù�©ÿ×�ù�ªÿ×�ù�«ÿ×�ù�¬ÿ×�ù�­ÿ×�ù�´ÿ×�ù�µÿ×�ù�¶ÿ×�ù�·ÿ×�ù�¸ÿ×�ù�ºÿ×�ù�Éÿ×�ù�Ëÿ×�ù�Íÿ×�ù�Ïÿ×�ù�Ñÿ×�ù�Óÿ×�ù�Õÿ×�ù�×ÿ×�ù�Ùÿ×�ù�Ûÿ×�ù�Ýÿ×�ùÿ×�ùÿ×�ùÿ×�ùÿ×�ùHÿ×�ù`ÿ×�ù6ÿ×�ù8ÿ×�ù:ÿ×�ù<ÿ×�ù@ÿ×�ùBÿ×�ùDÿ×�ùJÿ×�ùLÿ×�ùNÿ×�ùRÿ×�ùTÿ×�ùVÿ×�ùXÿ×�ùZÿ×�ù\ÿ×�ù^ÿ×�ù`ÿ×�ú�Fÿ×�ú�Gÿ×�ú�Hÿ×�ú�Rÿ×�ú�Tÿ×�ú�¢ÿ×�ú�©ÿ×�ú�ªÿ×�ú�«ÿ×�ú�¬ÿ×�ú�­ÿ×�ú�´ÿ×�ú�µÿ×�ú�¶ÿ×�ú�·ÿ×�ú�¸ÿ×�ú�ºÿ×�ú�Éÿ×�ú�Ëÿ×�ú�Íÿ×�ú�Ïÿ×�ú�Ñÿ×�ú�Óÿ×�ú�Õÿ×�ú�×ÿ×�ú�Ùÿ×�ú�Ûÿ×�ú�Ýÿ×�úÿ×�úÿ×�úÿ×�úÿ×�úHÿ×�ú`ÿ×�ú6ÿ×�ú8ÿ×�ú:ÿ×�ú<ÿ×�ú@ÿ×�úBÿ×�úDÿ×�úJÿ×�úLÿ×�úNÿ×�úRÿ×�úTÿ×�úVÿ×�úXÿ×�úZÿ×�ú\ÿ×�ú^ÿ×�ú`ÿ×�û�ÿ\�û� ÿ\�û�&ÿ×�û�*ÿ×�û�2ÿ×�û�4ÿ×�û�7ÿ×�û�8ÿì�û�9ÿ×�û�:ÿ×�û�<ÿÃ�û�‰ÿ×�û�”ÿ×�û�•ÿ×�û�–ÿ×�û�—ÿ×�û�˜ÿ×�û�šÿ×�û�›ÿì�û�œÿì�û�ÿì�û�žÿì�û�ŸÿÃ�û�Èÿ×�û�Êÿ×�û�Ìÿ×�û�Îÿ×�û�Þÿ×�û�àÿ×�û�âÿ×�û�äÿ×�ûÿ×�ûÿ×�ûÿ×�ûÿ×�û$ÿ×�û&ÿ×�û*ÿì�û,ÿì�û.ÿì�û0ÿì�û2ÿì�û4ÿì�û6ÿ×�û8ÿÃ�û:ÿÃ�ûGÿ×�ûúÿ×�ûüÿ×�ûþÿ×�û�ÿÃ�ûÿ\�û ÿ\�û_ÿ×�ûaÿì�ûIÿ×�ûKÿ×�ûMÿ×�ûOÿ×�ûQÿ×�ûSÿ×�ûUÿ×�ûWÿ×�ûYÿ×�û[ÿ×�û]ÿ×�û_ÿ×�ûaÿì�ûcÿì�ûeÿì�ûgÿì�ûiÿì�ûkÿì�ûmÿì�ûoÿÃ�ûqÿÃ�ûsÿÃ�ûÿ×�ý�ÿ\�ý� ÿ\�ý�&ÿ×�ý�*ÿ×�ý�2ÿ×�ý�4ÿ×�ý�7ÿ×�ý�8ÿì�ý�9ÿ×�ý�:ÿ×�ý�<ÿÃ�ý�‰ÿ×�ý�”ÿ×�ý�•ÿ×�ý�–ÿ×�ý�—ÿ×�ý�˜ÿ×�ý�šÿ×�ý�›ÿì�ý�œÿì�ý�ÿì�ý�žÿì�ý�ŸÿÃ�ý�Èÿ×�ý�Êÿ×�ý�Ìÿ×�ý�Îÿ×�ý�Þÿ×�ý�àÿ×�ý�âÿ×�ý�äÿ×�ýÿ×�ýÿ×�ýÿ×�ýÿ×�ý$ÿ×�ý&ÿ×�ý*ÿì�ý,ÿì�ý.ÿì�ý0ÿì�ý2ÿì�ý4ÿì�ý6ÿ×�ý8ÿÃ�ý:ÿÃ�ýGÿ×�ýúÿ×�ýüÿ×�ýþÿ×�ý�ÿÃ�ýÿ\�ý ÿ\�ý_ÿ×�ýaÿì�ýIÿ×�ýKÿ×�ýMÿ×�ýOÿ×�ýQÿ×�ýSÿ×�ýUÿ×�ýWÿ×�ýYÿ×�ý[ÿ×�ý]ÿ×�ý_ÿ×�ýaÿì�ýcÿì�ýeÿì�ýgÿì�ýiÿì�ýkÿì�ýmÿì�ýoÿÃ�ýqÿÃ�ýsÿÃ�ýÿ×�ÿ�ÿ\�ÿ� ÿ\�ÿ�&ÿ×�ÿ�*ÿ×�ÿ�2ÿ×�ÿ�4ÿ×�ÿ�7ÿ×�ÿ�8ÿì�ÿ�9ÿ×�ÿ�:ÿ×�ÿ�<ÿÃ�ÿ�‰ÿ×�ÿ�”ÿ×�ÿ�•ÿ×�ÿ�–ÿ×�ÿ�—ÿ×�ÿ�˜ÿ×�ÿ�šÿ×�ÿ�›ÿì�ÿ�œÿì�ÿ�ÿì�ÿ�žÿì�ÿ�ŸÿÃ�ÿ�Èÿ×�ÿ�Êÿ×�ÿ�Ìÿ×�ÿ�Îÿ×�ÿ�Þÿ×�ÿ�àÿ×�ÿ�âÿ×�ÿ�äÿ×�ÿÿ×�ÿÿ×�ÿÿ×�ÿÿ×�ÿ$ÿ×�ÿ&ÿ×�ÿ*ÿì�ÿ,ÿì�ÿ.ÿì�ÿ0ÿì�ÿ2ÿì�ÿ4ÿì�ÿ6ÿ×�ÿ8ÿÃ�ÿ:ÿÃ�ÿGÿ×�ÿúÿ×�ÿüÿ×�ÿþÿ×�ÿ�ÿÃ�ÿÿ\�ÿ ÿ\�ÿ_ÿ×�ÿaÿì�ÿIÿ×�ÿKÿ×�ÿMÿ×�ÿOÿ×�ÿQÿ×�ÿSÿ×�ÿUÿ×�ÿWÿ×�ÿYÿ×�ÿ[ÿ×�ÿ]ÿ×�ÿ_ÿ×�ÿaÿì�ÿcÿì�ÿeÿì�ÿgÿì�ÿiÿì�ÿkÿì�ÿmÿì�ÿoÿÃ�ÿqÿÃ�ÿsÿÃ�ÿÿ×���R�� �R�� ���"���@���E�=��K�=��N�=��O�=��`���ç�=��é���R� �R�ÿ\� ÿ\�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7ÿ×�8ÿì�9ÿ×�:ÿ×�<ÿÃ�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�›ÿì�œÿì�ÿì�žÿì�ŸÿÃ�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿ×&ÿ×*ÿì,ÿì.ÿì0ÿì2ÿì4ÿì6ÿ×8ÿÃ:ÿÃGÿ×úÿ×üÿ×þÿ×�ÿÃÿ\ ÿ\_ÿ×aÿìIÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×aÿìcÿìeÿìgÿìiÿìkÿìmÿìoÿÃqÿÃsÿÃÿ×�ÿ\� ÿ\�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7ÿ×�8ÿì�9ÿ×�:ÿ×�<ÿÃ�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�›ÿì�œÿì�ÿì�žÿì�ŸÿÃ�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿ×&ÿ×*ÿì,ÿì.ÿì0ÿì2ÿì4ÿì6ÿ×8ÿÃ:ÿÃGÿ×úÿ×üÿ×þÿ×�ÿÃÿ\ ÿ\_ÿ×aÿìIÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×aÿìcÿìeÿìgÿìiÿìkÿìmÿìoÿÃqÿÃsÿÃÿ×�ÿì� ÿìÿì ÿì�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�-�{��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×$�ÿ…$�ÿ®$�ÿ…$�"�)$�$ÿq$�&ÿ×$�*ÿ×$�2ÿ×$�4ÿ×$�7�)$�Dÿ\$�Fÿq$�Gÿq$�Hÿq$�Jÿq$�Pÿš$�Qÿš$�Rÿq$�Sÿš$�Tÿq$�Uÿš$�Vÿ…$�Xÿš$�Yÿ×$�Zÿ×$�[ÿ×$�\ÿ×$�]ÿ®$�‚ÿq$�ƒÿq$�„ÿq$�…ÿq$�†ÿq$�‡ÿq$�‰ÿ×$�”ÿ×$�•ÿ×$�–ÿ×$�—ÿ×$�˜ÿ×$�šÿ×$�¢ÿq$�£ÿ\$�¤ÿ\$�¥ÿ\$�¦ÿ\$�§ÿ\$�¨ÿ\$�©ÿq$�ªÿq$�«ÿq$�¬ÿq$�­ÿq$�´ÿq$�µÿq$�¶ÿq$�·ÿq$�¸ÿq$�ºÿq$�»ÿš$�¼ÿš$�½ÿš$�¾ÿš$�¿ÿ×$�Âÿq$�Ãÿ\$�Äÿq$�Åÿ\$�Æÿq$�Çÿ\$�Èÿ×$�Éÿq$�Êÿ×$�Ëÿq$�Ìÿ×$�Íÿq$�Îÿ×$�Ïÿq$�Ñÿq$�Óÿq$�Õÿq$�×ÿq$�Ùÿq$�Ûÿq$�Ýÿq$�Þÿ×$�ßÿq$�àÿ×$�áÿq$�âÿ×$�ãÿq$�äÿ×$�åÿq$�úÿš$ÿš$ÿš$ ÿš$ÿ×$ÿq$ÿ×$ÿq$ÿ×$ÿq$ÿ×$ÿq$ÿš$ÿš$ÿ…$!ÿ…$$�)$&�)$+ÿš$-ÿš$/ÿš$1ÿš$3ÿš$5ÿš$7ÿ×$<ÿ®$>ÿ®$@ÿ®$Cÿq$Dÿ\$Fÿ\$Gÿ×$Hÿq$Jÿ…$ûÿ×$ýÿ×$ÿ®$ÿ®$ÿ®$ÿ…$ ÿ…$Wÿš$Xÿq$Yÿ\$_ÿ×$`ÿq$bÿš$ÿq$ÿ\$ÿq$ ÿ\$!ÿq$"ÿ\$#ÿq$%ÿq$&ÿ\$'ÿq$(ÿ\$)ÿq$*ÿ\$+ÿq$,ÿ\$-ÿq$.ÿ\$/ÿq$0ÿ\$1ÿq$2ÿ\$3ÿq$4ÿ\$6ÿq$8ÿq$:ÿq$<ÿq$@ÿq$Bÿq$Dÿq$Iÿ×$Jÿq$Kÿ×$Lÿq$Mÿ×$Nÿq$Oÿ×$Qÿ×$Rÿq$Sÿ×$Tÿq$Uÿ×$Vÿq$Wÿ×$Xÿq$Yÿ×$Zÿq$[ÿ×$\ÿq$]ÿ×$^ÿq$_ÿ×$`ÿq$bÿš$dÿš$fÿš$hÿš$jÿš$lÿš$nÿš$pÿ×$�)%��)%� �)%�)% �)&�ÿ…&�ÿ®&�ÿ…&�"�)&�$ÿq&�&ÿ×&�*ÿ×&�2ÿ×&�4ÿ×&�7�)&�Dÿ\&�Fÿq&�Gÿq&�Hÿq&�Jÿq&�Pÿš&�Qÿš&�Rÿq&�Sÿš&�Tÿq&�Uÿš&�Vÿ…&�Xÿš&�Yÿ×&�Zÿ×&�[ÿ×&�\ÿ×&�]ÿ®&�‚ÿq&�ƒÿq&�„ÿq&�…ÿq&�†ÿq&�‡ÿq&�‰ÿ×&�”ÿ×&�•ÿ×&�–ÿ×&�—ÿ×&�˜ÿ×&�šÿ×&�¢ÿq&�£ÿ\&�¤ÿ\&�¥ÿ\&�¦ÿ\&�§ÿ\&�¨ÿ\&�©ÿq&�ªÿq&�«ÿq&�¬ÿq&�­ÿq&�´ÿq&�µÿq&�¶ÿq&�·ÿq&�¸ÿq&�ºÿq&�»ÿš&�¼ÿš&�½ÿš&�¾ÿš&�¿ÿ×&�Âÿq&�Ãÿ\&�Äÿq&�Åÿ\&�Æÿq&�Çÿ\&�Èÿ×&�Éÿq&�Êÿ×&�Ëÿq&�Ìÿ×&�Íÿq&�Îÿ×&�Ïÿq&�Ñÿq&�Óÿq&�Õÿq&�×ÿq&�Ùÿq&�Ûÿq&�Ýÿq&�Þÿ×&�ßÿq&�àÿ×&�áÿq&�âÿ×&�ãÿq&�äÿ×&�åÿq&�úÿš&ÿš&ÿš& ÿš&ÿ×&ÿq&ÿ×&ÿq&ÿ×&ÿq&ÿ×&ÿq&ÿš&ÿš&ÿ…&!ÿ…&$�)&&�)&+ÿš&-ÿš&/ÿš&1ÿš&3ÿš&5ÿš&7ÿ×&<ÿ®&>ÿ®&@ÿ®&Cÿq&Dÿ\&Fÿ\&Gÿ×&Hÿq&Jÿ…&ûÿ×&ýÿ×&ÿ®&ÿ®&ÿ®&ÿ…& ÿ…&Wÿš&Xÿq&Yÿ\&_ÿ×&`ÿq&bÿš&ÿq&ÿ\&ÿq& ÿ\&!ÿq&"ÿ\&#ÿq&%ÿq&&ÿ\&'ÿq&(ÿ\&)ÿq&*ÿ\&+ÿq&,ÿ\&-ÿq&.ÿ\&/ÿq&0ÿ\&1ÿq&2ÿ\&3ÿq&4ÿ\&6ÿq&8ÿq&:ÿq&<ÿq&@ÿq&Bÿq&Dÿq&Iÿ×&Jÿq&Kÿ×&Lÿq&Mÿ×&Nÿq&Oÿ×&Qÿ×&Rÿq&Sÿ×&Tÿq&Uÿ×&Vÿq&Wÿ×&Xÿq&Yÿ×&Zÿq&[ÿ×&\ÿq&]ÿ×&^ÿq&_ÿ×&`ÿq&bÿš&dÿš&fÿš&hÿš&jÿš&lÿš&nÿš&pÿ×&�)'��)'� �)'�)' �)(�ÿ…(�ÿ®(�ÿ…(�"�)(�$ÿq(�&ÿ×(�*ÿ×(�2ÿ×(�4ÿ×(�7�)(�Dÿ\(�Fÿq(�Gÿq(�Hÿq(�Jÿq(�Pÿš(�Qÿš(�Rÿq(�Sÿš(�Tÿq(�Uÿš(�Vÿ…(�Xÿš(�Yÿ×(�Zÿ×(�[ÿ×(�\ÿ×(�]ÿ®(�‚ÿq(�ƒÿq(�„ÿq(�…ÿq(�†ÿq(�‡ÿq(�‰ÿ×(�”ÿ×(�•ÿ×(�–ÿ×(�—ÿ×(�˜ÿ×(�šÿ×(�¢ÿq(�£ÿ\(�¤ÿ\(�¥ÿ\(�¦ÿ\(�§ÿ\(�¨ÿ\(�©ÿq(�ªÿq(�«ÿq(�¬ÿq(�­ÿq(�´ÿq(�µÿq(�¶ÿq(�·ÿq(�¸ÿq(�ºÿq(�»ÿš(�¼ÿš(�½ÿš(�¾ÿš(�¿ÿ×(�Âÿq(�Ãÿ\(�Äÿq(�Åÿ\(�Æÿq(�Çÿ\(�Èÿ×(�Éÿq(�Êÿ×(�Ëÿq(�Ìÿ×(�Íÿq(�Îÿ×(�Ïÿq(�Ñÿq(�Óÿq(�Õÿq(�×ÿq(�Ùÿq(�Ûÿq(�Ýÿq(�Þÿ×(�ßÿq(�àÿ×(�áÿq(�âÿ×(�ãÿq(�äÿ×(�åÿq(�úÿš(ÿš(ÿš( ÿš(ÿ×(ÿq(ÿ×(ÿq(ÿ×(ÿq(ÿ×(ÿq(ÿš(ÿš(ÿ…(!ÿ…($�)(&�)(+ÿš(-ÿš(/ÿš(1ÿš(3ÿš(5ÿš(7ÿ×(<ÿ®(>ÿ®(@ÿ®(Cÿq(Dÿ\(Fÿ\(Gÿ×(Hÿq(Jÿ…(ûÿ×(ýÿ×(ÿ®(ÿ®(ÿ®(ÿ…( ÿ…(Wÿš(Xÿq(Yÿ\(_ÿ×(`ÿq(bÿš(ÿq(ÿ\(ÿq( ÿ\(!ÿq("ÿ\(#ÿq(%ÿq(&ÿ\('ÿq((ÿ\()ÿq(*ÿ\(+ÿq(,ÿ\(-ÿq(.ÿ\(/ÿq(0ÿ\(1ÿq(2ÿ\(3ÿq(4ÿ\(6ÿq(8ÿq(:ÿq(<ÿq(@ÿq(Bÿq(Dÿq(Iÿ×(Jÿq(Kÿ×(Lÿq(Mÿ×(Nÿq(Oÿ×(Qÿ×(Rÿq(Sÿ×(Tÿq(Uÿ×(Vÿq(Wÿ×(Xÿq(Yÿ×(Zÿq([ÿ×(\ÿq(]ÿ×(^ÿq(_ÿ×(`ÿq(bÿš(dÿš(fÿš(hÿš(jÿš(lÿš(nÿš(pÿ×(�)*�ÿ×*�ÿ×*�$ÿì*�‚ÿì*�ƒÿì*�„ÿì*�…ÿì*�†ÿì*�‡ÿì*�Âÿì*�Äÿì*�Æÿì*Cÿì*ÿ×* ÿ×*Xÿì*ÿì*ÿì*!ÿì*#ÿì*%ÿì*'ÿì*)ÿì*+ÿì*-ÿì*/ÿì*1ÿì*3ÿì,�ÿ×,�ÿ×,�$ÿì,�‚ÿì,�ƒÿì,�„ÿì,�…ÿì,�†ÿì,�‡ÿì,�Âÿì,�Äÿì,�Æÿì,Cÿì,ÿ×, ÿ×,Xÿì,ÿì,ÿì,!ÿì,#ÿì,%ÿì,'ÿì,)ÿì,+ÿì,-ÿì,/ÿì,1ÿì,3ÿì.�ÿ×.�ÿ×.�$ÿì.�‚ÿì.�ƒÿì.�„ÿì.�…ÿì.�†ÿì.�‡ÿì.�Âÿì.�Äÿì.�Æÿì.Cÿì.ÿ×. ÿ×.Xÿì.ÿì.ÿì.!ÿì.#ÿì.%ÿì.'ÿì.)ÿì.+ÿì.-ÿì./ÿì.1ÿì.3ÿì0�ÿ×0�ÿ×0�$ÿì0�‚ÿì0�ƒÿì0�„ÿì0�…ÿì0�†ÿì0�‡ÿì0�Âÿì0�Äÿì0�Æÿì0Cÿì0ÿ×0 ÿ×0Xÿì0ÿì0ÿì0!ÿì0#ÿì0%ÿì0'ÿì0)ÿì0+ÿì0-ÿì0/ÿì01ÿì03ÿì2�ÿ×2�ÿ×2�$ÿì2�‚ÿì2�ƒÿì2�„ÿì2�…ÿì2�†ÿì2�‡ÿì2�Âÿì2�Äÿì2�Æÿì2Cÿì2ÿ×2 ÿ×2Xÿì2ÿì2ÿì2!ÿì2#ÿì2%ÿì2'ÿì2)ÿì2+ÿì2-ÿì2/ÿì21ÿì23ÿì4�ÿ×4�ÿ×4�$ÿì4�‚ÿì4�ƒÿì4�„ÿì4�…ÿì4�†ÿì4�‡ÿì4�Âÿì4�Äÿì4�Æÿì4Cÿì4ÿ×4 ÿ×4Xÿì4ÿì4ÿì4!ÿì4#ÿì4%ÿì4'ÿì4)ÿì4+ÿì4-ÿì4/ÿì41ÿì43ÿì6�ÿš6�ÿš6�"�)6�$ÿ®6�&ÿì6�*ÿì6�2ÿì6�4ÿì6�Dÿ×6�Fÿ×6�Gÿ×6�Hÿ×6�Jÿì6�Pÿì6�Qÿì6�Rÿ×6�Sÿì6�Tÿ×6�Uÿì6�Vÿì6�Xÿì6�‚ÿ®6�ƒÿ®6�„ÿ®6�…ÿ®6�†ÿ®6�‡ÿ®6�‰ÿì6�”ÿì6�•ÿì6�–ÿì6�—ÿì6�˜ÿì6�šÿì6�¢ÿ×6�£ÿ×6�¤ÿ×6�¥ÿ×6�¦ÿ×6�§ÿ×6�¨ÿ×6�©ÿ×6�ªÿ×6�«ÿ×6�¬ÿ×6�­ÿ×6�´ÿ×6�µÿ×6�¶ÿ×6�·ÿ×6�¸ÿ×6�ºÿ×6�»ÿì6�¼ÿì6�½ÿì6�¾ÿì6�Âÿ®6�Ãÿ×6�Äÿ®6�Åÿ×6�Æÿ®6�Çÿ×6�Èÿì6�Éÿ×6�Êÿì6�Ëÿ×6�Ìÿì6�Íÿ×6�Îÿì6�Ïÿ×6�Ñÿ×6�Óÿ×6�Õÿ×6�×ÿ×6�Ùÿ×6�Ûÿ×6�Ýÿ×6�Þÿì6�ßÿì6�àÿì6�áÿì6�âÿì6�ãÿì6�äÿì6�åÿì6�úÿì6ÿì6ÿì6 ÿì6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿì6ÿì6!ÿì6+ÿì6-ÿì6/ÿì61ÿì63ÿì65ÿì6Cÿ®6Dÿ×6Fÿ×6Gÿì6Hÿ×6Jÿì6ÿš6 ÿš6Wÿì6Xÿ®6Yÿ×6_ÿì6`ÿ×6bÿì6ÿ®6ÿ×6ÿ®6 ÿ×6!ÿ®6"ÿ×6#ÿ®6%ÿ®6&ÿ×6'ÿ®6(ÿ×6)ÿ®6*ÿ×6+ÿ®6,ÿ×6-ÿ®6.ÿ×6/ÿ®60ÿ×61ÿ®62ÿ×63ÿ®64ÿ×66ÿ×68ÿ×6:ÿ×6<ÿ×6@ÿ×6Bÿ×6Dÿ×6Iÿì6Jÿ×6Kÿì6Lÿ×6Mÿì6Nÿ×6Oÿì6Qÿì6Rÿ×6Sÿì6Tÿ×6Uÿì6Vÿ×6Wÿì6Xÿ×6Yÿì6Zÿ×6[ÿì6\ÿ×6]ÿì6^ÿ×6_ÿì6`ÿ×6bÿì6dÿì6fÿì6hÿì6jÿì6lÿì6nÿì7��R7� �R7�ÿ®7�ÿ®7�"�)7�R7ÿ®7 �R7 ÿ®8�ÿ…8�ÿ…8�"�)8�$ÿ…8�&ÿ×8�*ÿ×8�2ÿ×8�4ÿ×8�Dÿš8�Fÿš8�Gÿš8�Hÿš8�Jÿ×8�PÿÃ8�QÿÃ8�Rÿš8�SÿÃ8�Tÿš8�UÿÃ8�Vÿ®8�XÿÃ8�]ÿ×8�‚ÿ…8�ƒÿ…8�„ÿ…8�…ÿ…8�†ÿ…8�‡ÿ…8�‰ÿ×8�”ÿ×8�•ÿ×8�–ÿ×8�—ÿ×8�˜ÿ×8�šÿ×8�¢ÿš8�£ÿš8�¤ÿš8�¥ÿš8�¦ÿš8�§ÿš8�¨ÿš8�©ÿš8�ªÿš8�«ÿš8�¬ÿš8�­ÿš8�´ÿš8�µÿš8�¶ÿš8�·ÿš8�¸ÿš8�ºÿš8�»ÿÃ8�¼ÿÃ8�½ÿÃ8�¾ÿÃ8�Âÿ…8�Ãÿš8�Äÿ…8�Åÿš8�Æÿ…8�Çÿš8�Èÿ×8�Éÿš8�Êÿ×8�Ëÿš8�Ìÿ×8�Íÿš8�Îÿ×8�Ïÿš8�Ñÿš8�Óÿš8�Õÿš8�×ÿš8�Ùÿš8�Ûÿš8�Ýÿš8�Þÿ×8�ßÿ×8�àÿ×8�áÿ×8�âÿ×8�ãÿ×8�äÿ×8�åÿ×8�úÿÃ8ÿÃ8ÿÃ8 ÿÃ8ÿ×8ÿš8ÿ×8ÿš8ÿ×8ÿš8ÿ×8ÿš8ÿÃ8ÿÃ8ÿ®8!ÿ®8+ÿÃ8-ÿÃ8/ÿÃ81ÿÃ83ÿÃ85ÿÃ8<ÿ×8>ÿ×8@ÿ×8Cÿ…8Dÿš8Fÿš8Gÿ×8Hÿš8Jÿ®8ÿ…8 ÿ…8WÿÃ8Xÿ…8Yÿš8_ÿ×8`ÿš8bÿÃ8ÿ…8ÿš8ÿ…8 ÿš8!ÿ…8"ÿš8#ÿ…8%ÿ…8&ÿš8'ÿ…8(ÿš8)ÿ…8*ÿš8+ÿ…8,ÿš8-ÿ…8.ÿš8/ÿ…80ÿš81ÿ…82ÿš83ÿ…84ÿš86ÿš88ÿš8:ÿš8<ÿš8@ÿš8Bÿš8Dÿš8Iÿ×8Jÿš8Kÿ×8Lÿš8Mÿ×8Nÿš8Oÿ×8Qÿ×8Rÿš8Sÿ×8Tÿš8Uÿ×8Vÿš8Wÿ×8Xÿš8Yÿ×8Zÿš8[ÿ×8\ÿš8]ÿ×8^ÿš8_ÿ×8`ÿš8bÿÃ8dÿÃ8fÿÃ8hÿÃ8jÿÃ8lÿÃ8nÿÃ9��R9� �R9�ÿ®9�ÿ®9�"�)9�R9ÿ®9 �R9 ÿ®:�ÿ…:�ÿ…:�"�):�$ÿ…:�&ÿ×:�*ÿ×:�2ÿ×:�4ÿ×:�Dÿš:�Fÿš:�Gÿš:�Hÿš:�Jÿ×:�PÿÃ:�QÿÃ:�Rÿš:�SÿÃ:�Tÿš:�UÿÃ:�Vÿ®:�XÿÃ:�]ÿ×:�‚ÿ…:�ƒÿ…:�„ÿ…:�…ÿ…:�†ÿ…:�‡ÿ…:�‰ÿ×:�”ÿ×:�•ÿ×:�–ÿ×:�—ÿ×:�˜ÿ×:�šÿ×:�¢ÿš:�£ÿš:�¤ÿš:�¥ÿš:�¦ÿš:�§ÿš:�¨ÿš:�©ÿš:�ªÿš:�«ÿš:�¬ÿš:�­ÿš:�´ÿš:�µÿš:�¶ÿš:�·ÿš:�¸ÿš:�ºÿš:�»ÿÃ:�¼ÿÃ:�½ÿÃ:�¾ÿÃ:�Âÿ…:�Ãÿš:�Äÿ…:�Åÿš:�Æÿ…:�Çÿš:�Èÿ×:�Éÿš:�Êÿ×:�Ëÿš:�Ìÿ×:�Íÿš:�Îÿ×:�Ïÿš:�Ñÿš:�Óÿš:�Õÿš:�×ÿš:�Ùÿš:�Ûÿš:�Ýÿš:�Þÿ×:�ßÿ×:�àÿ×:�áÿ×:�âÿ×:�ãÿ×:�äÿ×:�åÿ×:�úÿÃ:ÿÃ:ÿÃ: ÿÃ:ÿ×:ÿš:ÿ×:ÿš:ÿ×:ÿš:ÿ×:ÿš:ÿÃ:ÿÃ:ÿ®:!ÿ®:+ÿÃ:-ÿÃ:/ÿÃ:1ÿÃ:3ÿÃ:5ÿÃ:<ÿ×:>ÿ×:@ÿ×:Cÿ…:Dÿš:Fÿš:Gÿ×:Hÿš:Jÿ®:ÿ…: ÿ…:WÿÃ:Xÿ…:Yÿš:_ÿ×:`ÿš:bÿÃ:ÿ…:ÿš:ÿ…: ÿš:!ÿ…:"ÿš:#ÿ…:%ÿ…:&ÿš:'ÿ…:(ÿš:)ÿ…:*ÿš:+ÿ…:,ÿš:-ÿ…:.ÿš:/ÿ…:0ÿš:1ÿ…:2ÿš:3ÿ…:4ÿš:6ÿš:8ÿš::ÿš:<ÿš:@ÿš:Bÿš:Dÿš:Iÿ×:Jÿš:Kÿ×:Lÿš:Mÿ×:Nÿš:Oÿ×:Qÿ×:Rÿš:Sÿ×:Tÿš:Uÿ×:Vÿš:Wÿ×:Xÿš:Yÿ×:Zÿš:[ÿ×:\ÿš:]ÿ×:^ÿš:_ÿ×:`ÿš:bÿÃ:dÿÃ:fÿÃ:hÿÃ:jÿÃ:lÿÃ:nÿÃ;�&ÿì;�*ÿì;�2ÿì;�4ÿì;�‰ÿì;�”ÿì;�•ÿì;�–ÿì;�—ÿì;�˜ÿì;�šÿì;�Èÿì;�Êÿì;�Ìÿì;�Îÿì;�Þÿì;�àÿì;�âÿì;�äÿì;ÿì;ÿì;ÿì;ÿì;Gÿì;_ÿì;Iÿì;Kÿì;Mÿì;Oÿì;Qÿì;Sÿì;Uÿì;Wÿì;Yÿì;[ÿì;]ÿì;_ÿì=�&ÿì=�*ÿì=�2ÿì=�4ÿì=�‰ÿì=�”ÿì=�•ÿì=�–ÿì=�—ÿì=�˜ÿì=�šÿì=�Èÿì=�Êÿì=�Ìÿì=�Îÿì=�Þÿì=�àÿì=�âÿì=�äÿì=ÿì=ÿì=ÿì=ÿì=Gÿì=_ÿì=Iÿì=Kÿì=Mÿì=Oÿì=Qÿì=Sÿì=Uÿì=Wÿì=Yÿì=[ÿì=]ÿì=_ÿì?�&ÿì?�*ÿì?�2ÿì?�4ÿì?�‰ÿì?�”ÿì?�•ÿì?�–ÿì?�—ÿì?�˜ÿì?�šÿì?�Èÿì?�Êÿì?�Ìÿì?�Îÿì?�Þÿì?�àÿì?�âÿì?�äÿì?ÿì?ÿì?ÿì?ÿì?Gÿì?_ÿì?Iÿì?Kÿì?Mÿì?Oÿì?Qÿì?Sÿì?Uÿì?Wÿì?Yÿì?[ÿì?]ÿì?_ÿìC�ÿqC� ÿqC�&ÿ×C�*ÿ×C�- C�2ÿ×C�4ÿ×C�7ÿqC�9ÿ®C�:ÿ®C�<ÿ…C�‰ÿ×C�”ÿ×C�•ÿ×C�–ÿ×C�—ÿ×C�˜ÿ×C�šÿ×C�Ÿÿ…C�Èÿ×C�Êÿ×C�Ìÿ×C�Îÿ×C�Þÿ×C�àÿ×C�âÿ×C�äÿ×Cÿ×Cÿ×Cÿ×Cÿ×C$ÿqC&ÿqC6ÿ®C8ÿ…C:ÿ…CGÿ×Cúÿ®Cüÿ®Cþÿ®C�ÿ…CÿqC ÿqC_ÿ×CIÿ×CKÿ×CMÿ×COÿ×CQÿ×CSÿ×CUÿ×CWÿ×CYÿ×C[ÿ×C]ÿ×C_ÿ×Coÿ…Cqÿ…Csÿ…CÿqD�ÿìD� ÿìDÿìD ÿìE�-�{G�ÿ®G�ÿ®G�$ÿ×G�7ÿÃG�9ÿìG�:ÿìG�;ÿ×G�<ÿìG�=ÿìG�‚ÿ×G�ƒÿ×G�„ÿ×G�…ÿ×G�†ÿ×G�‡ÿ×G�ŸÿìG�Âÿ×G�Äÿ×G�Æÿ×G$ÿÃG&ÿÃG6ÿìG8ÿìG:ÿìG;ÿìG=ÿìG?ÿìGCÿ×G ÿìGúÿìGüÿìGþÿìG�ÿìGÿ®G ÿ®GXÿ×Gÿ×Gÿ×G!ÿ×G#ÿ×G%ÿ×G'ÿ×G)ÿ×G+ÿ×G-ÿ×G/ÿ×G1ÿ×G3ÿ×GoÿìGqÿìGsÿìGÿÃV�ÿqV� ÿqVfÿ×Vmÿ×VqÿqVrÿ…Vsÿ×Vuÿ®Vxÿ…VÿqV ÿqVTÿ…[�ÿ®[�ÿ®[Vÿ×[_ÿ×[bÿ×[dÿì[iÿ×[pÿì[qÿÃ[rÿì[tÿ×[uÿì[xÿì[ˆÿì[ÿ®[ ÿ®[Tÿì\�ÿ…\�ÿ…\Vÿ…\_ÿ…\bÿ…\fÿ×\iÿ…\mÿ×\sÿÃ\vÿì\yÿš\zÿ®\{ÿÃ\|ÿÃ\}ÿÃ\~ÿš\ÿÃ\‚ÿ®\„ÿÃ\†ÿÃ\‡ÿÃ\‰ÿÃ\Œÿš\Žÿš\ÿš\ÿš\’ÿÃ\“ÿš\•ÿÃ\–ÿÃ\˜ÿÃ\™ÿš\šÿÃ\›ÿÃ\ÿ…\ ÿ…\!ÿì]qÿ×]rÿì]xÿì]Tÿì^�ÿ×^� ÿ×^ÿ×^ ÿ×_�ÿq_� ÿq_fÿ×_mÿ×_qÿq_rÿ…_sÿ×_uÿ®_xÿ…_ÿq_ ÿq_Tÿ…`�ÿ®`�ÿ®`Vÿ×`_ÿ×`bÿ×`iÿ×`tÿ×`ÿ®` ÿ®a�ÿ…a�ÿ®a�ÿ…aVÿ\a_ÿ\abÿ\afÿÃaiÿ\amÿÃasÿšavÿÃayÿqazÿša{ÿša|ÿ®a}ÿša~ÿqa€ÿ×aÿÃa‚ÿša„ÿša†ÿ®a‡ÿša‰ÿšaŠÿ×aŒÿqaŽÿšaÿqaÿqa’ÿša“ÿqa”ÿ×a•ÿša–ÿša˜ÿša™ÿqašÿša›ÿšaÿ®aÿ®aÿ®aÿ…a ÿ…a!ÿÃaSÿ×b�ÿqb� ÿqbfÿ×bmÿ×bqÿqbrÿ…bsÿ×buÿ®bxÿ…bÿqb ÿqbTÿ…dfÿìdmÿìdsÿÃf�ÿ®f�ÿ®fVÿ×f_ÿ×fbÿ×fdÿìfiÿ×fpÿìfqÿÃfrÿìftÿ×fuÿìfxÿìfˆÿìfÿ®f ÿ®fTÿìhfÿ×hmÿ×hsÿÃhÿìh‘ÿìi�ÿqi� ÿqifÿ×imÿ×iqÿqirÿ…isÿ×iuÿ®ixÿ…iÿqi ÿqiTÿ…m�ÿ®m�ÿ®mVÿ×m_ÿ×mbÿ×mdÿìmiÿ×mpÿìmqÿÃmrÿìmtÿ×muÿìmxÿìmˆÿìmÿ®m ÿ®mTÿìo�þöo�þöoVÿšo_ÿšobÿšodÿìoiÿšotÿ×oˆÿ×oþöo þöq�ÿ…q�ÿ®q�ÿ…qVÿ\q_ÿ\qbÿ\qfÿÃqiÿ\qmÿÃqsÿšqvÿÃqyÿqqzÿšq{ÿšq|ÿ®q}ÿšq~ÿqq€ÿ×qÿÃq‚ÿšq„ÿšq†ÿ®q‡ÿšq‰ÿšqŠÿ×qŒÿqqŽÿšqÿqqÿqq’ÿšq“ÿqq”ÿ×q•ÿšq–ÿšq˜ÿšq™ÿqqšÿšq›ÿšqÿ®qÿ®qÿ®qÿ…q ÿ…q!ÿÃqSÿ×r�ÿ…r�ÿ…rVÿ…r_ÿ…rbÿ…rfÿ×riÿ…rmÿ×rsÿÃrvÿìryÿšrzÿ®r{ÿÃr|ÿÃr}ÿÃr~ÿšrÿÃr‚ÿ®r„ÿÃr†ÿÃr‡ÿÃr‰ÿÃrŒÿšrŽÿšrÿšrÿšr’ÿÃr“ÿšr•ÿÃr–ÿÃr˜ÿÃr™ÿšršÿÃr›ÿÃrÿ…r ÿ…r!ÿìs�ÿšs�ÿšsVÿ×s_ÿ×sbÿ×sdÿÃsiÿ×spÿìsqÿ®srÿÃstÿìsxÿÃsˆÿìsÿšs ÿšsTÿÃtfÿ×tmÿ×tsÿÃtÿìt‘ÿìu�ÿ…u�ÿ…uVÿ®u_ÿ®ubÿ®ufÿìuiÿ®umÿìuÿ…u ÿ…vqÿ×vrÿìvxÿìvTÿìx�ÿ…x�ÿ…xVÿ…x_ÿ…xbÿ…xfÿ×xiÿ…xmÿ×xsÿÃxvÿìxyÿšxzÿ®x{ÿÃx|ÿÃx}ÿÃx~ÿšxÿÃx‚ÿ®x„ÿÃx†ÿÃx‡ÿÃx‰ÿÃxŒÿšxŽÿšxÿšxÿšx’ÿÃx“ÿšx•ÿÃx–ÿÃx˜ÿÃx™ÿšxšÿÃx›ÿÃxÿ…x ÿ…x!ÿìyˆ�){�ÿì{� ÿì{ÿì{ ÿì|�ÿ®|� ÿ®|ÿì|‘ÿì|ÿ®| ÿ®~ˆ�)€�ÿ®€�ÿ®€ˆÿì€ÿ®€ ÿ®ƒ�ÿšƒyÿ׃~ÿ׃ÿ׃Œÿ׃ÿ׃ÿ׃ÿ׃‘ÿ׃“ÿ׃™ÿ׃ÿšƒÿšƒÿš„�ÿì„� ÿì„ÿì„ ÿì…�ÿ×…�ÿ×…ÿ×… ÿ׆�ÿ®†� ÿ®†ÿ솑ÿì†ÿ®† ÿ®‡yÿׇ~ÿׇŒÿׇÿׇÿׇ“ÿׇ™ÿ׈�ÿ…ˆ� ÿ…ˆyÿìˆ~ÿ숀ÿ׈Šÿ׈Œÿìˆÿ׈ÿìˆÿ숑ÿ׈“ÿ숙ÿìˆÿ…ˆ ÿ…Š�ÿ®Š�ÿ®ŠˆÿìŠÿ®Š ÿ®Œ�ÿìŒ� ÿ쌀ÿ׌Šÿ׌ÿìŒ ÿìŽ�ÿìŽ� ÿ쎀ÿ׎Šÿ׎ÿìŽ ÿì�ÿì�ÿìÿì ÿì“�ÿì“� ÿì“€ÿדŠÿדÿì“ ÿì”�ÿÔ�ÿ×”�ÿÔyÿ×”~ÿ×”ÿ×”Œÿ×”ÿ×”ÿ×”“ÿ×”™ÿ×”ÿ×”ÿ×”ÿ×”ÿÔ ÿ×�ÿ×—� ÿ×—ÿ×— ÿ×™�ÿì™� ÿ와ÿ×™Šÿ×™ÿì™ ÿì�ÿ®� ÿ®ÿ…¦ÿ…¨ÿ×¼ÿš½ÿ×ÁÿšÄÿ…Üÿ×Ýÿ×áÿ×äÿ×öÿ×ÿ® ÿ®nÿ®|ÿš€ÿ®‚ÿ®—ÿ®›ÿ®§ÿ®©ÿ…ªÿ×µÿš¶ÿ×·ÿš¸ÿ×¹ÿšºÿ×½ÿ…¾ÿ׿ÿšÀÿ×ÁÿšÂÿ×ÔÿšÕÿ×÷ÿ×øÿ×ùÿ×úÿ×ûÿ×üÿ×ýÿšþÿ×ÿ® ÿšÿÃÿšÿÃÿ…ÿמ�ÿ…ž�ÿ®ž�ÿ…žŸÿמ¤ÿšžªÿqž®ÿšžµÿšž¸ÿמ»ÿמ¼�)ž¾ÿ®žÌÿšžÍÿšžÎÿ…žÏÿqžÐÿמÑÿמÒÿšžÓÿšžÔÿšžÕÿ…žÖÿšž×ÿšžØÿqžÙÿšžÚÿšžÛÿqžÜÿ®žÝÿ®žÞÿqžßÿמàÿšžáÿšžâÿšžãÿšžäÿ®žåÿšžæÿšžçÿמèÿšžéÿÞêÿqžìÿšžíÿqžîÿ…žòÿ…žóÿšžõÿšžöÿ®ž÷ÿšžùÿšžÿ®žÿ®žÿ®žÿ…ž ÿ…žjÿqžkÿšžlÿמmÿמqÿšžrÿqžsÿ…žuÿšžwÿšžyÿšž}ÿšž~ÿמÿqžÿמƒÿמ„ÿמ…ÿqž†ÿמ‡ÿqžˆÿמ‰ÿqžŠÿמ‹ÿמŒÿמÿqž–ÿšžšÿšžžÿšž ÿמ¢ÿמ¤ÿšž¦ÿšžªÿ®ž¬ÿšž®ÿšž°ÿšž±ÿמ²ÿqž³ÿמ´ÿqžµ�)ž¶ÿ®ž¸ÿ®žºÿ®ž¼ÿמ¾ÿ®žÀÿšžÂÿšžÄÿšžÅÿšžÆÿqžÇÿšžÈÿqžËÿמÍÿšžÎÿšžÏÿ…žÑÿšžÓÿšžÕÿšž×ÿšžÙÿqžÛÿqžÝÿqžàÿqžæÿמèÿמêÿÞìÿšžîÿšžïÿמðÿqžñÿמòÿqžóÿמôÿqžöÿמøÿ®žúÿ®žüÿ®žþÿšž�ÿšžÿšžÿמÿמ ÿqž ÿqž ÿqž ÿqžÿšžÿšžÿšžÿ…žÿšžÿמÿqžÿ®žÿqžÿšžÿ…ŸŸÿן¸ÿן»ÿן¾ÿןáÿןlÿן~ÿן„ÿן†ÿןˆÿןŠÿןŒÿן±ÿן³ÿןÀÿןÂÿןÅÿןÇÿןÕÿןïÿןñÿןóÿןþÿן ÿן ÿןÿןÿןÿ× ÿ× ÿפ�ÿ®¤� ÿ®¤ÿ…¤¦ÿ…¤¨ÿפ¼ÿš¤½ÿפÁÿš¤Äÿ…¤ÜÿפÝÿפáÿפäÿפöÿפÿ®¤ ÿ®¤nÿ®¤|ÿš¤€ÿ®¤‚ÿ®¤—ÿ®¤›ÿ®¤§ÿ®¤©ÿ…¤ªÿפµÿš¤¶ÿפ·ÿš¤¸ÿפ¹ÿš¤ºÿפ½ÿ…¤¾ÿפ¿ÿš¤ÀÿפÁÿš¤ÂÿפÔÿš¤Õÿפ÷ÿפøÿפùÿפúÿפûÿפüÿפýÿš¤þÿפÿ®¤ ÿš¤ÿäÿš¤ÿäÿ…¤ÿ×¥�ÿ®¥� ÿ®¥ÿ…¥¦ÿ…¥¨ÿ×¥¼ÿš¥½ÿ×¥Áÿš¥Äÿ…¥Üÿ×¥Ýÿ×¥áÿ×¥äÿ×¥öÿ×¥ÿ®¥ ÿ®¥nÿ®¥|ÿš¥€ÿ®¥‚ÿ®¥—ÿ®¥›ÿ®¥§ÿ®¥©ÿ…¥ªÿ×¥µÿš¥¶ÿ×¥·ÿš¥¸ÿ×¥¹ÿš¥ºÿ×¥½ÿ…¥¾ÿ×¥¿ÿš¥Àÿ×¥Áÿš¥Âÿ×¥Ôÿš¥Õÿ×¥÷ÿ×¥øÿ×¥ùÿ×¥úÿ×¥ûÿ×¥üÿ×¥ýÿš¥þÿ×¥ÿ®¥ ÿš¥ÿÃ¥ÿš¥ÿÃ¥ÿ…¥ÿצ�ÿ®¦� ÿ®¦ÿ…¦¦ÿ…¦¨ÿצ¼ÿš¦½ÿצÁÿš¦Äÿ…¦ÜÿצÝÿצáÿצäÿצöÿצÿ®¦ ÿ®¦nÿ®¦|ÿš¦€ÿ®¦‚ÿ®¦—ÿ®¦›ÿ®¦§ÿ®¦©ÿ…¦ªÿצµÿš¦¶ÿצ·ÿš¦¸ÿצ¹ÿš¦ºÿצ½ÿ…¦¾ÿצ¿ÿš¦ÀÿצÁÿš¦ÂÿצÔÿš¦Õÿצ÷ÿצøÿצùÿצúÿצûÿצüÿצýÿš¦þÿצÿ®¦ ÿš¦ÿæÿš¦ÿæÿ…¦ÿ×§Ÿÿ×§¸ÿ×§»ÿ×§¾ÿ×§Áÿ×§áÿ×§lÿ×§|ÿ×§~ÿ×§„ÿ×§†ÿ×§ˆÿ×§Šÿ×§Œÿ×§±ÿ×§³ÿ×§¿ÿ×§Àÿ×§Áÿ×§Âÿ×§Åÿš§Çÿš§Ôÿ×§Õÿ×§ïÿ×§ñÿ×§óÿ×§ýÿ×§þÿ×§ ÿ×§ ÿ×§ÿ×§ÿ×§ÿ×§ÿì¨�ÿ…¨�ÿ…¨Ÿÿ쨤ÿš¨ªÿq¨®ÿš¨µÿš¨¸ÿ쨻ÿ쨾ÿèÉÿì¨Îÿ®¨ÏÿרÕÿ®¨ØÿרÛÿרÞÿרáÿרêÿרë�f¨íÿרîÿì¨òÿ®¨ô�f¨ÿ…¨ ÿ…¨jÿרlÿì¨rÿq¨sÿ®¨~ÿì¨ÿר„ÿ쨅ÿר†ÿ쨇ÿרˆÿ쨉ÿרŠÿ쨌ÿì¨ÿר˜�f¨¨�f¨±ÿ쨲ÿר³ÿ쨴ÿרÀÿרÂÿרÅÿרÆÿèÇÿרÈÿèÎÿš¨Ïÿ®¨ÕÿרÙÿq¨Ûÿq¨Ýÿq¨àÿרïÿì¨ðÿרñÿì¨òÿרóÿì¨ôÿרþÿר ÿq¨ ÿר ÿq¨ ÿרÿš¨ÿ®¨ÿì¨ÿרÿרÿš¨ÿ®ª�ÿqª� ÿqªÿšª¦ÿšª¼ÿqª¾ÿתÁÿšªÄÿšªÜÿתáÿתäÿתÿqª ÿqªnÿת|ÿšª€ÿ®ª‚ÿ®ª—ÿת›ÿת§ÿת©ÿšªªÿתµÿqª¶ÿת·ÿ…ª¹ÿ…ª½ÿšª¾ÿת¿ÿšªÀÿתÁÿšªÂÿתÅÿšªÇÿšªÔÿšªÕÿתáÿתãÿתýÿšªþÿתÿת ÿqªÿתÿqªÿתÿšªÿ׫�ÿ׫� ÿ׫ªÿì«Áÿ׫ÿ׫ ÿ׫rÿì«|ÿ׫¿ÿ׫Áÿ׫Åÿ׫Çÿ׫Ôÿ׫Ùÿì«Ûÿì«Ýÿì«ýÿ׬�ÿ®¬�ÿ®¬ÿ®¬ ÿ®¬€ÿ쬂ÿ쬷ÿ쬹ÿì¬ ÿ׬ÿ×­�ÿ…­�ÿ®­�ÿ…­Ÿÿ×­¤ÿš­ªÿq­®ÿš­µÿš­¸ÿ×­»ÿ×­¼�)­¾ÿ®­Ìÿš­Íÿš­Îÿ…­Ïÿq­Ðÿ×­Ñÿ×­Òÿš­Óÿš­Ôÿš­Õÿ…­Öÿš­×ÿš­Øÿq­Ùÿš­Úÿš­Ûÿq­Üÿ®­Ýÿ®­Þÿq­ßÿ×­àÿš­áÿš­âÿš­ãÿš­äÿ®­åÿš­æÿš­çÿ×­èÿš­éÿíêÿq­ìÿš­íÿq­îÿ…­òÿ…­óÿš­õÿš­öÿ®­÷ÿš­ùÿš­ÿ®­ÿ®­ÿ®­ÿ…­ ÿ…­jÿq­kÿš­lÿ×­mÿ×­qÿš­rÿq­sÿ…­uÿš­wÿš­yÿš­}ÿš­~ÿ×­ÿq­ÿ×­ƒÿ×­„ÿ×­…ÿq­†ÿ×­‡ÿq­ˆÿ×­‰ÿq­Šÿ×­‹ÿ×­Œÿ×­ÿq­–ÿš­šÿš­žÿš­ ÿ×­¢ÿ×­¤ÿš­¦ÿš­ªÿ®­¬ÿš­®ÿš­°ÿš­±ÿ×­²ÿq­³ÿ×­´ÿq­µ�)­¶ÿ®­¸ÿ®­ºÿ®­¼ÿ×­¾ÿ®­Àÿš­Âÿš­Äÿš­Åÿš­Æÿq­Çÿš­Èÿq­Ëÿ×­Íÿš­Îÿš­Ïÿ…­Ñÿš­Óÿš­Õÿš­×ÿš­Ùÿq­Ûÿq­Ýÿq­àÿq­æÿ×­èÿ×­êÿíìÿš­îÿš­ïÿ×­ðÿq­ñÿ×­òÿq­óÿ×­ôÿq­öÿ×­øÿ®­úÿ®­üÿ®­þÿš­�ÿš­ÿš­ÿ×­ÿ×­ ÿq­ ÿq­ ÿq­ ÿq­ÿš­ÿš­ÿš­ÿ…­ÿš­ÿ×­ÿq­ÿ®­ÿq­ÿš­ÿ…®£�á®ê�)®ÿ×®ÿ×°Ÿÿ×°¸ÿ×°»ÿ×°¾ÿ×°Áÿ×°áÿ×°lÿ×°|ÿ×°~ÿ×°„ÿ×°†ÿ×°ˆÿ×°Šÿ×°Œÿ×°±ÿ×°³ÿ×°¿ÿ×°Àÿ×°Áÿ×°Âÿ×°Åÿš°Çÿš°Ôÿ×°Õÿ×°ïÿ×°ñÿ×°óÿ×°ýÿ×°þÿ×° ÿ×° ÿ×°ÿ×°ÿ×°ÿ×°ÿì±�ÿ®±�ÿ®±ÿ®± ÿ®±€ÿ챂ÿì±·ÿì±¹ÿì± ÿ×±ÿ×´Ÿÿ×´¸ÿ×´»ÿ×´¾ÿ×´Áÿ×´áÿ×´lÿ×´|ÿ×´~ÿ×´„ÿ×´†ÿ×´ˆÿ×´Šÿ×´Œÿ×´±ÿ×´³ÿ×´¿ÿ×´Àÿ×´Áÿ×´Âÿ×´Åÿš´Çÿš´Ôÿ×´Õÿ×´ïÿ×´ñÿ×´óÿ×´ýÿ×´þÿ×´ ÿ×´ ÿ×´ÿ×´ÿ×´ÿ×´ÿì¸�ÿ®¸�ÿ®¸ÿ츤ÿ׸¦ÿ츨ÿ׸ªÿ׸®ÿ׸°ÿ׸±ÿ층ÿ׸¼ÿø½ÿ׸¿ÿ׸Áÿ׸Äÿì¸Çÿì¸Îÿì¸Õÿì¸òÿì¸ÿ®¸ ÿ®¸rÿ׸sÿì¸zÿì¸|ÿ׸€ÿ츂ÿ츟ÿ׸¡ÿ츩ÿ층ÿø·ÿ츹ÿ츻ÿ׸½ÿ츿ÿ׸Áÿ׸Êÿ׸Îÿ׸Ïÿì¸Ôÿ׸Ùÿ׸Ûÿ׸Ýÿ׸åÿ׸çÿì¸õÿì¸÷ÿ׸ùÿ׸ûÿ׸ýÿ׸ÿ׸ÿ׸ ÿ׸ÿ׸ÿ׸ÿì¸ÿì¸ÿ׸ÿìº�þöº�þöº¤ÿ…ºªÿšº®ÿ…º°ÿ׺µÿ…º¿ÿ׺ÎÿšºÕÿšºòÿšºþöº þöºrÿšºsÿšºvÿ캟ÿ׺»ÿ׺Êÿ׺Îÿ…ºÏÿšºÙÿšºÛÿšºÝÿšºåÿ׺ÿ׺ÿ׺ ÿ®º ÿ®ºÿ…ºÿšºÿ…ºÿš»Ÿÿ×»¸ÿ×»»ÿ×»¾ÿ×»áÿ×»lÿ×»~ÿ×»„ÿ×»†ÿ×»ˆÿ×»Šÿ×»Œÿ×»±ÿ×»³ÿ×»Àÿ×»Âÿ×»Åÿ×»Çÿ×»Õÿ×»ïÿ×»ñÿ×»óÿ×»þÿ×» ÿ×» ÿ×»ÿ×»ÿ×»ÿ×¼�ÿ…¼�ÿ®¼�ÿ…¼Ÿÿ×¼¤ÿš¼ªÿq¼®ÿš¼µÿš¼¸ÿ×¼»ÿ×¼¼�)¼¾ÿ®¼Ìÿš¼Íÿš¼Îÿ…¼Ïÿq¼Ðÿ×¼Ñÿ×¼Òÿš¼Óÿš¼Ôÿš¼Õÿ…¼Öÿš¼×ÿš¼Øÿq¼Ùÿš¼Úÿš¼Ûÿq¼Üÿ®¼Ýÿ®¼Þÿq¼ßÿ×¼àÿš¼áÿš¼âÿš¼ãÿš¼äÿ®¼åÿš¼æÿš¼çÿ×¼èÿš¼éÿüêÿq¼ìÿš¼íÿq¼îÿ…¼òÿ…¼óÿš¼õÿš¼öÿ®¼÷ÿš¼ùÿš¼ÿ®¼ÿ®¼ÿ®¼ÿ…¼ ÿ…¼jÿq¼kÿš¼lÿ×¼mÿ×¼qÿš¼rÿq¼sÿ…¼uÿš¼wÿš¼yÿš¼}ÿš¼~ÿ×¼ÿq¼ÿ×¼ƒÿ×¼„ÿ×¼…ÿq¼†ÿ×¼‡ÿq¼ˆÿ×¼‰ÿq¼Šÿ×¼‹ÿ×¼Œÿ×¼ÿq¼–ÿš¼šÿš¼žÿš¼ ÿ×¼¢ÿ×¼¤ÿš¼¦ÿš¼ªÿ®¼¬ÿš¼®ÿš¼°ÿš¼±ÿ×¼²ÿq¼³ÿ×¼´ÿq¼µ�)¼¶ÿ®¼¸ÿ®¼ºÿ®¼¼ÿ×¼¾ÿ®¼Àÿš¼Âÿš¼Äÿš¼Åÿš¼Æÿq¼Çÿš¼Èÿq¼Ëÿ×¼Íÿš¼Îÿš¼Ïÿ…¼Ñÿš¼Óÿš¼Õÿš¼×ÿš¼Ùÿq¼Ûÿq¼Ýÿq¼àÿq¼æÿ×¼èÿ×¼êÿüìÿš¼îÿš¼ïÿ×¼ðÿq¼ñÿ×¼òÿq¼óÿ×¼ôÿq¼öÿ×¼øÿ®¼úÿ®¼üÿ®¼þÿš¼�ÿš¼ÿš¼ÿ×¼ÿ×¼ ÿq¼ ÿq¼ ÿq¼ ÿq¼ÿš¼ÿš¼ÿš¼ÿ…¼ÿš¼ÿ×¼ÿq¼ÿ®¼ÿq¼ÿš¼ÿ…½�ÿ…½�ÿ…½Ÿÿ콤ÿš½ªÿq½®ÿš½µÿš½¸ÿì½»ÿì½¾ÿýÉÿì½Îÿ®½Ïÿ×½Õÿ®½Øÿ×½Ûÿ×½Þÿ×½áÿ×½êÿ×½ë�f½íÿ×½îÿì½òÿ®½ô�f½ÿ…½ ÿ…½jÿ×½lÿì½rÿq½sÿ®½~ÿì½ÿ×½„ÿì½…ÿ×½†ÿ콇ÿ×½ˆÿ콉ÿ×½Šÿ콌ÿì½ÿ×½˜�f½¨�f½±ÿì½²ÿ×½³ÿì½´ÿ×½Àÿ×½Âÿ×½Åÿ×½ÆÿýÇÿ×½ÈÿýÎÿš½Ïÿ®½Õÿ×½Ùÿq½Ûÿq½Ýÿq½àÿ×½ïÿì½ðÿ×½ñÿì½òÿ×½óÿì½ôÿ×½þÿ×½ ÿq½ ÿ×½ ÿq½ ÿ×½ÿš½ÿ®½ÿì½ÿ×½ÿ×½ÿš½ÿ®¾�ÿ®¾�ÿ®¾ÿ×¾¤ÿ×¾¦ÿ×¾¨ÿþªÿ×¾®ÿ×¾°ÿ×¾±ÿ×¾µÿ×¾¼ÿþ½ÿþ¿ÿ×¾Äÿ×¾Çÿ×¾Îÿì¾Õÿì¾òÿì¾ÿ®¾ ÿ®¾rÿ×¾sÿì¾zÿ×¾€ÿ쾂ÿ쾟ÿ×¾¡ÿ×¾©ÿ×¾µÿþ·ÿþ¹ÿþ»ÿ×¾½ÿ×¾Êÿ×¾Îÿ×¾Ïÿì¾Ùÿ×¾Ûÿ×¾Ýÿ×¾åÿ×¾çÿ×¾õÿ×¾÷ÿþùÿþûÿþÿ×¾ÿ×¾ ÿ×¾ÿ×¾ÿ×¾ÿì¾ÿ×¾ÿ×¾ÿ쿟ÿ׿¸ÿ׿»ÿ׿¾ÿ׿Áÿ׿áÿ׿lÿ׿|ÿ׿~ÿ׿„ÿ׿†ÿ׿ˆÿ׿Šÿ׿Œÿ׿±ÿ׿³ÿ׿¿ÿ׿Àÿ׿Áÿ׿Âÿ׿Åÿš¿Çÿš¿Ôÿ׿Õÿ׿ïÿ׿ñÿ׿óÿ׿ýÿ׿þÿ׿ ÿ׿ ÿ׿ÿ׿ÿ׿ÿ׿ÿìÀ£�áÀê�)Àÿ×Àÿ×ã�áÃê�)Ãÿ×Ãÿ×Ä�ÿ®Ä� ÿ®Äÿ…Ħÿ…Ĩÿ×ļÿšÄ½ÿ×ÄÁÿšÄÄÿ…ÄÜÿ×ÄÝÿ×Äáÿ×Ääÿ×Äöÿ×Äÿ®Ä ÿ®Änÿ®Ä|ÿšÄ€ÿ®Ä‚ÿ®Ä—ÿ®Ä›ÿ®Ä§ÿ®Ä©ÿ…Īÿ×ĵÿšÄ¶ÿ×Ä·ÿšÄ¸ÿ×ĹÿšÄºÿ׼ÿ…ľÿ×Ä¿ÿšÄÀÿ×ÄÁÿšÄÂÿ×ÄÔÿšÄÕÿ×Ä÷ÿ×Äøÿ×Äùÿ×Äúÿ×Äûÿ×Äüÿ×ÄýÿšÄþÿ×Äÿ®Ä ÿšÄÿÃÄÿšÄÿÃÄÿ…Äÿׯ�ÿ®Æ� ÿ®Æÿ…Ʀÿ…ƨÿׯ¼ÿšÆ½ÿׯÁÿšÆÄÿ…ÆÜÿׯÝÿׯáÿׯäÿׯöÿׯÿ®Æ ÿ®Ænÿ®Æ|ÿšÆ€ÿ®Æ‚ÿ®Æ—ÿ®Æ›ÿ®Æ§ÿ®Æ©ÿ…ƪÿׯµÿšÆ¶ÿׯ·ÿšÆ¸ÿׯ¹ÿšÆºÿׯ½ÿ…ƾÿׯ¿ÿšÆÀÿׯÁÿšÆÂÿׯÔÿšÆÕÿׯ÷ÿׯøÿׯùÿׯúÿׯûÿׯüÿׯýÿšÆþÿׯÿ®Æ ÿšÆÿÃÆÿšÆÿÃÆÿ…Æÿ×Ç�ÿ®Ç�ÿ®ÇÿìǤÿ×ǦÿìǨÿ×Ǫÿ×Ç®ÿ×ǰÿ×DZÿìǵÿ×ǼÿÃǽÿ×Ç¿ÿ×ÇÁÿ×ÇÄÿìÇÇÿìÇÎÿìÇÕÿìÇòÿìÇÿ®Ç ÿ®Çrÿ×ÇsÿìÇzÿìÇ|ÿ×Ç€ÿìÇ‚ÿìÇŸÿ×Ç¡ÿìÇ©ÿìǵÿÃÇ·ÿìǹÿìÇ»ÿ×ǽÿìÇ¿ÿ×ÇÁÿ×ÇÊÿ×ÇÎÿ×ÇÏÿìÇÔÿ×ÇÙÿ×ÇÛÿ×ÇÝÿ×Çåÿ×ÇçÿìÇõÿìÇ÷ÿ×Çùÿ×Çûÿ×Çýÿ×Çÿ×Çÿ×Ç ÿ×Çÿ×Çÿ×ÇÿìÇÿìÇÿ×ÇÿìÈ�ÿ®È�ÿ®ÈÿìȤÿ×ȦÿìȨÿ×Ȫÿ×È®ÿ×Ȱÿ×ȱÿìȵÿ×ȼÿÃȽÿ×È¿ÿ×ÈÁÿ×ÈÄÿìÈÇÿìÈÎÿìÈÕÿìÈòÿìÈÿ®È ÿ®Èrÿ×ÈsÿìÈzÿìÈ|ÿ×È€ÿìÈ‚ÿìÈŸÿ×È¡ÿìÈ©ÿìȵÿÃÈ·ÿìȹÿìÈ»ÿ×ȽÿìÈ¿ÿ×ÈÁÿ×ÈÊÿ×ÈÎÿ×ÈÏÿìÈÔÿ×ÈÙÿ×ÈÛÿ×ÈÝÿ×Èåÿ×ÈçÿìÈõÿìÈ÷ÿ×Èùÿ×Èûÿ×Èýÿ×Èÿ×Èÿ×È ÿ×Èÿ×Èÿ×ÈÿìÈÿìÈÿ×ÈÿìÊ�ÿìÊ� ÿìÊÿìÊ ÿìÌé�)Í�ÿšÍ�ÿ×Í�ÿšÍÎÿÃÍÏÿìÍÕÿÃÍØÿìÍÛÿìÍÞÿìÍêÿìÍíÿìÍòÿÃÍÿ×Íÿ×Íÿ×ÍÿšÍ ÿšÍjÿìÍsÿÃÍÿìÍ…ÿì͇ÿì͉ÿìÍÿìͲÿìÍ´ÿìÍÏÿÃÍàÿìÍðÿìÍòÿìÍôÿìÍ ÿìÍ ÿìÍÿÃÍÿìÍÿìÍÿÃÎ�ÿìÎ� ÿìÎÿìÎ ÿìÏ�ÿìÏ� ÿìÏÿìÏ ÿìÐÏÿ×ÐØÿ×ÐÛÿ×ÐÞÿ×Ðáÿ×Ðêÿ×Ðíÿ×Ðjÿ×Ðÿ×Ð…ÿ×Їÿ×Љÿ×Ðÿ×вÿ×дÿ×ÐÀÿ×ÐÂÿ×ÐÆÿ×ÐÈÿ×ÐÕÿ×Ðàÿ×Ððÿ×Ðòÿ×Ðôÿ×Ðþÿ×Ð ÿ×Ð ÿ×Ðÿ×Ðÿ×Ñé�)ÔÏÿ×ÔØÿ×ÔÛÿ×ÔÞÿ×Ôáÿ×Ôêÿ×Ôíÿ×Ôjÿ×Ôÿ×Ô…ÿ×Ô‡ÿ×Ô‰ÿ×Ôÿ×Ô²ÿ×Ô´ÿ×ÔÀÿ×ÔÂÿ×ÔÆÿ×ÔÈÿ×ÔÕÿ×Ôàÿ×Ôðÿ×Ôòÿ×Ôôÿ×Ôþÿ×Ô ÿ×Ô ÿ×Ôÿ×Ôÿר�ÿìØ� ÿìØÐÿרÜÿìØÝÿìØßÿרáÿìØäÿìØöÿìØÿìØ ÿìØ ÿרªÿìØ¶ÿìØ¼ÿר¾ÿìØÀÿìØÂÿìØËÿרÕÿìØæÿרøÿìØúÿìØüÿìØþÿìØÿרÿרÿìØÿìØÿìÚ�ÿìÚ� ÿìÚÐÿ×ÚÜÿìÚÝÿìÚßÿ×ÚáÿìÚäÿìÚöÿìÚÿìÚ ÿìÚ ÿ×ÚªÿìÚ¶ÿìÚ¼ÿ×Ú¾ÿìÚÀÿìÚÂÿìÚËÿ×ÚÕÿìÚæÿ×ÚøÿìÚúÿìÚüÿìÚþÿìÚÿ×Úÿ×ÚÿìÚÿìÚÿìÜ�ÿšÜ�ÿ×Ü�ÿšÜÎÿÃÜÏÿìÜÕÿÃÜØÿìÜÛÿìÜÞÿìÜêÿìÜíÿìÜòÿÃÜÿ×Üÿ×Üÿ×ÜÿšÜ ÿšÜjÿìÜsÿÃÜÿìÜ…ÿì܇ÿì܉ÿìÜÿìܲÿìÜ´ÿìÜÏÿÃÜàÿìÜðÿìÜòÿìÜôÿìÜ ÿìÜ ÿìÜÿÃÜÿìÜÿìÜÿÃÝ�ÿ®Ý�ÿ®ÝÎÿ×ÝÕÿ×Ýòÿ×Ýÿ®Ý ÿ®Ýsÿ×ÝÏÿ×Ýÿ×Ýÿ×Þ�ÿìÞ� ÿìÞÐÿ×ÞÜÿìÞÝÿìÞßÿ×ÞáÿìÞäÿìÞöÿìÞÿìÞ ÿìÞ ÿ×ÞªÿìÞ¶ÿìÞ¼ÿ×Þ¾ÿìÞÀÿìÞÂÿìÞËÿ×ÞÕÿìÞæÿ×ÞøÿìÞúÿìÞüÿìÞþÿìÞÿ×Þÿ×ÞÿìÞÿìÞÿìßÏÿ×ߨÿ×ßÛÿ×ßÞÿ×ßáÿ×ßêÿ×ßíÿ×ßjÿ×ßÿ×ß…ÿ×߇ÿ×߉ÿ×ßÿ×ß²ÿ×ß´ÿ×ßÀÿ×ßÂÿ×߯ÿ×ßÈÿ×ßÕÿ×ßàÿ×ßðÿ×ßòÿ×ßôÿ×ßþÿ×ß ÿ×ß ÿ×ßÿ×ßÿ×à�ÿìà� ÿìàÿìà ÿìã�ÿìã� ÿìãÿìã ÿìä�ÿ…ä� ÿ…äÐÿ×äÜÿšäÝÿÃäßÿ×äáÿ®ääÿšäöÿÃäÿ…ä ÿ…ämÿ×äÿ×äƒÿ×ä‹ÿ×ä ÿ×äªÿšä¶ÿšä¸ÿÃäºÿÃä¼ÿ×ä¾ÿšäÀÿ®äÂÿ®äÆÿ×äÈÿ×äËÿ×äÕÿ®äæÿ×äêÿ×äøÿÃäúÿÃäüÿÃäþÿ®äÿ×äÿ×äÿšäÿšäÿšæ�ÿ…æ� ÿ…æÐÿ׿ÜÿšæÝÿÃæßÿ׿áÿ®æäÿšæöÿÃæÿ…æ ÿ…æmÿ׿ÿ׿ƒÿ׿‹ÿ׿ ÿ׿ªÿšæ¶ÿšæ¸ÿÃæºÿÃæ¼ÿ׿¾ÿšæÀÿ®æÂÿ®æÆÿ׿Èÿ׿Ëÿ׿Õÿ®ææÿ׿êÿ׿øÿÃæúÿÃæüÿÃæþÿ®æÿ׿ÿ׿ÿšæÿšæÿšç�ÿìç� ÿìçÐÿ×çÜÿìçÝÿìçßÿ×çáÿìçäÿìçöÿìçÿìç ÿìç ÿ×çªÿìç¶ÿìç¼ÿ×ç¾ÿìçÀÿìçÂÿìçËÿ×çÕÿìçæÿ×çøÿìçúÿìçüÿìçþÿìçÿ×çÿ×çÿìçÿìçÿìè�ÿìè� ÿìèÐÿ×èÜÿìèÝÿìèßÿ×èáÿìèäÿìèöÿìèÿìè ÿìè ÿ×èªÿìè¶ÿìè¼ÿ×è¾ÿìèÀÿìèÂÿìèËÿ×èÕÿìèæÿ×èøÿìèúÿìèüÿìèþÿìèÿ×èÿ×èÿìèÿìèÿìê�ÿìê� ÿìêÿìê ÿìë�ÿìë� ÿìëÿìë ÿìëÿ×ëÿ×ì�ÿšì�ÿ×ì�ÿšìÎÿÃìÏÿììÕÿÃìØÿììÛÿììÞÿììêÿììíÿììòÿÃìÿ×ìÿ×ìÿ×ìÿšì ÿšìjÿììsÿÃìÿìì…ÿìì‡ÿìì‰ÿììÿìì²ÿìì´ÿììÏÿÃìàÿììðÿììòÿììôÿìì ÿìì ÿììÿÃìÿììÿììÿÃò�ÿ…ò� ÿ…òÐÿ×òÜÿšòÝÿÃòßÿ×òáÿ®òäÿšòöÿÃòÿ…ò ÿ…òmÿ×òÿ×òƒÿ×ò‹ÿ×ò ÿ×òªÿšò¶ÿšò¸ÿÃòºÿÃò¼ÿ×ò¾ÿšòÀÿ®òÂÿ®òÆÿ×òÈÿ×òËÿ×òÕÿ®òæÿ×òêÿ×òøÿÃòúÿÃòüÿÃòþÿ®òÿ×òÿ×òÿšòÿšòÿšó�ÿ…ó� ÿ…óÐÿ×óÜÿšóÝÿÃóßÿ×óáÿ®óäÿšóöÿÃóÿ…ó ÿ…ómÿ×óÿ×óƒÿ×ó‹ÿ×ó ÿ×óªÿšó¶ÿšó¸ÿÃóºÿÃó¼ÿ×ó¾ÿšóÀÿ®óÂÿ®óÆÿ×óÈÿ×óËÿ×óÕÿ®óæÿ×óêÿ×óøÿÃóúÿÃóüÿÃóþÿ®óÿ×óÿ×óÿšóÿšóÿšô�ÿìô� ÿìôÿìô ÿìôÿ×ôÿ×õÏÿ×õØÿ×õÛÿ×õÞÿ×õáÿ×õêÿ×õíÿ×õjÿ×õÿ×õ…ÿ×õ‡ÿ×õ‰ÿ×õÿ×õ²ÿ×õ´ÿ×õÀÿ×õÂÿ×õÆÿ×õÈÿ×õÕÿ×õàÿ×õðÿ×õòÿ×õôÿ×õþÿ×õ ÿ×õ ÿ×õÿ×õÿ×ö�ÿ®ö�ÿ®öÎÿ×öÕÿ×öòÿ×öÿ®ö ÿ®ösÿ×öÏÿ×öÿ×öÿ×ø�ÿ…ø�ÿ®ø�ÿ…øŸÿ×ø¤ÿšøªÿqø®ÿšøµÿšø¸ÿ×ø»ÿ×ø¼�)ø¾ÿ®øÌÿšøÍÿšøÎÿ…øÏÿqøÐÿ×øÑÿ×øÒÿšøÓÿšøÔÿšøÕÿ…øÖÿšø×ÿšøØÿqøÙÿšøÚÿšøÛÿqøÜÿ®øÝÿ®øÞÿqøßÿ×øàÿšøáÿšøâÿšøãÿšøäÿ®øåÿšøæÿšøçÿ×øèÿšøéÿÃøêÿqøìÿšøíÿqøîÿ…øòÿ…øóÿšøõÿšøöÿ®ø÷ÿšøùÿšøÿ®øÿ®øÿ®øÿ…ø ÿ…øjÿqøkÿšølÿ×ømÿ×øqÿšørÿqøsÿ…øuÿšøwÿšøyÿšø}ÿšø~ÿ×øÿqøÿ×øƒÿ×ø„ÿ×ø…ÿqø†ÿ×ø‡ÿqøˆÿ×ø‰ÿqøŠÿ×ø‹ÿ×øŒÿ×øÿqø–ÿšøšÿšøžÿšø ÿ×ø¢ÿ×ø¤ÿšø¦ÿšøªÿ®ø¬ÿšø®ÿšø°ÿšø±ÿ×ø²ÿqø³ÿ×ø´ÿqøµ�)ø¶ÿ®ø¸ÿ®øºÿ®ø¼ÿ×ø¾ÿ®øÀÿšøÂÿšøÄÿšøÅÿšøÆÿqøÇÿšøÈÿqøËÿ×øÍÿšøÎÿšøÏÿ…øÑÿšøÓÿšøÕÿšø×ÿšøÙÿqøÛÿqøÝÿqøàÿqøæÿ×øèÿ×øêÿÃøìÿšøîÿšøïÿ×øðÿqøñÿ×øòÿqøóÿ×øôÿqøöÿ×øøÿ®øúÿ®øüÿ®øþÿšø�ÿšøÿšøÿ×øÿ×ø ÿqø ÿqø ÿqø ÿqøÿšøÿšøÿšøÿ…øÿšøÿ×øÿqøÿ®øÿqøÿšøÿ…ù�ÿšù�ÿ×ù�ÿšùÎÿÃùÏÿìùÕÿÃùØÿìùÛÿìùÞÿìùêÿìùíÿìùòÿÃùÿ×ùÿ×ùÿ×ùÿšù ÿšùjÿìùsÿÃùÿìù…ÿìù‡ÿìù‰ÿìùÿìù²ÿìù´ÿìùÏÿÃùàÿìùðÿìùòÿìùôÿìù ÿìù ÿìùÿÃùÿìùÿìùÿÃú�ÿšú�ÿšú�"�)ú�$ÿ®ú�&ÿìú�*ÿìú�2ÿìú�4ÿìú�Dÿ×ú�Fÿ×ú�Gÿ×ú�Hÿ×ú�Jÿìú�Pÿìú�Qÿìú�Rÿ×ú�Sÿìú�Tÿ×ú�Uÿìú�Vÿìú�Xÿìú�‚ÿ®ú�ƒÿ®ú�„ÿ®ú�…ÿ®ú�†ÿ®ú�‡ÿ®ú�‰ÿìú�”ÿìú�•ÿìú�–ÿìú�—ÿìú�˜ÿìú�šÿìú�¢ÿ×ú�£ÿ×ú�¤ÿ×ú�¥ÿ×ú�¦ÿ×ú�§ÿ×ú�¨ÿ×ú�©ÿ×ú�ªÿ×ú�«ÿ×ú�¬ÿ×ú�­ÿ×ú�´ÿ×ú�µÿ×ú�¶ÿ×ú�·ÿ×ú�¸ÿ×ú�ºÿ×ú�»ÿìú�¼ÿìú�½ÿìú�¾ÿìú�Âÿ®ú�Ãÿ×ú�Äÿ®ú�Åÿ×ú�Æÿ®ú�Çÿ×ú�Èÿìú�Éÿ×ú�Êÿìú�Ëÿ×ú�Ìÿìú�Íÿ×ú�Îÿìú�Ïÿ×ú�Ñÿ×ú�Óÿ×ú�Õÿ×ú�×ÿ×ú�Ùÿ×ú�Ûÿ×ú�Ýÿ×ú�Þÿìú�ßÿìú�àÿìú�áÿìú�âÿìú�ãÿìú�äÿìú�åÿìú�úÿìúÿìúÿìú ÿìúÿìúÿ×úÿìúÿ×úÿìúÿ×úÿìúÿ×úÿìúÿìúÿìú!ÿìú+ÿìú-ÿìú/ÿìú1ÿìú3ÿìú5ÿìúCÿ®úDÿ×úFÿ×úGÿìúHÿ×úJÿìúÿšú ÿšúWÿìúXÿ®úYÿ×ú_ÿìú`ÿ×úbÿìúÿ®úÿ×úÿ®ú ÿ×ú!ÿ®ú"ÿ×ú#ÿ®ú%ÿ®ú&ÿ×ú'ÿ®ú(ÿ×ú)ÿ®ú*ÿ×ú+ÿ®ú,ÿ×ú-ÿ®ú.ÿ×ú/ÿ®ú0ÿ×ú1ÿ®ú2ÿ×ú3ÿ®ú4ÿ×ú6ÿ×ú8ÿ×ú:ÿ×ú<ÿ×ú@ÿ×úBÿ×úDÿ×úIÿìúJÿ×úKÿìúLÿ×úMÿìúNÿ×úOÿìúQÿìúRÿ×úSÿìúTÿ×úUÿìúVÿ×úWÿìúXÿ×úYÿìúZÿ×ú[ÿìú\ÿ×ú]ÿìú^ÿ×ú_ÿìú`ÿ×úbÿìúdÿìúfÿìúhÿìújÿìúlÿìúnÿìû��Rû� �Rû�ÿ®û�ÿ®û�"�)û�Rûÿ®û �Rû ÿ®ü�ÿšü�ÿšü�"�)ü�$ÿ®ü�&ÿìü�*ÿìü�2ÿìü�4ÿìü�Dÿ×ü�Fÿ×ü�Gÿ×ü�Hÿ×ü�Jÿìü�Pÿìü�Qÿìü�Rÿ×ü�Sÿìü�Tÿ×ü�Uÿìü�Vÿìü�Xÿìü�‚ÿ®ü�ƒÿ®ü�„ÿ®ü�…ÿ®ü�†ÿ®ü�‡ÿ®ü�‰ÿìü�”ÿìü�•ÿìü�–ÿìü�—ÿìü�˜ÿìü�šÿìü�¢ÿ×ü�£ÿ×ü�¤ÿ×ü�¥ÿ×ü�¦ÿ×ü�§ÿ×ü�¨ÿ×ü�©ÿ×ü�ªÿ×ü�«ÿ×ü�¬ÿ×ü�­ÿ×ü�´ÿ×ü�µÿ×ü�¶ÿ×ü�·ÿ×ü�¸ÿ×ü�ºÿ×ü�»ÿìü�¼ÿìü�½ÿìü�¾ÿìü�Âÿ®ü�Ãÿ×ü�Äÿ®ü�Åÿ×ü�Æÿ®ü�Çÿ×ü�Èÿìü�Éÿ×ü�Êÿìü�Ëÿ×ü�Ìÿìü�Íÿ×ü�Îÿìü�Ïÿ×ü�Ñÿ×ü�Óÿ×ü�Õÿ×ü�×ÿ×ü�Ùÿ×ü�Ûÿ×ü�Ýÿ×ü�Þÿìü�ßÿìü�àÿìü�áÿìü�âÿìü�ãÿìü�äÿìü�åÿìü�úÿìüÿìüÿìü ÿìüÿìüÿ×üÿìüÿ×üÿìüÿ×üÿìüÿ×üÿìüÿìüÿìü!ÿìü+ÿìü-ÿìü/ÿìü1ÿìü3ÿìü5ÿìüCÿ®üDÿ×üFÿ×üGÿìüHÿ×üJÿìüÿšü ÿšüWÿìüXÿ®üYÿ×ü_ÿìü`ÿ×übÿìüÿ®üÿ×üÿ®ü ÿ×ü!ÿ®ü"ÿ×ü#ÿ®ü%ÿ®ü&ÿ×ü'ÿ®ü(ÿ×ü)ÿ®ü*ÿ×ü+ÿ®ü,ÿ×ü-ÿ®ü.ÿ×ü/ÿ®ü0ÿ×ü1ÿ®ü2ÿ×ü3ÿ®ü4ÿ×ü6ÿ×ü8ÿ×ü:ÿ×ü<ÿ×ü@ÿ×üBÿ×üDÿ×üIÿìüJÿ×üKÿìüLÿ×üMÿìüNÿ×üOÿìüQÿìüRÿ×üSÿìüTÿ×üUÿìüVÿ×üWÿìüXÿ×üYÿìüZÿ×ü[ÿìü\ÿ×ü]ÿìü^ÿ×ü_ÿìü`ÿ×übÿìüdÿìüfÿìühÿìüjÿìülÿìünÿìý��Rý� �Rý�ÿ®ý�ÿ®ý�"�)ý�Rýÿ®ý �Rý ÿ®þ�ÿšþ�ÿšþ�"�)þ�$ÿ®þ�&ÿìþ�*ÿìþ�2ÿìþ�4ÿìþ�Dÿ×þ�Fÿ×þ�Gÿ×þ�Hÿ×þ�Jÿìþ�Pÿìþ�Qÿìþ�Rÿ×þ�Sÿìþ�Tÿ×þ�Uÿìþ�Vÿìþ�Xÿìþ�‚ÿ®þ�ƒÿ®þ�„ÿ®þ�…ÿ®þ�†ÿ®þ�‡ÿ®þ�‰ÿìþ�”ÿìþ�•ÿìþ�–ÿìþ�—ÿìþ�˜ÿìþ�šÿìþ�¢ÿ×þ�£ÿ×þ�¤ÿ×þ�¥ÿ×þ�¦ÿ×þ�§ÿ×þ�¨ÿ×þ�©ÿ×þ�ªÿ×þ�«ÿ×þ�¬ÿ×þ�­ÿ×þ�´ÿ×þ�µÿ×þ�¶ÿ×þ�·ÿ×þ�¸ÿ×þ�ºÿ×þ�»ÿìþ�¼ÿìþ�½ÿìþ�¾ÿìþ�Âÿ®þ�Ãÿ×þ�Äÿ®þ�Åÿ×þ�Æÿ®þ�Çÿ×þ�Èÿìþ�Éÿ×þ�Êÿìþ�Ëÿ×þ�Ìÿìþ�Íÿ×þ�Îÿìþ�Ïÿ×þ�Ñÿ×þ�Óÿ×þ�Õÿ×þ�×ÿ×þ�Ùÿ×þ�Ûÿ×þ�Ýÿ×þ�Þÿìþ�ßÿìþ�àÿìþ�áÿìþ�âÿìþ�ãÿìþ�äÿìþ�åÿìþ�úÿìþÿìþÿìþ ÿìþÿìþÿ×þÿìþÿ×þÿìþÿ×þÿìþÿ×þÿìþÿìþÿìþ!ÿìþ+ÿìþ-ÿìþ/ÿìþ1ÿìþ3ÿìþ5ÿìþCÿ®þDÿ×þFÿ×þGÿìþHÿ×þJÿìþÿšþ ÿšþWÿìþXÿ®þYÿ×þ_ÿìþ`ÿ×þbÿìþÿ®þÿ×þÿ®þ ÿ×þ!ÿ®þ"ÿ×þ#ÿ®þ%ÿ®þ&ÿ×þ'ÿ®þ(ÿ×þ)ÿ®þ*ÿ×þ+ÿ®þ,ÿ×þ-ÿ®þ.ÿ×þ/ÿ®þ0ÿ×þ1ÿ®þ2ÿ×þ3ÿ®þ4ÿ×þ6ÿ×þ8ÿ×þ:ÿ×þ<ÿ×þ@ÿ×þBÿ×þDÿ×þIÿìþJÿ×þKÿìþLÿ×þMÿìþNÿ×þOÿìþQÿìþRÿ×þSÿìþTÿ×þUÿìþVÿ×þWÿìþXÿ×þYÿìþZÿ×þ[ÿìþ\ÿ×þ]ÿìþ^ÿ×þ_ÿìþ`ÿ×þbÿìþdÿìþfÿìþhÿìþjÿìþlÿìþnÿìÿ��Rÿ� �Rÿ�ÿ®ÿ�ÿ®ÿ�"�)ÿ�Rÿÿ®ÿ �Rÿ ÿ®��ÿ…��ÿ…��"�)��$ÿ…��&ÿ×��*ÿ×��2ÿ×��4ÿ×��Dÿš��Fÿš��Gÿš��Hÿš��Jÿ×��PÿÃ��QÿÃ��Rÿš��SÿÃ��Tÿš��UÿÃ��Vÿ®��XÿÃ��]ÿ×��‚ÿ…��ƒÿ…��„ÿ…��…ÿ…��†ÿ…��‡ÿ…��‰ÿ×��”ÿ×��•ÿ×��–ÿ×��—ÿ×��˜ÿ×��šÿ×��¢ÿš��£ÿš��¤ÿš��¥ÿš��¦ÿš��§ÿš��¨ÿš��©ÿš��ªÿš��«ÿš��¬ÿš��­ÿš��´ÿš��µÿš��¶ÿš��·ÿš��¸ÿš��ºÿš��»ÿÃ��¼ÿÃ��½ÿÃ��¾ÿÃ��Âÿ…��Ãÿš��Äÿ…��Åÿš��Æÿ…��Çÿš��Èÿ×��Éÿš��Êÿ×��Ëÿš��Ìÿ×��Íÿš��Îÿ×��Ïÿš��Ñÿš��Óÿš��Õÿš��×ÿš��Ùÿš��Ûÿš��Ýÿš��Þÿ×��ßÿ×��àÿ×��áÿ×��âÿ×��ãÿ×��äÿ×��åÿ×��úÿÃ�ÿÃ�ÿÃ� ÿÃ�ÿ×�ÿš�ÿ×�ÿš�ÿ×�ÿš�ÿ×�ÿš�ÿÃ�ÿÃ�ÿ®�!ÿ®�+ÿÃ�-ÿÃ�/ÿÃ�1ÿÃ�3ÿÃ�5ÿÃ�<ÿ×�>ÿ×�@ÿ×�Cÿ…�Dÿš�Fÿš�Gÿ×�Hÿš�Jÿ®�ÿ…� ÿ…�WÿÃ�Xÿ…�Yÿš�_ÿ×�`ÿš�bÿÃ�ÿ…�ÿš�ÿ…� ÿš�!ÿ…�"ÿš�#ÿ…�%ÿ…�&ÿš�'ÿ…�(ÿš�)ÿ…�*ÿš�+ÿ…�,ÿš�-ÿ…�.ÿš�/ÿ…�0ÿš�1ÿ…�2ÿš�3ÿ…�4ÿš�6ÿš�8ÿš�:ÿš�<ÿš�@ÿš�Bÿš�Dÿš�Iÿ×�Jÿš�Kÿ×�Lÿš�Mÿ×�Nÿš�Oÿ×�Qÿ×�Rÿš�Sÿ×�Tÿš�Uÿ×�Vÿš�Wÿ×�Xÿš�Yÿ×�Zÿš�[ÿ×�\ÿš�]ÿ×�^ÿš�_ÿ×�`ÿš�bÿÃ�dÿÃ�fÿÃ�hÿÃ�jÿÃ�lÿÃ�nÿÃ��R� �R�ÿ®�ÿ®�"�)�Rÿ® �R ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�$ÿq�7�)�9�)�:�)�<��Dÿ®�Fÿ…�Gÿ…�Hÿ…�JÿÃ�PÿÃ�QÿÃ�Rÿ…�SÿÃ�Tÿ…�UÿÃ�VÿÃ�XÿÃ�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�Ÿ��¢ÿ…�£ÿ®�¤ÿ®�¥ÿ®�¦ÿ®�§ÿ®�¨ÿ®�©ÿ…�ªÿ…�«ÿ…�¬ÿ…�­ÿ…�´ÿ…�µÿ…�¶ÿ…�·ÿ…�¸ÿ…�ºÿ…�»ÿÃ�¼ÿÃ�½ÿÃ�¾ÿÃ�Âÿq�Ãÿ®�Äÿq�Åÿ®�Æÿq�Çÿ®�Éÿ…�Ëÿ…�Íÿ…�Ïÿ…�Ñÿ…�Óÿ…�Õÿ…�×ÿ…�Ùÿ…�Ûÿ…�Ýÿ…�ßÿÃ�áÿÃ�ãÿÃ�åÿÃ�úÿÃÿÃÿà ÿÃÿ…ÿ…ÿ…ÿ…ÿÃÿÃÿÃ!ÿÃ$�)&�)+ÿÃ-ÿÃ/ÿÃ1ÿÃ3ÿÃ5ÿÃ6�)8�:�CÿqDÿ®Fÿ®Hÿ…JÿÃVÿq_ÿqbÿqiÿqyÿ®zÿ×{ÿ×~ÿ®ÿÂÿ׃ÿׄÿׇÿ׉ÿ׌ÿ®ŽÿÃÿ®ÿ®“ÿ®™ÿ®¤ÿ…ªÿq®ÿ…µÿ…Êÿ×ÎÿqÏÿ…ÕÿqØÿ…Ûÿ…Þÿ…êÿ…íÿ…îÿÃòÿqú�)ü�)þ�)��WÿÃXÿqYÿ®`ÿ…bÿÃjÿ…rÿqsÿq}ÿìÿ……ÿ…‡ÿ…‰ÿ…ÿ…²ÿ…´ÿ…Îÿ…ÏÿqÙÿqÚÿ×ÛÿqÜÿ×ÝÿqÞÿ×àÿ…âÿ×äÿ×ðÿ…òÿ…ôÿ… ÿq ÿ… ÿq ÿ…ÿ…ÿqÿ…ÿ…ÿ…ÿqÿqÿ®ÿq ÿ®!ÿq"ÿ®#ÿq%ÿq&ÿ®'ÿq(ÿ®)ÿq*ÿ®+ÿq,ÿ®-ÿq.ÿ®/ÿq0ÿ®1ÿq2ÿ®3ÿq4ÿ®6ÿ…8ÿ…:ÿ…<ÿ…@ÿ…Bÿ…Dÿ…Jÿ…Lÿ…Nÿ…Rÿ…Tÿ…Vÿ…Xÿ…Zÿ…\ÿ…^ÿ…`ÿ…bÿÃdÿÃfÿÃhÿÃjÿÃlÿÃnÿÃo�q�s��)�$ÿq�7�)�9�)�:�)�<��Dÿ®�Fÿ…�Gÿ…�Hÿ…�JÿÃ�PÿÃ�QÿÃ�Rÿ…�SÿÃ�Tÿ…�UÿÃ�VÿÃ�XÿÃ�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�Ÿ��¢ÿ…�£ÿ®�¤ÿ®�¥ÿ®�¦ÿ®�§ÿ®�¨ÿ®�©ÿ…�ªÿ…�«ÿ…�¬ÿ…�­ÿ…�´ÿ…�µÿ…�¶ÿ…�·ÿ…�¸ÿ…�ºÿ…�»ÿÃ�¼ÿÃ�½ÿÃ�¾ÿÃ�Âÿq�Ãÿ®�Äÿq�Åÿ®�Æÿq�Çÿ®�Éÿ…�Ëÿ…�Íÿ…�Ïÿ…�Ñÿ…�Óÿ…�Õÿ…�×ÿ…�Ùÿ…�Ûÿ…�Ýÿ…�ßÿÃ�áÿÃ�ãÿÃ�åÿÃ�úÿÃÿÃÿà ÿÃÿ…ÿ…ÿ…ÿ…ÿÃÿÃÿÃ!ÿÃ$�)&�)+ÿÃ-ÿÃ/ÿÃ1ÿÃ3ÿÃ5ÿÃ6�)8�:�CÿqDÿ®Fÿ®Hÿ…JÿÃVÿq_ÿqbÿqiÿqyÿ®zÿ×{ÿ×~ÿ®ÿÂÿ׃ÿׄÿׇÿ׉ÿ׌ÿ®ŽÿÃÿ®ÿ®“ÿ®™ÿ®¤ÿ…ªÿq®ÿ…µÿ…Êÿ×ÎÿqÏÿ…ÕÿqØÿ…Ûÿ…Þÿ…êÿ…íÿ…îÿÃòÿqú�)ü�)þ�)��WÿÃXÿqYÿ®`ÿ…bÿÃjÿ…rÿqsÿq}ÿìÿ……ÿ…‡ÿ…‰ÿ…ÿ…²ÿ…´ÿ…Îÿ…ÏÿqÙÿqÚÿ×ÛÿqÜÿ×ÝÿqÞÿ×àÿ…âÿ×äÿ×ðÿ…òÿ…ôÿ… ÿq ÿ… ÿq ÿ…ÿ…ÿqÿ…ÿ…ÿ…ÿqÿqÿ®ÿq ÿ®!ÿq"ÿ®#ÿq%ÿq&ÿ®'ÿq(ÿ®)ÿq*ÿ®+ÿq,ÿ®-ÿq.ÿ®/ÿq0ÿ®1ÿq2ÿ®3ÿq4ÿ®6ÿ…8ÿ…:ÿ…<ÿ…@ÿ…Bÿ…Dÿ…Jÿ…Lÿ…Nÿ…Rÿ…Tÿ…Vÿ…Xÿ…Zÿ…\ÿ…^ÿ…`ÿ…bÿÃdÿÃfÿÃhÿÃjÿÃlÿÃnÿÃo�q�s��)�&ÿš�*ÿš�2ÿš�4ÿš�7ÿq�8ÿ×�9ÿ…�:ÿ…�<ÿ…�‰ÿš�”ÿš�•ÿš�–ÿš�—ÿš�˜ÿš�šÿš�›ÿ×�œÿ×�ÿ×�žÿ×�Ÿÿ…�Èÿš�Êÿš�Ìÿš�Îÿš�Þÿš�àÿš�âÿš�äÿšÿšÿšÿšÿš$ÿq&ÿq*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ…8ÿ…:ÿ…Gÿšfÿ®mÿ®qÿqrÿ…sÿšuÿ…xÿ……ÿ×ÿqŸÿš¦ÿq¸ÿš»ÿš¼ÿq¾ÿ®Áÿ\ÄÿqÜÿšáÿ…äÿšúÿ…üÿ…þÿ…�ÿ…Tÿ…_ÿšaÿ×lÿš|ÿ\~ÿš€ÿ…‚ÿ…„ÿš†ÿšˆÿšŠÿšŒÿš©ÿqªÿš±ÿš³ÿšµÿq¶ÿš·ÿ…¹ÿ…½ÿq¾ÿš¿ÿ\Àÿ…Áÿ\Âÿ…Åÿ…Çÿ…Ôÿ\Õÿ…ïÿšñÿšóÿšýÿ\þÿ… ÿ…ÿšÿ…ÿšÿšÿqÿšIÿšKÿšMÿšOÿšQÿšSÿšUÿšWÿšYÿš[ÿš]ÿš_ÿšaÿ×cÿ×eÿ×gÿ×iÿ×kÿ×mÿ×oÿ…qÿ…sÿ…ÿq �$ÿq �7�) �9�) �:�) �<� �Dÿ® �Fÿ… �Gÿ… �Hÿ… �Jÿà �Pÿà �Qÿà �Rÿ… �Sÿà �Tÿ… �Uÿà �Vÿà �Xÿà �‚ÿq �ƒÿq �„ÿq �…ÿq �†ÿq �‡ÿq �Ÿ� �¢ÿ… �£ÿ® �¤ÿ® �¥ÿ® �¦ÿ® �§ÿ® �¨ÿ® �©ÿ… �ªÿ… �«ÿ… �¬ÿ… �­ÿ… �´ÿ… �µÿ… �¶ÿ… �·ÿ… �¸ÿ… �ºÿ… �»ÿà �¼ÿà �½ÿà �¾ÿà �Âÿq �Ãÿ® �Äÿq �Åÿ® �Æÿq �Çÿ® �Éÿ… �Ëÿ… �Íÿ… �Ïÿ… �Ñÿ… �Óÿ… �Õÿ… �×ÿ… �Ùÿ… �Ûÿ… �Ýÿ… �ßÿà �áÿà �ãÿà �åÿà �úÿà ÿà ÿà  ÿà ÿ… ÿ… ÿ… ÿ… ÿà ÿà ÿà !ÿà $�) &�) +ÿà -ÿà /ÿà 1ÿà 3ÿà 5ÿà 6�) 8� :� Cÿq Dÿ® Fÿ® Hÿ… Jÿà Vÿq _ÿq bÿq iÿq yÿ® zÿ× {ÿ× ~ÿ® ÿà ‚ÿ× ƒÿ× „ÿ× ‡ÿ× ‰ÿ× Œÿ® Žÿà ÿ® ÿ® “ÿ® ™ÿ® ¤ÿ… ªÿq ®ÿ… µÿ… Êÿ× Îÿq Ïÿ… Õÿq Øÿ… Ûÿ… Þÿ… êÿ… íÿ… îÿà òÿq ú�) ü�) þ�) �� Wÿà Xÿq Yÿ® `ÿ… bÿà jÿ… rÿq sÿq }ÿì ÿ… …ÿ… ‡ÿ… ‰ÿ… ÿ… ²ÿ… ´ÿ… Îÿ… Ïÿq Ùÿq Úÿ× Ûÿq Üÿ× Ýÿq Þÿ× àÿ… âÿ× äÿ× ðÿ… òÿ… ôÿ…  ÿq  ÿ…  ÿq  ÿ… ÿ… ÿq ÿ… ÿ… ÿ… ÿq ÿq ÿ® ÿq  ÿ® !ÿq "ÿ® #ÿq %ÿq &ÿ® 'ÿq (ÿ® )ÿq *ÿ® +ÿq ,ÿ® -ÿq .ÿ® /ÿq 0ÿ® 1ÿq 2ÿ® 3ÿq 4ÿ® 6ÿ… 8ÿ… :ÿ… <ÿ… @ÿ… Bÿ… Dÿ… Jÿ… Lÿ… Nÿ… Rÿ… Tÿ… Vÿ… Xÿ… Zÿ… \ÿ… ^ÿ… `ÿ… bÿà dÿà fÿà hÿà jÿà lÿà nÿà o� q� s� �) �&ÿš �*ÿš �2ÿš �4ÿš �7ÿq �8ÿ× �9ÿ… �:ÿ… �<ÿ… �‰ÿš �”ÿš �•ÿš �–ÿš �—ÿš �˜ÿš �šÿš �›ÿ× �œÿ× �ÿ× �žÿ× �Ÿÿ… �Èÿš �Êÿš �Ìÿš �Îÿš �Þÿš �àÿš �âÿš �äÿš ÿš ÿš ÿš ÿš $ÿq &ÿq *ÿ× ,ÿ× .ÿ× 0ÿ× 2ÿ× 4ÿ× 6ÿ… 8ÿ… :ÿ… Gÿš fÿ® mÿ® qÿq rÿ… sÿš uÿ… xÿ… …ÿ× ÿq Ÿÿš ¦ÿq ¸ÿš »ÿš ¼ÿq ¾ÿ® Áÿ\ Äÿq Üÿš áÿ… äÿš úÿ… üÿ… þÿ… �ÿ… Tÿ… _ÿš aÿ× lÿš |ÿ\ ~ÿš €ÿ… ‚ÿ… „ÿš †ÿš ˆÿš Šÿš Œÿš ©ÿq ªÿš ±ÿš ³ÿš µÿq ¶ÿš ·ÿ… ¹ÿ… ½ÿq ¾ÿš ¿ÿ\ Àÿ… Áÿ\ Âÿ… Åÿ… Çÿ… Ôÿ\ Õÿ… ïÿš ñÿš óÿš ýÿ\ þÿ…  ÿ… ÿš ÿ… ÿš ÿš ÿq ÿš Iÿš Kÿš Mÿš Oÿš Qÿš Sÿš Uÿš Wÿš Yÿš [ÿš ]ÿš _ÿš aÿ× cÿ× eÿ× gÿ× iÿ× kÿ× mÿ× oÿ… qÿ… sÿ… ÿq!qÿ×!rÿì!xÿì!TÿìS�ÿÃS�ÿÃSÿÃS ÿÃT�ÿ…T�ÿ…TVÿ…T_ÿ…Tbÿ…Tfÿ×Tiÿ…Tmÿ×TsÿÃTvÿìTyÿšTzÿ®T{ÿÃT|ÿÃT}ÿÃT~ÿšTÿÃT‚ÿ®T„ÿÃT†ÿÃT‡ÿÃT‰ÿÃTŒÿšTŽÿšTÿšTÿšT’ÿÃT“ÿšT•ÿÃT–ÿÃT˜ÿÃT™ÿšTšÿÃT›ÿÃTÿ…T ÿ…T!ÿìX�ÿqX� ÿqX�&ÿ×X�*ÿ×X�- X�2ÿ×X�4ÿ×X�7ÿqX�9ÿ®X�:ÿ®X�<ÿ…X�‰ÿ×X�”ÿ×X�•ÿ×X�–ÿ×X�—ÿ×X�˜ÿ×X�šÿ×X�Ÿÿ…X�Èÿ×X�Êÿ×X�Ìÿ×X�Îÿ×X�Þÿ×X�àÿ×X�âÿ×X�äÿ×Xÿ×Xÿ×Xÿ×Xÿ×X$ÿqX&ÿqX6ÿ®X8ÿ…X:ÿ…XGÿ×Xúÿ®Xüÿ®Xþÿ®X�ÿ…XÿqX ÿqX_ÿ×XIÿ×XKÿ×XMÿ×XOÿ×XQÿ×XSÿ×XUÿ×XWÿ×XYÿ×X[ÿ×X]ÿ×X_ÿ×Xoÿ…Xqÿ…Xsÿ…XÿqY�ÿìY� ÿìYÿìY ÿìZ�ÿ®Z�ÿ®ZVÿ×Z_ÿ×Zbÿ×ZdÿìZiÿ×ZpÿìZqÿÃZrÿìZtÿ×ZuÿìZxÿìZˆÿìZÿ®Z ÿ®ZTÿì`�I�R`�W�R`�Y�f`�Z�f`�[�f`�\�f`�¿�f`%�R`'�R`7�f`û�f`ý�f`4�R`5�R`]�R`^�R`p�f`�R`�Rb�I�fb�W�fb�Y�fb�Z�fb�[�fb�\�fb�¿�fb%�fb'�fb7�fbû�fbý�fb4�fb5�fb]�fb^�fbp�fb�fb�fj�ÿìj� ÿìjÿìj ÿìl�ÿ®l�ÿ®lÿìl¤ÿ×l¦ÿìl¨ÿ×lªÿ×l®ÿ×l°ÿ×l±ÿìlµÿ×l¼ÿÃl½ÿ×l¿ÿ×lÁÿ×lÄÿìlÇÿìlÎÿìlÕÿìlòÿìlÿ®l ÿ®lrÿ×lsÿìlzÿìl|ÿ×l€ÿìl‚ÿìlŸÿ×l¡ÿìl©ÿìlµÿÃl·ÿìl¹ÿìl»ÿ×l½ÿìl¿ÿ×lÁÿ×lÊÿ×lÎÿ×lÏÿìlÔÿ×lÙÿ×lÛÿ×lÝÿ×låÿ×lçÿìlõÿìl÷ÿ×lùÿ×lûÿ×lýÿ×lÿ×lÿ×l ÿ×lÿ×lÿ×lÿìlÿìlÿ×lÿìm�ÿ®m�ÿ®mÎÿ×mÕÿ×mòÿ×mÿ®m ÿ®msÿ×mÏÿ×mÿ×mÿ×n�ÿ®n� ÿ®nÿ×n¦ÿ×n¼ÿ®nÁÿ®nÄÿ×nÜÿ×näÿ×nÿ®n ÿ®n|ÿ®n€ÿÃn‚ÿÃn©ÿ×nªÿ×nµÿ®n¶ÿ×n·ÿÃn¹ÿÃn½ÿ×n¾ÿ×n¿ÿ®nÁÿ®nÔÿ®nýÿ®n ÿšnÿšnÿ×nÿ×o�ÿ…o� ÿ…oÐÿ×oÜÿšoÝÿÃoßÿ×oáÿ®oäÿšoöÿÃoÿ…o ÿ…omÿ×oÿ×oƒÿ×o‹ÿ×o ÿ×oªÿšo¶ÿšo¸ÿÃoºÿÃo¼ÿ×o¾ÿšoÀÿ®oÂÿ®oÆÿ×oÈÿ×oËÿ×oÕÿ®oæÿ×oêÿ×oøÿÃoúÿÃoüÿÃoþÿ®oÿ×oÿ×oÿšoÿšoÿšpŸÿ×p¸ÿ×p»ÿ×p¾ÿ×páÿ×plÿ×p~ÿ×p„ÿ×p†ÿ×pˆÿ×pŠÿ×pŒÿ×p±ÿ×p³ÿ×pÀÿ×pÂÿ×pÅÿ×pÇÿ×pÕÿ×pïÿ×pñÿ×póÿ×pþÿ×p ÿ×p ÿ×pÿ×pÿ×pÿ×r�ÿqr� ÿqrÿšr¦ÿšr¼ÿqr¾ÿ×rÁÿšrÄÿšrÜÿ×ráÿ×räÿ×rÿqr ÿqrnÿ×r|ÿšr€ÿ®r‚ÿ®r—ÿ×r›ÿ×r§ÿ×r©ÿšrªÿ×rµÿqr¶ÿ×r·ÿ…r¹ÿ…r½ÿšr¾ÿ×r¿ÿšrÀÿ×rÁÿšrÂÿ×rÅÿšrÇÿšrÔÿšrÕÿ×ráÿ×rãÿ×rýÿšrþÿ×rÿ×r ÿqrÿ×rÿqrÿ×rÿšrÿ×s�ÿqs� ÿqsÏÿ×sØÿ×sÛÿ×sÜÿšsÝÿÃsÞÿ×sáÿÃsäÿšsêÿ×síÿ×söÿÃsÿqs ÿqsjÿ×smÿ×s}ÿìsÿ×sÿ×sƒÿ×s…ÿ×s‡ÿ×s‰ÿ×s‹ÿ×sÿ×sªÿšs²ÿ×s´ÿ×s¶ÿšs¸ÿ×sºÿ×s¾ÿšsÀÿÃsÂÿÃsÆÿ×sÈÿ×sÕÿÃsàÿ×sðÿ×sòÿ×sôÿ×søÿÃsúÿÃsüÿÃsþÿÃs ÿ×s ÿ×sÿ…sÿ…sÿ×sÿšsÿ×t�ÿqt� ÿqtÿšt¦ÿšt¼ÿqt¾ÿ×tÁÿštÄÿštÜÿ×táÿ×täÿ×tÿqt ÿqtnÿ×t|ÿšt€ÿ®t‚ÿ®t—ÿ×t›ÿ×t§ÿ×t©ÿštªÿ×tµÿqt¶ÿ×t·ÿ…t¹ÿ…t½ÿšt¾ÿ×t¿ÿštÀÿ×tÁÿštÂÿ×tÅÿštÇÿštÔÿštÕÿ×táÿ×tãÿ×týÿštþÿ×tÿ×t ÿqtÿ×tÿqtÿ×tÿštÿ×u�ÿqu� ÿquÏÿ×uØÿ×uÛÿ×uÜÿšuÝÿÃuÞÿ×uáÿÃuäÿšuêÿ×uíÿ×uöÿÃuÿqu ÿqujÿ×umÿ×u}ÿìuÿ×uÿ×uƒÿ×u…ÿ×u‡ÿ×u‰ÿ×u‹ÿ×uÿ×uªÿšu²ÿ×u´ÿ×u¶ÿšu¸ÿ×uºÿ×u¾ÿšuÀÿÃuÂÿÃuÆÿ×uÈÿ×uÕÿÃuàÿ×uðÿ×uòÿ×uôÿ×uøÿÃuúÿÃuüÿÃuþÿÃu ÿ×u ÿ×uÿ…uÿ…uÿ×uÿšuÿ×v ÿìvÿìx ÿìxÿìz�ÿ®z�ÿ®zÿ®z ÿ®z€ÿìz‚ÿìz·ÿìz¹ÿìz ÿ×zÿ×|�ÿq|�ÿq|¤ÿÃ|ªÿ®|®ÿÃ|µÿÃ|Îÿ×|Õÿ×|òÿ×|ÿq| ÿq|rÿ®|sÿ×|ÎÿÃ|Ïÿ×|Ùÿ®|Ûÿ®|Ýÿ®| ÿ®| ÿ®|ÿÃ|ÿ×|ÿÃ|ÿ×}�ÿì}� ÿì}Ðÿ×}Üÿì}Ýÿì}ßÿ×}áÿì}äÿì}öÿì}ÿì} ÿì} ÿ×}ªÿì}¶ÿì}¼ÿ×}¾ÿì}Àÿì}Âÿì}Ëÿ×}Õÿì}æÿ×}øÿì}úÿì}üÿì}þÿì}ÿ×}ÿ×}ÿì}ÿì}ÿì~�ÿ®~�ÿ®~ÿì~¤ÿ×~¦ÿì~¨ÿ×~ªÿ×~®ÿ×~°ÿ×~±ÿì~µÿ×~¼ÿÃ~½ÿ×~¿ÿ×~Áÿ×~Äÿì~Çÿì~Îÿì~Õÿì~òÿì~ÿ®~ ÿ®~rÿ×~sÿì~zÿì~|ÿ×~€ÿì~‚ÿì~Ÿÿ×~¡ÿì~©ÿì~µÿÃ~·ÿì~¹ÿì~»ÿ×~½ÿì~¿ÿ×~Áÿ×~Êÿ×~Îÿ×~Ïÿì~Ôÿ×~Ùÿ×~Ûÿ×~Ýÿ×~åÿ×~çÿì~õÿì~÷ÿ×~ùÿ×~ûÿ×~ýÿ×~ÿ×~ÿ×~ ÿ×~ÿ×~ÿ×~ÿì~ÿì~ÿ×~ÿì�ÿì� ÿìÐÿ×ÜÿìÝÿìßÿ×áÿìäÿìöÿìÿì ÿì ÿתÿì¶ÿì¼ÿ×¾ÿìÀÿìÂÿìËÿ×Õÿìæÿ×øÿìúÿìüÿìþÿìÿ×ÿ×ÿìÿìÿì€�ÿ…€�ÿ…€Ÿÿ쀤ÿš€ªÿq€®ÿš€µÿš€¸ÿ쀻ÿ쀾ÿÀÉÿì€Îÿ®€Ïÿ×€Õÿ®€Øÿ×€Ûÿ×€Þÿ×€áÿ×€êÿ×€ë�f€íÿ×€îÿì€òÿ®€ô�f€ÿ…€ ÿ…€jÿ×€lÿì€rÿq€sÿ®€~ÿì€ÿ×€„ÿ쀅ÿ×€†ÿ쀇ÿ×€ˆÿ쀉ÿ×€Šÿ쀌ÿì€ÿ×€˜�f€¨�f€±ÿ쀲ÿ×€³ÿ쀴ÿ×€Àÿ×€Âÿ×€Åÿ×€ÆÿÀÇÿ×€ÈÿÀÎÿš€Ïÿ®€Õÿ×€Ùÿq€Ûÿq€Ýÿq€àÿ×€ïÿì€ðÿ×€ñÿì€òÿ×€óÿì€ôÿ×€þÿ×€ ÿq€ ÿ×€ ÿq€ ÿ×€ÿš€ÿ®€ÿì€ÿ×€ÿ×€ÿš€ÿ®�ÿ®�ÿ®Îÿ×Õÿ×òÿ×ÿ® ÿ®sÿ×Ïÿ×ÿ×ÿׂ�ÿ…‚�ÿ…‚Ÿÿ삤ÿš‚ªÿq‚®ÿš‚µÿš‚¸ÿì‚»ÿ삾ÿÂÉÿì‚Îÿ®‚ÏÿׂÕÿ®‚ØÿׂÛÿׂÞÿׂáÿׂêÿׂë�f‚íÿׂîÿì‚òÿ®‚ô�f‚ÿ…‚ ÿ…‚jÿׂlÿì‚rÿq‚sÿ®‚~ÿì‚ÿׂ„ÿì‚…ÿׂ†ÿ삇ÿׂˆÿ삉ÿׂŠÿ삌ÿì‚ÿׂ˜�f‚¨�f‚±ÿ삲ÿׂ³ÿì‚´ÿׂÀÿׂÂÿׂÅÿׂÆÿÂÇÿׂÈÿÂÎÿš‚Ïÿ®‚ÕÿׂÙÿq‚Ûÿq‚Ýÿq‚àÿׂïÿì‚ðÿׂñÿì‚òÿׂóÿì‚ôÿׂþÿׂ ÿq‚ ÿׂ ÿq‚ ÿׂÿš‚ÿ®‚ÿì‚ÿׂÿׂÿš‚ÿ®ƒ�ÿ®ƒ�ÿ®ƒÎÿ׃Õÿ׃òÿ׃ÿ®ƒ ÿ®ƒsÿ׃Ïÿ׃ÿ׃ÿׄ�ÿ®„�ÿ®„ÎÿׄÕÿׄòÿׄÿ®„ ÿ®„sÿׄÏÿׄÿׄÿ×…�ÿ®…�ÿ®…Îÿ×…Õÿ×…òÿ×…ÿ®… ÿ®…sÿ×…Ïÿ×…ÿ×…ÿ׆�ÿ®†�ÿ®†ÿ솤ÿ׆¦ÿ솨ÿ׆ªÿ׆®ÿ׆°ÿ׆±ÿ솵ÿ׆¼ÿƽÿ׆¿ÿ׆Áÿ׆Äÿì†Çÿì†Îÿì†Õÿì†òÿì†ÿ®† ÿ®†rÿ׆sÿì†zÿì†|ÿ׆€ÿ솂ÿ솟ÿ׆¡ÿ솩ÿ솵ÿÆ·ÿ솹ÿ솻ÿ׆½ÿ솿ÿ׆Áÿ׆Êÿ׆Îÿ׆Ïÿì†Ôÿ׆Ùÿ׆Ûÿ׆Ýÿ׆åÿ׆çÿì†õÿì†÷ÿ׆ùÿ׆ûÿ׆ýÿ׆ÿ׆ÿ׆ ÿ׆ÿ׆ÿ׆ÿì†ÿì†ÿ׆ÿì‡�ÿì‡� ÿì‡ÐÿׇÜÿì‡Ýÿì‡ßÿׇáÿì‡äÿì‡öÿì‡ÿì‡ ÿ쇠ÿׇªÿ쇶ÿ쇼ÿׇ¾ÿì‡Àÿì‡Âÿì‡ËÿׇÕÿì‡æÿׇøÿì‡úÿì‡üÿì‡þÿì‡ÿׇÿׇÿì‡ÿì‡ÿìˆ�ÿ®ˆ�ÿ®ˆÿ숤ÿ׈¦ÿ숨ÿ׈ªÿ׈®ÿ׈°ÿ׈±ÿ숵ÿ׈¼ÿȽÿ׈¿ÿ׈Áÿ׈ÄÿìˆÇÿìˆÎÿìˆÕÿìˆòÿìˆÿ®ˆ ÿ®ˆrÿ׈sÿìˆzÿìˆ|ÿ׈€ÿ숂ÿ숟ÿ׈¡ÿ숩ÿ숵ÿÈ·ÿ숹ÿ숻ÿ׈½ÿ숿ÿ׈Áÿ׈Êÿ׈Îÿ׈ÏÿìˆÔÿ׈Ùÿ׈Ûÿ׈Ýÿ׈åÿ׈çÿìˆõÿìˆ÷ÿ׈ùÿ׈ûÿ׈ýÿ׈ÿ׈ÿ׈ ÿ׈ÿ׈ÿ׈ÿìˆÿìˆÿ׈ÿì‰�ÿì‰� ÿì‰Ðÿ׉Üÿì‰Ýÿì‰ßÿ׉áÿì‰äÿì‰öÿì‰ÿì‰ ÿ쉠ÿ׉ªÿ쉶ÿ쉼ÿ׉¾ÿì‰Àÿì‰Âÿì‰Ëÿ׉Õÿì‰æÿ׉øÿì‰úÿì‰üÿì‰þÿì‰ÿ׉ÿ׉ÿì‰ÿì‰ÿìŠ�ÿ®Š�ÿ®Šÿ스ÿ׊¦ÿ슨ÿ׊ªÿ׊®ÿ׊°ÿ׊±ÿ습ÿ׊¼ÿʽÿ׊¿ÿ׊Áÿ׊ÄÿìŠÇÿìŠÎÿìŠÕÿìŠòÿìŠÿ®Š ÿ®Šrÿ׊sÿìŠzÿìŠ|ÿ׊€ÿ슂ÿ슟ÿ׊¡ÿ슩ÿ습ÿÊ·ÿ승ÿ슻ÿ׊½ÿ슿ÿ׊Áÿ׊Êÿ׊Îÿ׊ÏÿìŠÔÿ׊Ùÿ׊Ûÿ׊Ýÿ׊åÿ׊çÿìŠõÿìŠ÷ÿ׊ùÿ׊ûÿ׊ýÿ׊ÿ׊ÿ׊ ÿ׊ÿ׊ÿ׊ÿìŠÿìŠÿ׊ÿì‹�ÿ®‹�ÿ®‹Îÿ׋Õÿ׋òÿ׋ÿ®‹ ÿ®‹sÿ׋Ïÿ׋ÿ׋ÿ׌Ÿÿ׌¸ÿ׌»ÿ׌¾ÿ׌áÿ׌lÿ׌~ÿ׌„ÿ׌†ÿ׌ˆÿ׌Šÿ׌Œÿ׌±ÿ׌³ÿ׌Àÿ׌Âÿ׌Åÿ׌Çÿ׌Õÿ׌ïÿ׌ñÿ׌óÿ׌þÿ׌ ÿ׌ ÿ׌ÿ׌ÿ׌ÿו£�á•ê�)•ÿוÿ×–�ÿì–� ÿì–ÿì– ÿì—�ÿ®—� ÿ®—ÿ×—¦ÿ×—¼ÿ®—Áÿ®—Äÿ×—Üÿ×—äÿ×—ÿ®— ÿ®—|ÿ®—€ÿׂÿשÿ×—ªÿ×—µÿ®—¶ÿ×—·ÿ×¹ÿ×½ÿ×—¾ÿ×—¿ÿ®—Áÿ®—Ôÿ®—ýÿ®— ÿš—ÿš—ÿ×—ÿט�ÿ…˜� ÿ…˜ÐÿטÜÿš˜ÝÿØßÿטáÿ®˜äÿš˜öÿØÿ…˜ ÿ…˜mÿטÿטƒÿט‹ÿט ÿטªÿš˜¶ÿš˜¸ÿغÿؼÿט¾ÿš˜Àÿ®˜Âÿ®˜ÆÿטÈÿטËÿטÕÿ®˜æÿטêÿטøÿØúÿØüÿØþÿ®˜ÿטÿטÿš˜ÿš˜ÿš™�þö™�þö™¤ÿ…™ªÿš™®ÿ…™°ÿ×™µÿ…™¿ÿ×™Îÿš™Õÿš™òÿš™þö™ þö™rÿš™sÿš™vÿ왟ÿ×™»ÿ×™Êÿ×™Îÿ…™Ïÿš™Ùÿš™Ûÿš™Ýÿš™åÿ×™ÿ×™ÿ×™ ÿ®™ ÿ®™ÿ…™ÿš™ÿ…™ÿšš�ÿìš� ÿìšÐÿךÜÿìšÝÿìšßÿךáÿìšäÿìšöÿìšÿìš ÿìš ÿךªÿìš¶ÿìš¼ÿך¾ÿìšÀÿìšÂÿìšËÿךÕÿìšæÿךøÿìšúÿìšüÿìšþÿìšÿךÿךÿìšÿìšÿì›�ÿš›�ÿ×›�ÿš›�)›Ÿÿ×›¤ÿ®›¦�)›ªÿ…›®ÿ®›µÿ®›¸ÿ×›»ÿ×›¼�)›¾ÿÛÄ�)›ÌÿÛÍÿÛÎÿš›Ïÿ®›Ðÿ×›Ñÿ×›ÒÿÛÓÿÛÔÿÛÕÿš›ÖÿÛ×ÿÛØÿ®›ÙÿÛÚÿÛÛÿ®›Þÿ®›ßÿ×›àÿÛáÿš›âÿÛãÿÛåÿÛæÿÛçÿ×›èÿÛêÿ®›ë�)›ìÿÛíÿ®›îÿÛòÿš›óÿÛô�)›õÿÛ÷ÿÛùÿÛÿ×›ÿ×›ÿ×›ÿš› ÿš›jÿ®›kÿÛlÿ×›qÿÛrÿ…›sÿš›uÿÛwÿ×›yÿÛ}ÿÛ~ÿ×›ÿ®›„ÿ×›…ÿ®›†ÿ×›‡ÿ®›ˆÿ×›‰ÿ®›Šÿ×›Œÿ×›ÿ®›–ÿÛ˜�)›šÿÛžÿÛ ÿ×›¢ÿ×›¤ÿÛ¦ÿÛ¨�)›©�)›¬ÿÛ®ÿÛ°ÿÛ±ÿ×›²ÿ®›³ÿ×›´ÿ®›µ�)›¼ÿ×›½�)›Àÿš›Âÿš›ÄÿÛÅÿ×›ÆÿÛÇÿ×›ÈÿÛËÿ×›ÍÿÛÎÿ®›Ïÿš›ÑÿÛÓÿÛÕÿš›×ÿÛÙÿ…›Ûÿ…›Ýÿ…›àÿ®›æÿ×›èÿ×›ìÿÛîÿÛïÿ×›ðÿ®›ñÿ×›òÿ®›óÿ×›ôÿ®›öÿ×›þÿš›�ÿÛÿÛÿ×›ÿ×› ÿš› ÿ®› ÿš› ÿ®›ÿ×›ÿ×›ÿ®›ÿš›ÿÛÿ×›ÿ®›�)›ÿ®›ÿ®›ÿšœ�ÿÜ�ÿÜÎÿÜÏÿלÕÿÜØÿלÛÿלÞÿלêÿלíÿלòÿÜÿÜ ÿÜjÿלsÿÜÿל…ÿל‡ÿל‰ÿלÿל²ÿל´ÿלÏÿÜàÿלðÿלòÿלôÿל ÿל ÿלÿÜÿלÿלÿÃ�ÿÃ� ÿÃÿã�f¦ÿüÿÃÁÿ®ÄÿÃÜÿ×áÿ×äÿ×ÿà ÿÃ|ÿ®€ÿÂÿéÿêÿ×µÿöÿ×·ÿ×¹ÿ×½ÿþÿ׿ÿ®Àÿ×Áÿ®Âÿ×Ôÿ®Õÿ×ýÿ®þÿ× ÿ×ÿÃÿ×ÿÃÿÃÿמ�ÿÞ� ÿÞÿÞ ÿÞÿמÿןŸÿן£�៸ÿן»ÿן¾ÿßÜÿןáÿ®Ÿäÿןlÿן{�=Ÿ}ÿìŸ~ÿן„ÿן†ÿןˆÿןŠÿןŒÿןªÿן±ÿן³ÿן¶ÿן¾ÿןÀÿ®ŸÂÿ®ŸÅÿ߯ÿןÇÿßÈÿןÕÿ®Ÿïÿןñÿןóÿןþÿ®Ÿÿןÿןÿןÿ× Ïÿì Øÿì Ûÿì Þÿì áÿì êÿì íÿì jÿì ÿì …ÿì ‡ÿì ‰ÿì ÿì ²ÿì ´ÿì Àÿì Âÿì Õÿì àÿì ðÿì òÿì ôÿì þÿì  ÿì  ÿì ÿ× ÿ× ÿì ÿì¡�ÿ®¡�ÿ®¡ÿ®¡ ÿ®¡€ÿì¡‚ÿì¡·ÿ졹ÿì¡ ÿסÿ×¢é�)£Ÿÿ×££�᣸ÿ×£»ÿ×£¾ÿãÜÿ×£áÿ®£äÿ×£lÿ×£{�=£}ÿì£~ÿ×£„ÿ×£†ÿ×£ˆÿ×£Šÿ×£Œÿ×£ªÿ×£±ÿ×£³ÿ×£¶ÿ×£¾ÿ×£Àÿ®£Âÿ®£ÅÿãÆÿ×£ÇÿãÈÿ×£Õÿ®£ïÿ×£ñÿ×£óÿ×£þÿ®£ÿ×£ÿ×£ÿ×£ÿפÏÿì¤Øÿì¤Ûÿì¤Þÿì¤áÿì¤êÿì¤íÿì¤jÿì¤ÿ줅ÿ줇ÿ줉ÿì¤ÿ줲ÿ줴ÿì¤Àÿì¤Âÿì¤Õÿì¤àÿì¤ðÿì¤òÿì¤ôÿì¤þÿì¤ ÿì¤ ÿì¤ÿפÿפÿì¤ÿ쥟ÿ×¥¸ÿ×¥»ÿ×¥¾ÿ×¥Áÿ×¥áÿ×¥lÿ×¥|ÿ×¥~ÿ×¥„ÿ×¥†ÿ×¥ˆÿ×¥Šÿ×¥Œÿ×¥±ÿ×¥³ÿ×¥¿ÿ×¥Àÿ×¥Áÿ×¥Âÿ×¥Åÿš¥Çÿš¥Ôÿ×¥Õÿ×¥ïÿ×¥ñÿ×¥óÿ×¥ýÿ×¥þÿ×¥ ÿ×¥ ÿ×¥ÿ×¥ÿ×¥ÿ×¥ÿì¦ÏÿצØÿצÛÿצÞÿצáÿצêÿצíÿצjÿצÿצ…ÿצ‡ÿצ‰ÿצÿצ²ÿצ´ÿצÀÿצÂÿצÆÿצÈÿצÕÿצàÿצðÿצòÿצôÿצþÿצ ÿצ ÿצÿצÿ×§Ÿÿ×§¸ÿ×§»ÿ×§¾ÿ×§Áÿ×§áÿ×§lÿ×§|ÿ×§~ÿ×§„ÿ×§†ÿ×§ˆÿ×§Šÿ×§Œÿ×§±ÿ×§³ÿ×§¿ÿ×§Àÿ×§Áÿ×§Âÿ×§Åÿš§Çÿš§Ôÿ×§Õÿ×§ïÿ×§ñÿ×§óÿ×§ýÿ×§þÿ×§ ÿ×§ ÿ×§ÿ×§ÿ×§ÿ×§ÿì¨ÏÿרØÿרÛÿרÞÿרáÿרêÿרíÿרjÿרÿר…ÿר‡ÿר‰ÿרÿר²ÿר´ÿרÀÿרÂÿרÆÿרÈÿרÕÿרàÿרðÿרòÿרôÿרþÿר ÿר ÿרÿרÿשŸÿש¸ÿש»ÿש¾ÿשÁÿשáÿשlÿש|ÿש~ÿש„ÿש†ÿשˆÿשŠÿשŒÿש±ÿש³ÿש¿ÿשÀÿשÁÿשÂÿשÅÿš©Çÿš©ÔÿשÕÿשïÿשñÿשóÿשýÿשþÿש ÿש ÿשÿשÿשÿשÿìªÏÿתØÿתÛÿתÞÿתáÿתêÿתíÿתjÿתÿת…ÿת‡ÿת‰ÿתÿת²ÿת´ÿתÀÿתÂÿתÆÿתÈÿתÕÿתàÿתðÿתòÿתôÿתþÿת ÿת ÿתÿתÿ׫£�á«ê�)«ÿ׫ÿ׬�ÿì¬� ÿì¬ÿì¬ ÿì­�ÿš­�ÿ×­�ÿš­�)­Ÿÿ×­¤ÿ®­¦�)­ªÿ…­®ÿ®­µÿ®­¸ÿ×­»ÿ×­¼�)­¾ÿíÄ�)­ÌÿíÍÿíÎÿš­Ïÿ®­Ðÿ×­Ñÿ×­ÒÿíÓÿíÔÿíÕÿš­Öÿí×ÿíØÿ®­ÙÿíÚÿíÛÿ®­Þÿ®­ßÿ×­àÿíáÿš­âÿíãÿíåÿíæÿíçÿ×­èÿíêÿ®­ë�)­ìÿííÿ®­îÿíòÿš­óÿíô�)­õÿí÷ÿíùÿíÿ×­ÿ×­ÿ×­ÿš­ ÿš­jÿ®­kÿílÿ×­qÿírÿ…­sÿš­uÿíwÿ×­yÿí}ÿí~ÿ×­ÿ®­„ÿ×­…ÿ®­†ÿ×­‡ÿ®­ˆÿ×­‰ÿ®­Šÿ×­Œÿ×­ÿ®­–ÿí˜�)­šÿížÿí ÿ×­¢ÿ×­¤ÿí¦ÿí¨�)­©�)­¬ÿí®ÿí°ÿí±ÿ×­²ÿ®­³ÿ×­´ÿ®­µ�)­¼ÿ×­½�)­Àÿš­Âÿš­ÄÿíÅÿ×­ÆÿíÇÿ×­ÈÿíËÿ×­ÍÿíÎÿ®­Ïÿš­ÑÿíÓÿíÕÿš­×ÿíÙÿ…­Ûÿ…­Ýÿ…­àÿ®­æÿ×­èÿ×­ìÿíîÿíïÿ×­ðÿ®­ñÿ×­òÿ®­óÿ×­ôÿ®­öÿ×­þÿš­�ÿíÿíÿ×­ÿ×­ ÿš­ ÿ®­ ÿš­ ÿ®­ÿ×­ÿ×­ÿ®­ÿš­ÿíÿ×­ÿ®­�)­ÿ®­ÿ®­ÿš®�ÿš®�ÿ×®�ÿš®ÎÿîÏÿì®ÕÿîØÿì®Ûÿì®Þÿì®êÿì®íÿì®òÿîÿ×®ÿ×®ÿ×®ÿš® ÿš®jÿì®sÿîÿì®…ÿ쮇ÿ쮉ÿì®ÿ쮲ÿì®´ÿì®Ïÿîàÿì®ðÿì®òÿì®ôÿì® ÿì® ÿì®ÿîÿì®ÿì®ÿï�ÿ\¯� ÿ\¯ÿš¯£�f¯¦ÿš¯¼ÿH¯Áÿ…¯Äÿš¯Üÿ®¯áÿׯäÿ®¯ÿ\¯ ÿ\¯|ÿ…¯€ÿq¯‚ÿq¯©ÿš¯ªÿ®¯µÿH¯¶ÿ®¯·ÿš¯¹ÿš¯½ÿš¯¾ÿ®¯¿ÿ…¯ÀÿׯÁÿ…¯ÂÿׯÅÿïÆÿׯÇÿïÈÿׯÔÿ…¯Õÿׯýÿ…¯þÿׯ ÿH¯ÿ®¯ÿH¯ÿ®¯ÿš¯ÿ®°�ÿq°� ÿq°Üÿš°áÿ×°äÿš°ÿq° ÿq°mÿ×°ÿ×°ƒÿ×°‹ÿ×°ªÿš°¶ÿš°¸ÿ×°ºÿ×°¾ÿš°Àÿ×°Âÿ×°Æÿ×°Èÿ×°Õÿ×°þÿ×°ÿq°ÿq°ÿš±ÿ×±¦ÿ×±¼ÿñÄÿ×±€ÿ챂ÿ챩ÿ×±µÿñ·ÿì±¹ÿì±½ÿ×± ÿ×±ÿ×±ÿײ�ÿì²� ÿì²ÐÿײÜÿì²Ýÿì²ßÿײáÿì²äÿì²öÿì²ÿì² ÿì² ÿײªÿì²¶ÿì²¼ÿײ¾ÿì²Àÿì²Âÿì²ËÿײÕÿì²æÿײøÿì²úÿì²üÿì²þÿì²ÿײÿײÿì²ÿì²ÿ쳟ÿ׳¸ÿ׳»ÿ׳¾ÿ׳áÿ׳lÿ׳~ÿ׳„ÿ׳†ÿ׳ˆÿ׳Šÿ׳Œÿ׳±ÿ׳³ÿ׳Àÿ׳Âÿ׳Åÿ׳Çÿ׳Õÿ׳ïÿ׳ñÿ׳óÿ׳þÿ׳ ÿ׳ ÿ׳ÿ׳ÿ׳ÿ×µ�ÿ…µ�ÿ®µ�ÿ…µŸÿ×µ¤ÿšµªÿqµ®ÿšµµÿšµ¸ÿ×µ»ÿ×µ¼�)µ¾ÿ®µÌÿšµÍÿšµÎÿ…µÏÿqµÐÿ×µÑÿ×µÒÿšµÓÿšµÔÿšµÕÿ…µÖÿšµ×ÿšµØÿqµÙÿšµÚÿšµÛÿqµÜÿ®µÝÿ®µÞÿqµßÿ×µàÿšµáÿšµâÿšµãÿšµäÿ®µåÿšµæÿšµçÿ×µèÿšµéÿõêÿqµìÿšµíÿqµîÿ…µòÿ…µóÿšµõÿšµöÿ®µ÷ÿšµùÿšµÿ®µÿ®µÿ®µÿ…µ ÿ…µjÿqµkÿšµlÿ×µmÿ×µqÿšµrÿqµsÿ…µuÿšµwÿšµyÿšµ}ÿšµ~ÿ×µÿqµÿ×µƒÿ×µ„ÿ×µ…ÿqµ†ÿ×µ‡ÿqµˆÿ×µ‰ÿqµŠÿ×µ‹ÿ×µŒÿ×µÿqµ–ÿšµšÿšµžÿšµ ÿ×µ¢ÿ×µ¤ÿšµ¦ÿšµªÿ®µ¬ÿšµ®ÿšµ°ÿšµ±ÿ×µ²ÿqµ³ÿ×µ´ÿqµµ�)µ¶ÿ®µ¸ÿ®µºÿ®µ¼ÿ×µ¾ÿ®µÀÿšµÂÿšµÄÿšµÅÿšµÆÿqµÇÿšµÈÿqµËÿ×µÍÿšµÎÿšµÏÿ…µÑÿšµÓÿšµÕÿšµ×ÿšµÙÿqµÛÿqµÝÿqµàÿqµæÿ×µèÿ×µêÿõìÿšµîÿšµïÿ×µðÿqµñÿ×µòÿqµóÿ×µôÿqµöÿ×µøÿ®µúÿ®µüÿ®µþÿšµ�ÿšµÿšµÿ×µÿ×µ ÿqµ ÿqµ ÿqµ ÿqµÿšµÿšµÿšµÿ…µÿšµÿ×µÿqµÿ®µÿqµÿšµÿ…¶�ÿš¶�ÿ×¶�ÿš¶ÎÿöÏÿì¶ÕÿöØÿì¶Ûÿì¶Þÿì¶êÿì¶íÿì¶òÿöÿ×¶ÿ×¶ÿ×¶ÿš¶ ÿš¶jÿì¶sÿöÿì¶…ÿ춇ÿ춉ÿì¶ÿì¶²ÿì¶´ÿì¶Ïÿöàÿì¶ðÿì¶òÿì¶ôÿì¶ ÿì¶ ÿì¶ÿöÿì¶ÿì¶ÿ÷�ÿ…·�ÿ…·Ÿÿ×·¤ÿ®·ªÿ…·®ÿ®·µÿ®·¸ÿ×·»ÿ×·¾ÿ÷Êÿ®·Ìÿ÷Íÿ÷Îÿš·Ïÿš·Òÿ÷Óÿ÷Ôÿ÷Õÿš·Öÿ÷×ÿ÷Øÿš·Ùÿ÷Úÿ÷Ûÿš·Þÿš·àÿ÷áÿ®·âÿ÷ãÿ÷åÿ÷æÿ÷èÿ÷éÿ×·êÿš·ë�)·ìÿ÷íÿš·îÿ®·òÿš·óÿ÷ô�)·õÿ÷÷ÿ÷ùÿ÷ÿ…· ÿ…·jÿš·kÿ÷lÿ×·qÿ÷rÿ…·sÿš·uÿ÷wÿ×·yÿ÷}ÿ×·~ÿ×·ÿš·„ÿ×·…ÿš·†ÿ×·‡ÿš·ˆÿ×·‰ÿš·Šÿ×·Œÿ×·ÿš·–ÿ÷˜�)·šÿ÷žÿ÷¤ÿ÷¦ÿ÷¨�)·¬ÿ÷®ÿ÷°ÿ÷±ÿ×·²ÿš·³ÿ×·´ÿš·Àÿ®·Âÿ®·Äÿ÷Æÿ®·Èÿ®·Íÿ÷Îÿ®·Ïÿš·Ñÿ÷Óÿ÷Õÿ®·×ÿ÷Ùÿ…·Úÿ®·Ûÿ…·Üÿ®·Ýÿ…·Þÿ®·àÿš·áÿì·âÿ®·ãÿì·äÿ®·ìÿ÷îÿ÷ïÿ×·ðÿš·ñÿ×·òÿš·óÿ×·ôÿš·þÿ®·�ÿ÷ÿ÷ ÿ®· ÿš· ÿ®· ÿš·ÿ×·ÿ×·ÿ®·ÿš·ÿ÷ÿ×·ÿš·ÿì·ÿš·ÿ®·ÿš¸�ÿ®¸�ÿ®¸Îÿì¸Õÿì¸òÿì¸ÿ®¸ ÿ®¸sÿì¸Ïÿì¸ÿì¸ÿì¹�ÿ…¹�ÿ…¹Ÿÿ×¹¤ÿ®¹ªÿ…¹®ÿ®¹µÿ®¹¸ÿ×¹»ÿ×¹¾ÿùÊÿ®¹ÌÿùÍÿùÎÿš¹Ïÿš¹ÒÿùÓÿùÔÿùÕÿš¹Öÿù×ÿùØÿš¹ÙÿùÚÿùÛÿš¹Þÿš¹àÿùáÿ®¹âÿùãÿùåÿùæÿùèÿùéÿ×¹êÿš¹ë�)¹ìÿùíÿš¹îÿ®¹òÿš¹óÿùô�)¹õÿù÷ÿùùÿùÿ…¹ ÿ…¹jÿš¹kÿùlÿ×¹qÿùrÿ…¹sÿš¹uÿùwÿ×¹yÿù}ÿ×¹~ÿ×¹ÿš¹„ÿ×¹…ÿš¹†ÿ×¹‡ÿš¹ˆÿ×¹‰ÿš¹Šÿ×¹Œÿ×¹ÿš¹–ÿù˜�)¹šÿùžÿù¤ÿù¦ÿù¨�)¹¬ÿù®ÿù°ÿù±ÿ×¹²ÿš¹³ÿ×¹´ÿš¹Àÿ®¹Âÿ®¹ÄÿùÆÿ®¹Èÿ®¹ÍÿùÎÿ®¹Ïÿš¹ÑÿùÓÿùÕÿ®¹×ÿùÙÿ…¹Úÿ®¹Ûÿ…¹Üÿ®¹Ýÿ…¹Þÿ®¹àÿš¹áÿì¹âÿ®¹ãÿì¹äÿ®¹ìÿùîÿùïÿ×¹ðÿš¹ñÿ×¹òÿš¹óÿ×¹ôÿš¹þÿ®¹�ÿùÿù ÿ®¹ ÿš¹ ÿ®¹ ÿš¹ÿ×¹ÿ×¹ÿ®¹ÿš¹ÿùÿ×¹ÿš¹ÿì¹ÿš¹ÿ®¹ÿšº�ÿ®º�ÿ®ºÎÿìºÕÿìºòÿìºÿ®º ÿ®ºsÿìºÏÿìºÿìºÿ컟ÿ×»£�Ỹÿ×»»ÿ×»¾ÿûÜÿ×»áÿ®»äÿ×»lÿ×»{�=»}ÿì»~ÿ×»„ÿ×»†ÿ×»ˆÿ×»Šÿ×»Œÿ×»ªÿ×»±ÿ×»³ÿ×»¶ÿ×»¾ÿ×»Àÿ®»Âÿ®»ÅÿûÆÿ×»ÇÿûÈÿ×»Õÿ®»ïÿ×»ñÿ×»óÿ×»þÿ®»ÿ×»ÿ×»ÿ×»ÿ×¼Ïÿì¼Øÿì¼Ûÿì¼Þÿì¼áÿì¼êÿì¼íÿì¼jÿì¼ÿì¼…ÿ켇ÿ켉ÿì¼ÿì¼²ÿì¼´ÿì¼Àÿì¼Âÿì¼Õÿì¼àÿì¼ðÿì¼òÿì¼ôÿì¼þÿì¼ ÿì¼ ÿì¼ÿ×¼ÿ×¼ÿì¼ÿì½£�á½ê�)½ÿ×½ÿ×¾�ÿì¾� ÿì¾ÿì¾ ÿì¿£�á¿ê�)¿ÿ׿ÿ×À�ÿìÀ� ÿìÀÿìÀ ÿìÃ�ÿÃÃ� ÿÃÃÿ׿ÿ×üÿ…ÃÁÿ®ÃÄÿ×ÃÜÿ×ÃÝÿìÃáÿìÃäÿ×ÃöÿìÃÿÃà ÿÃÃ|ÿ®Ã€ÿÃÂÿÃéÿ×êÿ×õÿ…öÿ×÷ÿšÃ¹ÿšÃ½ÿ×þÿ×ÿÿ®ÃÀÿìÃÁÿ®ÃÂÿìÃÔÿ®ÃÕÿìÃøÿìÃúÿìÃüÿìÃýÿ®Ãþÿìà ÿ®Ãÿ×Ãÿ®Ãÿ×Ãÿ×Ãÿ×Ä�ÿšÄ� ÿšÄÜÿ×ÄÝÿ×Ääÿ×Äöÿ×ÄÿšÄ ÿšÄªÿ×Ķÿ×ĸÿ×ĺÿ׾ÿ×Äøÿ×Äúÿ×Äüÿ×Äÿ®Äÿ®Äÿ׿ÿ×Å€ÿìÅ‚ÿìŵÿ×Å·ÿìŹÿìÅ ÿìÅÿìÆ�ÿìÆ� ÿìÆÿìÆ ÿìǼÿ×Ç€ÿìÇ‚ÿìǵÿ×Ç·ÿìǹÿìÇ ÿìÇÿìÈ�ÿìÈ� ÿìÈÿìÈ ÿìÊŸÿ×ʸÿ×Ê»ÿ×ʾÿ×ÊÁÿ×Êáÿ×Êlÿ×Ê|ÿ×Ê~ÿ×Ê„ÿ×ʆÿ×ʈÿ×ÊŠÿ×ÊŒÿ×ʱÿ×ʳÿ×Ê¿ÿ×ÊÀÿ×ÊÁÿ×ÊÂÿ×ÊÅÿšÊÇÿšÊÔÿ×ÊÕÿ×Êïÿ×Êñÿ×Êóÿ×Êýÿ×Êþÿ×Ê ÿ×Ê ÿ×Êÿ×Êÿ×Êÿ×ÊÿìËÏÿ×ËØÿ×ËÛÿ×ËÞÿ×Ëáÿ×Ëêÿ×Ëíÿ×Ëjÿ×Ëÿ×Ë…ÿסÿ×ˉÿ×Ëÿ×˲ÿ×Ë´ÿ×ËÀÿ×ËÂÿ×ËÆÿ×ËÈÿ×ËÕÿ×Ëàÿ×Ëðÿ×Ëòÿ×Ëôÿ×Ëþÿ×Ë ÿ×Ë ÿ×Ëÿ×Ëÿ×Ì�ÿÃÌ� ÿÃÌ£�f̼ÿ×̾ÿ×ÌÁÿ®ÌÜÿÃÌáÿ×ÌäÿÃÌÿÃÌ ÿÃÌmÿìÌ|ÿ®Ì€ÿ×ÌÿìÌ‚ÿ×̃ÿìÌ‹ÿì̪ÿÃ̵ÿ×̶ÿÃÌ·ÿ×̸ÿì̹ÿ×̺ÿì̾ÿÃÌ¿ÿ®ÌÀÿ×ÌÁÿ®ÌÂÿ×ÌÅÿÃÌÆÿ×ÌÇÿÃÌÈÿ×ÌÔÿ®ÌÕÿ×Ìýÿ®Ìþÿ×Ì ÿ×ÌÿÃÌÿ×ÌÿÃÌÿÃÍáÿ×ÍÀÿ×ÍÂÿ×ÍÕÿ×Íþÿ×Σ�áÎê�)Îÿ×Îÿ×Ï�ÿìÏ� ÿìÏÿìÏ ÿìÒ£�áÒê�)Òÿ×Òÿ×Ó�ÿìÓ� ÿìÓÿìÓ ÿìÖ£�áÖê�)Öÿ×Öÿ××�ÿì×� ÿì×ÿì× ÿìÙ�ÿqÙ� ÿqÙÿšÙ¦ÿšÙ¼ÿqÙ¾ÿ×ÙÁÿšÙÄÿšÙÜÿ×Ùáÿ×Ùäÿ×ÙÿqÙ ÿqÙnÿ×Ù|ÿšÙ€ÿ®Ù‚ÿ®Ù—ÿ×Ù›ÿ×Ù§ÿ×Ù©ÿšÙªÿ×ÙµÿqÙ¶ÿ×Ù·ÿ…Ù¹ÿ…Ù½ÿšÙ¾ÿ×Ù¿ÿšÙÀÿ×ÙÁÿšÙÂÿ×ÙÅÿšÙÇÿšÙÔÿšÙÕÿ×Ùáÿ×Ùãÿ×ÙýÿšÙþÿ×Ùÿ×Ù ÿqÙÿ×ÙÿqÙÿ×ÙÿšÙÿ×Ú�ÿìÚ� ÿìÚÿìÚ ÿìÛ�ÿqÛ� ÿqÛÿšÛ¦ÿšÛ¼ÿqÛ¾ÿ×ÛÁÿšÛÄÿšÛÜÿ×Ûáÿ×Ûäÿ×ÛÿqÛ ÿqÛnÿ×Û|ÿšÛ€ÿ®Û‚ÿ®Û—ÿ×Û›ÿ×Û§ÿ×Û©ÿšÛªÿ×ÛµÿqÛ¶ÿ×Û·ÿ…Û¹ÿ…Û½ÿšÛ¾ÿ×Û¿ÿšÛÀÿ×ÛÁÿšÛÂÿ×ÛÅÿšÛÇÿšÛÔÿšÛÕÿ×Ûáÿ×Ûãÿ×ÛýÿšÛþÿ×Ûÿ×Û ÿqÛÿ×ÛÿqÛÿ×ÛÿšÛÿ×Ü�ÿìÜ� ÿìÜÿìÜ ÿìÞ�ÿìÞ� ÿìÞÿìÞ ÿìà�ÿìà� ÿìàÿìà ÿìá�ÿ®á�ÿ®áÿìá¤ÿ×á¦ÿìá¨ÿ×áªÿ×á®ÿ×á°ÿ×á±ÿìáµÿ×á¼ÿÃá½ÿ×á¿ÿ×áÁÿ×áÄÿìáÇÿìáÎÿìáÕÿìáòÿìáÿ®á ÿ®árÿ×ásÿìázÿìá|ÿ×á€ÿìá‚ÿìáŸÿ×á¡ÿìá©ÿìáµÿÃá·ÿìá¹ÿìá»ÿ×á½ÿìá¿ÿ×áÁÿ×áÊÿ×áÎÿ×áÏÿìáÔÿ×áÙÿ×áÛÿ×áÝÿ×áåÿ×áçÿìáõÿìá÷ÿ×áùÿ×áûÿ×áýÿ×áÿ×áÿ×á ÿ×áÿ×áÿ×áÿìáÿìáÿ×áÿìâ�ÿìâ� ÿìâÐÿ×âÜÿìâÝÿìâßÿ×âáÿìâäÿìâöÿìâÿìâ ÿìâ ÿ×âªÿìâ¶ÿìâ¼ÿ×â¾ÿìâÀÿìâÂÿìâËÿ×âÕÿìâæÿ×âøÿìâúÿìâüÿìâþÿìâÿ×âÿ×âÿìâÿìâÿìã�ÿ®ã�ÿ®ãÿìã¤ÿ×ã¦ÿìã¨ÿ×ãªÿ×ã®ÿ×ã°ÿ×ã±ÿìãµÿ×ã¼ÿÃã½ÿ×ã¿ÿ×ãÁÿ×ãÄÿìãÇÿìãÎÿìãÕÿìãòÿìãÿ®ã ÿ®ãrÿ×ãsÿìãzÿìã|ÿ×ã€ÿìã‚ÿìãŸÿ×ã¡ÿìã©ÿìãµÿÃã·ÿìã¹ÿìã»ÿ×ã½ÿìã¿ÿ×ãÁÿ×ãÊÿ×ãÎÿ×ãÏÿìãÔÿ×ãÙÿ×ãÛÿ×ãÝÿ×ãåÿ×ãçÿìãõÿìã÷ÿ×ãùÿ×ãûÿ×ãýÿ×ãÿ×ãÿ×ã ÿ×ãÿ×ãÿ×ãÿìãÿìãÿ×ãÿìä�ÿìä� ÿìäÐÿ×äÜÿìäÝÿìäßÿ×äáÿìääÿìäöÿìäÿìä ÿìä ÿ×äªÿìä¶ÿìä¼ÿ×ä¾ÿìäÀÿìäÂÿìäËÿ×äÕÿìäæÿ×äøÿìäúÿìäüÿìäþÿìäÿ×äÿ×äÿìäÿìäÿìåŸÿ×å¸ÿ×å»ÿ×å¾ÿ×åÁÿ×åáÿ×ålÿ×å|ÿ×å~ÿ×å„ÿ×å†ÿ×åˆÿ×åŠÿ×åŒÿ×å±ÿ×å³ÿ×å¿ÿ×åÀÿ×åÁÿ×åÂÿ×åÅÿšåÇÿšåÔÿ×åÕÿ×åïÿ×åñÿ×åóÿ×åýÿ×åþÿ×å ÿ×å ÿ×åÿ×åÿ×åÿ×åÿìæÏÿ׿Øÿ׿Ûÿ׿Þÿ׿áÿ׿êÿ׿íÿ׿jÿ׿ÿ׿…ÿ׿‡ÿ׿‰ÿ׿ÿ׿²ÿ׿´ÿ׿Àÿ׿Âÿ׿Æÿ׿Èÿ׿Õÿ׿àÿ׿ðÿ׿òÿ׿ôÿ׿þÿ׿ ÿ׿ ÿ׿ÿ׿ÿ×ç�ÿ®ç�ÿ®çÿ®ç ÿ®ç€ÿìç‚ÿìç·ÿìç¹ÿìç ÿ×çÿ×èé�)é�ÿìé� ÿìéÿìé ÿìéÿ×éÿ×ï�ÿ®ï�ÿ®ïÿìï¤ÿ×ï¦ÿìï¨ÿ×ïªÿ×ï®ÿ×ï°ÿ×ï±ÿìïµÿ×ï¼ÿÃï½ÿ×ï¿ÿ×ïÁÿ×ïÄÿìïÇÿìïÎÿìïÕÿìïòÿìïÿ®ï ÿ®ïrÿ×ïsÿìïzÿìï|ÿ×ï€ÿìï‚ÿìïŸÿ×ï¡ÿìï©ÿìïµÿÃï·ÿìï¹ÿìï»ÿ×ï½ÿìï¿ÿ×ïÁÿ×ïÊÿ×ïÎÿ×ïÏÿìïÔÿ×ïÙÿ×ïÛÿ×ïÝÿ×ïåÿ×ïçÿìïõÿìï÷ÿ×ïùÿ×ïûÿ×ïýÿ×ïÿ×ïÿ×ï ÿ×ïÿ×ïÿ×ïÿìïÿìïÿ×ïÿìð�ÿìð� ÿìðÐÿ×ðÜÿìðÝÿìðßÿ×ðáÿìðäÿìðöÿìðÿìð ÿìð ÿ×ðªÿìð¶ÿìð¼ÿ×ð¾ÿìðÀÿìðÂÿìðËÿ×ðÕÿìðæÿ×ðøÿìðúÿìðüÿìðþÿìðÿ×ðÿ×ðÿìðÿìðÿìñ�ÿ®ñ�ÿ®ñÿìñ¤ÿ×ñ¦ÿìñ¨ÿ×ñªÿ×ñ®ÿ×ñ°ÿ×ñ±ÿìñµÿ×ñ¼ÿÃñ½ÿ×ñ¿ÿ×ñÁÿ×ñÄÿìñÇÿìñÎÿìñÕÿìñòÿìñÿ®ñ ÿ®ñrÿ×ñsÿìñzÿìñ|ÿ×ñ€ÿìñ‚ÿìñŸÿ×ñ¡ÿìñ©ÿìñµÿÃñ·ÿìñ¹ÿìñ»ÿ×ñ½ÿìñ¿ÿ×ñÁÿ×ñÊÿ×ñÎÿ×ñÏÿìñÔÿ×ñÙÿ×ñÛÿ×ñÝÿ×ñåÿ×ñçÿìñõÿìñ÷ÿ×ñùÿ×ñûÿ×ñýÿ×ñÿ×ñÿ×ñ ÿ×ñÿ×ñÿ×ñÿìñÿìñÿ×ñÿìò�ÿìò� ÿìòÐÿ×òÜÿìòÝÿìòßÿ×òáÿìòäÿìòöÿìòÿìò ÿìò ÿ×òªÿìò¶ÿìò¼ÿ×ò¾ÿìòÀÿìòÂÿìòËÿ×òÕÿìòæÿ×òøÿìòúÿìòüÿìòþÿìòÿ×òÿ×òÿìòÿìòÿìó�ÿ®ó�ÿ®óÿìó¤ÿ×ó¦ÿìó¨ÿ×óªÿ×ó®ÿ×ó°ÿ×ó±ÿìóµÿ×ó¼ÿÃó½ÿ×ó¿ÿ×óÁÿ×óÄÿìóÇÿìóÎÿìóÕÿìóòÿìóÿ®ó ÿ®órÿ×ósÿìózÿìó|ÿ×ó€ÿìó‚ÿìóŸÿ×ó¡ÿìó©ÿìóµÿÃó·ÿìó¹ÿìó»ÿ×ó½ÿìó¿ÿ×óÁÿ×óÊÿ×óÎÿ×óÏÿìóÔÿ×óÙÿ×óÛÿ×óÝÿ×óåÿ×óçÿìóõÿìó÷ÿ×óùÿ×óûÿ×óýÿ×óÿ×óÿ×ó ÿ×óÿ×óÿ×óÿìóÿìóÿ×óÿìô�ÿìô� ÿìôÐÿ×ôÜÿìôÝÿìôßÿ×ôáÿìôäÿìôöÿìôÿìô ÿìô ÿ×ôªÿìô¶ÿìô¼ÿ×ô¾ÿìôÀÿìôÂÿìôËÿ×ôÕÿìôæÿ×ôøÿìôúÿìôüÿìôþÿìôÿ×ôÿ×ôÿìôÿìôÿìõ�ÿ®õ�ÿ®õÿìõ¤ÿ×õ¦ÿìõ¨ÿ×õªÿ×õ®ÿ×õ°ÿ×õ±ÿìõµÿ×õ¼ÿÃõ½ÿ×õ¿ÿ×õÁÿ×õÄÿìõÇÿìõÎÿìõÕÿìõòÿìõÿ®õ ÿ®õrÿ×õsÿìõzÿìõ|ÿ×õ€ÿìõ‚ÿìõŸÿ×õ¡ÿìõ©ÿìõµÿÃõ·ÿìõ¹ÿìõ»ÿ×õ½ÿìõ¿ÿ×õÁÿ×õÊÿ×õÎÿ×õÏÿìõÔÿ×õÙÿ×õÛÿ×õÝÿ×õåÿ×õçÿìõõÿìõ÷ÿ×õùÿ×õûÿ×õýÿ×õÿ×õÿ×õ ÿ×õÿ×õÿ×õÿìõÿìõÿ×õÿìö�ÿìö� ÿìöÐÿ×öÜÿìöÝÿìößÿ×öáÿìöäÿìööÿìöÿìö ÿìö ÿ×öªÿìö¶ÿìö¼ÿ×ö¾ÿìöÀÿìöÂÿìöËÿ×öÕÿìöæÿ×öøÿìöúÿìöüÿìöþÿìöÿ×öÿ×öÿìöÿìöÿì÷�ÿ…÷�ÿ…÷Ÿÿì÷¤ÿš÷ªÿq÷®ÿš÷µÿš÷¸ÿì÷»ÿì÷¾ÿÃ÷Éÿì÷Îÿ®÷Ïÿ×÷Õÿ®÷Øÿ×÷Ûÿ×÷Þÿ×÷áÿ×÷êÿ×÷ë�f÷íÿ×÷îÿì÷òÿ®÷ô�f÷ÿ…÷ ÿ…÷jÿ×÷lÿì÷rÿq÷sÿ®÷~ÿì÷ÿ×÷„ÿì÷…ÿ×÷†ÿì÷‡ÿ×÷ˆÿì÷‰ÿ×÷Šÿì÷Œÿì÷ÿ×÷˜�f÷¨�f÷±ÿì÷²ÿ×÷³ÿì÷´ÿ×÷Àÿ×÷Âÿ×÷Åÿ×÷ÆÿÃ÷Çÿ×÷ÈÿÃ÷Îÿš÷Ïÿ®÷Õÿ×÷Ùÿq÷Ûÿq÷Ýÿq÷àÿ×÷ïÿì÷ðÿ×÷ñÿì÷òÿ×÷óÿì÷ôÿ×÷þÿ×÷ ÿq÷ ÿ×÷ ÿq÷ ÿ×÷ÿš÷ÿ®÷ÿì÷ÿ×÷ÿ×÷ÿš÷ÿ®ø�ÿ®ø�ÿ®øÎÿ×øÕÿ×øòÿ×øÿ®ø ÿ®øsÿ×øÏÿ×øÿ×øÿ×ù�ÿ…ù�ÿ…ùŸÿìù¤ÿšùªÿqù®ÿšùµÿšù¸ÿìù»ÿìù¾ÿÃùÉÿìùÎÿ®ùÏÿ×ùÕÿ®ùØÿ×ùÛÿ×ùÞÿ×ùáÿ×ùêÿ×ùë�fùíÿ×ùîÿìùòÿ®ùô�fùÿ…ù ÿ…ùjÿ×ùlÿìùrÿqùsÿ®ù~ÿìùÿ×ù„ÿìù…ÿ×ù†ÿìù‡ÿ×ùˆÿìù‰ÿ×ùŠÿìùŒÿìùÿ×ù˜�fù¨�fù±ÿìù²ÿ×ù³ÿìù´ÿ×ùÀÿ×ùÂÿ×ùÅÿ×ùÆÿÃùÇÿ×ùÈÿÃùÎÿšùÏÿ®ùÕÿ×ùÙÿqùÛÿqùÝÿqùàÿ×ùïÿìùðÿ×ùñÿìùòÿ×ùóÿìùôÿ×ùþÿ×ù ÿqù ÿ×ù ÿqù ÿ×ùÿšùÿ®ùÿìùÿ×ùÿ×ùÿšùÿ®ú�ÿ®ú�ÿ®úÎÿ×úÕÿ×úòÿ×úÿ®ú ÿ®úsÿ×úÏÿ×úÿ×úÿ×û�ÿ…û�ÿ…ûŸÿìû¤ÿšûªÿqû®ÿšûµÿšû¸ÿìû»ÿìû¾ÿÃûÉÿìûÎÿ®ûÏÿ×ûÕÿ®ûØÿ×ûÛÿ×ûÞÿ×ûáÿ×ûêÿ×ûë�fûíÿ×ûîÿìûòÿ®ûô�fûÿ…û ÿ…ûjÿ×ûlÿìûrÿqûsÿ®û~ÿìûÿ×û„ÿìû…ÿ×û†ÿìû‡ÿ×ûˆÿìû‰ÿ×ûŠÿìûŒÿìûÿ×û˜�fû¨�fû±ÿìû²ÿ×û³ÿìû´ÿ×ûÀÿ×ûÂÿ×ûÅÿ×ûÆÿÃûÇÿ×ûÈÿÃûÎÿšûÏÿ®ûÕÿ×ûÙÿqûÛÿqûÝÿqûàÿ×ûïÿìûðÿ×ûñÿìûòÿ×ûóÿìûôÿ×ûþÿ×û ÿqû ÿ×û ÿqû ÿ×ûÿšûÿ®ûÿìûÿ×ûÿ×ûÿšûÿ®ü�ÿ®ü�ÿ®üÎÿ×üÕÿ×üòÿ×üÿ®ü ÿ®üsÿ×üÏÿ×üÿ×üÿ×ÿ�ÿ…ÿ�ÿ®ÿ�ÿ…ÿŸÿ×ÿ¤ÿšÿªÿqÿ®ÿšÿµÿšÿ¸ÿ×ÿ»ÿ×ÿ¼�)ÿ¾ÿ®ÿÌÿšÿÍÿšÿÎÿ…ÿÏÿqÿÐÿ×ÿÑÿ×ÿÒÿšÿÓÿšÿÔÿšÿÕÿ…ÿÖÿšÿ×ÿšÿØÿqÿÙÿšÿÚÿšÿÛÿqÿÜÿ®ÿÝÿ®ÿÞÿqÿßÿ×ÿàÿšÿáÿšÿâÿšÿãÿšÿäÿ®ÿåÿšÿæÿšÿçÿ×ÿèÿšÿéÿÃÿêÿqÿìÿšÿíÿqÿîÿ…ÿòÿ…ÿóÿšÿõÿšÿöÿ®ÿ÷ÿšÿùÿšÿÿ®ÿÿ®ÿÿ®ÿÿ…ÿ ÿ…ÿjÿqÿkÿšÿlÿ×ÿmÿ×ÿqÿšÿrÿqÿsÿ…ÿuÿšÿwÿšÿyÿšÿ}ÿšÿ~ÿ×ÿÿqÿÿ×ÿƒÿ×ÿ„ÿ×ÿ…ÿqÿ†ÿ×ÿ‡ÿqÿˆÿ×ÿ‰ÿqÿŠÿ×ÿ‹ÿ×ÿŒÿ×ÿÿqÿ–ÿšÿšÿšÿžÿšÿ ÿ×ÿ¢ÿ×ÿ¤ÿšÿ¦ÿšÿªÿ®ÿ¬ÿšÿ®ÿšÿ°ÿšÿ±ÿ×ÿ²ÿqÿ³ÿ×ÿ´ÿqÿµ�)ÿ¶ÿ®ÿ¸ÿ®ÿºÿ®ÿ¼ÿ×ÿ¾ÿ®ÿÀÿšÿÂÿšÿÄÿšÿÅÿšÿÆÿqÿÇÿšÿÈÿqÿËÿ×ÿÍÿšÿÎÿšÿÏÿ…ÿÑÿšÿÓÿšÿÕÿšÿ×ÿšÿÙÿqÿÛÿqÿÝÿqÿàÿqÿæÿ×ÿèÿ×ÿêÿÃÿìÿšÿîÿšÿïÿ×ÿðÿqÿñÿ×ÿòÿqÿóÿ×ÿôÿqÿöÿ×ÿøÿ®ÿúÿ®ÿüÿ®ÿþÿšÿ�ÿšÿÿšÿÿ×ÿÿ×ÿ ÿqÿ ÿqÿ ÿqÿ ÿqÿÿšÿÿšÿÿšÿÿ…ÿÿšÿÿ×ÿÿqÿÿ®ÿÿqÿÿšÿÿ…��ÿš��ÿ×��ÿš�ÎÿÃ�Ïÿì�ÕÿÃ�Øÿì�Ûÿì�Þÿì�êÿì�íÿì�òÿÃ�ÿ×�ÿ×�ÿ×�ÿš� ÿš�jÿì�sÿÃ�ÿì�…ÿì�‡ÿì�‰ÿì�ÿì�²ÿì�´ÿì�ÏÿÃ�àÿì�ðÿì�òÿì�ôÿì� ÿì� ÿì�ÿÃ�ÿì�ÿì�ÿÃ�ÿš�ÿ×�ÿš�)Ÿÿפÿ®¦�)ªÿ…®ÿ®µÿ®¸ÿ×»ÿ×¼�)¾ÿÃÄ�)ÌÿÃÍÿÃÎÿšÏÿ®Ðÿ×Ñÿ×ÒÿÃÓÿÃÔÿÃÕÿšÖÿÃ×ÿÃØÿ®ÙÿÃÚÿÃÛÿ®Þÿ®ßÿ×àÿÃáÿšâÿÃãÿÃåÿÃæÿÃçÿ×èÿÃêÿ®ë�)ìÿÃíÿ®îÿÃòÿšóÿÃô�)õÿÃ÷ÿÃùÿÃÿ×ÿ×ÿ×ÿš ÿšjÿ®kÿÃlÿ×qÿÃrÿ…sÿšuÿÃwÿ×yÿÃ}ÿÃ~ÿ×ÿ®„ÿ×…ÿ®†ÿׇÿ®ˆÿ׉ÿ®Šÿ׌ÿ×ÿ®–ÿØ�)šÿÞÿàÿ×¢ÿפÿæÿè�)©�)¬ÿîÿðÿñÿײÿ®³ÿ×´ÿ®µ�)¼ÿ×½�)ÀÿšÂÿšÄÿÃÅÿׯÿÃÇÿ×ÈÿÃËÿ×ÍÿÃÎÿ®ÏÿšÑÿÃÓÿÃÕÿš×ÿÃÙÿ…Ûÿ…Ýÿ…àÿ®æÿ×èÿ×ìÿÃîÿÃïÿ×ðÿ®ñÿ×òÿ®óÿ×ôÿ®öÿ×þÿš�ÿÃÿÃÿ×ÿ× ÿš ÿ® ÿš ÿ®ÿ×ÿ×ÿ®ÿšÿÃÿ×ÿ®�)ÿ®ÿ®ÿš�ÿÃ�ÿÃÎÿÃÏÿ×ÕÿÃØÿ×Ûÿ×Þÿ×êÿ×íÿ×òÿÃÿà ÿÃjÿ×sÿÃÿ×…ÿׇÿ׉ÿ×ÿײÿ×´ÿ×ÏÿÃàÿ×ðÿ×òÿ×ôÿ× ÿ× ÿ×ÿÃÿ×ÿ×ÿßÿ×£�á¸ÿ×»ÿ×¾ÿÃÜÿ×áÿ®äÿ×lÿ×{�=}ÿì~ÿׄÿ׆ÿ׈ÿ׊ÿ׌ÿתÿ×±ÿ׳ÿ×¶ÿ×¾ÿ×Àÿ®Âÿ®ÅÿÃÆÿ×ÇÿÃÈÿ×Õÿ®ïÿ×ñÿ×óÿ×þÿ®ÿ×ÿ×ÿ×ÿ×ÏÿìØÿìÛÿìÞÿìáÿìêÿìíÿìjÿìÿì…ÿì‡ÿì‰ÿìÿì²ÿì´ÿìÀÿìÂÿìÕÿìàÿìðÿìòÿìôÿìþÿì ÿì ÿìÿ×ÿ×ÿìÿìŸÿ׸ÿ×»ÿ×¾ÿ×Áÿ×áÿ×lÿ×|ÿ×~ÿׄÿ׆ÿ׈ÿ׊ÿ׌ÿ×±ÿ׳ÿ׿ÿ×Àÿ×Áÿ×Âÿ×ÅÿšÇÿšÔÿ×Õÿ×ïÿ×ñÿ×óÿ×ýÿ×þÿ× ÿ× ÿ×ÿ×ÿ×ÿ×ÿìÏÿìØÿìÛÿìÞÿìáÿìêÿìíÿìjÿìÿì…ÿì‡ÿì‰ÿìÿì²ÿì´ÿìÀÿìÂÿìÕÿìàÿìðÿìòÿìôÿìþÿì ÿì ÿìÿ×ÿ×ÿìÿì �ÿš � ÿš ÿ® ¦ÿ® ¨ÿà ªÿà °ÿà ¼ÿq ½ÿà ¿ÿà Áÿà Äÿ® Ðÿ× Üÿà ßÿ× áÿ× äÿà ÿš  ÿš rÿà vÿ× |ÿà €ÿà ‚ÿà Ÿÿà  ÿ× ©ÿ® ªÿà µÿq ¶ÿà ·ÿà ¹ÿà »ÿà ¼ÿ× ½ÿ® ¾ÿà ¿ÿà Àÿ× Áÿà Âÿ× Êÿà Ëÿ× Ôÿà Õÿ× Ùÿà Ûÿà Ýÿà åÿà æÿ× ÷ÿà ùÿà ûÿà ýÿà þÿ× ÿà ÿ× ÿà ÿ×  ÿ× ÿ× ÿ× ÿ× ÿ® ÿà �ÿš � ÿš Ðÿ× Üÿà Ýÿ× ßÿ× áÿ× äÿà öÿ× ÿš  ÿš  ÿ× ªÿà ¶ÿà ¼ÿ× ¾ÿà Àÿ× Âÿ× Ëÿ× Õÿ× æÿ× øÿ× úÿ× üÿ× þÿ× ÿ× ÿ× ÿš ÿš ÿà �ÿš � ÿš ÿ® ¦ÿ® ¨ÿà ªÿà °ÿà ¼ÿq ½ÿà ¿ÿà Áÿà Äÿ® Ðÿ× Üÿà ßÿ× áÿ× äÿà ÿš  ÿš rÿà vÿ× |ÿà €ÿà ‚ÿà Ÿÿà  ÿ× ©ÿ® ªÿà µÿq ¶ÿà ·ÿà ¹ÿà »ÿà ¼ÿ× ½ÿ® ¾ÿà ¿ÿà Àÿ× Áÿà Âÿ× Êÿà Ëÿ× Ôÿà Õÿ× Ùÿà Ûÿà Ýÿà åÿà æÿ× ÷ÿà ùÿà ûÿà ýÿà þÿ× ÿà ÿ× ÿà ÿ×  ÿ× ÿ× ÿ× ÿ× ÿ® ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿã�áê�)ÿ×ÿ×�ÿì� ÿìÿì ÿì�ÿš� ÿšÿ®¦ÿ®¨ÿêÿðÿüÿq½ÿÿÿÃÁÿÃÄÿ®Ðÿ×ÜÿÃßÿ×áÿ×äÿÃÿš ÿšrÿÃvÿ×|ÿÀÿÂÿßÿàÿשÿ®ªÿõÿq¶ÿ÷ÿùÿûÿüÿ×½ÿ®¾ÿÿÿÃÀÿ×ÁÿÃÂÿ×ÊÿÃËÿ×ÔÿÃÕÿ×ÙÿÃÛÿÃÝÿÃåÿÃæÿ×÷ÿÃùÿÃûÿÃýÿÃþÿ×ÿÃÿ×ÿÃÿ× ÿ×ÿ×ÿ×ÿ×ÿ®ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃ�ÿš� ÿšÿ®¦ÿ®¨ÿêÿðÿüÿq½ÿÿÿÃÁÿÃÄÿ®Ðÿ×ÜÿÃßÿ×áÿ×äÿÃÿš ÿšrÿÃvÿ×|ÿÀÿÂÿßÿàÿשÿ®ªÿõÿq¶ÿ÷ÿùÿûÿüÿ×½ÿ®¾ÿÿÿÃÀÿ×ÁÿÃÂÿ×ÊÿÃËÿ×ÔÿÃÕÿ×ÙÿÃÛÿÃÝÿÃåÿÃæÿ×÷ÿÃùÿÃûÿÃýÿÃþÿ×ÿÃÿ×ÿÃÿ× ÿ×ÿ×ÿ×ÿ×ÿ®ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃ�ÿ®�ÿ®ªÿì°ÿ×¼ÿ׿ÿ×ÿ® ÿ®rÿì€ÿì‚ÿìŸÿ×µÿ×·ÿì¹ÿì»ÿ×Êÿ×ÙÿìÛÿìÝÿìåÿ×ÿ×ÿ×�ÿ×� ÿ×ÐÿìÝÿìßÿìöÿìÿ× ÿ× ÿì¼ÿìËÿìæÿìøÿìúÿìüÿìÿìÿìÿ×ÿ×�ÿ®� ÿ®ÿæÿêÿ×°ÿ×¼ÿÿÿ×Áÿ×ÄÿÃÜÿ×äÿ×ÿ® ÿ®rÿ×|ÿ×€ÿׂÿןÿשÿêÿ×µÿöÿ×·ÿ×¹ÿ×»ÿ×½ÿþÿ׿ÿ×Áÿ×Êÿ×Ôÿ×Ùÿ×Ûÿ×Ýÿ×åÿ×ýÿ×ÿ×ÿ× ÿ×ÿ×ÿÃÿ×�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃáÿ×Àÿ×Âÿ×Õÿ×þÿ×£�áê�)ÿ×ÿ×�ÿì� ÿìÿì ÿì�ÿq� ÿq�&ÿ×�*ÿ×�- �2ÿ×�4ÿ×�7ÿq�9ÿ®�:ÿ®�<ÿ…�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�Ÿÿ…�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿq&ÿq6ÿ®8ÿ…:ÿ…Gÿ×úÿ®üÿ®þÿ®�ÿ…ÿq ÿq_ÿ×Iÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×oÿ…qÿ…sÿ…ÿq�ÿì� ÿìÿì ÿì�ÿq� ÿq�&ÿ×�*ÿ×�- �2ÿ×�4ÿ×�7ÿq�9ÿ®�:ÿ®�<ÿ…�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�Ÿÿ…�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿq&ÿq6ÿ®8ÿ…:ÿ…Gÿ×úÿ®üÿ®þÿ®�ÿ…ÿq ÿq_ÿ×Iÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×oÿ…qÿ…sÿ…ÿq �ÿì � ÿì ÿì  ÿì!�ÿq!� ÿq!�&ÿ×!�*ÿ×!�- !�2ÿ×!�4ÿ×!�7ÿq!�9ÿ®!�:ÿ®!�<ÿ…!�‰ÿ×!�”ÿ×!�•ÿ×!�–ÿ×!�—ÿ×!�˜ÿ×!�šÿ×!�Ÿÿ…!�Èÿ×!�Êÿ×!�Ìÿ×!�Îÿ×!�Þÿ×!�àÿ×!�âÿ×!�äÿ×!ÿ×!ÿ×!ÿ×!ÿ×!$ÿq!&ÿq!6ÿ®!8ÿ…!:ÿ…!Gÿ×!úÿ®!üÿ®!þÿ®!�ÿ…!ÿq! ÿq!_ÿ×!Iÿ×!Kÿ×!Mÿ×!Oÿ×!Qÿ×!Sÿ×!Uÿ×!Wÿ×!Yÿ×![ÿ×!]ÿ×!_ÿ×!oÿ…!qÿ…!sÿ…!ÿq"�ÿì"� ÿì"ÿì" ÿì#�ÿq#� ÿq#�&ÿ×#�*ÿ×#�- #�2ÿ×#�4ÿ×#�7ÿq#�9ÿ®#�:ÿ®#�<ÿ…#�‰ÿ×#�”ÿ×#�•ÿ×#�–ÿ×#�—ÿ×#�˜ÿ×#�šÿ×#�Ÿÿ…#�Èÿ×#�Êÿ×#�Ìÿ×#�Îÿ×#�Þÿ×#�àÿ×#�âÿ×#�äÿ×#ÿ×#ÿ×#ÿ×#ÿ×#$ÿq#&ÿq#6ÿ®#8ÿ…#:ÿ…#Gÿ×#úÿ®#üÿ®#þÿ®#�ÿ…#ÿq# ÿq#_ÿ×#Iÿ×#Kÿ×#Mÿ×#Oÿ×#Qÿ×#Sÿ×#Uÿ×#Wÿ×#Yÿ×#[ÿ×#]ÿ×#_ÿ×#oÿ…#qÿ…#sÿ…#ÿq$�ÿì$� ÿì$ÿì$ ÿì%�ÿq%� ÿq%�&ÿ×%�*ÿ×%�- %�2ÿ×%�4ÿ×%�7ÿq%�9ÿ®%�:ÿ®%�<ÿ…%�‰ÿ×%�”ÿ×%�•ÿ×%�–ÿ×%�—ÿ×%�˜ÿ×%�šÿ×%�Ÿÿ…%�Èÿ×%�Êÿ×%�Ìÿ×%�Îÿ×%�Þÿ×%�àÿ×%�âÿ×%�äÿ×%ÿ×%ÿ×%ÿ×%ÿ×%$ÿq%&ÿq%6ÿ®%8ÿ…%:ÿ…%Gÿ×%úÿ®%üÿ®%þÿ®%�ÿ…%ÿq% ÿq%_ÿ×%Iÿ×%Kÿ×%Mÿ×%Oÿ×%Qÿ×%Sÿ×%Uÿ×%Wÿ×%Yÿ×%[ÿ×%]ÿ×%_ÿ×%oÿ…%qÿ…%sÿ…%ÿq&�ÿì&� ÿì&ÿì& ÿì'�ÿq'� ÿq'�&ÿ×'�*ÿ×'�- '�2ÿ×'�4ÿ×'�7ÿq'�9ÿ®'�:ÿ®'�<ÿ…'�‰ÿ×'�”ÿ×'�•ÿ×'�–ÿ×'�—ÿ×'�˜ÿ×'�šÿ×'�Ÿÿ…'�Èÿ×'�Êÿ×'�Ìÿ×'�Îÿ×'�Þÿ×'�àÿ×'�âÿ×'�äÿ×'ÿ×'ÿ×'ÿ×'ÿ×'$ÿq'&ÿq'6ÿ®'8ÿ…':ÿ…'Gÿ×'úÿ®'üÿ®'þÿ®'�ÿ…'ÿq' ÿq'_ÿ×'Iÿ×'Kÿ×'Mÿ×'Oÿ×'Qÿ×'Sÿ×'Uÿ×'Wÿ×'Yÿ×'[ÿ×']ÿ×'_ÿ×'oÿ…'qÿ…'sÿ…'ÿq(�ÿì(� ÿì(ÿì( ÿì)�ÿq)� ÿq)�&ÿ×)�*ÿ×)�- )�2ÿ×)�4ÿ×)�7ÿq)�9ÿ®)�:ÿ®)�<ÿ…)�‰ÿ×)�”ÿ×)�•ÿ×)�–ÿ×)�—ÿ×)�˜ÿ×)�šÿ×)�Ÿÿ…)�Èÿ×)�Êÿ×)�Ìÿ×)�Îÿ×)�Þÿ×)�àÿ×)�âÿ×)�äÿ×)ÿ×)ÿ×)ÿ×)ÿ×)$ÿq)&ÿq)6ÿ®)8ÿ…):ÿ…)Gÿ×)úÿ®)üÿ®)þÿ®)�ÿ…)ÿq) ÿq)_ÿ×)Iÿ×)Kÿ×)Mÿ×)Oÿ×)Qÿ×)Sÿ×)Uÿ×)Wÿ×)Yÿ×)[ÿ×)]ÿ×)_ÿ×)oÿ…)qÿ…)sÿ…)ÿq*�ÿì*� ÿì*ÿì* ÿì+�ÿq+� ÿq+�&ÿ×+�*ÿ×+�- +�2ÿ×+�4ÿ×+�7ÿq+�9ÿ®+�:ÿ®+�<ÿ…+�‰ÿ×+�”ÿ×+�•ÿ×+�–ÿ×+�—ÿ×+�˜ÿ×+�šÿ×+�Ÿÿ…+�Èÿ×+�Êÿ×+�Ìÿ×+�Îÿ×+�Þÿ×+�àÿ×+�âÿ×+�äÿ×+ÿ×+ÿ×+ÿ×+ÿ×+$ÿq+&ÿq+6ÿ®+8ÿ…+:ÿ…+Gÿ×+úÿ®+üÿ®+þÿ®+�ÿ…+ÿq+ ÿq+_ÿ×+Iÿ×+Kÿ×+Mÿ×+Oÿ×+Qÿ×+Sÿ×+Uÿ×+Wÿ×+Yÿ×+[ÿ×+]ÿ×+_ÿ×+oÿ…+qÿ…+sÿ…+ÿq,�ÿì,� ÿì,ÿì, ÿì-�ÿq-� ÿq-�&ÿ×-�*ÿ×-�- -�2ÿ×-�4ÿ×-�7ÿq-�9ÿ®-�:ÿ®-�<ÿ…-�‰ÿ×-�”ÿ×-�•ÿ×-�–ÿ×-�—ÿ×-�˜ÿ×-�šÿ×-�Ÿÿ…-�Èÿ×-�Êÿ×-�Ìÿ×-�Îÿ×-�Þÿ×-�àÿ×-�âÿ×-�äÿ×-ÿ×-ÿ×-ÿ×-ÿ×-$ÿq-&ÿq-6ÿ®-8ÿ…-:ÿ…-Gÿ×-úÿ®-üÿ®-þÿ®-�ÿ…-ÿq- ÿq-_ÿ×-Iÿ×-Kÿ×-Mÿ×-Oÿ×-Qÿ×-Sÿ×-Uÿ×-Wÿ×-Yÿ×-[ÿ×-]ÿ×-_ÿ×-oÿ…-qÿ…-sÿ…-ÿq.�ÿì.� ÿì.ÿì. ÿì/�ÿq/� ÿq/�&ÿ×/�*ÿ×/�- /�2ÿ×/�4ÿ×/�7ÿq/�9ÿ®/�:ÿ®/�<ÿ…/�‰ÿ×/�”ÿ×/�•ÿ×/�–ÿ×/�—ÿ×/�˜ÿ×/�šÿ×/�Ÿÿ…/�Èÿ×/�Êÿ×/�Ìÿ×/�Îÿ×/�Þÿ×/�àÿ×/�âÿ×/�äÿ×/ÿ×/ÿ×/ÿ×/ÿ×/$ÿq/&ÿq/6ÿ®/8ÿ…/:ÿ…/Gÿ×/úÿ®/üÿ®/þÿ®/�ÿ…/ÿq/ ÿq/_ÿ×/Iÿ×/Kÿ×/Mÿ×/Oÿ×/Qÿ×/Sÿ×/Uÿ×/Wÿ×/Yÿ×/[ÿ×/]ÿ×/_ÿ×/oÿ…/qÿ…/sÿ…/ÿq0�ÿì0� ÿì0ÿì0 ÿì1�ÿq1� ÿq1�&ÿ×1�*ÿ×1�- 1�2ÿ×1�4ÿ×1�7ÿq1�9ÿ®1�:ÿ®1�<ÿ…1�‰ÿ×1�”ÿ×1�•ÿ×1�–ÿ×1�—ÿ×1�˜ÿ×1�šÿ×1�Ÿÿ…1�Èÿ×1�Êÿ×1�Ìÿ×1�Îÿ×1�Þÿ×1�àÿ×1�âÿ×1�äÿ×1ÿ×1ÿ×1ÿ×1ÿ×1$ÿq1&ÿq16ÿ®18ÿ…1:ÿ…1Gÿ×1úÿ®1üÿ®1þÿ®1�ÿ…1ÿq1 ÿq1_ÿ×1Iÿ×1Kÿ×1Mÿ×1Oÿ×1Qÿ×1Sÿ×1Uÿ×1Wÿ×1Yÿ×1[ÿ×1]ÿ×1_ÿ×1oÿ…1qÿ…1sÿ…1ÿq2�ÿì2� ÿì2ÿì2 ÿì3�ÿq3� ÿq3�&ÿ×3�*ÿ×3�- 3�2ÿ×3�4ÿ×3�7ÿq3�9ÿ®3�:ÿ®3�<ÿ…3�‰ÿ×3�”ÿ×3�•ÿ×3�–ÿ×3�—ÿ×3�˜ÿ×3�šÿ×3�Ÿÿ…3�Èÿ×3�Êÿ×3�Ìÿ×3�Îÿ×3�Þÿ×3�àÿ×3�âÿ×3�äÿ×3ÿ×3ÿ×3ÿ×3ÿ×3$ÿq3&ÿq36ÿ®38ÿ…3:ÿ…3Gÿ×3úÿ®3üÿ®3þÿ®3�ÿ…3ÿq3 ÿq3_ÿ×3Iÿ×3Kÿ×3Mÿ×3Oÿ×3Qÿ×3Sÿ×3Uÿ×3Wÿ×3Yÿ×3[ÿ×3]ÿ×3_ÿ×3oÿ…3qÿ…3sÿ…3ÿq4�ÿì4� ÿì4ÿì4 ÿì5�-�{6�ÿì6� ÿì6�Yÿ×6�Zÿ×6�[ÿ×6�\ÿ×6�]ÿì6�¿ÿ×67ÿ×6<ÿì6>ÿì6@ÿì6ûÿ×6ýÿ×6ÿì6 ÿì6pÿ×7�-�{8�ÿì8� ÿì8�Yÿ×8�Zÿ×8�[ÿ×8�\ÿ×8�]ÿì8�¿ÿ×87ÿ×8<ÿì8>ÿì8@ÿì8ûÿ×8ýÿ×8ÿì8 ÿì8pÿ×9�-�{:�ÿì:� ÿì:�Yÿ×:�Zÿ×:�[ÿ×:�\ÿ×:�]ÿì:�¿ÿ×:7ÿ×:<ÿì:>ÿì:@ÿì:ûÿ×:ýÿ×:ÿì: ÿì:pÿ×;�-�{<�ÿì<� ÿì<�Yÿ×<�Zÿ×<�[ÿ×<�\ÿ×<�]ÿì<�¿ÿ×<7ÿ×<<ÿì<>ÿì<@ÿì<ûÿ×<ýÿ×<ÿì< ÿì<pÿ×=�-�{>�ÿì>� ÿì>�Yÿ×>�Zÿ×>�[ÿ×>�\ÿ×>�]ÿì>�¿ÿ×>7ÿ×><ÿì>>ÿì>@ÿì>ûÿ×>ýÿ×>ÿì> ÿì>pÿ×?�-�{@�ÿì@� ÿì@�Yÿ×@�Zÿ×@�[ÿ×@�\ÿ×@�]ÿì@�¿ÿ×@7ÿ×@<ÿì@>ÿì@@ÿì@ûÿ×@ýÿ×@ÿì@ ÿì@pÿ×A�-�{B�ÿìB� ÿìB�Yÿ×B�Zÿ×B�[ÿ×B�\ÿ×B�]ÿìB�¿ÿ×B7ÿ×B<ÿìB>ÿìB@ÿìBûÿ×Býÿ×BÿìB ÿìBpÿ×C�-�{D�ÿìD� ÿìD�Yÿ×D�Zÿ×D�[ÿ×D�\ÿ×D�]ÿìD�¿ÿ×D7ÿ×D<ÿìD>ÿìD@ÿìDûÿ×Dýÿ×DÿìD ÿìDpÿ×I�ÿ®I�ÿ®I�$ÿ×I�7ÿÃI�9ÿìI�:ÿìI�;ÿ×I�<ÿìI�=ÿìI�‚ÿ×I�ƒÿ×I�„ÿ×I�…ÿ×I�†ÿ×I�‡ÿ×I�ŸÿìI�Âÿ×I�Äÿ×I�Æÿ×I$ÿÃI&ÿÃI6ÿìI8ÿìI:ÿìI;ÿìI=ÿìI?ÿìICÿ×I ÿìIúÿìIüÿìIþÿìI�ÿìIÿ®I ÿ®IXÿ×Iÿ×Iÿ×I!ÿ×I#ÿ×I%ÿ×I'ÿ×I)ÿ×I+ÿ×I-ÿ×I/ÿ×I1ÿ×I3ÿ×IoÿìIqÿìIsÿìIÿÃJ�ÿìJ� ÿìJ�Yÿ×J�Zÿ×J�[ÿ×J�\ÿ×J�]ÿìJ�¿ÿ×J7ÿ×J<ÿìJ>ÿìJ@ÿìJûÿ×Jýÿ×JÿìJ ÿìJpÿ×K�ÿ®K�ÿ®K�$ÿ×K�7ÿÃK�9ÿìK�:ÿìK�;ÿ×K�<ÿìK�=ÿìK�‚ÿ×K�ƒÿ×K�„ÿ×K�…ÿ×K�†ÿ×K�‡ÿ×K�ŸÿìK�Âÿ×K�Äÿ×K�Æÿ×K$ÿÃK&ÿÃK6ÿìK8ÿìK:ÿìK;ÿìK=ÿìK?ÿìKCÿ×K ÿìKúÿìKüÿìKþÿìK�ÿìKÿ®K ÿ®KXÿ×Kÿ×Kÿ×K!ÿ×K#ÿ×K%ÿ×K'ÿ×K)ÿ×K+ÿ×K-ÿ×K/ÿ×K1ÿ×K3ÿ×KoÿìKqÿìKsÿìKÿÃL�ÿìL� ÿìL�Yÿ×L�Zÿ×L�[ÿ×L�\ÿ×L�]ÿìL�¿ÿ×L7ÿ×L<ÿìL>ÿìL@ÿìLûÿ×Lýÿ×LÿìL ÿìLpÿ×M�ÿ®M�ÿ®M�$ÿ×M�7ÿÃM�9ÿìM�:ÿìM�;ÿ×M�<ÿìM�=ÿìM�‚ÿ×M�ƒÿ×M�„ÿ×M�…ÿ×M�†ÿ×M�‡ÿ×M�ŸÿìM�Âÿ×M�Äÿ×M�Æÿ×M$ÿÃM&ÿÃM6ÿìM8ÿìM:ÿìM;ÿìM=ÿìM?ÿìMCÿ×M ÿìMúÿìMüÿìMþÿìM�ÿìMÿ®M ÿ®MXÿ×Mÿ×Mÿ×M!ÿ×M#ÿ×M%ÿ×M'ÿ×M)ÿ×M+ÿ×M-ÿ×M/ÿ×M1ÿ×M3ÿ×MoÿìMqÿìMsÿìMÿÃO�ÿ®O�ÿ®O�$ÿ×O�7ÿÃO�9ÿìO�:ÿìO�;ÿ×O�<ÿìO�=ÿìO�‚ÿ×O�ƒÿ×O�„ÿ×O�…ÿ×O�†ÿ×O�‡ÿ×O�ŸÿìO�Âÿ×O�Äÿ×O�Æÿ×O$ÿÃO&ÿÃO6ÿìO8ÿìO:ÿìO;ÿìO=ÿìO?ÿìOCÿ×O ÿìOúÿìOüÿìOþÿìO�ÿìOÿ®O ÿ®OXÿ×Oÿ×Oÿ×O!ÿ×O#ÿ×O%ÿ×O'ÿ×O)ÿ×O+ÿ×O-ÿ×O/ÿ×O1ÿ×O3ÿ×OoÿìOqÿìOsÿìOÿÃQ�ÿ®Q�ÿ®Q�$ÿ×Q�7ÿÃQ�9ÿìQ�:ÿìQ�;ÿ×Q�<ÿìQ�=ÿìQ�‚ÿ×Q�ƒÿ×Q�„ÿ×Q�…ÿ×Q�†ÿ×Q�‡ÿ×Q�ŸÿìQ�Âÿ×Q�Äÿ×Q�Æÿ×Q$ÿÃQ&ÿÃQ6ÿìQ8ÿìQ:ÿìQ;ÿìQ=ÿìQ?ÿìQCÿ×Q ÿìQúÿìQüÿìQþÿìQ�ÿìQÿ®Q ÿ®QXÿ×Qÿ×Qÿ×Q!ÿ×Q#ÿ×Q%ÿ×Q'ÿ×Q)ÿ×Q+ÿ×Q-ÿ×Q/ÿ×Q1ÿ×Q3ÿ×QoÿìQqÿìQsÿìQÿÃS�ÿ®S�ÿ®S�$ÿ×S�7ÿÃS�9ÿìS�:ÿìS�;ÿ×S�<ÿìS�=ÿìS�‚ÿ×S�ƒÿ×S�„ÿ×S�…ÿ×S�†ÿ×S�‡ÿ×S�ŸÿìS�Âÿ×S�Äÿ×S�Æÿ×S$ÿÃS&ÿÃS6ÿìS8ÿìS:ÿìS;ÿìS=ÿìS?ÿìSCÿ×S ÿìSúÿìSüÿìSþÿìS�ÿìSÿ®S ÿ®SXÿ×Sÿ×Sÿ×S!ÿ×S#ÿ×S%ÿ×S'ÿ×S)ÿ×S+ÿ×S-ÿ×S/ÿ×S1ÿ×S3ÿ×SoÿìSqÿìSsÿìSÿÃU�ÿ®U�ÿ®U�$ÿ×U�7ÿÃU�9ÿìU�:ÿìU�;ÿ×U�<ÿìU�=ÿìU�‚ÿ×U�ƒÿ×U�„ÿ×U�…ÿ×U�†ÿ×U�‡ÿ×U�ŸÿìU�Âÿ×U�Äÿ×U�Æÿ×U$ÿÃU&ÿÃU6ÿìU8ÿìU:ÿìU;ÿìU=ÿìU?ÿìUCÿ×U ÿìUúÿìUüÿìUþÿìU�ÿìUÿ®U ÿ®UXÿ×Uÿ×Uÿ×U!ÿ×U#ÿ×U%ÿ×U'ÿ×U)ÿ×U+ÿ×U-ÿ×U/ÿ×U1ÿ×U3ÿ×UoÿìUqÿìUsÿìUÿÃX�I�RX�W�RX�Y�fX�Z�fX�[�fX�\�fX�¿�fX%�RX'�RX7�fXû�fXý�fX4�RX5�RX]�RX^�RXp�fX�RX�RZ�I�RZ�W�RZ�Y�fZ�Z�fZ�[�fZ�\�fZ�¿�fZ%�RZ'�RZ7�fZû�fZý�fZ4�RZ5�RZ]�RZ^�RZp�fZ�RZ�R\�I�R\�W�R\�Y�f\�Z�f\�[�f\�\�f\�¿�f\%�R\'�R\7�f\û�f\ý�f\4�R\5�R\]�R\^�R\p�f\�R\�R^�I�R^�W�R^�Y�f^�Z�f^�[�f^�\�f^�¿�f^%�R^'�R^7�f^û�f^ý�f^4�R^5�R^]�R^^�R^p�f^�R^�R`�I�R`�W�R`�Y�f`�Z�f`�[�f`�\�f`�¿�f`%�R`'�R`7�f`û�f`ý�f`4�R`5�R`]�R`^�R`p�f`�R`�Ra�ÿ×a�ÿ×a�$ÿìa�‚ÿìa�ƒÿìa�„ÿìa�…ÿìa�†ÿìa�‡ÿìa�Âÿìa�Äÿìa�ÆÿìaCÿìaÿ×a ÿ×aXÿìaÿìaÿìa!ÿìa#ÿìa%ÿìa'ÿìa)ÿìa+ÿìa-ÿìa/ÿìa1ÿìa3ÿìf�I�ff�W�ff�Y�ff�Z�ff�[�ff�\�ff�¿�ff%�ff'�ff7�ffû�ffý�ff4�ff5�ff]�ff^�ffp�ff�ff�fh�I�fh�W�fh�Y�fh�Z�fh�[�fh�\�fh�¿�fh%�fh'�fh7�fhû�fhý�fh4�fh5�fh]�fh^�fhp�fh�fh�fj�I�fj�W�fj�Y�fj�Z�fj�[�fj�\�fj�¿�fj%�fj'�fj7�fjû�fjý�fj4�fj5�fj]�fj^�fjp�fj�fj�fl�I�fl�W�fl�Y�fl�Z�fl�[�fl�\�fl�¿�fl%�fl'�fl7�flû�flý�fl4�fl5�fl]�fl^�flp�fl�fl�fn�I�fn�W�fn�Y�fn�Z�fn�[�fn�\�fn�¿�fn%�fn'�fn7�fnû�fný�fn4�fn5�fn]�fn^�fnp�fn�fn�fo�ÿ…o�ÿ…o�"�)o�$ÿ…o�&ÿ×o�*ÿ×o�2ÿ×o�4ÿ×o�Dÿšo�Fÿšo�Gÿšo�Hÿšo�Jÿ×o�PÿÃo�QÿÃo�Rÿšo�SÿÃo�Tÿšo�UÿÃo�Vÿ®o�XÿÃo�]ÿ×o�‚ÿ…o�ƒÿ…o�„ÿ…o�…ÿ…o�†ÿ…o�‡ÿ…o�‰ÿ×o�”ÿ×o�•ÿ×o�–ÿ×o�—ÿ×o�˜ÿ×o�šÿ×o�¢ÿšo�£ÿšo�¤ÿšo�¥ÿšo�¦ÿšo�§ÿšo�¨ÿšo�©ÿšo�ªÿšo�«ÿšo�¬ÿšo�­ÿšo�´ÿšo�µÿšo�¶ÿšo�·ÿšo�¸ÿšo�ºÿšo�»ÿÃo�¼ÿÃo�½ÿÃo�¾ÿÃo�Âÿ…o�Ãÿšo�Äÿ…o�Åÿšo�Æÿ…o�Çÿšo�Èÿ×o�Éÿšo�Êÿ×o�Ëÿšo�Ìÿ×o�Íÿšo�Îÿ×o�Ïÿšo�Ñÿšo�Óÿšo�Õÿšo�×ÿšo�Ùÿšo�Ûÿšo�Ýÿšo�Þÿ×o�ßÿ×o�àÿ×o�áÿ×o�âÿ×o�ãÿ×o�äÿ×o�åÿ×o�úÿÃoÿÃoÿÃo ÿÃoÿ×oÿšoÿ×oÿšoÿ×oÿšoÿ×oÿšoÿÃoÿÃoÿ®o!ÿ®o+ÿÃo-ÿÃo/ÿÃo1ÿÃo3ÿÃo5ÿÃo<ÿ×o>ÿ×o@ÿ×oCÿ…oDÿšoFÿšoGÿ×oHÿšoJÿ®oÿ…o ÿ…oWÿÃoXÿ…oYÿšo_ÿ×o`ÿšobÿÃoÿ…oÿšoÿ…o ÿšo!ÿ…o"ÿšo#ÿ…o%ÿ…o&ÿšo'ÿ…o(ÿšo)ÿ…o*ÿšo+ÿ…o,ÿšo-ÿ…o.ÿšo/ÿ…o0ÿšo1ÿ…o2ÿšo3ÿ…o4ÿšo6ÿšo8ÿšo:ÿšo<ÿšo@ÿšoBÿšoDÿšoIÿ×oJÿšoKÿ×oLÿšoMÿ×oNÿšoOÿ×oQÿ×oRÿšoSÿ×oTÿšoUÿ×oVÿšoWÿ×oXÿšoYÿ×oZÿšo[ÿ×o\ÿšo]ÿ×o^ÿšo_ÿ×o`ÿšobÿÃodÿÃofÿÃohÿÃojÿÃolÿÃonÿÃp��Rp� �Rp�ÿ®p�ÿ®p�"�)p�Rpÿ®p �Rp ÿ®q�ÿ…q�ÿ…q�"�)q�$ÿ…q�&ÿ×q�*ÿ×q�2ÿ×q�4ÿ×q�Dÿšq�Fÿšq�Gÿšq�Hÿšq�Jÿ×q�PÿÃq�QÿÃq�Rÿšq�SÿÃq�Tÿšq�UÿÃq�Vÿ®q�XÿÃq�]ÿ×q�‚ÿ…q�ƒÿ…q�„ÿ…q�…ÿ…q�†ÿ…q�‡ÿ…q�‰ÿ×q�”ÿ×q�•ÿ×q�–ÿ×q�—ÿ×q�˜ÿ×q�šÿ×q�¢ÿšq�£ÿšq�¤ÿšq�¥ÿšq�¦ÿšq�§ÿšq�¨ÿšq�©ÿšq�ªÿšq�«ÿšq�¬ÿšq�­ÿšq�´ÿšq�µÿšq�¶ÿšq�·ÿšq�¸ÿšq�ºÿšq�»ÿÃq�¼ÿÃq�½ÿÃq�¾ÿÃq�Âÿ…q�Ãÿšq�Äÿ…q�Åÿšq�Æÿ…q�Çÿšq�Èÿ×q�Éÿšq�Êÿ×q�Ëÿšq�Ìÿ×q�Íÿšq�Îÿ×q�Ïÿšq�Ñÿšq�Óÿšq�Õÿšq�×ÿšq�Ùÿšq�Ûÿšq�Ýÿšq�Þÿ×q�ßÿ×q�àÿ×q�áÿ×q�âÿ×q�ãÿ×q�äÿ×q�åÿ×q�úÿÃqÿÃqÿÃq ÿÃqÿ×qÿšqÿ×qÿšqÿ×qÿšqÿ×qÿšqÿÃqÿÃqÿ®q!ÿ®q+ÿÃq-ÿÃq/ÿÃq1ÿÃq3ÿÃq5ÿÃq<ÿ×q>ÿ×q@ÿ×qCÿ…qDÿšqFÿšqGÿ×qHÿšqJÿ®qÿ…q ÿ…qWÿÃqXÿ…qYÿšq_ÿ×q`ÿšqbÿÃqÿ…qÿšqÿ…q ÿšq!ÿ…q"ÿšq#ÿ…q%ÿ…q&ÿšq'ÿ…q(ÿšq)ÿ…q*ÿšq+ÿ…q,ÿšq-ÿ…q.ÿšq/ÿ…q0ÿšq1ÿ…q2ÿšq3ÿ…q4ÿšq6ÿšq8ÿšq:ÿšq<ÿšq@ÿšqBÿšqDÿšqIÿ×qJÿšqKÿ×qLÿšqMÿ×qNÿšqOÿ×qQÿ×qRÿšqSÿ×qTÿšqUÿ×qVÿšqWÿ×qXÿšqYÿ×qZÿšq[ÿ×q\ÿšq]ÿ×q^ÿšq_ÿ×q`ÿšqbÿÃqdÿÃqfÿÃqhÿÃqjÿÃqlÿÃqnÿÃr��Rr� �Rr�ÿ®r�ÿ®r�"�)r�Rrÿ®r �Rr ÿ®s�ÿ…s�ÿ…s�"�)s�$ÿ…s�&ÿ×s�*ÿ×s�2ÿ×s�4ÿ×s�Dÿšs�Fÿšs�Gÿšs�Hÿšs�Jÿ×s�PÿÃs�QÿÃs�Rÿšs�SÿÃs�Tÿšs�UÿÃs�Vÿ®s�XÿÃs�]ÿ×s�‚ÿ…s�ƒÿ…s�„ÿ…s�…ÿ…s�†ÿ…s�‡ÿ…s�‰ÿ×s�”ÿ×s�•ÿ×s�–ÿ×s�—ÿ×s�˜ÿ×s�šÿ×s�¢ÿšs�£ÿšs�¤ÿšs�¥ÿšs�¦ÿšs�§ÿšs�¨ÿšs�©ÿšs�ªÿšs�«ÿšs�¬ÿšs�­ÿšs�´ÿšs�µÿšs�¶ÿšs�·ÿšs�¸ÿšs�ºÿšs�»ÿÃs�¼ÿÃs�½ÿÃs�¾ÿÃs�Âÿ…s�Ãÿšs�Äÿ…s�Åÿšs�Æÿ…s�Çÿšs�Èÿ×s�Éÿšs�Êÿ×s�Ëÿšs�Ìÿ×s�Íÿšs�Îÿ×s�Ïÿšs�Ñÿšs�Óÿšs�Õÿšs�×ÿšs�Ùÿšs�Ûÿšs�Ýÿšs�Þÿ×s�ßÿ×s�àÿ×s�áÿ×s�âÿ×s�ãÿ×s�äÿ×s�åÿ×s�úÿÃsÿÃsÿÃs ÿÃsÿ×sÿšsÿ×sÿšsÿ×sÿšsÿ×sÿšsÿÃsÿÃsÿ®s!ÿ®s+ÿÃs-ÿÃs/ÿÃs1ÿÃs3ÿÃs5ÿÃs<ÿ×s>ÿ×s@ÿ×sCÿ…sDÿšsFÿšsGÿ×sHÿšsJÿ®sÿ…s ÿ…sWÿÃsXÿ…sYÿšs_ÿ×s`ÿšsbÿÃsÿ…sÿšsÿ…s ÿšs!ÿ…s"ÿšs#ÿ…s%ÿ…s&ÿšs'ÿ…s(ÿšs)ÿ…s*ÿšs+ÿ…s,ÿšs-ÿ…s.ÿšs/ÿ…s0ÿšs1ÿ…s2ÿšs3ÿ…s4ÿšs6ÿšs8ÿšs:ÿšs<ÿšs@ÿšsBÿšsDÿšsIÿ×sJÿšsKÿ×sLÿšsMÿ×sNÿšsOÿ×sQÿ×sRÿšsSÿ×sTÿšsUÿ×sVÿšsWÿ×sXÿšsYÿ×sZÿšs[ÿ×s\ÿšs]ÿ×s^ÿšs_ÿ×s`ÿšsbÿÃsdÿÃsfÿÃshÿÃsjÿÃslÿÃsnÿÃt��Rt� �Rt�ÿ®t�ÿ®t�"�)t�Rtÿ®t �Rt ÿ®��{� �{�{ �{�ÿ…�ÿ®�ÿ…�"�)�$ÿq�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7�)�Dÿ\�Fÿq�Gÿq�Hÿq�Jÿq�Pÿš�Qÿš�Rÿq�Sÿš�Tÿq�Uÿš�Vÿ…�Xÿš�Yÿ×�Zÿ×�[ÿ×�\ÿ×�]ÿ®�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�¢ÿq�£ÿ\�¤ÿ\�¥ÿ\�¦ÿ\�§ÿ\�¨ÿ\�©ÿq�ªÿq�«ÿq�¬ÿq�­ÿq�´ÿq�µÿq�¶ÿq�·ÿq�¸ÿq�ºÿq�»ÿš�¼ÿš�½ÿš�¾ÿš�¿ÿ×�Âÿq�Ãÿ\�Äÿq�Åÿ\�Æÿq�Çÿ\�Èÿ×�Éÿq�Êÿ×�Ëÿq�Ìÿ×�Íÿq�Îÿ×�Ïÿq�Ñÿq�Óÿq�Õÿq�×ÿq�Ùÿq�Ûÿq�Ýÿq�Þÿ×�ßÿq�àÿ×�áÿq�âÿ×�ãÿq�äÿ×�åÿq�úÿšÿšÿš ÿšÿ×ÿqÿ×ÿqÿ×ÿqÿ×ÿqÿšÿšÿ…!ÿ…$�)&�)+ÿš-ÿš/ÿš1ÿš3ÿš5ÿš7ÿ×<ÿ®>ÿ®@ÿ®CÿqDÿ\Fÿ\Gÿ×HÿqJÿ…ûÿ×ýÿ×ÿ®ÿ®ÿ®ÿ… ÿ…WÿšXÿqYÿ\_ÿ×`ÿqbÿšÿqÿ\ÿq ÿ\!ÿq"ÿ\#ÿq%ÿq&ÿ\'ÿq(ÿ\)ÿq*ÿ\+ÿq,ÿ\-ÿq.ÿ\/ÿq0ÿ\1ÿq2ÿ\3ÿq4ÿ\6ÿq8ÿq:ÿq<ÿq@ÿqBÿqDÿqIÿ×JÿqKÿ×LÿqMÿ×NÿqOÿ×Qÿ×RÿqSÿ×TÿqUÿ×VÿqWÿ×XÿqYÿ×Zÿq[ÿ×\ÿq]ÿ×^ÿq_ÿ×`ÿqbÿšdÿšfÿšhÿšjÿšlÿšnÿšpÿ×�)��)� �)�) �)�����>��������9��������� �9��������B��������I������� �9������� �g��������s�������R�{��������Í������ ��á������ �.�ý������ �.+�������*Y�� ���rƒ�� ��õ�� ���� ��<�� ��õ�� ��Q�� ��i�� ��¤y�� ��(�� � �8E�� � �\}�� � �\Ù�� ��T5Digitized data copyright © 2010-2011, Google Corporation.Open SansRegularAscender - Open Sans Build 100Version 1.10OpenSansOpen Sans is a trademark of Google and may be registered in certain jurisdictions.Ascender Corporationhttp://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0�D�i�g�i�t�i�z�e�d� �d�a�t�a� �c�o�p�y�r�i�g�h�t� �©� �2�0�1�0�-�2�0�1�1�,� �G�o�o�g�l�e� �C�o�r�p�o�r�a�t�i�o�n�.�O�p�e�n� �S�a�n�s�R�e�g�u�l�a�r�A�s�c�e�n�d�e�r� �-� �O�p�e�n� �S�a�n�s� �B�u�i�l�d� �1�0�0�V�e�r�s�i�o�n� �1�.�1�0�O�p�e�n�S�a�n�s�O�p�e�n� �S�a�n�s� �i�s� �a� �t�r�a�d�e�m�a�r�k� �o�f� �G�o�o�g�l�e� �a�n�d� �m�a�y� �b�e� �r�e�g�i�s�t�e�r�e�d� �i�n� �c�e�r�t�a�i�n� �j�u�r�i�s�d�i�c�t�i�o�n�s�.�A�s�c�e�n�d�e�r� �C�o�r�p�o�r�a�t�i�o�n�h�t�t�p�:�/�/�w�w�w�.�a�s�c�e�n�d�e�r�c�o�r�p�.�c�o�m�/�h�t�t�p�:�/�/�w�w�w�.�a�s�c�e�n�d�e�r�c�o�r�p�.�c�o�m�/�t�y�p�e�d�e�s�i�g�n�e�r�s�.�h�t�m�l�L�i�c�e�n�s�e�d� �u�n�d�e�r� �t�h�e� �A�p�a�c�h�e� �L�i�c�e�n�s�e�,� �V�e�r�s�i�o�n� �2�.�0�h�t�t�p�:�/�/�w�w�w�.�a�p�a�c�h�e�.�o�r�g�/�l�i�c�e�n�s�e�s�/�L�I�C�E�N�S�E�-�2�.�0��������ÿf�f��������������������ª      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«.notdefnullnonmarkingreturnspaceexclamquotedbl numbersigndollarpercent ampersand quotesingle parenleft parenrightasteriskpluscommahyphenperiodslashzeroonetwothreefourfivesixseveneightninecolon semicolonlessequalgreaterquestionatABCDEFGHI.altJKLMNOPQRSTUVWXYZ bracketleft backslash bracketright asciicircum underscoregraveabcdefghijklmnopqrstuvwxyz braceleftbar braceright asciitildenonbreakingspace exclamdowncentsterlingcurrencyyen brokenbarsectiondieresis copyright ordfeminine guillemotleft logicalnotuni00AD registered overscoredegree plusminus twosuperior threesuperioracutemu paragraphperiodcenteredcedilla onesuperior ordmasculineguillemotright onequarteronehalf threequarters questiondownAgraveAacute AcircumflexAtilde AdieresisAringAECcedillaEgraveEacute Ecircumflex Edieresis Igrave.alt Iacute.altIcircumflex.alt Idieresis.altEthNtildeOgraveOacute OcircumflexOtilde OdieresismultiplyOslashUgraveUacute Ucircumflex UdieresisYacuteThorn germandblsagraveaacute acircumflexatilde adieresisaringaeccedillaegraveeacute ecircumflex edieresisigraveiacute icircumflex idieresisethntildeograveoacute ocircumflexotilde odieresisdivideoslashugraveuacute ucircumflex udieresisyacutethorn ydieresisAmacronamacronAbreveabreveAogonekaogonekCacutecacute Ccircumflex ccircumflexCdotcdotCcaronccaronDcarondcaronDcroatdcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGbrevegbreveGdotgdot Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbar Itilde.altitilde Imacron.altimacron Ibreve.altibreve Iogonek.altiogonekIdotaccent.altdotlessiIJ.altij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotLslashlslashNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautOEoeRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflexScedillascedillaScaronscaron Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflex YdieresisZacutezacute Zdotaccent zdotaccentZcaronzcaronlongsflorin Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute Scommaaccent scommaaccent circumflexcaronmacronbreve dotaccentringogonektilde hungarumlauttonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos.alt Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIota.altKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9Iotadieresis.altUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronpirhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055.alt afii10056.alt afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygraveendashemdash afii00208 underscoredbl quoteleft quoterightquotesinglbase quotereversed quotedblleft quotedblright quotedblbasedagger daggerdblbulletellipsis perthousandminutesecond guilsinglleftguilsinglright exclamdblfraction nsuperiorfranc afii08941pesetaEuro afii61248 afii61289 afii61352 trademarkOmega estimated oneeighth threeeighths fiveeighths seveneighths partialdiffDeltaproduct summationminusradicalinfinityintegral approxequalnotequal lessequal greaterequallozengeuniFB01uniFB02 cyrillicbrevedotlessjcaroncommaaccent commaaccentcommaaccentrotate zerosuperior foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperioruni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni1F4Duni02F3 dasiaoxiauniFB03uniFB04OhornohornUhornuhornuni0300uni0301uni0303hookdotbelowuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BF uni04C0.altuni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CE uni04CF.altuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7 uni1EC8.altuni1EC9 uni1ECA.altuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABuni030Fcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCcyrillicbighookLCone.pnumzero.osone.ostwo.osthree.osfour.osfive.ossix.osseven.oseight.osnine.osffuni2120Tcedillatcedillag.altgcircumflex.alt gbreve.altgdot.altgcommaaccent.altIIgraveIacute Icircumflex IdieresisItildeImacronIbreveIogonek IdotaccentIJ IotatonosIota Iotadieresis afii10055 afii10056uni04C0uni04CFuni1EC8uni1ECA����� � �ÿÿ����� ���������©������������� �4�6�latn���MOL �ROM ���ÿÿ����ÿÿ����ÿÿ���������� �nä�latn���MOL �(ROM �B��ÿÿ� ��� ���������ÿÿ� ��� � ��������ÿÿ� ��� � �������liga�°liga�¶liga�¼lnum�Âlnum�Èlnum�Îlocl�Ôlocl�Úonum�àonum�èonum�ðpnum�øpnum�þpnumsalt saltsaltss01"ss01*ss012ss02:ss02@ss02Fss03Lss03Rss03Xtnum^tnumftnumn���� ���� ���� ������������������������������������������������������������������������������������������������������������������������������ ���&�.�6�>�F�N�V�^�����P�����z�����ª�����Æ�����î�����ô������������2����H���‘’“”•���J�J���ß�ß��á�á��ã�ã��å�å���.��,�Ž���‘�ê�ì�î�ð�ò�ôZgw¡¢ÉØEG��–©����� ƒ„…†‡ˆ‰Š‹Œ��������� ƒ…†‡ˆ‰Š‹Œ„���������‚‚� ���‚������ ������������ƒŒ��������‚��� �‚����������ƒŒ����� !��$%��IJ���6���� ���"�(^��I�O]��I�L��I5��O4��L���I������������^���������V0‚R *†H†÷  ‚C0‚?1 0 +�0a +‚7 S0Q0, +‚7¢€�<�<�<�O�b�s�o�l�e�t�e�>�>�>0!0 +�‚¸¹€Ùõ@¦mn³TA6™ÞÓ} ‚]0‚z0‚b 8%×úøa¯žôç&µÖZÕ0  *†H†÷ �0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0 070615000000Z 120614235959Z0\1 0 UUS10U VeriSign, Inc.1402U+VeriSign Time Stamping Services Signer - G20Ÿ0  *†H†÷ ��0‰�ĵòR¼ˆ†`)J[/K‘k‡‘ó5TX5êÑ6^bMRQ4qÂ{f‰ÈÝ*Äj ö7Ù˜t‘ö’®°µv–ñ©JcEG.k ’NK+ŒîXJ‹Ôä,ø‚ªXÙÍBó-ÀuÞ«ÇŽšlL•ÞÛïgárÂIž`<áâ¾£cxi{­-�£Ä0Á04+(0&0$+0†http://ocsp.verisign.com0 Uÿ0�03U,0*0( & $†"http://crl.verisign.com/tss-ca.crl0U%ÿ 0 +0UÿÀ0U0¤010 UTSA1-20  *†H†÷ �‚�PÅKÈ$€ßä $ÂÞ±¡¡¦‚- ƒ7 ‚,°ZaµØþˆÛñ‘‘³V@¦ë’¾89°u6t:˜Oä7º™‰Ê•B°¹Ç WàúÕdB5NÑ3¢ÈMª'Çòá†L8MƒxÆüSàëà‡Ý¤–ž^ ˜â¥¾¿‚…Ã`áß­(ØÇ¥KdÚÇ[½¬9Õ8"¡3‹/Ššë¼!?DA µe$¼HÓD€ë¡ÏÉ´ÏTÇ£€\ùy>]r}ˆž,C¢ÊSÎ}=ö*:¸O”�¥m ƒ]ù^Sô³WpÃûõ­• ÞÄ€`É+n†ñëôx'ÑÅî4[^¹I2ò30‚Ä0‚- G¿•ßRFC÷ÛmH 1¤0  *†H†÷ �0‹1 0 UZA10U Western Cape10U Durbanville10 U Thawte10U Thawte Certification10UThawte Timestamping CA0 031204000000Z 131203235959Z0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0‚"0  *†H†÷ �‚�0‚ ‚�©Ê²¤ÌÍ ¯ }‰¬‡uð´NñßÁ¿ga½£dÚ»ùÊ3«„0‰X~ŒÛkÝ6ž¿Ñìxòw¦~o<¿“¯ ºhôl”ʽR-«H=õ¶Õ]_Ÿú/k¤÷£š¦ÈáLRã`ì@~¹ Þ?Ǵ߇½_zj1.™¨G Î1s W-Íx43•™¹Þh/ªæãŠŒ*Ë!‡f½ƒXWou¿<ª&‡]Ê<Ÿ„êTÁ nÄþÅJݹ—"|Û>'ÑxìŸ1Éñæ"ÛijGCš_ ä^õî|ñ}«bõM ÞÐ"V¨•Í®ˆv®îº óäMÙ ûh ®;³‡Á»�£Û0Ø04+(0&0$+0†http://ocsp.verisign.com0Uÿ0ÿ�0AU:0806 4 2†0http://crl.verisign.com/ThawteTimestampingCA.crl0U% 0 +0Uÿ0$U0¤010U TSA2048-1-530  *†H†÷ ��JkùêXÂD1‰y™+–¿‚¬ÖLͰŠXnß)£^ÈÊ“çR ïG'/�8°äÉ“NšÔ"b÷?7!Op1€ñ‹8‡³èè—�þÏU–N$Ò©'Nz®·aAó*ÎçÉÙ^Ý»+…>µµÙáWÿ¾´Å~õÏ žð—þ+Ó;R8'÷?J0‚ü0‚e eR&á².áY)…¬"ç\0  *†H†÷ �0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority0 090521000000Z 190520235959Z0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0‚"0  *†H†÷ �‚�0‚ ‚�¾g´`ªIoV|fÉ^† Õñ¬§qƒŽ‹‰øˆ‰º-„!•äÑœPLûÒ"½Úò²5;à ûü.Z¿‰|=;%öóX{œôµÆ ¸€Î¾'tag'MjåìaXy£à'°áM4+G D¹Þf$fŠÍOºÅ8ÈTáröfuj¹IhÏ8y ª0¨Û,`Hž×ª©ƒ×8‘09–:|@T¶­à/ƒÜ¨R>³×+ý!¶§\£ ©¦P4.M§ÎÉ^%ÔŒ¼ón|)¼]ü1‡ZÕŒ…gXˆ ¿5ðê+£!çöƒå¨í`x^{`ƒýW ]A cT`ÖC!ï�£‚Û0‚×0Uÿ0ÿ�0pU i0g0e `†H†øE0V0(+https://www.verisign.com/cps0*+0https://www.verisign.com/rpa0Uÿ0m+ a0_¡] [0Y0W0U image/gif0!00+åÓ†¬ŽkÃÏ€jÔH,{.0%#http://logo.verisign.com/vslogo.gif0U%0++04+(0&0$+0†http://ocsp.verisign.com01U*0(0& $ "† http://crl.verisign.com/pca3.crl0)U"0 ¤010UClass3CA2048-1-550U—Ðk¨&pÈ¡?”-Ä5›¤¡ò0  *†H†÷ ��‹ÀÝ”ØA¢ai°¨xÇ0Æ<~B÷$¶äƒsœ¡âú/ëÀÊDçràP¶U ƒn–’äšQj´71Ü¥-ëŒ�ÇOçM2º…øN¾úgUeðj¾zÊd8xEv1ó†z`³]ö‹fv‚Yáƒå½I¥8VåÞAwX0‚0‚û fãðgyÊmPSoˆƒ0  *†H†÷ �0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0 100729000000Z 120808235959Z0Ð1 0 UUS10U Massachusetts10 UWoburn10U Monotype Imaging Inc.1>0<U 5Digital ID Class 3 - Microsoft Software Validation v210U Type Operations10UMonotype Imaging Inc.0Ÿ0  *†H†÷ ��0‰�”D •i|U ÐÛ25ŠL3«^ ¡L×*‡8ט¥@ðI "SOÂC¦Ê‹©VïnH¨9c;$¹˜ÏÊ5}rãGWýyËŠJç@p-5c®€ÏįØû÷Éü‰Ø×¤ Û ò¢ò{ïÍuÁ÷ePd"½}¼­¸KÌXEMÑYLM�£‚ƒ0‚0 U0�0Uÿ€0DU=0;09 7 5†3http://csc3-2009-2-crl.verisign.com/CSC3-2009-2.crl0DU =0;09 `†H†øE0*0(+https://www.verisign.com/rpa0U% 0 +0u+i0g0$+0†http://ocsp.verisign.com0?+0†3http://csc3-2009-2-aia.verisign.com/CSC3-2009-2.cer0U#0€—Ðk¨&pÈ¡?”-Ä5›¤¡ò0 `†H†øB0 +‚70�ÿ0  *†H†÷ �‚�Næ"‡ßgAâÒî~ΙÖc½ðµ“åjrbáõÒ<8î¨=_ºG‚_[KIô ú“ ÐVD¢ˆóû®÷ 5Þ< ¬D”`E*›þ›oL;±4gp†ÿZ9\Zãl‚«5|eKý˜mµ”Iœˆp¾=±b•´Û´ÔÚèA~þ}¹¤’ënò"ŠÆw6MŠZ S1Ó+(¯RázkµwD½ ­ô]%,ãÍŠ0>KœyʦN® ÂÌ$ Á”‚öñº¶›šØ\<ñê'M<‰o3ŠÓ†ÞéX3u=ë“iâDoN�lÏÕ…ÚV¦š¦?ËL!hò`ºáè]9!2í1‚g0‚c0Ë0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CAfãðgyÊmPSoˆƒ0 +� p0 +‚7 10�0 *†H†÷  1  +‚70 +‚7 10  +‚70# *†H†÷  1HãêÛcƱW' ·eôS•0  *†H†÷ �€E;¼ÔºïÚ¿b;ÞìJ„EqAÉþ.•ó‰±RôAëm2,H¿)‘¼²/]d$4.º–´¶Js—àöŸA÷÷h¶õ€xA¾SÀ~xR[ª!Bܾ œ3ÓFP;™+Yiì…ØcÑ-Ö4í£œòñT@ÕG  �ŒfïÞ>¡‚0‚{ *†H†÷  1‚l0‚h0g0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA8%×úøa¯žôç&µÖZÕ0 +� ]0 *†H†÷  1  *†H†÷ 0 *†H†÷  1 110505165510Z0# *†H†÷  1T+ ½î'üS“ô8V0  *†H†÷ �€Áw‰®›o"ãkåEÚN‘@ðŸï;'JV¬:ý¨”j|÷œÁ{“`NÄ+W•”Ëášg3Ñ+)Èì¾¼Y±¤)™ìˆ™$‡w›ÊûÔÔILtÈ=.o ÉÍèåÐ!9³VÕû¬½¬©8½°Õ £Ùc­°•´hXÃâ×)ÿ‘¤Ç��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fonts/data/OpenSans-Italic.ttf����������������������������������������������0000644�0001750�0001751�00000637640�14507104216�022157� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0DSIGZwý@�*,��tGDEF�&¯�& ���GPOS 77�&,���8GSUB+=·�&d��ÆOS/2¡M–=��¸���`cmap)«/h��´��cvt À����� fpgm~a¶��Ð��´gasp��#�%ü���glyfPXòT��%ˆ�headøGùc��<���6hhea à��t���$hmtxÙÍã����škernT+ ~�C¨�¶6locaM…©��0��VmaxpC ��˜��� nameSt"Î�ùà��îpost7ïl�ÿÐ��&+prepT–“��„�� ����šßåŸ_<õ� �����ÉcHÀ����ÉèKÎüýÛ Ùb�� ����������ý¨�� yüþ Ù�³������������£���ª�Š��W����/�\������k��š3��š3��Ñ�f��� à�ï@� [���(����1ASC�� ÿýþ�„X �Ÿ����H¶��� �Í�Á���������+�á+�?h�HX�¨\�B¼�áH�RHÿ`j�×h�ìÿœ�7�+Íÿ¢h�yh/h� h�/h�h�Ph�…h�®h�`h�b�+ÿœh�yh�h�yj�žÇ�oqÿ‹É�V®�–T�V�VÇ�Vj�–m�V‹ÿÙ#þÁu�VË�V²�Tž�TÃ�–‡�VÃ�–�V�'ü�ºh�¤b�¼Ñ�ß'ÿ˜�¼?ÿðJÿðÍ�ÝJÿj#�5'ÿDo?…�bž�;š�bž�bò�bÿÿž�;�;þþç�9�9ú�;ž�;}�bžÿÕž�b+�;m�˜�Zž�q²�b¼�uÓÿ¶²ÿ;ÿãË�hËÿ¶h�s��ÿòh�áhÿéh�¨h�hã�;oɨ�‹®�ª¾�Xh��7¨�‹�ãm�×h�Í�`Í�woªÿÕ=�Ç�ª¤ÿVͰ�¨¾�î�{î�B�Wjÿüqÿ‹qÿ‹qÿ‹qÿ‹qÿ‹qÿ‹‰ÿ‰®�–�V�V�V�V‹ÿÙ‹ÿÙ‹ÿÙ‹ÿÙT�Hž�TÃ�–Ã�–Ã�–Ã�–Ã�–h�¨Ã�wh�¤h�¤h�¤h�¤�¼‡�Vžÿ�…�b…�b…�b…�b…�b…�b…�bš�bò�bò�bò�bò�b�;�;�;�;�Zž�;}�b}�b}�b}�b}�bh�}�=ž�qž�qž�qž�q²ÿ;žÿÕ²ÿ;qÿ‹…�bqÿ‹…�bqÿ‹…�b®�–š�b®�–š�b®�–š�b®�–š�bT�Vž�bT�Hž�b�Vò�b�Vò�b�Vò�b�Vò�b�Vò�bj�–ÿj�–ÿj�–ÿj�–ÿm�Vž�;m�Vž�;‹ÿÙ�;‹ÿÙ�;‹ÿÙ�;‹ÿÙÿš‹ÿÙ�;¬ÿÙ�;#þÁþþu�Vç�9ç�9Ë�V�9Ë�VÿŸË�V�9Ë�VD�9Ë���ž�Tž�;ž�Tž�;ž�Tž�;)�]ž�Tž�;Ã�–}�bÃ�–}�bÃ�–}�b×�–é�b�V+�;�V+ÿ™�V+�;�'m��'m��'m��'m�ü�‘˜�;ü�º˜�Zü�ª˜�+h�¤ž�qh�¤ž�qh�¤ž�qh�¤ž�qh�¤ž�qh�¤ž�qÑ�ß¼�u�¼²ÿ;�¼?ÿðÿã?ÿðÿã?ÿðÿã3þüh�qÿ…�b‰ÿ‰…�bÃ�w}�=�'m�ooÓH–oÛòDž'uÿooPooÁqÿ‹�ª‹�G�G…�`�\�GB�Tq�hqÿ‹É�VÏ�VwÿÉ�V?ÿðm�VÃ�–‹ÿÙu�Vwÿ‹²�Tž�T#ÿåÃ�–o�V‡�V+ÿãü�º�¼ô�–'ÿ˜á�ÇÝÿð‹ÿÙ�¼�b�?ž�;q�hœ�…�bžÿÓ¬�Tm�J�? �bž�;X�bq�hç�9Ýÿ‹ªÿÕé�b�R}�bÍ�L‘ÿÕ �b¦�bX�Lœ�…\�bÿ¶�×�bq�hœ�…}�bœ�…×�b�VV�ºÏ�V®�–�'‹ÿÙ‹ÿÙ#þÁÿ¾7�VV�ºy�V“ÿüo�Vqÿ‹ƒ�ZÉ�VÏ�VøÿV�V5ÿœBÿú“�V“�Vy�V'ÿ¾²�Tm�VÃ�–o�V‡�V®�–ü�º“ÿüô�–'ÿ˜R�T�ú¤�T¨�T��ºJ�Vo�V®�Õ�Vÿ´…�bT�dN�b¨�7�bò�b¬ÿÝ}ÿüž�qž�qÑ�;Lÿ¢…�=Ë�;}�bž�;žÿÕš�bú�;²ÿ;N�bÓÿ¶Ã�qq�žú�q�q¼�TÅ�'�¦�?�;7ÿÕò�bž�;¨�7š�bm��;�;þþÿ¢�9ž�;Ñ�;²ÿ;ž�qÏ�V�;Ñ�ß¼�uÑ�ß¼�uÑ�ß¼�u�¼²ÿ;×�7®�7®�7'ÿ1\�{\�}ìÿœ\�åÏ�{Ï�}Zÿœº�ÙÏ�N�Ç�+ð�¨¼�á�áD�XD�×ÿæ þò�¨h�-hÿéÍ�7h�?9�Ë�� š�;þ�yÝÿðô��V�S�]��R¦�wwÿÉî�ö �Ãh�d�b¨�˜�'h�sh�h�h�ª�˜ÿÿ\þþÑÑ�œÑøÍ�“Í�\Í�‡Í�¦Í�®Í�ƒÍ�������������ª�����V��y��!��š���Í������������T��Tþþ\�}�/�¼V�m²�Tú�;qÿ‹…�b;ÿsªÿºZ3ÿÿ¶�–}�bÇ�¤ð�q��ý.��ýæ��üµ��ý¾��ü¬�V“�Vò�bž�q �–¾�hô�–j�\ã�V�;Ïÿ‹;ÿ¤º�VÅ�;Hÿ®Óÿ‘R�Vh�;Bÿ¸“ÿ¢á�Ƕ�Ã�–}�by�¼…�`y�¼…�` y�–�bé�– �b �–�b �–¾�h®�–š�bß�u{T¤w¤}¤˜é�)¦�) �V¢�qm�V'�‡�VžÿÕÅ�N� Ó�Vî�;“ÿœ´ÿÝBÿú}ÿüÍ�T+�;y�TÍ�5u�VÓ�9�º{�Nj�VË�; �V�;ø�V‹�;Ã�–Ë�b®�–š�bü�º�;�¼²�b�m²ÿåÿ˜+ÿò7�ºB�Z�øZ�œ�øo�œ�Tœ�;�¾�P�¾�P‹ÿÙ5ÿœ¬ÿÝ�V�;3ÿ¾Jÿ¢m�VË�;y�VË�;�ø �˼�Tƒ�=‹ÿÙqÿ‹…�bqÿ‹…�b‰ÿ‰…�b�Vò�bJ�Tò�;J�Tò�;5ÿœ¬ÿÝBÿú}ÿü5�¦ÿu“�Vž�q“�Vž�qÃ�–}�bÃ�–}�bÃ�–}�b®�¦�“ÿü²ÿ;“ÿü²ÿ;“ÿü²ÿ;�úq�žÏ�V�;J�VÅ�Å�ÿúÿ˜Áÿ¶'ÿ˜Óÿ¶‡�`ž�b¦�fÍ�b´�øò�˜�öÃ�˜+ÿ¾sÿ¢ �Vð�;‘�–¶�bD�º²�VP�y�?ÿ¾7ÿ¢qÿ‹…�bqÿ‹…�bqÿ‹…�bqÿ‹…�bqÿ‹…�bqÿ‹…�bqÿ‹…�bqÿ‹…�bqÿ‹…�bqÿ‹…�bqÿ‹…�bqÿ‹…�b�Vò�b�Vò�b�Vò�b�Vò�b�Vò�b�Vò�b�Vò�b�Vò�b‹ÿÙ�;‹ÿÙÿúÃ�–}�bÃ�–}�bÃ�–}�bÃ�–}�bÃ�–}�bÃ�–}�bÃ�–}�b¶�–}�b¶�–}�b¶�–}�b¶�–}�b¶�–}�bh�¤ž�qh�¤ž�qÇ�¤ð�qÇ�¤ð�qÇ�¤ð�qÇ�¤ð�qÇ�¤ð�q�¼²ÿ;�¼²ÿ;�¼²ÿ;ž���ü��üç��üV��üç��üé��ý ��ý ��ý ��üö¤ÿTÿãÿã��ˆo�bß�JßÿÕßÿxRÿÇÿÞR�oËÿÿF�<9� ÿ�Rü�•˜�/ž�5ž�5ž�5ž�5ž�5/�V/�V/�V/�V/�V/�V/�V/�V/ÿµ/�VR�Vé�`/�V�V�V�V�V�V�V����������� ����°�€��0�H�I�~�Ë�Ï'2a’¡°ðÿ7¼ÇÉÝó #ŠŒ¡ªÎÒÖ O_†‘¿Ï?…ÇÊñùM   " & 0 3 : < D p y  ¤ § ¬!!!! !"!&!.!^"""""""+"H"`"e%Êûþÿÿýÿÿ��� �I�J� �Ì�Ð(3b’ ¯ðú7¼ÆÉØó� #„ŒŽ£«ÑÖ�P`ˆ’ÀÐ�>€ ÈËòM �   & 0 2 9 < D p t  £ § «!!!! !"!&!.!["""""""+"H"`"d%Êû�þÿÿüÿÿÿã��ÿãÿÂ��ÿÂ��ÿÂ��ÿ°�¿�²�aÿI����ÿ–þ…þ„þvÿhÿcÿbÿ]�gÿD��ýÏ����ýÍþ‚þ��ýš��þ ��þ ��þ äXäãzä}��ä}��ã âBáïáîáíáêáááàáÛáÚáÓáËáÈá™ávát��áá á ânàþàûàôàÈà%à"àààààßçßÐßÍÜi��OS����®�����ª���®���À�����������ð�����������������������à���ê��������0��L��\����������p��r��������������������������������`��������������������������������������<������–—˜™š›�ëœ�í�ïž�ñŸ�ó &'()*+,-./0123456789:;<=>?@AIJ$%TUVWXY¡\]^_`abcdef¢hijklmnopqrstuv£hœžŸ ¤¥£¤¥¦§ijêëìíîïðñòóôõkö÷“”•–—˜™šøù¦ÊËÌÍÎÏÐÑÒÓÔÕÖ×§¨F©�opqrstu45]^��@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! �, °`E°% Fa#E#aH-, EhD-,E#F`° a °F`°&#HH-,E#F#a° ` °&a° a°&#HH-,E#F`°@a °f`°&#HH-,E#F#a°@` °&a°@a°&#HH-, <�<-, E# °ÍD# ¸ZQX# °D#Y °íQX# °MD#Y °&QX# ° D#Y!!-, EhD °` E°FvhŠE`D-,± C#Ce -,�± C#C -,�°(#p±(>°(#p±(E:±� -, E°%Ead°PQXED!!Y-,I°#D-, E°�C`D-,°C°Ce -, i°@a°�‹ ±,ÀŠŒ¸�b`+ d#da\X°aY-,ŠEŠŠ‡°+°)#D°)zä-,Ee°,#DE°+#D-,KRXED!!Y-,KQXED!!Y-,°%# Šõ�°`#íì-,°%# Šõ�°a#íì-,°%õ�íì-,°C°RX!!!!!F#F`ŠŠF# FŠ`Ša¸ÿ€b# #б ŠpE` °�PX°a¸ÿº‹°FŒY°`h:Y-, E°%FRK°Q[X°%F ha°%°%?#!8!Y-, E°%FPX°%F ha°%°%?#!8!Y-,�°C°C -,!! d#d‹¸@�b-,!°€QX d#d‹¸ �b²�@/+Y°`-,!°ÀQX d#d‹¸Ub²�€/+Y°`-, d#d‹¸@�b`#!-,KSXа%Id#Ei°@‹a°€b° aj°#D#°ö!#Š 9/Y-,KSX °%Idi °&°%Id#a°€b° aj°#D°&°öа#D°ö°#D°íа& 9# 9//Y-,E#E`#E`#E`#vh°€b -,°H+-, E°�TX°@D E°@aD!!Y-,E±0/E#Ea`°`iD-,KQX°/#p°#B!!Y-,KQX °%EiSXD!!Y!!Y-,E°C°�`c°`iD-,°/ED-,E# EŠ`D-,E#E`D-,K#QX¹�3ÿà±4 ³3�4�YDD-,°CX°&EŠXdf°`d° `f X!°@Y°aY#XeY°)#D#°)à!!!!!Y-,°CTXKS#KQZX8!!Y!!!!Y-,°CX°%Ed° `f X!°@Y°a#XeY°)#D°%°% XY°%°% F°%#B<°%°%°%°% F°%°`#B< X�Y°%°%°)à°) EeD°%°%°)à°%°% XY°%°%CH°%°%°%°%°`CH!Y!!!!!!!-,°% F°%#B°%°%EH!!!!-,°% °%°%CH!!!-,E# E °�P X#e#Y#h °@PX!°@Y#XeYŠ`D-,KS#KQZX EŠ`D!!Y-,KTX EŠ`D!!Y-,KS#KQZX8!!Y-,°�!KTX8!!Y-,°CTX°F+!!!!Y-,°CTX°G+!!!Y-,°CTX°H+!!!!Y-,°CTX°I+!!!Y-, Š#KSŠKQZX#8!!Y-,�°%I°�SX °@8!Y-,F#F`#Fa#  FŠa¸ÿ€bб@@ŠpE`h:-, Š#IdŠ#SX<!Y-,KRX}zY-,°�KKTB-,±�B±#ˆQ±@ˆSZX¹�� ˆTX²C`BY±$ˆQX¹ ��@ˆTX²C`B±$ˆTX² C`B�KKRX²C`BY¹@��€ˆTX²C`BY¹@��€c¸�ˆTX²C`BY¹@��c¸�ˆTX²C`BY±&ˆQX¹@��c¸�ˆTX²@C`BY¹@��c¸�ˆTX²€C`BYYYYYY±�CTX@ @@ @  ±CTX²@º�� �³  ±€CRX²@¸€± @²@º€� @Y¹@��€ˆU¹@��c¸�ˆUZX³ � ³ � YYYBBBBB-,Eh#KQX# E d°@PX|YhŠ`YD-,°�°%°%°#>�°#>± ° #eB° #B°#?�°#?± °#eB°#B°-,°€°CP°°CT[X!#° ÉŠíY-,°Y+-,Šå-@™ !H U UHU?¯MK&LK3KF%&4U%3$UÿÿÿJI3IF%3UU3U?¯GFëF#"33U3UU3UOÏÿ�U3�Uo��¯�ï��€¸±TS++K¸ÿRK° P[°ˆ°%S°ˆ°@QZ°ˆ°�UZ[X±ŽY…�BK°2SX° YK°dSX°±�BYss++^stu+++++t+st+++++++++++++st+++^�����N¶��u¶Í��������������H��‘��ÿì����ÿì����ÿì��þÿì��¶�ü”ÿëþ…þ^þ¨ÿë�þ¼������������������������������������������������������������‹��Ý�˜��Ž�™�ˆ��������I�f�½%“ÿ3S}œ²Äàõ/PŒÞ`·Õ9¿ê (KšF“Ê�.TœÃî  ; U Œ · ö * v ´ ÿ  P s ³ Û û # ? S n Š œ ´ ý I } ÈZâ!Cx§¹J…ÑN•Ô .ršÒúDV ÑÑûA‹Û%D­×F„¹ÎÖFX…µæ&@~¨±Õõ"Vl—äö,?Y¢®ÀÒä÷ -@Œž°ÂÔæù%“¥·Ê܉š«¼Íßñt€‘¢³Å×éûrƒ”¥¶ÇÙ h y Š › ­ ¾!! !2!C!U!f!r!~!!¡!³!Ä!Ö!è!ú" "")"1"‘"£"´"Æ"×"é"û###%#6#H#Y#k#|#Ž# #¬#½#Ï#á$'$r$„$–$¨$º$Ì$Þ$ê$õ%%%$%0%B%T%`%k%™%«%½%É%Õ%á%ì%ø&&4&^&p&&&˜&ª&»&Ç''P'b's'…'–'©'»((‡(™(ª(¶(Â(Ô(æ(ø) ))-)9)E)W)i)t))‘))Ò*$*6*G*Y*j*|** *²*Å*×*ã*ï+++$+6+I+[+l+~++¢+´+é,2,”--$-6-H-Y-d-o-’-¶-È-ê..4.W.….°.Ê////'/4/A/M/Z/f/z/‚/Š/£/«/³/»/Ã000!0A0I0Q0‚0Š0©0±0â0ê0ò1E1M1“1à1ó222(292K2^2¾33H33î444o4º4â4ê51595^5¿5Ç66K6Ž6Î7737…7Ñ88e8x8Š8›8¬8¾8Ñ99.9s9{9ƒ9–9ž9ò:7:t:†:˜:Á:É;; ;;R;Z;’;ã<< .>a>©>ÿ?FF‡FF¢FªGGaGiGzG‹GÐGñHH#H4HFHXHkH}HH¡H³HÅHÍHÚHñI I I7I]I…I©IØJ'JAJQJìJôJüKK9KEKZK‹KÈL2LMMaM­NNSN[NœN³NÊNáNøOJOuO“O¿OÐOðPJPsPÌQQ3Q`Q„QQœQ¿QâQüRR/R_R’RËSS.S„SËSËSËSËSËSËSËSËSËSËSËSËSËSËTßU1UCUKUÃUôVTVfVxV„VVœVÈVýW•XXmX¼XþYFYOYXYaY‡Y¢Y´YÆYØYéZUZZÚ[/[‡[Ú\\F\\Õ],]]ç^I^ä_y__‰_Ú`"`\`‘`¤`·`Ã`Ïa:a¢b?bÖcac¼cõd,dcd‡d³dÖd÷eÍfWfªg gQg¦góhVh†hµhÿiJiŽjjjjPj‚j¾jùk3ktk£kÒll9lklœlêm8m«nn#n/nYnÈnÐn÷o-oeo™oÍpp[ppâq,qzq±qërVr¹s,s™s¡s³sÅt tPttÕuuLuu³uõv9v|v¾vÆvØvévüwwww0wAwwÚwíwÿxx%x8xKxxÒxäxõyyy-y?yGyOybyty‡yšy¬y½yÐyâyõzzz,zRzwzŠzzé{5{y{½{ù|4|e|m|Â}'}‡}ã~5~Š~Ú6É€€U€’€Ó%-{ÐÜèú‚ ‚‚2‚E‚X‚k‚~‚‘‚¤‚º‚Ï‚â‚õƒƒƒ.ƒAƒTƒgƒ}ƒ’ƒžƒªƒ¼ƒÎƒàƒñ„„„*„=„P„c„v„‰„Ÿ„´„Ƅ؄ä„ð„ü………,…?…R…e…x…‹…ž…±…Ä…Ú…÷† ††,†=†O†a†s†„††œ†¨†´†Æ†Ø†ê†û‡ ‡‡0‡B‡T‡e‡q‡}‡‰‡•‡§‡¹‡Ë‡Ü‡èˆˆHˆ}ˆÂ‰‰@‰r‰²‰üŠŠ>Š^ŠgŠ¡ŠÃŠþ‹P‹‰‹Ï‹×‹ö‹þŒTŒÎ,8Dž¯ÀÒãöŽŽŽ,Ž?ŽQŽcŽuŽ€Ž’ŽžŽ«Ž³ŽÆŽÎŽáŽéŽñ����Á�� ¶��� ³��/2/310!!7!!ÁIü·hyý‡¶úJhæ���+ÿã+¶���@ OY�?+�?9/10#4632#"&+þîm1þÏOD-8NB17¶ûÝ3ðú˜LX76D^8���á¦B¶��� ´�?3Í210#!#å™kHškH¶ýðýð���?��;¶���3@�   �?3?39/333339/3333310!!#!#!7!!7!3!3!!! b þË}‹þÑ…yþû eþë -{‹}1…} üÅ/`þуþ¬þR®þR®T´þL´þLþ¬T��Hÿ‰/��%�,�:@ )", % KY&&KY�//99//3+�3+�92310#7&'53&&546773&'6654&'®áÅ1…1ÃxR»Ib‹}Ø¿'…'Ÿ|?y^•þwk~BQlvEIì¢Êáß:š)-Ä1•rŸÀ°² @‡? þH7Œþ¢ |\6T J tb5Y���¨ÿìüË����+�:�%@%118 �?3?3??9/39/310#"32654#"&54632"32>54#"&54632¼ûØ>!Ci?jAhE‹?eVnze¬rtxü#Bj@k4T?&Œ@eŒWmze¬rtx¶úJ¶ýNøt­‚ g¦—kþþ±c•‘¦1˜\~ùw¬O•ÉG¦˜lþý°b–‘¦1˜��BÿìÍ� ��5�0@**'(0-/�JY3 JY3�?+�?+�?999/10">54&267%467&54632673#'#"&áitR‹h>Uþ¥W¢rþ΀p;…þØ´ÕgÔ¯•¯An‘QH¨e¶ËÉux܉¨ÊDxnxsGN\:MYû3B[±CbuIjß”Ûf¥¤Éž…NvbR*þ{n¹ì°þò¦dVÀ��á¦å¶�� ²�?Í10#å™kH¶ýð��Rþ¼ç¶� � ³ '�??103#Rðöûrƒ“S@þòý¨þÂþ­þý �ÿ`þ¼ô¶� � ³'�??10#�3ôóþûœðqƒ’`þ«ý¿þò'}Uþó��×j�� ³��?3/10%%'%7V{¦þw´¦qþþt5þ‹7s)øþ/þ4–þ¨ymš¹�� � �´ �/33310!5!3!!#þ{…†þz‹Ž‡þyŽþ}�ÿœþø+�î��±��/Í10%#7#q~NîëôØ��7Õ9s��±��/3107!7#ß"Õžž���+ÿã#�ò� � µ OY �?+1074632#"&+QG+5PD.6JM[45G_4��ÿ¢��}¶�� ³�??10#}üÓ®-¶úJ¶���yÿìDÍ� ��@ KY KY�?+�?+10#"&5$3 "3265Dþñ³°¼¤ ­mþ‰qÀwdos¼qöþÙþûÿòéùçþaج¸ä¬ïC�/��…¶� �¶ �??9910!#7'3L¬Ä;2Y²PÁ•‘b5:n��� ��7Ë��@ KYLY�?+�?+�310!!7>54&#"'632!ü}Ñf•a/xgM“YR¿Þ°ÍO·Ëþ ¦“¤]}{Kct9Dsš±›oºÐ±þÍ���/ÿì1Ë�(�-@KY && KY& KY �?+�?+�9/+�910#"'532654&##732654&#"'6321ʳ|Ž|î¤Ò¯^ÒU¢´žƒ‹¥ÚtcPšbPÃå³Ä‡œÙ §}…ËrO¤15ŸŠƒ‡®Œ\l6Bv®�����L¾� ��%@LY �??9/3+�33310##!733!6#+éH¤JýpBÄÏèþu.@TcýëNþ²NžÒü)Ü2$‡ý���PÿìH¶��-@��MY��LY KY�?+�?+�9/+�9102#"'532654&#"'!!6V¶×’þú±Á‰¤ª¿Ö“ƒ0bZJÅœ!ýöW}Ѳ¡ôyO¤fÀ¬~“ 9¬™þI���…ÿìZË��(�-@ !MY KYKY�?+�?+�9/+�9104$32&#"�36632#"&26654#"…yθoI#DfÔþßD;¯jš´…ä“¶Ç^“QÝB|`0y¨Ñœ#“‘þÖþÚO^Ŭ¡þõæYl¾qø9_rb‰ž���®��y¶��@ �LY�?+�?3103!7!®�ý1{ý!•‹úÕ��`ÿìVÍ��%�1�2@)# �,KY,, ��&KY� KY �?+�?+�9/+�9102#"&5467&&546632654&"6654&å®Ãœ¼jxᘾؾÉ_QmÆ ¹™Šp‹¦hrˆNX¡†sÍ­•ÀPN®sz¿fÅ©”ØLE—`o¯aüï<§wk~”zc–½ƒnR|654&+p·þü¬ˆj†pÊú@ 3£c©¸ç´Çþ–¶mlL€Y)v ÂþWþÙŒ"ž/,KWž™ÿåWâ±|„9jzZƒ™���+ÿãÃd� ��@ OY OY �?+�?+1074632#"&4632#"&+QG+5PD.6 PGaSA.6JM[45G_4¥M[hI]5��ÿœþøÃd���@ @ OY �?+�/Î10%#74632#"&#q~NcPGaSA.6îëôØÎM[hI]5��y�òÙ��@ ��//3293310%5üi—ýëò¦bß•þþ¸���¼å���·���/33/]3105!5!˜üh˜Z‹‹þbŽŽ���y�òÙ��@ ��//33933105yéý—üi‰Hs•þ!bþZ���žÿãšË��&�%@��$$OY$ IY�?+�?+�9/910667>54&#"'66324632#"&es€P.g`Q”B=\ÈR§¸ƒ }XíPG,5SA.6“‚§[dXd>]e332¨š{Ïx[qhþ·M[45I]4���oÿF´�5�A�*@&+ 6==   +2�?3Ä99//33339310#"'##"&546632326654�#"�!27# �$!2"32&nÈzÅo´sƒˆñŒA}X@DfNJÿ�òäþ›Å ÅßÛàþÆþ—ý² ¿‘ýJ^ \BA»UF?T³þÞ¤¸¸—†þ$òü.u†éï�Öþ‡äþòþ×VZqAÁö—þì_nÄr]S;��ÿ‹��¶���@ IY�?3?9/+�310!#3#&'-þõ½®¸ªK#%WÎÑþ/¶úJm+³«X®þ}��V��²¶��� �-@  JY JY JY �?+�?+�9/+�910 #!32654&##!2654&##°®žs{þÐÿþ15'øœº…Óú µÂ•Œì¶þ°Â nÔñ¶ýŽ’~hgûn¡“t{���–ÿì Ë��@ �IY IY �?+�?+10"327#"�$32&®®þí¡Ã«‹·VœnòþìÓdáÅ—EŠ3Âþ‰Ý»ß9•+ÁìPE��V��¶� ��@ JYJY�?+�?+10!!! �2$54&##Òþzþúþ 5Vü­Ê2£ÎDzúmþûþpضþÕü·N××Ýûp���V��j¶� �&@ IYIY IY�?+�?+�9/+10!!!!!!!5ý!5ß ýÊbýïr5¶™þ+˜ýè��V��j¶� �@ IYIY�?+�?9/+10!#!!!!¬5ßýÈn ýï¶™ýë™���–ÿìNË��&@�IY�� IY IY�?+�?+�9/+10!# �$32&&#"327!5ËšØËþøþÛËhÛuÍhBM±j©þëšÍ¸šj`þßþý9K! ¹õ(,˜"2Ëþ”á¾Ú'¼���V��s¶� �@ IY �?3?39/+10!#!#3!3=§ýl‘ª5ªƒ”…¨°ýP¶ý’n�ÿÙ���¶� � @ JY  JY�?+�3?+�310!!77'7!Ëþ¬�œò¯ÿ�ž\%´#^^#ûL%�þÁþ5¶� �· �IY�"�?+�?10"'732673¦i0ELdƒ3ªþËOþ“}xªúDþ…��V��+¶� �@  �?3?39910!#&'#33 ºH•H®}ª5ª—¼Ñýµe·ƒý²¶ý:Æýƒ��V��V¶��·�IY��?+�?1033!V5ªþì5!¶úäš���T��¸¶��@  ��?33?3910!##33!#7#¤¦+¾¢5ô• “ þÑ®~†ý3HûJü}¶ûL´úJNwMúî���T��¨¶��@  �?3?39910!###33673sµþ4 *¬¢5´Ë*®¤ÇÝÅüÛ¶û<àµ/���–ÿìƒÍ� ��@ IY IY�?+�?+10# �$32�%"32654&ƒ°þ¸×ÿ�þâÀOÒôýçø‰Ä¨˜ñŒ¼‹þóþWé+´ìþÌœÊþ˜ÚÇßÃlÝÇß��V��‡¶� ��@ JY JY�?+�?9/+10�!##!232654&##‡þ¸þÇ{ª5JÖÜý…Øà‹£=øþúýÁ¶½ýض°}o��–þ¤ƒÍ� ��#@�IYIY�?+�?+�9Æ10"32654&# �$32��#jø‰Ä¨žò…¼þÄÿ�þâÀOÒôþêìÛ5Êþ˜ÚÇßÈiÛÇßú¹+´ìþÌþòþ³þNþš��V��‰¶� ��&@��JY  IY�?+�?39/+�910#! #32654&#ª5@¾þïºÑif¨ÁЇ˜`ý ¶þ’þ¤eýy`ÁþAªŸym���'ÿì#Ë�&� @ IY IY�?+�?+�9910#"&'532654&'&&54$32&&#"‘þçÿj¡G¢²¢¾i—u×c«_BB¤E†£"Ji“g5¨Óé"ªT—„NwQUªt»ë&.–&,‹w6MD=Xdy��º��´¶��@ IY�?+�3?10!#!7!!ç¬þj!Ùþh——���¤ÿì¶��@  IY�?+�?310#"&54733267Í7þãöæÞ½ª¿’‘¬¿,Ͷü:þùýÐÃQxnü…jRu‡¯Êº���¼��¶� �¶ �??3310%673#3ß=eß¿ü󴢪aÅŸúJ¶ü^Ä‹���ß��¶��@    �?3?33910!#&5##336733673éª1Hþ,®>ª YC•²+  K8ƒ¶ň’H¥ü¶ü{Xb óŒ`ü¤™—Sà‚}�ÿ˜��Ѷ� �@  �?3?39910!##33¼´ÕþºTþù¬Ë»ºýÕý®ýÍ3ýJ���¼��ö��¶��??39103#3éÁýq¬wîªËëügýã%‘��ÿð��“¶� � @IYIY�?+�?+�3310!!7!7!!uü{œýq Züd¹‰’›‹ûo�ÿðþ¼ð¶��µ'�?3?310!!#3sþ}}ƒãþÂãþ¼úú!��Ý��P¶�� ³�??10#wÙšÙ¶úJ¶��ÿjþ¼j¶��µ�'�?3?3103#7!!wá@ã…þ„þ|¶ßù��5)Á��´��//33103#5Vm ‘Éþ-)˜ühëý��ÿDþ¼ÿH��±�/310!7!oüÕ+þ¼Œ�?Ù‡!��±�/Í10#&&'53‡h=‡µ+hÙ=º<ˆ§��bÿì`\�� �'@  ��FY� FY �?+�?+�??99102373#7##"&5462654&#"\( C酳ƋžŽú)aÀxp[h³f^\c]¬û¸Ñ寬ÐdÊü¹)•gz¬þÚ£rq���;ÿì9��"�'@ ��FY�FY��?+�?+�??9910"&'##336632"32654!a“% F}J¨370 ]µ`ž‰õ `Çuoic«hfXªòþÿ¬voÆ­ÑþÇã¾þà—nu¢+¨ã��bÿìª\��@  FY�FY��?+�?+10"&54$32&#"3267úÂÖ”£‰ƒ/xcp¹i…uH€>|ÖÃÈR½33™þï €Ž(?��bÿìÃ��!�'@ �� �FY�FY�?+�?+�??991023673#7##"&5462654&#"ÂW N¦þ¶‹e°^‹œõ&^Èulge­iZ¾›wfùìÑ}hÄ®ÖdÂü»#—ot¥þÕ¥ã��bÿì´\��"�&@FY�FY�FY��?+�?+�9/+10"&54632!#3267"32654&ì¸Ò•ö”™šþ´þË!{?…c^4gµ0 äóIÚÁ¼YÀ…w´ÍPƒ“$0’,#á¼§wq5F�ÿþƒ� �*@  FY�GY�FY��?+�?+�?+�3310"'53267#?6632&#"3#hE8@0LRãÁ Î.£ (t +L=W]îíè'¢þ|s:CBdÈ¥alû¶½®�ÿþL\�(�5�C�H@(33FY9GY &33(&@GY&(GY(,GY�?+�?+�?+�99//9++�310#"'!"&5467&5467&&5463232654&''326654&#"LÓ)éÃ7‹B?uµ£þÜþ÷ÂÜ¡Nf[?PïºNLýJ‚€¶Íl‚Ÿx€ZPOv?XRNuAHk>`¿ã5N)„€¸Ê“†iš6)PEc+ }SÂøúõMZt>H~UYT“VRVQ‘��;��)��@  �� FY�?+�?3?910!654#"#336632Ù”“Y©!e¨J¨!#) ^·dƒ'j´^)”váŸþ'RšŸ®f{j„>hÁþ!���;��ß���µ �??Ä2103#34632#"&ã¨ê¨y@3XC,(4H8GZ7L1��þþþß� ��@ �FY��?+�?Ä210"'532734632#"&‡E6=:}+¦þö$T@3VC,&4þÍÛû«ŸL8GZ7L1���9��!��@   � �?3??99103##3JÉþ+'»ë˜RªJªHr-/þ-ý‹ {þoþ°ýì���9��-�� ³��??103#3á¨L¨���;��‡\�,�'@  �! 'FY�?3+�3?33?9910!654&#"#33663236632#654&#"¸”>KTŸy!e¨ê‹ W­\qz VÂc‹ª”EJQžwk´^)FNxßþ%HËwh‚t}yˆ‚Dný`´h*>KtÕ’þ ���;��)\��@  � FY�?+�?3?910!654&#"#336632Ù”GNY©!e¨ê‹ `³`“´h(?LxÞžþ%HËze‹}Oeý`��bÿðV� ��@ �FY�FY�?+�?+102#"&5464&#"326ƒ¾Üö›ÀÚ’øƒ}km­_wh¦]Váżþ²¶âľO³þqs”þù¡ƒ’ ��ÿÕþ9Z��"�'@ �  FY�FY��?+�?+�??9910"&'##33632"32654!a’( k¦P‹³Á‰žŠô `Çuoic«hdZ&Zþ4ÑãðÔþžÅã¾þà—nu¢+¨ã��bþ`\��"�'@ ��FY�FY�?+�?+�??99102373#67##"&5462654&#"^% C}þ°¦e 0_´`ŒŸ‘÷)\Äymbe°h\e[¬ùÌà-°ylïÔhÂü¸"›iz©þ×£ã���;��h\��@  �  �FY��?+�??9102&#"#33>ðE3$54[Ÿwk¨ê‹ H^g\– xÕ‚þ HË_S-���ÿìD\�$� @ FY FY�?+�?+�9910#"'532654&'&&54632'&#"ßÝÉ©ˆF¢E~€Ft‚lÊ¥«Ÿ68ew]jGok].7œ¯Ež*.dN9NDIŒ`Š©J‰+WE8P?n¤���b��H� �µ ��?2?31033373ß}¨@4E²ý±Hý›þþh`\û¸���u��H��@   � �?3?33910!&55##33673363?  2SþÝÊ+¤/Z'¶%n ²þZ^Nœv½ý‘Hý®X“z|Æuý®¨^5*V Xû¸��ÿ¶��H� �@ � �?3?39910#33#Óþ¦ÃÛ类JÂþ9ü¨²þN5þdœýåýÓ�ÿ;þH��@  �FY�?+�?3910336673#"'532677b¨J #hE²ýH]¶€HD?DRu7LHýßEþóRWâ+aúþ¬†‡ecˆ��ÿã��}H� � @GYGY�?+�?+�3310!!7!7!!²ý1¶þ!‘ýXuV}ŒüÁ��þ¼/¶�'�@  &�'�??329/3910"&54766'4#72676633"3º—!%Ñ vD"§«!i\G~h—/ISþ¼iw4Y’£WhF €IWþ¿{z)¬;HÓ:(51Ž��þ¨�� ³���??103#‹‹÷ü�ÿ¶þ¼Á¶�'�@  &�'�??239/391023"##52676675&5477654™1Ñ!wŽC$¦µ shH~k–2Qc¶iv0]ÛD+Vhþº¤}ŽHWBu}*©;HÕA"51��sP3T��¶�€ �/33Í2210"56323267#"&'&&\62b‘EtV@a273dH~HKZÉE4—m%B7–n! ��ÿòþ‹ò^���@ ��" OY�?+�?9/103#4632#"&m°ÏQF,5QA06¬ûß+L\46G]3��áÿì)Ë��(@ MYKY   �??99//++�3310#7&&546773&#"3267F}+„æ‘#{%wb/mnp¹i„vH€>{£×"Ïš¹>Ǫ¨ (Ž4™þï }’(>��ÿé��šÉ��1@ NY��KY�LY�?+�3?+�9/3+�3102&#"!!!!7677#73J¸˜B’„Õ2Erþ/XPÕ!üGÍ5/ÈÉLKÉV…OíþºÛb‰+š.óÝ^a���¨!‡��'�$@  %�/3Ä2Î2Î291047'76327'#"''7&732654&#"åD\grre\FFZbuwbZDgh’’hfÓub\FFZhqwbZDFZ`wggh’‘�����ì¶��=@! MYMY�  �??399//]92+�33+�31033!!!#!7!7!733Fë»ýëçþ×!)þÙ9š8þÝ"!þÝáÊ£Ëëüþ… …þö … …�þ¨���@ ���??9/9/103#3#‹‹‹‹üøþ ü÷���;ÿøå�,�8�-@3--603'$GY GY��?+�?+�9331047&54632&#"#"'532654&'&&6654&®îwϰ½„5”vfyL|db4zorä̰w8¡L…ŽefŠpTRje”P_cÉjF…žD{EÎŒ‹��X�q¨¾�� �!@  �  �//3399333310X‡Nþ׬qç‹oVþåžq×HvQþ¸þ}1º•Eþ“þ¢1����� ²�/3/10#!5Œüôýÿÿ�7Õ9s�����‹ÿìjË���-�5�-@%))5** #''..  �?3?39/399/39/3104$32#"$732$54$#"%###323254&##‹È^ÊÅZÐÉþ§ÍÏþ¢Ãi®-¬®*¯®þ×°®þÖ¯¶_Uã•Ïqå£þZ\Ã_fZÛÈ^ÊÂþ¢ÐÌþ§ÊÏZÆ­þÓ­®)°®*¯®þ×N~#þ`þ p~þå•L>���ã˜��±�/310!7!ôüï!„��×^FË� �� ³ �/3/3104632#"&732654&#"×µ‚S‘Tµƒƒ´qtRSrsRPv“€¸S‘T€µµ€PtuORuu����˜� ��!@     �//399//33310!5!3!!#5!þ{…†þzþ{˜ƒˆþxþ}ÿ����`JöÉ��·  �?33?310!7%>54&#"'632!‘ýÏsW(>;Zi;}•m}i‘Ý‹JjädcW/5@PZeq^c¡}»��w9ôÉ�"�@ " !�?3?39/3910#"'53254##732654&#"'632%EN¶¢}l}rͲZ_atE;f^7mŸr€Ï fE„“8Hª‰kRG<=D]Xp_´6��Ùº!� �± �/Í106673#8‹%¾&ÌKiô<·:1Ì6��ÿÕþjH�� @  FY�?+�???39910326673#7##"'##3'“Y¦ƒ"i£é‹ ]´bp3 H¤P¨” “vàÞû¸Íyh`NŒþ¢4ýI\��Çþü¶��@ IY�/+�/39/910####"&563!¶rÕs>TØËÚè-þü}ùƒ3úûþ�ÿÿ�ªK¢Z���h�ÿVþ�Ó���� ³�/3Ì210#"'53254&'73Ó¢…)-&®NZÆW¨¶„Ÿ„QîQG+5PC/6¨‚¤]K”]f304§š|ÐwbmdJM[44G_3�ÿÿÿ‹��s&�$���CÿØR� ´&�+5ÿÿÿ‹��Ms&�$���v�“R� ´&�+5ÿÿÿ‹��8s&�$��K�BR� ´&�+5ÿÿÿ‹��/&�$��R�`R� ´&�+5ÿÿÿ‹��+%&�$���j�BR� µ""&�+55ÿÿÿ‹��&�$��P���@ïßo�]]]]55��ÿ‰��ݶ���<@ IY IY IYIY�?+�?+�3?9/+�9/+10!!!#!!!!!#¨ýbþJþÛŪª!ýËdýïs6ý“TþNÑþ/¶™þ+–ýæÕ°ýPÿÿ�–þ Ë&�&����z#��ÿÿ�V��js&�(���CÿûR� ´ &�+5ÿÿ�V��js&�(���v�}R� ´&�+5ÿÿ�V��js&�(��K�FR� ´&�+5ÿÿ�V��j%&�(���j�BR� µ&�+55ÿÿÿÙ���s&�,���CþóR� ´ &�+5ÿÿÿÙ��ks&�,���vÿ±R� ´&�+5ÿÿÿÙ��Is&�,��KÿSR� ´&�+5ÿÿÿÙ��>%&�,���jÿUR� µ&�+55��H��¶� ��-@IY JY JY�?+�?+�9/3+�310!!#73! �2$54&##!!Òþzþúþ ‡• –Vü­Ê2£ÎDzoJ!þ¶jmþûþp؉–—þÕü·N××Ýýü–þ ÿÿ�T��¨/&�1��RR� ´&�+5ÿÿ�–ÿìƒs&�2���C�hR� ´&�+5ÿÿ�–ÿìƒs&�2���v!R� ´%%&�+5ÿÿ�–ÿìƒs&�2��K�ÕR� ´""&�+5ÿÿ�–ÿìƒ/&�2��R�åR� ´%%&�+5ÿÿ�–ÿìƒ%&�2���j�ÏR� µ//&�+55��¨1ðw� �@ �  �/3/39107'çþÁb@Ccþ¼Baþ½þÀ`ÓAcþÀ@`þ¼þ¾`@þÂ`��wÿ¬¶�� �)�#@#$&IY&IY�?+�?+�910#"''7&5$327"&4'326ƒ°þ¸×È„p‰jÀOÒcŸHƒr•02ýçø‰)áZÑ!ý#[Ž˜ñŒ‹þóþWé` \ªˆë´ì69¦\¸>¼?Êþ˜Ú‡\—XþZvWüqJÃl�ÿÿ�¤ÿìs&�8���C�XR� ´&�+5ÿÿ�¤ÿìs&�8���v#R� ´&�+5ÿÿ�¤ÿìs&�8��K�ËR� ´&�+5ÿÿ�¤ÿì%&�8���j�ºR� µ))&�+55ÿÿ�¼��Ãs&�<���v�TR� ´&�+5��V��R¶� ��%@ JY  JY �??9/+�9/+10�!##33232654&##Rþ¸þÁ…Fª5ª5 ÕÝý‡×⌑¦=øþúþÁ¶ÿ�¾ýÙ¶°~n��ÿ�þX�?�,@6' 9FY �$FY�FY��?+�?+�?+�910"'532676632#"'532654&&'&&54667>54&#"…E6=2AT+⢴*QŠn_/l3Ù¸¯]3‡>qˆ.BXE%>]P>"iYw†þî(¢þ_n"ÈÆŽ}9c[iSD8B"kqC¯ÇG )3te(>87En<5VGE8?C%@I}€úé½®ÿÿ�bÿì`!&�D���C±�� ´""&�+5ÿÿ�bÿì`!&�D���vD�� ´**&�+5ÿÿ�bÿì`!&�D��K÷�� ´''&�+5ÿÿ�bÿì`Ý&�D��R�� ´**&�+5ÿÿ�bÿì`Ó&�D���jý�� µ44&�+55ÿÿ�bÿì`&�D��PÚ�� µ$$&�+55��bÿìX\�*�8�B�B@$ õy=<:?>���=ÿ´P“���'�0@ "!FY$FY�?+�?+�9ÆÆ10#"''7&546327%"&4'326‹ôš’dbimD“ô–’jhiw>þpl¯d6Œ ýþ9eg«_Á½þª¾A}R‡g¢ÂN°DOai’þþ—\-…/þåT#ý-�ÿÿ�qÿì^!&�X���C™�� ´&�+5ÿÿ�qÿì^!&�X���vh�� ´""&�+5ÿÿ�qÿì^!&�X��K �� ´&�+5ÿÿ�qÿì^Ó&�X���j��� µ,,&�+55ÿÿÿ;þ!&�\���và�� ´""&�+5�ÿÕþ9��$�'@ � �FY�FY��?+�?+�??9910"&'##336632"32654!b”' f¦°¨_( e°_‹žŠô `Çuoic«hfXhHþ�þG¢D}hðÔþžÅã¾þà—nu¢+¨ã�ÿÿÿ;þÓ&�\���j†�� µ,,&�+55ÿÿÿ‹��7¸&�$��M�bR� ´&�+5ÿÿ�bÿì`f&�D��M#�� ´$$&�+5ÿÿÿ‹��N7&�$��N�3R� ´&�+5ÿÿ�bÿì`å&�D��N�� ´$$&�+5ÿÿÿ‹þH¶&�$���Q-��ÿÿ�bþH`\&�D���Q¨��ÿÿ�–ÿì s&�&���v9R� ´""&�+5ÿÿ�bÿì�!&�F���vF�� ´""&�+5ÿÿ�–ÿì s&�&��K�ÕR� ´&�+5ÿÿ�bÿìÄ!&�F��KÎ�� ´&�+5ÿÿ�–ÿì 1&�&��OòR� ´!!&�+5ÿÿ�bÿìªß&�F��O��� ´!!&�+5ÿÿ�–ÿì s&�&��L�¦R� ´%%&�+5ÿÿ�bÿìø!&�F��L¿�� ´%%&�+5ÿÿ�V��s&�'��L�^R� ´ &�+5ÿÿ�bÿì&�G���8Õ��ÿÿ�H��¶�’����bÿì/��*�9@� GY � ��%FY�FY�?+�?+�??9/3+�3991023?!7!733##7##"&5462654&#"{a“) þ‹y'¦)••þú‹e°^‹œˆö^Étqfg©gbdZW_ººû'Ñ}hÄ®ÕcÌüÁ#—pu¨þÖ«ã�ÿÿ�V��j¸&�(��M�ZR� ´&�+5ÿÿ�bÿìÕf&�H��M��� ´&&&�+5ÿÿ�V��j7&�(��N�=R� ´&�+5ÿÿ�bÿìíå&�H��NÒ�� ´&&&�+5ÿÿ�V��j&�(��O\5� ´&�+5ÿÿ�bÿì´ß&�H��O��� ´++&�+5ÿÿ�VþHj¶&�(���QL��ÿÿ�bþg´\&�H���Q!�ÿÿ�V��js&�(��L�/R� ´&�+5ÿÿ�bÿìû!&�H��LÂ�� ´//&�+5ÿÿ�–ÿìNs&�*��K�ÍR� ´$$&�+5ÿÿÿþL!&�J��KÂ�� ´JJ&�+5ÿÿ�–ÿìN7&�*��N�ºR� ´!!&�+5ÿÿÿþLå&�J��N¹�� ´GG&�+5ÿÿ�–ÿìN1&�*��O�R� ´&&&�+5ÿÿÿþLß&�J��O��� ´LL&�+5ÿÿ�–þ;NË&�*���9-��ÿÿÿþL!&�J��:u�� ´HH&�+5ÿÿ�V��ss&�+��K�ºR� ´&�+5ÿÿ�;��3ª&�K��K�=‰� ´$$&�+5��V��þ¶���4@ JY� IY  �?3?39/+�9/33+�33103##!##7373!737!Dº¾á¬ýl‘ªáºº5ª5“8¬þÏ1ýj/¼ûÓ°ýP-úúúý’åå��;��%��/@   GY�� FY�?+�?3?9/3+�3910!654#"##7373!!3632Ù‘”Xª‚!a¨ ˜•'¨)^þ£X ¥Ñ†¦Y.˜xáþ1Ùººþ‘擆:lýoÿÿÿÙ��£/&�,��RÿtR� ´&�+5ÿÿ�;��Ý&�ó��Rþï��� ´ &�+5ÿÿÿÙ��E¸&�,��MÿpR� ´&�+5ÿÿ�;��¿f&�ó��Mþê��� ´&�+5ÿÿÿÙ��`7&�,��NÿER� ´&�+5ÿÿ�;��Øå&�ó��Nþ½��� ´&�+5ÿÿÿÙþH�¶&�,���Q�¢��ÿÿÿšþHß&�L���Q+�ÿÿÿÙ���1&�,��O�…R� ´&�+5��;��ÍH�� ³�??103#3ã¨ê¨HÿÿÿÙþ¾¶�&�,����-‰��ÿÿ�;þ%ß�&�L����M��ÿÿþÁþs&�-��Kÿ&R� ´&�+5ÿÿþþþ¼!&7��KþÆ��� ´&�+5ÿÿ�Vþ;+¶&�.���9���ÿÿ�9þ;!&�N���9F���9��!H��@    �?3?399103##3JÉþ+'»ë˜Rªèª>2/þ-ý‹ {þoHþç;¦�ÿÿ�V��[s&�/���vÿ¡R� ´&�+5ÿÿ�9��<¬&�O���vÿ‚‹� ´ &�+5ÿÿ�Vþ;V¶&�/���9�œ��ÿÿÿŸþ;-&�O���9ÿ��ÿÿ�V��$·&�/���8�éÿ£ÿÿ�9��z&�O���8?�ÿÿ�V��v¶&�/���Ohýeÿÿ�9��¸�&�O���O�ªý‚����V¶� �%@ �� IY��?+�?99//9103'737!Tmo;ʨ¬‘í<þ¶b5!ú@muýV‡j¿þ0š����J� �@ � ��??99//9107#'73˜|6Õ˜¨w9Ѭ¨fJhý7THi �ÿÿ�T��¨s&�1���v+R� ´&�+5ÿÿ�;��+!&�Q���vq�� ´##&�+5ÿÿ�Tþ;¨¶&�1���9�á��ÿÿ�;þ;)\&�Q���9V�ÿÿ�T��¨s&�1��L�¶R� ´&�+5ÿÿ�;��2!&�Q��Lù�� ´&&&�+5ÿÿ�]��´¶�'�Q�‹���à���Tþ¨¶��@ �IY�"�?+�??39910"'73267##3363Íh2@Rfþ) ' ¬¢5¶Ç1±¦þËRþ“xoÇþ÷™üÛ¶ûB{5úJþ���;þ)\�"�%@ FY�FY��?+�?+�??910"'5327654&#"#336632C8@6|*³GNY©!e¨ê‹ `³`“²$þÏEh(?LxÞžþ%HËze‹}Lhü¼¬œ�ÿÿ�–ÿ샸&�2��M�çR� ´&�+5ÿÿ�bÿðf&�R��M�� ´&�+5ÿÿ�–ÿìƒ7&�2��N�¦R� ´&�+5ÿÿ�bÿðå&�R��NÒ�� ´&�+5ÿÿ�–ÿìœs&�2��S'R� µ//&�+55ÿÿ�bÿðŒ!&�R��S�� µ//&�+55��–ÿì)Í��!�9@ IY IY  IY IYIY+�??+�?+�?+�9/+10!!# �$32!!!!!27&#"ôý^NPÿ�þâÀOÒ˜UÅýËdýðs5ü¤E6öLsø‰Ä+´ì™þ+–ýæ‰Êþ˜ÚÇß��bÿì¬\�!�.�8�;@ 2FY22 /" "FY ))FY��?3+�3?3+�39/+�9910 '#"&5463 6632!#3267"324&%"32654&ãþòVEÍ{¸Ø–ü›YKÍw‹œþ·þÐ){KŒP¢ým®`|n±Êw‰}¿,çð@ákräÂÁN±ámzƒy·ÊPƒ“1#–KÝ’þþ›’A |†½¦zt0E�ÿÿ�V��‰s&�5���v�‘R� ´&�+5ÿÿ�;��ž!&�U���vä�� ´&�+5ÿÿ�Vþ;‰¶&�5���9�ž��ÿÿÿ™þ;h\&�U���9þý��ÿÿ�V��‰s&�5��L�3R� ´""&�+5ÿÿ�;��±!&�U��Lÿx��� ´&�+5ÿÿ�'ÿì's&�6���v�mR� ´00&�+5ÿÿ�ÿìŒ!&�V���vÒ�� ´..&�+5ÿÿ�'ÿì#s&�6��K��R� ´--&�+5ÿÿ�ÿìU!&�V��Kÿ_��� ´++&�+5ÿÿ�'þ#Ë&�6����zF��ÿÿ�þD\&�V����z���ÿÿ�'ÿì#s&�6��LÿÒR� ´33&�+5ÿÿ�ÿì‚!&�V��LÿI��� ´11&�+5ÿÿ�‘þ;´¶&�7���9õ�ÿÿ�;þ;ÛD&�W���9Ÿ�ÿÿ�º��´s&�7��L�R� ´&�+5ÿÿ�ZÿìÀ&�W���8�…ÿÿ��ª��´¶��(@JY�  IY �?+�3?9/3+�310!!#!7!!7!!“'þÙ¬þßkþj!Ùþh/‰ýZ¦‰ð——��+ÿìÛD�"�:@@  GYGY�FY�?+�?+�39/3+�3Í310%27#"&5467#737#?3!!3#‹7Y"d}… 2‰‰7¬¹}b7þï7ôô4 :uwv#^ðþINäüþõJ"8<�ÿÿ�¤ÿì/&�8��R�ÝR� ´&�+5ÿÿ�qÿì`Ý&�X��R1�� ´""&�+5ÿÿ�¤ÿì¸&�8��M�ÝR� ´&�+5ÿÿ�qÿì^f&�X��M-�� ´&�+5ÿÿ�¤ÿì7&�8��N�ªR� ´&�+5ÿÿ�qÿì^å&�X��Nõ�� ´&�+5ÿÿ�¤ÿìÓ&�8��P�R� µ&�+55ÿÿ�qÿì^&�X��PÚ�� µ&�+55ÿÿ�¤ÿìs&�8��S R� µ))&�+55ÿÿ�qÿì²!&�X��S=�� µ,,&�+55ÿÿ�¤þH¶&�8���Qw��ÿÿ�qþH^H&�X���Q¨��ÿÿ�ß��s&�:��KjR� ´$$&�+5ÿÿ�u��!&�Z��K}�� ´&&&�+5ÿÿ�¼��Ãs&�<��KÿæR� ´&�+5ÿÿÿ;þ!&�\��Kÿp��� ´&�+5ÿÿ�¼��Ã%&�<���jÿûR� µ&�+55ÿÿÿð��“s&�=���v�“R� ´&�+5ÿÿÿã��±!&�]���v÷�� ´&�+5ÿÿÿð��“1&�=��OTR� ´&�+5ÿÿÿã��}ß&�]��O�°��� ´&�+5ÿÿÿð��“s&�=��L�#R� ´&�+5ÿÿÿã��±!&�]��Lÿx��� ´&�+5�þüþd��@ FY ��FY��?+�?+10"'53276632&#"‰A:@1‚2(¤¥)q$+L=X^þè(¤þï#êe}ú˾­��þXË��,@FY GY�@�FY��?+�Ì9/+�33+10"'5327#?6632&#"3#E6=4‚2É¿ Í&«¢*r +L=X^!îíÍ'¡þï¹AB“êc›}ü7¼¯��ÿ��bª���%�,�/@ &&#)IY#))#�?3Ä9////+�399310#!#&54632%673#4&#"326!'ÏZ›¨7þ÷»·-}^dyþÛg|Õ+ÎHwº?32A;83?¡%U¶Š3 ˜v?ûÓþ-6Z_us»[.¡-þú6<<66==¨O¨þ¬º‘��bÿì`ª��� �3�A�7@*$-! !&)!;FY!-4FY-�?+�?+�??Ä2Ä2Ä9/9910673##"&546324&#"3262373#7##"&5462654&#"“vnÕ1Ö=uybb{}`byhB13@:91BÇ\( C酳ƋžŽú)aÀxp[h³f^²m‹3£$þåcrpcatsb6==65>>ðc]¬û¸Ñ寬ÐdÊü¹)•gz¬þÚ£rq�ÿÿÿ‰��Ýs&�ˆ���vTR� ´&�+5ÿÿ�bÿìX!&�¨���v…��� ´LL&�+5ÿÿ�wÿ¬¶s&�š���vR� ´33&�+5ÿÿ�=ÿ´P!&�º���v9�� ´11&�+5ÿÿ�'þ;#Ë&�6���9�ÿÿ�þ;D\&�V���9Ä��Ùö!�� ³ �/3Í210#&'#56673ög9h†lm‘wž%[*Ù0wF…'cˆB��ÓÙ9!�� ³ �/3Í2103673#&''Ód)O*{{jW§  "[+! d9mPM¬4_ŒB�–ÙÕf��±��/310!!´!ýàf�ÛÙå��´�€ �/3Ì210#"&5573327"¢„q‡bJ\©"劂wj;B}��Dß� �±�/Í104632#"&D@2XC+&6`5JZ7L1�'ÙÝ� ��@  / ?  �/3Ì]210#"&546324&#"326Ýxcev|_evh@31B;83@°ctsb^ura5>>56==��ÿoþH�¸���� ² ��/Ì2103327#"&54667¸RD(E')*DS_*P[AJM*F kSN5YO:�PÙ/Ý��¶ €� �/Ä2ÝÆ310".#"#3232673F(KGC ,3d:­,OG>,3dBÛ#+#9:$+$6?þþ��Ùu!� �� ³  �/3Í2106673#%6673#,‹/¬3ÄEVV/‹,¬3ÄEVô-µKAÀ23´FAÀ2��oÙ×u� �± �/Í106673#o.m¶ GhöTë@<ïT��Á´����@    �/3Ì99//310673#4632#"&%4632#"&šj3Ã2šIKÙ9-N=&#.“8.N;'#/ƒ»vE«C 1@P2C,(.CP1D,ÿÿÿ‹�� &�$���Tþÿ—ÿÿ�ªK¢Z���hÿÿ�G��á �&�(w��TýØÿ—ÿÿ�G��  �'�+�š���TýØÿ—ÿÿ�`��ú �'�,�ú���Týñÿ—ÿÿ�\ÿìÏ �&�2L��Týíÿ—ÿÿ�G��Ü �'�<���TýØÿ—ÿÿ�T��ó �'Týÿÿ—�vd�ÿÿ�hÿìд&†��UþÈ���¶..&�+555ÿÿÿ‹��¶�$��ÿÿ�V��²¶�%����V��j¶��·IY�?+�?10!#!!¬5ßý˶™ÿÿÿÉ��ð´(��ÿÿ�V��j¶�(��ÿÿÿð��“¶�=��ÿÿ�V��s¶�+����–ÿìƒÍ����&@IYIYIY�?+�?+�9/+10!7%# �$32�%"32654&!ýìw°þ¸×ÿ�þâÀOÒôýçø‰Ä¨˜ñŒ¼3••XþóþWé+´ìþÌœÊþ˜ÚÇßÃlÝÇßÿÿÿÙ���¶�,��ÿÿ�V��+¶�.���ÿ‹��´� �µ��?2?310#3#&'u!²»¨qHhþ´úL¦›½§¿ühÿÿ�T��¸¶�0��ÿÿ�T��¨¶�1���ÿå��o¶��� �&@�IY�� IY IY �?+�?+�9/+10!!!!!7ýL-!üÓ`!ü…H–™û{˜˜�ÿÿ�–ÿìƒÍ�2����V��y¶��@ IY�?+�?310!#!#!D®ýjþìª5îúá¶ÿÿ�V��‡¶�3���ÿã��¶� �(@� IY� IY��?+�?+�339910#77!!!þô^!ý\ þÝ!‹i9‰™ýÊý±˜�ÿÿ�º��´¶�7��ÿÿ�¼��ö�<����–ÿì´Ë���!�0@IY �!JY!!��??9/3+�39/3+�3107&&5�%73654&'#")1ÙëaM$±%×é©þÎÏ1Päù “°˜Øo¡’áíÏ"S ´´ êʾþÙœát ÚŸwב“«ÿÿÿ˜��Ѷ�;����Ç��D¶��!@JY �??339/3+�310###"&547333332673b'^°^ÒäT²X–¸°¸¹Ý+fµi5þÅÁþ?ÁÑÂ]r“þZoG†‚düœ¶Ëãþþ÷û�ÿð��Í�!�&@ �IY IY �?3+�33?+�310"!7!&4$32!!7654&dœíerýÙ\ƺEΩöÞÔký¦÷ùÃ5§þÔ¾ªþûrƒ˜ËKáuÉŠþÿ¬æþtŒ˜ƒ”‘ôÅÔ�ÿÿÿÙ��>%&�,���jÿUR� µ&�+55ÿÿ�¼��Ã%&�<���jÿûR� µ&�+55ÿÿ�bÿì‰u&~��T �� ´;;&�+5ÿÿ�?ÿìœu&‚��T»�� ´22&�+5ÿÿ�;þ)u&„��T �� ´##&�+5ÿÿ�hÿì±u&†��TþÚ��� ´&�+5ÿÿ�…ÿìJ´&’��Uè��¶00&�+555��bÿì‰\�#�1�*@��+FY�$$FY�?3+�3?+�?991023673327#"55##"&5462654&#"wc‘& 6.‡%XH"&N² Y©\ŠŸŒô!`¼up[f­f^\a]x2Lþèuþ¾D-#& y Á maÅ­ÑeÈü¸)–gz¦þݬrq�ÿÓþ��+�6@ $%%$FY%%�FY�FY�?+�?+�?9/+�9910>32�#"&'"32665!#732654&-R'|ÀЬÁ°®|‚þôÞV•R}…w“)½5–Pf›QþÕDH«µoþ0®Åh¸¢¢Ð(±†ßþô3?ý¶–»ü‹&2ZŸ_ާœcv���Tþ H��¶ �??3910#633673°*ŽªªPJV?²ý¹‹þæ<ý°Ó’¸§VûÌý��Jÿìj��+�#@�&& FY� FY�?+�?+�9/310&&54632&&#"#"&54�2654&'TaQÇ¡·©Ckz:ae>Ts„òŸ¸Û ™¿DNÚè´N”Q‹­sy>%ZU,EECr؇žþýÖ±Ø&ýêºeC2ó°“��?ÿìœZ�(�-@''FY'' !FY FY �?+�?+�9/+�910#"327#"&54675&&546632&&#"33p›Ÿrg“¹FÁ_©¼”—JOg¶r´ˆ>/Œ;w‚hZ}ðleTVZ&-”„¡sN_KHƒ"`YFM��bþ^3��@ # FY��?+�39?99104�##7!��#6654&'&&bP¢s•Í•²þ·þòXw–hN\°nJC{ŸzÔÜ^ ‘…þáþqªku3>uYGˆN`p6-?2C¨���;þ)\��@   �FY�?+�???910654&#"#336632oþGNY©!e¨ê‹ `³`“ûþ h(?LxÞžþ%HËze‹}Oeût���bÿì1+� ���&@FY�FY� FY��?+�?+�9/+10"&5�3 �'2!"!65¬­¶ÂE°þꪇÂJýÓ!\u…ÁK+ÏØ nþFùýšþÚ#=©Œž!þøþÙ€Š%��hÿìH��· �FY�?+�?10%27#"&5463}0[c&yœ§›u wv!kãý L0w�ÿÿ�9��!H�ú���ÿ‹ÿì!� �"@  FY FY�?+�?+�?910'&&#"'632327#"&'&5#ú HC70?H`w@w :""AZS +7Bþ—°5”m` …<xûçb } ci–™ ŽxýÿÿÿÕþjH�w����b��þH� �¶ � �??2310363�#b¨J ²íJ¦Kþãü±Hý¡£¢ÇNþ“þÝ���Rþ^é�0�4@-&//GY//&#(!%&&%FY&��?+�92?9/+�99910#"#6654&'&&54675&&5467##7!#"!37PŠá~\ôI_¶mKG€€ãËlm¶”.DY9^‡ì‹/\îY¦r^k3W­E‡S_l91C07§†­ð, …_tº$‘…R”_×ÿÿ�bÿðV�R����LÿìöH��"@ FY�FY�?+�?+�33?10%27#"&547!##77!#°,(F?[_‡þuͪÍÝ ÅÙÑ‹ !u…]Q=RüJ¶BP’ýc:)$��ÿÕþ-Z���"@ FYFY�?+�?+�?910#"&'#!2%"32654&-Œë•XG\¦ò_„½Æþsy—,Rn’k¨^uª»þ¬¯9E/nþGwÏÜQ§Æþƒn— ’“���bþRª\�!�@ � #FY�?+�?9910#6654&'&&54$32&&#"-]^…dM]´^ZDn™Œš£……/*pGn²l¶VsN$4yYF‰TS€?4=)9Ö™¾H°3"Œþü���bÿðÃH���@  FY FY�?+�?+�310#"&5�!!!"3254'üŠå—ÇÍF%öþö+"þË×â|x£±HX®þÛ•æÞ5_’O]kþùùœŸö½v��Lÿì˜H��@  FY�FY�?+�?+�310%27#"&547!77!!5[!e }…qþÉ Å{þ´u:uwvA‚BP’ýào>::���…ÿìJH��@ � FY��?+�?3?10"&54633263üµÂz¨n!udƒ¸a¨»«0Eýó”MxmÖ²Kû¤��bþúZ��#�(@FY �� FY��?+�33?+�3?10&&5476632#4#"66կаk’ì‡'­Œ þãÄb¦áGP……Äoã¹ÆF¦dŒþò¢þ僸¨À¬áþ¦½ þ+Óæh‚ý” œ�ÿþLJ��'@��FY� FY�?+�?+�??991023327#"&'#&&#"'6öem)š°ýÛC9;&)B?fr/þ9¾`< 5.3FJtˆþ Züéþ]F }~™sýv@áO?{��þf�� @�  FY �?3+�3??3?10663#&&5473þÑ©Ùe¬“þÏîd¦fºÇj¨jzz/úmÑ’Uþyþ+ìþ"Þů\yüþ�uHƒ —��bÿìwH�'�$@%� � FY"��?2+�3?39/910"&5473�32332654'3#"&'y„“ˆ~±þõ’Ÿ:A¦=C­³‘“«Ùþ}ÂaOXXÿÿ�hÿì«Ó&†���jþÂ��� µ%%&�+55ÿÿ�…ÿìJÓ&’���jù�� µ''&�+55ÿÿ�bÿðu&�R��T�� ´%%&�+5ÿÿ�…ÿìJu&’��Tó�� ´&�+5ÿÿ�bÿìwu&–��T�®��� ´11&�+5ÿÿ�V��j%&�(���j�BR� µ&�+55��ºÿìö��+@IYIY�IY��?+�?+�3?9/+10"'53267654&#!#!7!!! %FCYÃ×9•%ÁìPE�ÿÿ�'ÿì#Ë�6��ÿÿÿÙ���¶�,��ÿÿÿÙ��>%&�,���jÿUR� µ&�+55ÿÿþÁþ5¶�-���ÿ¾ÿ餶��!�0@ IY�IY!JY IY�?+�?+�?+�9/+10 #"'53266!3 !! 4&##åK°®€L143Si^‘˜šƒm¸þÖþìþ½Js‡ŽtþÜý›þÖ–XÐØ5ýþ›çúût@snýß��V��¾¶���)@  IY �  JY �?+�??39/3+�31033 !!!#3! 4&##sª…n¸þØþëþ¼ýÄ‘ª5ªƒ<®r{‹u¶ý’þ™æû°ýP¶ý’ýI@poýá��º��ö��#@ IY   IY �?+�3?39/+104&#!#!7!!!2#6ilþ霬þ¾!…þfZ¸­Z¬Z;ZPý——þ^œ›'qþR ]ÿÿ�V��1s&´���v�ÃR� ´&�+5ÿÿÿüÿìPb&½��6�qR� ´&�+5��Vþƒy¶� �@ � " IY �?3+�??3103!3!#!‹¬þ앬þËþZP¢RþX¶úâúJþƒ}ÿÿÿ‹��¶�$����Z��j¶� ��&@ IY  IYJY�?+�?+�9/+10 !!!! 4&##N¸þØþëþ‘5Û ýÑe-s}‰¸uFþ›æû¶™þ)ýK@roýßÿÿ�V��²¶�%��ÿÿ�V��j¶a���ÿVþ…î¶���$@�" IY  IY�?+�33?+�?310!#36�7!3#�!PüuPžqf‹¼'DþëºrúMþ§“=þ…{þ…ÀõÅ¢úäýë–ùýEÍÿÿ�V��j¶�(���ÿœ��ð¶��@�  �?33?33910333###Hþ“»h“¦“}Ïý^w¿þ–š¦šýwÑ�¶ýFºýFºý!ý)Ûý%Ûý%�ÿúÿì'Ë�'�-@JY %%JY% JY �?+�?+�9/+�910#"'532654!#732654&#"'6632'ɬšþ÷´á¦N×p¹Ïþ¾×È»Ù}i¨¸Hq׬ɇšÖ#¦‰‚ÎpO¦-;«ø¦’jbv{J?­��V��ž¶��@  ��?3?29910333#7##‹¤®Z¬ÇþÊ¡®<"üNǶüÏþu¼úJ9!yû-ÿÿ�V��žb&²��6�òR� ´&�+5��V��1¶� �@  �?3?3910!##33)Åþ4˜ª5ª—ÀÓýÕý+¶ý8Èý%�ÿ¾ÿé1¶��@�IY IY�?+�?+�?10 #"'53266!#åK°®€L143Si^‘˜ÉþɬþÜý›þÖ–XÐØ5úJÿÿ�T��¸¶�0��ÿÿ�V��s¶�+��ÿÿ�–ÿìƒÍ�2��ÿÿ�V��y¶n��ÿÿ�V��‡¶�3��ÿÿ�–ÿì Ë�&��ÿÿ�º��´¶�7���ÿüÿìP¶��@ � �IY��?+�?3910"&'532673673¢'`[Ij bþˬ²1=@•ÃýV|ë ¨'{ ý}¶kˆn®û°Ë¯�ÿÿ�–ÿì´Ës��ÿÿÿ˜��Ѷ�;����Tþ…X¶� �@ " IY�?+�3??310%#!3!3ér¢Pü/5¬þìw¬þìœýé{¶úäúæ���ú��'¶��@ IY ��??39/+�910!#"&547332673FÇÉœŸmªkRdKµ‡–¬þËXX…J^úþd%NO!.ÏúJ��T��®¶� �@ �IY��?+�3?331033!3!3T5¬þêªþëªþ˶úäúäúJ��Tþ…®¶��@ " IY�?+�33??3310%#!3!3!3?r¢PùÙ5¬þêªþëªþìœýé{¶úäúäúæ��º��‡¶� ��&@ IY  IY JY�?+�?+�9/+10 !!!7! 4&##ϸþØþëþþ¾!î… s}‰¬uFþ›æû—ýýK@roýß��V��T¶�� ��"@IY   JY �?+�??39/+10!#3!3 !! 4&##ª5ªûã…‹¹þÖþìþž5Hsˆ¬t¶ýþ›çú¶úÛ@snýß���V��ö¶� ��@IY JY�?+�?9/+103 !! 4&##7…‹¹þÖþìþž5Hsˆ¬t¶ýþ›çú¶úÛ@snýß��ÿìoË��&@IY IY�IY�?+�?+�9/+10%2�!7!74&#"'632�#"&'5wã-ý‘d¹¥?‚€+ǵö»þ¨ôd£Oj\cƒ˜\ºÑ)HþàþýþÕþLÝ%*¦0���Vÿì–Í��#�*@ IY  IYIY�?+�?+�??9/+10#"�47!#3!�!2�%"32654&–ªþÃÒüþðþº‘ª5ªƒ>Bt�ç ý÷–§“ä…±‹þóþXê+DGýP¶ý’(]þÏ™ÊþšÜÉݾoßÉÝ��ÿ´��˜¶� ��&@��JY JY�?+�?39/+�910#&54$!!##"33Rþ1ÏÀ}þ̬˜¶¼¹•Š•býžžeààóúJbÁ›”|„�ÿÿ�bÿì`\�D����dÿìš��(�$@ "FY�FY�?+�?9/+�910476$76632#"&26654&#"dé”JpZjþövþþoJÁg››ç›¼É”`‘VXX\ÅI¸ßØ+BŠ,%UþhXcîÉþÊžé^ö‰‚v{gsfþÅ���bÿìZ���'�-@!!FY!! ��FY� FY �?+�?+�9/+�9102#"&54$!2654&#"32654&¤¯¿þènahÉŠÑÛë|‚Œ˜É&á°¸fZŠŠÝ@r[`˜WæÞÆ:ªý/þÍi[QMð·®kbHP��7ÿìmZ�$� @ "FYFY�?+�?+�99106632327#"&54667>54#"Ët¬L—Ÿ˜º£d3bi‡²Ÿ°¥ºL‹”…g5š€± ,"€zj£G=?M7DOT–K’€Tc93>I/wH���bÿì5)��)�-@ #GY FYFY�?+�?+�9/+�910#"&54632375&#"'66322&&#"5¦þ겨½‰êe-‰‹pg+8Ÿ?ÖÔý¤‰×1ƒY[¥ai´ÿþ.÷Ѻ²B°S[ �ø/z$þÊû†/p~“üz†�ÿÿ�bÿì´\�H���ÿÝÿìç\�<�G@':GY  <(FY#/  FY4�?3+�3?3+�3??9/_^]3+�310##"'732654&#"5632333>32&#"327#"&547##¢!’Ô…C85/i«chc1?W2ª´¨dši¤™ÏzO>67h¤bbd9:S4¬²ªg—é˜å€‹š‘‹’‰×ÏLÞþ"“ìs‰–þ퓊’‹ÙÌ8 þ�ÿüÿìF\�&�-@%&&%FY&& FY FY�?+�?+�9/+�910 54&#"'6632#"'532654&##7{#VVB‚S5[¤c˜¦ƒº÷Ö´G®Qfk•!Ã?J &+)‡ym”1º¤ÀEž+-qbYM‘ÿÿ�qÿì^H�X��ÿÿ�qÿìl&�X��67�� ´&�+5��;��3H� �@  ��?3?39103##3^ÕýÛwËþœs¨è¦qHýãýÕ#ýÝHýï��ÿ¢ÿòP��@FY FY �?+�?+�?10!#&#"#"'5326676632#ªÏ''M`RSTtŒd/'@[W@Wz¦„~xº =—íðÇY‘ L«»¾W���=��FH��@   � �?33?3910!#&'#3673ɼeþy{nº’èºs"WœÇêq#ªý\¦qXü‘Hý+ŽAM”Ãû¸��;��‹H� �@ FY   �?3?39/+10!3#!#Ëab¨çªhýêi¨êHþ5Ëû¸îþH�ÿÿ�bÿðV�R��ÿÿ�;��)\�Q��ÿÿÿÕþ9Z�S��ÿÿ�bÿìª\�F��ÿÿ�;��‡\�P��ÿÿÿ;þH�\����bþð���#�+@� #FY� FY �?3+�3?3+�3??10##&&54$73>54&'j½É¡þá³fšd¹Æš»^œýC„n´ÀeŽ€½k€tXêÆ»þÊ®þ$ÚïÌ·-­¼û¯“­PŠéþ)…ñ‘‘¦ �ÿÿÿ¶��H�[����qþ…^H�!�!@ !"FY�?3+�3??3910326673327#&&5##"&547Ö“Xª‚"d¦¤N3q¬P>9œÙ‚’’HýIY2xàžÛüúN3NþkdeÝ>n¤���ž��3H��@ FY  �??39/+�910326773#67## 477–''PG”Ð0¨é¬A+‘Øþê6H¹º5SAïÜqû¸5‚ˆÀ7€þ���qÿì¼H�,�%@, ''FY�?3+�3??339910326673#7##"&'##"&547332667?“=LTŸy!d¨é‹ W­\qz TÃd‹ª”EJPœzkHýK\+ENxßÛû¸Ëwh‚sz{ˆ‚Dn ýKf+>KrØ‘ô���qþ…¼H�6�)@!$6 *"1$1FY$�?33+�33??3399103266733267#&&5##"&'##"&547332667B–=LTŸy!d¨£+#' q¬P?<™Éqz ©Ò‹ª”EJPžxkHýIY,GLxßÛüúQ0'' þkdeÝ‚sõˆ‚?s ýId+>KtÔ“ô��Tÿì;H���-@�FYFYFY�?+�?+�9/+�910632#"&547!7!"32654&}_©¦ÞǪ·bþ¦Gegadr„bh–‹©Ë ™_cÏ’ý¬j>a]vcTN���ÿì…H��� �)@ FY FY �?+�??39/+�910!#3!632#"&547"32654&œ¦ç¨ü>g}^©§àƪ¶ñdiads‚bHþ –‹©Ë¡˜_caý¬n:a]vcUM���ÿì¦H���$@� FY  FY �?+�?9/+�910632#"&5473"32654&Z‡m¨°mÉ„±ºªfkij|Œih‘m«\Ÿž[caý¬lM`B11D7 UF]dåÇžè¦]Œpy×¼N²Fnƒ[, …>pDe`Ÿ»û¸²j\¨n�ÿÿ�bÿì½Ó&�H���jÔ�� µ66&�+55��;þ!�*�<@$ &'&GY�''$) FY $)�$FY�?+�??9/+�9/3+�3910!!3632#"'5327654#"##7373^þ¦&  –à…Žª$œ‡E6;;|*ª–X©‚!\¨ ˜•'¨Z±€Tá’„>hü嫟Ïc$”wßžþLÙºÿÿ�7ÿìÄ!&Í���v �� ´..&�+5��bÿìª\��&@FY� FY�FY��?+�?+�9/+10"&54$32&#"!!3267úÂÖ˜£‰ƒ/xc}À2×!þ+ƒw9r[|ÖÃÇ\´33°¢”#A{“)?ÿÿ�ÿìD\�V��ÿÿ�;��ß�L��ÿÿ�;��ÁÓ&�ó���jþØ��� µ&�+55ÿÿþþþß�M���ÿ¢ÿì˜P�(�4�5@�&)FY&&FY&/FY�?+�33?+�9/+�910632#"&547&#"#"'532667>32"32654&j€^©§àƪ·`!¾vcTN���9ÿìþH��"�2@�FYFYFY�?+�??399//++�910632#"&547!#3!3"32654&Ó{`©§àƪ¶þn¤���V��¤á��@ IY�?+�?Æ103!#Á?¤býÕþë¬7¶+þ:úå¶��;��¢‡��@ GY�?+�?Æ10!#!3Bþnͨê‹F¬Ëü5H?�ÿÿ�ß��s&�:���C�R� ´&�+5ÿÿ�u��!&�Z���C�� ´!!&�+5ÿÿ�ß��s&�:���váR� ´''&�+5ÿÿ�u��!&�Z���v�ð��� ´))&�+5ÿÿ�ß��%&�:���jmR� µ11&�+55ÿÿ�u��Ó&�Z���j{�� µ33&�+55ÿÿ�¼��Ãs&�<���CÿzR� ´ &�+5ÿÿÿ;þ!&�\���Cÿ ��� ´&�+5��7Õ‘u��±��/3107!7#7"Õ  ���7Õhu��±��/3107!7#"Õ  �ÿÿ�7Õhu��ÿÿÿ1þ0²ÿÕ�'�Bÿíÿt��B�%���{Á¶�� ²��?Í10'63b¬“8ÁÓ þ§œ��}Á ¶�� ²�?Æ10#7(ŽX†E¶[ÿ…*Ë��ÿœþø+�î��±��/Í10%#7#q~NîëôØ��åÁ¬¶�� ²�?Í10#&'7œw=¶þïäàÿ���{Á{¶�� � ´� �?3Í210'63!'63ôPÀ¢)ýÑb¬“8Áµ*þ…zÓ þ§œ���}Á¶��� ´ �?3Æ210#7!#67(ŽX†E- ^´9u¶[ÿ…*ËÎþï~%R�ÿœþøœ�î�� � ³ ��/Í3210%#7!#7#q~N+ _°Œ?îëôØÍþî<º��Ù��Ë� �"@ � ��??9/333910%#73%¨þǦÏþÏ#%%ÎFãûþ!® £þ] ���N��é��?@! �      ��??99//3333339910%%#773%%F%þÅÏ…þ½%7gþ¼'7Ï…G&þÆç¬þ‡y¬+!® zþ† ®!þå��Çì‘é� �± �/Í104632#"&Ç“}\^”€Y]¬‘¬b\²cÿÿ�+ÿã5�ò�'����'���������¨ÿì“Ë����,�;�G�V�1@<<TT&22 9 BBM�?333?3??9/39/33310#"32>54#"&54632"32>54#"&54632"32654#"&54632¼ûØ>wBj@k4T?&‹?eVmze¬rtwùŒBj@k4T?&Œ@eŒWmze¬rtxíCi?jAhE‹?eVnze¬rtx¶úJ¶ýN~öy­O•ÉG¦—kþþ±c•‘¦1˜\~ùw¬O•ÉG¦˜lþý°b–‘¦1˜ýÉøt­‚ g¦—kþþ±c•‘¦1˜ÿÿ�á¦å¶� ��ÿÿ�á¦B¶�����X�q-¾��¶��//93310X‡Nþ׬qçB|Qþ²þƒ1´���sìÁ��¶��//93310'7ìþxM(¬qèðþƒRM}2þK�ÿÿÿæÿã5¶�&�»��� ���þ��ð¶�� ³�??10#ðû™=¶úJ¶���¨!Ç��@ � �?3?Í2910654#"#3632=VLO“=wehDVN!}D*T®yþ虚NP1iþ’���-��¨¶��2@LY NY�  LY �?+�?9/3+�39/+10!!##73!!!!šþå9¢7®®áÏ!ýÕs !ýú‰ƒþúƒ-™ýå˜��ÿé��šÉ�'�S@/"#"MY#&'&MY'''?'O' #'#'LY KY�?+�?+�399//_^]3+�33+�31076632&&#"!!!!!!76677#737#7f6(Ö°[´AB9œAiƒ/sþ%rþ\KÕ!üGny¾Á!Çsõ´­/'…!.q|Ý…¢…ƒ)št…¢…��7ÿ쨶��$�-�H@%$ NY$%"KY%%$-KY�MY�?+�?+�?9/+�9/+�339/10%27#"&547#?33#32�!##32654&##²6EIa`gPš¦dh-ÅÄT*üãÌÏÛþÈþÜ1{¦BÊÕ‰‰1w a[5ywNK·ÏþV-&,?¼¶÷þòýÁѶ¦w���?ÿì×É�&�K@) NYMY !!�LY! LY�?+�?+�99//_^]3+�33+�310"!!!!!27#"55#7367#73�32& ŒäKªþG }þ—){…—Ô骚—ŸaEÏYŽFPq1ÆÇ…Acƒþ‹7“; õ ƒPT… +1ŠN���Ëÿúª¾����2�%@-(#�?3?3??9/39/310##"&546632%"326654%"&546632&#"3267ªû¼›Gºžu‚S˜iv…þúYm=¥Yƒ üyBo63nHNbb¦Bo95pGNbd̓·âgt¬…´pqip«þ»[da]lTUhwZda^pQPl��'þ�� ³ ��/2/3102&#"#"'53265˜R(/?²ª¤F7<3^`‰ïúë¹À‹ytr��s}3��/�!@� '$*�/3/Í232Í322310"56323267#"&'&&"56323267#"&'&&\37b‘EtV@a273eH~HKZ046b‘AO@a273dH~HBbö@9—m%B7•o! A7—m "B7–n! ���¦��'@  �//99//33333310#5!!5!3!!!'{ü=þDþ…jþþÀÁýý¼Ž‹9ä‹þðŽþê5�����Ñ�� �%@����//39/3393310%5 5!üh˜ýìüh˜é¦cß–þŽþ¸þ����Ñ�� �%@��//39/339331055!îý˜üh˜Hp˜þ!cþZé��˜��fÃ�� �´ �/33/10#3 fþÂHþÇþÇ9áýßäý ýóýòÿÿÿþ¢�&�I����Lƒ��ÿÿÿþ°�&�I����Oƒ���\Ù5�� ³� �/3Í210!"&5473326759þœœ  ’®pqþÉzx %"Xb��þþþËH� �·�FY��?+�?10"'53273‡E6=:}+¦þö$þÍÛû«Ÿ�Í;� � ²��?Í106673#%^ŸŽ7\ç7Á57Ç5��œþ;Ïÿƒ� �± �/Í106673#œX ˆ9\þV0¯N;Â7�øÙ+!� �± �/Í10#56673+$\ Š=\6¿8.É<���“5üÍ� ��µ !�?3?310#"&54632"3254übªmt|c§kôúBk>7?j‚¨´þÖ•žž¤%“w‡æx`e,Ò¬��\Jß¼� ��!@   �??9/3333310##7!733#7É++þ“á…yþEZÍÊÊeCýÍBI$qö���‡9�¶��@ �� !�?3?39/39102#"'532654&#"'!!6Ép¾¦s\wdhoYJCC5{ªþÇA+jp”®4…HkZ?N-¤yß ���¦7Á��%�@ �  !�?3?39/39106632#"&54632&#"2654#"Je8`qP_s‚Õ”C>;Fp #bQaw/X3B-/~ha¡Zƒª8•sž•þ‹`1ODIU��®J-¶��¶� �?3?310!7!®Óþyþ-Júr^üò��ƒ9Ç��"�/�@ )) #� !�?3?29/399102#"&547&&54632654&">54&j}m^I<­Œu‹á=/ $dTP@H[EGJ*8D<$BÇpYQy /f@t’ve´U.U?j~þ&\<;@QD1Y•I6*B%,=)06��=úÇ��%�@   !�?3?39/3910#"&546632#"'53267"326654&Vf7`qP‘^s„Ó•C>=Sy¥#bOcw/X3Bá-/|ia¢ZƒªþÈ•{ž•ub1ODIU���TþÁî�� �����#�'�+�/�3�7�;�?�C�G�S�[�k�t�|�‰�@N*B)AF>E=&2%1 VKcuult\ZQ‚} �vk K}kl…\Q��Q\…lk}K -, 48!59�/333/3339/////////333333933Ä2Ä2Ä2Æ2Æ2Æ210!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3#"&546323254#"%32##32654&##32654#"'53253T/ÀÎ0mù�oÀÃmýIûáþò·mmmmûÂü0ooÀwú¨ooooþmmûŸ‡‡‡~ˆþs‡‡‡‡á¬mp.,;0m^Ï{B.$*/;J1%Z^4+V}i¾0oÁÁoþÐÁù/ÂmmÂþÑmmmmþooú¨ú;mm¦Jooooü/yýhI‘œœ‘’›š“ÅÅÄaCS1D D8QYb" "ãš+%Jþú fV’þr_c��TþÁª���*�@ (""��//9////310 54676654&#"63232654&#"þ¬üTüVë,AgI»¥OºGR Z?>1HT;GFBIHCHEüVüW©û/2A1R~X‡š8*²P:/5K6DpJ;þí?HI>@IHÿÿþþþÿ!&7��LþÆ��� ´&�+5ÿÿ�}Á ¶����/ÿì¾+�1�;�<@�%5%GY/55*FY**9FY*!FY�?+�?+�9/+�9/3+�310#"&54676654&#"76323267$46323365#"%¾þø¡–¥*9 NHJV$"¤p»‹ýóÞ­»ýn°ÃÀWbPþöþkÅ’†K…C&K" ƒ VGR‡N°°‘A˜BP’~~Êþª®cWºÒ…sþ²þæjk´—K:RT¡�ÿÿ�T��¸u&�0���vÁT� ´&�+5ÿÿ�;��‡!&�P���v˜��� ´66&�+5ÿÿÿ‹ýÛ¶&�$���[��ÿÿ�býÛ`\&�D���[/��ÿÿÿsÿìüÍ�&�2y��\þ@���ÿºýÛyÿƒ� �� ³ �/3Ì210#"&546324&#"326ydewvfdq?33@:93?þ°atrabsv_6==65>>�3^{Å���µ   �/ÌÄÆ310673#'467#"&+V@º`®Bø‘‚>A 4.07›š‚²eZ„V0 "2>��ÿþ-�=�A�L�J@&JE@@(?3$$FY-� (: ( GY7(FY��?3+�3?3+�333?3+�3?3Ä210"'53267!#"'53267#?6632&#"!76632&#"3##34632#"&!G6=6DVãþè'¢„E8@0FXãÁ Î.£ (t +L=W]â-¢¢)q$-L=Y\ïîç+¡O¨é¨y@3XC,&6þs|:û¶½®t{:CBdÈ¥allŦf|lû¶Ã¨ìH6IZ7L1��ÿþ;��A�A@"�7("(FY1"�>,,GY;, FY�?3+�3?3+�333?3+�3??10!#3"'53267!#"'53267#?6632&#"!76632&#"3#ð¨K¨úæG6=6DVãþè'¢„E8@0FXãÁ Î.£ (t +L=W]â-¢¢)q$-L=Y\ïîç+¡ø�s|:û¶½®t{:CBdÈ¥allŦf|lû¶Ã¨��–ÿì¦��%�%@ IY IY�?+�?+�Æ9310# �$326673"32654&ƒ°þ¸×ÿ�þâÀOÒ”ÜBQV»!¢-ýçø‰Ä¨˜ñŒ¼‹þóþWé+´ìqj!€Š«8uÊþ˜ÚÇßÃlÝÇß��bÿðHð��$�%@ ��FY�!FY�?+�?+�Æ931026673#"&5464&#"326ƒØnOX¸ !£Š#ö›ÀÚ’øƒ}km­_wh¦]V!ƒ…†°4Vi¼þ²¶âľO³þqs”þù¡ƒ’ ���¤ÿìå��@  IY �?+�?3Æ931063#"&5473!267+©(¸"Ý«‰7þæòç佪¿#©À.ͶÆ$�™Ê"ýwþöúÔÃVonüƒhDþö®Í¸��qÿì×ð� �#@  FY�?+�??3Æ9931032667363#7##"&546Ö“Xª‚"d¦«(¸"⨴‹ b²_‚–HýIY,•xàžÛ"›É!ü¬Ë}b…,j´�ÿÿý.Ùþv!��Cúï��ÿÿýæÙÿŒ!��vûÒ��ÿÿüµÙÿ”Ý�Rûe���ý¾¶ÿ�� ² �/Ì210#76654&#"5632éncj^^@4'82AowìR^o¹5.*d T�ü¬þ ýwÿ}� �± �/Í104632#"&ü¬@3.*C,&6þþ4K4&7L1�ÿÿ�V��js&�(���CÿûR� ´ &�+5ÿÿ�V��žs&²���C�fR� ´&�+5ÿÿ�bÿì´!&�H���CÿU��� ´$$&�+5ÿÿ�qÿì^!&�X���CŽ�� ´&�+5��–ÿì`É�8�/@,,�!!IY2((IY�?3+�3?3+�39/910"'632#"&'#"&54$32&&#"32677332654&ß6^4H„¡¯½u½è’rœ!1±tÈѱ ´¯hR3Z6|Ä~m€5®6 ²’ÜŽl/+ŽVðÛ·þfþÎ]SR^ýðþóÿXŠ"&åþ[À­µŠõÿ�E®÷¹Ë™˜���h��ßH��@  ��???33910333663�##섪F ?9¦C oœq:¨Kþòܪ5þïHýv§ôÍý²Àžtþ3þŠýûͬþT��–��{���1@IY IY @JY�?+�Í99//+�3+�310!!!7!3!!3232654&##{þ·þÊþáîþË"4=¬=r!þŽ>ÖÜý+uÔÜŽ“áê÷`œþèœþæ´þªœrc��\ÿìé'��#�8@ FY FY FY �?+�?3+�39/+�Æ910!63 #"&547#7373!"32654&þáI‚qXñɬ¾báá/¨-¸}oij{Žm¶þ²þߩ˜¡UiÏ’ßßý¬s5c[teWK���VÿìBË�%�1@IY!�IY! IY�?+�?+�??9/3+�310"�!!327#"�47!#3!6$32&ãÉþÔBoý Á®Š·Vœnöþòþ¼‘ª5ªƒB5Ø,¶ÃœH‡3þüç˜>U¿Û9•$ Z=ýP¶ý’Ã'™PE��;ÿì\�%�1@ FY  FY� FY��?+�?+�??9/3+�310"&547!#336$32&#"!!3267ÝÃÒþþi¨ê¦aþ;‡…/xc~¿2Ø!þ-†vNz<~ÕÄ45þHþ5âý33²¢’6|’(?�ÿ‹��q´� ��#@ IY �?33?9/+�3310!###3##7'&'Rž”{þp²Züªow^C†°ýP´úL°š›œh;}ç��ÿ¤��¶H� ��!@FY   �?3?339/+�310#####&'ú¼¨P\fžo\þ߬…JsHû¸òþòþHþ1³‹Å��V��Z¶���+@ IY   �?3?39/3+�33310!###!#3!3##7'&';•}þs´•þ‡¬5¬…°m‘úªnw\ F€°ýP°ýP¶ý’lúL°š—•xI‡Ô���;��=H���,@  FY �?3?3?9/3+�3310#######3!¼§L^gžm\þá®!üi¬êª`3 ‡1¼Hû¸îþîþîþHþ5Ëþ1Pþ°��ÿ®��{¶��!�4@ !JY �!IY�?+�?339/3+�3333310#>77!#&&####"!RÑPt–kþþ)g{>¬ \f˜ª–IcQ6×…¦ýw¼ª‹Iì‹‹þM•þ7É’hý=Ã,cqþ=\Ã�ÿ‘��¤H���4@  GY�FY�?+�?339/3+�3333310#>77!#&&###"!oÃ8d„]á þŒQc:1¨/Wcq›n [r<´)7þjjg< ^iiþ¢ @opþªTlTýìMqþ¬ƒ1���V��{¶�$�'�=@! '%   IY  � 'IY �?+�?3?9/3+�333310!667!#3!7!#&&+##"!®Õ"D+þ}‘ª5ªƒùþ)g{>ª\f˜ª–m}I×…¦ýwÅHx+ýP¶ý’ã‹‹þM•þ7ɉqý=Ãf˜þ;\Ã���;��FH� �#�?@" # GY! FY  � #FY �?+�?3?9/+�3+�333310!67!#3!7!#&&###"!3Å.4þöi¬êª`ä¢þ‹ws1§0Y`qœm \l>µ)8þmW*þHþ5biiþ¢€™þªVpNýìMsþ®ƒ1�ÿ¸þZ'Í�J�d@8?9IY??A00GIY)JY00AA5IYA" JY/ # JY�?+�33Ô_^]Ä+�?+�99//+�9+�9/+104!#732654&#"'67&''53>32&#"327632&#"&5467>þ¾×È»Ù}i¨¸H®Á'J$xTBPAF'&*%7>54##7‘#®D€P6‡w0_yAPZh6/'%"C?[aƒWcdÄ¿’^:;Z˜L0Z0\`š¨}Ep‹G˜rBÑ”É##>Y~HjtFq3Z{[m”xViŒY6,#) 'kYN^76SBš‘�ÿÿ�Ç��D¶u��ÿÿ�þf•����–ÿìƒÍ� ���&@IY IY IY�?+�?+�9/+10# �$32�2�7!"�!654&ƒ°þ¸×ÿ�þâÀOÒôý5º5üœÄZ¹þì5a¼‹þóþWé+´ìþÌûêü#RÇß²þìî,0Çß���bÿðV� ���&@FY FY FY�?+�?+�9/+10#"&54632267!"!74&ô™ÂÚ’ø—¿Ùýéx¸$ý¼úw·*>uÁÁþ«»ä¾O³ÙüþÈ©AƒN±›J{‡��¼��dÃ��@  �JY�JY��?++�??3102&#"#3767>î0F*<&>>Aýü¹¢ªa)b``pÉ+^…ûÓ¶üX¸“Ó&Ì‚8��`��!N��@ � GY �?3?+�?103367>32&#"#`ª@FVž=MaF)%'*=0þ}æHýŸË?d±½T„c,y;aüÉ�ÿÿ�¼��ds&€��v+R� µ""&�+55ÿÿ�`��!!&��v��� µ""&�+55ÿÿ�–þ ÙÍ�&�2����\Ç��ÿÿ�bþhV�&�R����\V����–ÿƒª1��.�9@% @--IY (# #IY �?+�33?+�33ÍÍ2310#"&'&54$7632%"&'6326'ªŸþØÄJ6;>²Æ£+½'p;>¸¿ýÕ3B€Èntu0^1AÄóæ-{ôþsñ>1K6+%ßìúlC82þÞg/-!Òþ¿ºžÕ(P1,0Ÿ!>^R���bÿ‘;¶��0�5@%/ @(##FY /FY �?+�3?3+�3Í32Í210#"&'&&546766322654&'#"&'6;jÆ„F328‘œpÍ„E638Œ’ýî"2 vŽFG(Q#6 N€IQM.°ªþÛÅ ;0;2ᮜ(Æ;-;3"Óý|%-'Ù`…A"™å†nF��–ÿì`3��!�U�C@!$>' .  H4.4IYN.A;';IY"'�?3+�3?3+�3Ä23Ä2Ô299Ä210#".#"#63237654&'&&5432"'#"54$32&&#"326732654&#"'632fQuc^9dƒ2à=kjnAþáør!\9B‘ ‘Nž`Àͱ ´¯hR3Z6|À|S¨:5›O£Ü‘la6^4H„¡¯½yÂîÁy$*$rë$*$Õ˜DJ,. GCøÉX'1�íþóÿXŠ"&åþ[À°²F9Š“…í{}C[+cƒ‘h>kimAþì{s'$1+;B;;ÖÅËX°#Œ0Šþ럅PP”$·Ó-‰$¸²ìþ˜°£x9ryp#+#×Hq"J.. #&E���–ÿì`ü� �F�E@") ::") @ )/)/IY)@6"6IY"�?3+�3?3+�3ÔÍ339/33910#'##'##'7"'632#"&'#"&54$32&&#"32677332654&Nw¿H¾J-Ñ6^4H„¡¯½u½è’rœ!1±tÈѱ ´¯hR3Z6|Ä~m€5®6 ²’ÜŽlü¬gggg¬þ3+ŽVðÛ·þfþÎ]SR^ýðþóÿXŠ"&åþ[À­µŠõÿ�E®÷¹Ë™˜��h��ߤ��&�*@$  #�???33Ö23Í22910#'##'##'733663�##ßw¾HÁH+´„ªF ?9¦C oœq:¨Kþòܪ5þï¤ ¬gggg¬ú\Hýv§ôÍý²Àžtþ3þŠýûͬþT���–þ Ë��@� IYIY�?+�?+�?10&5$32&#"327gàÿÓdáÅ—EŠ®þí¡Ç¥MCŒþÚ (÷ÁìPEÂþ‰Ý¾Úýr��bþª\��@� FYFY�?+�?+�?10"&54$32&#"327‘eÀÔ–�¦‰ƒ/xcq¸iƒwgXŽþØÓ¼Ë_·33›þ뢀|/ýd��uÿú… ��@  �?Í910%'%7%7%XHþã´´þæEÇþãH··Jþæ°¦{¤þÇJ;¤{¤Z¤}¤9IþĤ{¤�T‘N¶��´� �/Ì9/310#"&543!632Y(,‰°Z*,CHð_1w^'">=�wåhÑ��´ �/3Í23102>32#754&#"##7˜Q‡ys>dj{5)+p…—Q ^$+$_O$"%+%y�}Ùj/�� ²�/Ì3104632&}A>*,#xí¸9>("0.JC��˜Ù}/�� ² �/Ì3105654'&&5432}srp)"^9B´Hq"J,. HC��)þÁÁ‘� ��(�6�D�R�_�m�e@4 �>7ELHZS`gc")0,,,%3Vcc\j:HHAO3jOOj3  �/3/39///333333333Ä2Ä2Ä2Ä2Ä210&&#"#632&&#"#6632&&#"#6632!&&#"#6632&&#"#6632!&&#"#6632&&#"#632!&&#"#6632o�3@,/2('$ < 8�//99//Ä2Ä2ÄÆÄÄÄÄÆÄ10#677&''6673&&'7%'67'&'7%'766&&'57 F$a5sGÈAÝýR Ia4!‚&b'Zü›©E±x+REC{L%E±xbC¾ûWB¿OÝ#B¿OÝ® Ia5uGÈAÜ‚ú¸2Ã4bEÂ<ºY?DnXÞŒ·FÆcÓDnXb'Xý< F$a5��Vþƒžb��"�,@�"@� " IY �?+�???3Ö2Ì299103333##7##!"&54733267‹¤®Z¬Çþë¬úÈï›®<"üNÇÑ9þ››  ‘®qr¶üÏþu¼úäýé}9!yû-bþÉ{w %"[_��qþ…o��,�1@,$@) " FYFY�?+�?+�??3Ö2Ì29103266733##7##"&547!"&54733267Ö“Xª‚"d¦È£ã¨Ç‰ ]³c‚’V9þ››  ‘®qrHýIZ1xàžÛüIýô{Ëze‹>n¤ÈþÉ{w %"[_��V��ò¶���3@IY IY  JY �?+�?9/+�9/3+�310!!3 !!#73732654&##5'/ þÑ<³þ¹þÊþáîš!™)ntÓÞŽ”¶¶˜þÞþ›éøh˜¶úÝ©rc��ÿì¤��"�9@ GY� FY � FY �?+�?9/+�9/3+�3910!!63 # 47#7373"32654&ìþúy„pXóÈþš“–”5¨}ohk|ŒmƒýÌþߪÊ=[cµƒõûàn:c[udWK��V��‡¶���9@  JY JY�?+�?9/+�99//9933102'###327'7654&##ÕÖÜÿ�JRu‡‡{ª5…`BDJ‘‹’£¶½¼þÉ}›@ªýÁ¶ý”=ž\É}o��ÿÕþ9Z��)�?@ "%"%$$$ � FY� FY��?+�?+�??9999//993310"&'##33632'"327'7654!b”' m¦P‹³Á‰žœ‹JL=‰`ÇurhJHWgfXV9ýù4ÑãðÞþŠ`™@ ã¾þà—mv¤;—S,¨ã���N��b¶� �$@ IY  IY �?+�?9/3+�310!!!##73b ýÕmF!þº‡¬‰‘‘¶›þ–ýw‰–—��� ��^H� �$@ GY �FY�?+�?9/3+�3103#73!!!!;i˜—g9þoJþôié}âþ®}þ���Vþ�j¶��.@  �JY IYIY�?+�?+�?9/+�910"#!!632�#"'532654& \%‡¬5ßýËq?Jì¥þÕÁ˜q}w‘ှýy¶™ý÷ þãþæþž¾/œ5˜&¶¹Ì��;þ hH��.@ FYFY�FY��?+�?+�?9/+�910"&'53254&#"#!!632ND^+)[?¦ÌrK4c¨êCþdFS3l«]òþ ž$,ð þ)Hþ²nˆËþÅ®�ÿœþƒð¶��)@�  " IY�?+�??3?339103333####Hþ“»h“¦“}Ïý^%¸s¡O`þ–š¦šýwÑ�¶ýFºýFºý!ýÅýç}Ûý%Ûý%��ÿÝþ‡ç\�<�@@":GY<<3"(FY#/  FY4�?3+�3?3+�3???9/3+�310##"'732654&#"5632333>32&#"327#&&547##¢!’Ô…C85/i«chc1?W2ª´¨dši¤™ÏzO>67h¤bbdNBo¤JŸšªg—é˜å€‹š‘‹’‰×ÏLÞþ"“ìs‰–þ퓊’ýúg Û½8 þ�ÿÿÿúþH'Ë&±���d��ÿÿÿüþHF\&Ñ�������Tþ…/¶��"@  "�IY�?+�???3910%3###33ÁŸp¢R<þ4˜¬5¬—ÀÑý!œýé{Õý+¶ý8Èý%��;þ…#H��"@  �  "FY�?+�???391033###3^ÅýÛ°q PNþœs¨è¦oHýåþ`ýø{#ýÝHý÷��T��-¶��+@      �?3?399//99910!###3733 ºô+‰P}}¬5¬—‡Eˆ‡Óýf�Íy`ý´¶ý8‡?ý^���5��?F��(@  �?3?39999//9103#373%3##'Û¦æ¨o{E‚% Äýð\¿¼)JLFý÷uD¬üþlþÅXs���V��)¶��)@ IY  �?3?39/3+�39910!###73733#3 ºþÛ®}ªì”!“)ª'¿!¾P¼ÏýƒÑý°f˜¸¸˜þŠÆý…��9��!��*@  �GY�� � �?3??9/3+�39910373!!33###Ç•)¨)þþ=40Éþ+'»ë˜Rª ˜\¸¸ƒþÙâ¡þ-ý‹ {þoÙ��º��Ƕ� �!@   IY�?+�?3?910!##!7!3¾Äþ3—ªþº!ð˜ÁÓýÕý+—ý8Èý%���N��ËH� �!@  � FY �?+�?3?9103##!7!ÅýÙÁþšs¦Çþ¤oHýåýÓ#ýݶ’ý÷��Vþƒs¶��%@ IY   "�IY�?+�???39/+10%3##!#3!3^ q¡O¥ýl‘ª5ªƒ”…¨œýç}°ýP¶ý’n��;þ…‹H��%@ FY " FY �?+�???39/+10!33##!#Ëab¨Ê‘q¬P‹hýêi¨êHþ5ËüEýø{îþH��V��¾¶� �$@ IY   �IY �?+�?3?9/+10#!#3!!Tþé§ýl‘ª5ªƒ”…óúá°ýP¶ý’n—���;��çH� �$@ FY   FY�?+�?3?9/+10!!!#!#Ëabþ¤Éªhýêi¨êHþ5Ë’üJîþH��Vþ�N¶��0@ JY IYIY�?+�?+�?39/+�910!#!#!632�#"'532654&#"'®ý‡þìª5Ñ‘?Jî ¥þÔÁ—q}w‘ှ­\%úá¶ý^ þâýæþŸ¿/œ5˜&¶¹Ì���;þ þH�!�0@�FY  FY  FY �?+�?+�?39/+�910632#"'5326654&#"#!#!üS:m¬\€ñ¤qY*X>mª\qP5bªËþɨê=jmʈËþÄ­3ž$‡ú› þ+¶üJH��–ÿ¬yÍ�-�9�3@ %.++4JY IY IY#IY�?+�?+�Ä++�910327#"&'#"�$32&#"327&54632654&#"y¾¤.@CJ@WQ‹.9Z4ûþç¿VÔy}:]aŸþü޾¨*VuÉ€‹Žþ!‰ 9<¾<å{yÃþ½E óÓÀA©Žƒþü¦‰¢o›‡Ü|ŽzšþïQ‹ÿÿ�–þH Ë&�&���7��ÿÿ�bþHª\&�F���w����ºþƒ´¶� � @" IY�IY�?+�?+�3?10%3##!7!!šq P¦þj!Ùþhœýç}——��;þ…°\�9�2@&-#*$#"*FY1*GY�?+�?3+�3??3?9910%327#&547654#"#654&#"#33663236632%-3n¬O…\Qžwk¨”>KTŸy!e¨ê‹ W­\qz VÂc‹X º 'ýþk$Š0<ª].tÕ’þ ´^)FNxßþ%HËwh‚t}y‹‹)P-þl,ÿÿ�¼��ö�<����bþH� �¶  �??3910%673#3u3…3²ýŒg¨g”¨D Çyëû´þèLýénü��m��ö��%@�  IY  �??39/3+�39103!!#!7!73éÁý " þáJ¬Lþî îªËëüg#šþ `š+‘�ÿåþH��!@   GY �?3+�3??3910%673!!##733u3…3²ýŽ�þþL¨Löô’¨D Çyëû¸}þ‘o}Hýénü��ÿ˜þƒÑ¶�� @  "�IY�?+�???39910%3###33¤q¢R\ÓþºTþù¬Ë»ºýÕœýç}ý®ýÍ3ýJ��ÿòþ…?H�� @� " FY�?+�???39910#333##þ¤ÀÙJÂþ:¼–q¢R=²þN5þdœýåþ`ýø{���ºþ…=¶��'@ " IY  IY�?+�3?+�3??10%#!!7!!!3Íq¢Pü/þjß þdôu¬þì˜ýí{™™û}úâ���Zþ…øH�'�/@% "�$%$FY%FY�?3+�3?+�3??910326673327#&&5##"&547!7!;v”Y«!d¨° #-3n®Q?@\±aƒrþɶýÛU0•zá›ÛüÆ,& )ýþkdexeŽ„0v’’���øþ…'¶��)@ IY "IY�?+�??39/+�910###"&5473326733;¡O£Ù­§ uªqR`W®ˆ–¬þëœþ…{XX‘4TýäK(GH0Ïúæ��œþ…3H��)@FY " FY�?+�??39/+�9103267733##67##"&546–>“—Ð3¨Ë’q¬PS#©È… EHþß^)”íàoüEýø{ƒ„?Ç’‚&JE���ø��'¶��2@ � IY ��??39/+�9/9/33910!# 47333673F”bB‡>þ´ yªw WeJ‰G_˜–¬þËX:þÍ'1;3ýË.(KHZþ²5ÏúJ���œ��1H��4@ FY �??39/+�9/9/339103673#67##7"&546–>‡?=ÐS¨é¬A-mƒ75€’AHþÝZ+’#þìJrsû¸5„Ž-þøüˆŠ+\0��T��ƒ¶��@�IY�?3?9/+�91063 #654&#"#3¸Þ¥H w¬y T`Y¨Š˜¬5¬\Zþè3:ýÏ3.*II1ý3¶���;��ÓH��@ FY � �?3?9/+�910!654#"#336632Ù>“šÖ,¨ê¬U!Y±`ƒ’9#[,“øÛjHþl~2i\„±ÌwQJa¨è¦oHþݸÇþɬ3ž$!÷Ž¥þ/Hý÷��ÿ¾þ…1¶��$@"IY IYIY �??++�?+�?103##! #"'532661þêªöÉð¨þK°®€L143Si^‘˜¶úâýí{þÜý›þÖ–XÐØ5�ÿ¢þ…P��&@"FY�FYFY�?+�?+�?+�?10%3##&#"#"'5326676632B£å¦Ç¨Ï''M`RSTtŒd/'@[W@Wz¦„~x‘ýô{º =—íðÇY‘ L«»¾W��Vþ�s¶��"@ IY   �IY��?+�??39/+10"'5326!#3!3�Ë”t}‡Ôß8sýl‘ª5ªƒ”…¨þãDþÆþ�1¤5à 'ýP¶ý’nú·þ½þÖ���;þ ‹H��"@FY FY�?+�??39/+10!3#"'53267!#Ëab¨á?ßÅy`[|“/\ýêi¨êHþ5ËûÙþÜó1¢?¹ä³þH��Vþƒs¶��%@ IY   "�IY�?+�???39/+10%3##!#3!3^°÷Éï§ýl‘ª5ªƒ”…¨šýé}°ýP¶ý’n��;þ…‹H��%@ FY " FY �?+�???39/+10!33##!#Ëab¨È£ã¨Ç¨hýêi¨êHþ5ËüIýô{îþH��øþ…'¶��)@IY� "�IY��?+�??39/+�910!#3# 47332673NR q™_Ù­þ¹ yªw R`X°ƒ˜¬þËþ…¼X1;3ýË.(JI /ÏúJ���Ëþ…`H��)@FY " FY �?+�??39/+�910326773##3667##"&546Å>‘›Õ.¦ç‰RªlŒ/! “؇AHþß^)”òßkû¸þ…æ~F¸…*V5��Tþ…¸¶��%@  �"IY�?+�?33?3910!##33!3##7#¤¦+¾¢5ô• “ þð¬öÈïª~†ý3HûJü}¶ûL´úâýí{NwMúî���=þ‡FH��%@� " FY�?+�33??3910#&'#36733##…eþy{nº’èºs"WœÇˤâªÇ‘q#ªý\¦qXü‘Hý+ŽAM”ÃüGýøyÿÿÿÙ���¶�,��ÿÿÿ‹��´b&�$��6�R� ´&�+5ÿÿ�bÿì…&�D��6P�� ´##&�+5ÿÿÿ‹��+%&�$���j�BR� µ""&�+55ÿÿ�bÿì`Ó&�D���jý�� µ44&�+55ÿÿÿ‰��ݶ�ˆ��ÿÿ�bÿìX\�¨��ÿÿ�V��¨b&�(��6�sR� ´&�+5ÿÿ�bÿì,&�H��6÷�� ´%%&�+5��TÿìÍ��#�&@IY �IY IY �?+�?+�9/+10"56632��#"&54$!36554&2�7# îÝÒyÌzõÉþ–ÕÈàºÕi»þcÇ(DjþŸþª~5` 2&þÔþûýþNþÿ°¡ûã9ÆâûPòš§Ze��;ÿì\��"�&@FY��FY�FY�?+�?+�9/+102#"&54$!374&#"566267#"ºÏ–õ“™›O2!‚zKŒPd‘:h¶. åòI\ݾ¾þ§¾…vµÌPŠ.&‘."ü¿£wp5F�ÿÿ�Tÿì%&á���j�R� µ77&�+55ÿÿ�;ÿìÓ&â���jŒ�� µ66&�+55ÿÿÿœ��ð%&°���j9R� µ%%&�+55ÿÿÿÝÿìçÓ&Ð���j#��� µPP&�+55ÿÿÿúÿì'%&±���j�R� µ;;&�+55ÿÿÿüÿìcÓ&Ñ���jÿz��� µ::&�+55��ÿìZ¶��/@��JY��IY JY�?+�?+�39/+�310#"&'532654&##7!7!#ÁÔŒþô¶^»A³¶¸Ó’Ÿý» 3¾¦Úv-$¤h¯ts‰曋�ÿuþ‹H��/@��GY��FY FY�?+�?+�9/+�3310#"'532654&##7!7!¤É‰þþ«Å~“¹³Ò¬¨HçýåÝŠرŸõ‡FœXÓ¸’rü}�ÿÿ�V��ž¸&²��M�ðR� ´&�+5ÿÿ�qÿì^f&�X��M-�� ´&�+5ÿÿ�V��ž%&²���j�ÏR� µ""&�+55ÿÿ�qÿì^Ó&�X���j��� µ,,&�+55ÿÿ�–ÿìƒ%&�2���j�ÏR� µ//&�+55ÿÿ�bÿðÓ&�R���jè�� µ//&�+55ÿÿ�–ÿìƒÍ~��ÿÿ�bÿðV��ÿÿ�–ÿìƒ%&~���j�ÏR� µ33&�+55ÿÿ�bÿðÓ&���jè�� µ22&�+55ÿÿ�ÿìo%&Ç���jÿõR� µ22&�+55ÿÿ�ÿìBÓ&ç���jÿU��� µ//&�+55ÿÿÿüÿìP¸&½��M�\R� ´&�+5ÿÿÿ;þf&�\��M™�� ´&�+5ÿÿÿüÿìP%&½���j�DR� µ))&�+55ÿÿÿ;þÓ&�\���jˆ�� µ,,&�+55ÿÿÿüÿìPs&½��S�‡R� µ))&�+55ÿÿÿ;þ(!&�\��S³�� µ,,&�+55ÿÿ�ú��'%&Á���j�R� µ**&�+55ÿÿ�ž��3Ó&á���jÔ�� µ,,&�+55��Vþƒj¶� �@" IY IY�?+�?+�?10!3##jýËô›r¢P¤5¶™ûýç}¶��;þ…^H� �@"FY�FY��?+�?+�?103!!3#;ê9þo¬n®PHüÕýø{�ÿÿ�V��T%&Å���j'R� µ))&�+55ÿÿ�ÿì…Ó&å���j�…��� µ44&�+55��þyb¶��7@IYIYJY JY "�?+�?+�?+�9/3+�310!!!3#"'532677##73b ýÕmF!þºh‹=scC%.4(5 ª‰‘‘¶›þ–þ þÕzuŽ>3‡‰–—��ÿúþy^H��7@GYFYFY FY"�?+�?+�?+�9/3+�310%#"'532677##73!!!!=qbB&/3(6 ¦i˜—g9þoJþôL‹þÝpŽ>3‡é}âþ®}þ¢��ÿ˜þyѶ��'@�JY JY"�?+�?+�??39910%3#"'532677##33…‹=paB&02(6 ZÓþºTþù¬Ë»ºýÕ“þÕ~qŽ>3‡ý®ýÍ3ýJ��ÿ¶þyH��'@� FYFY"�?+�?+�??39910#333#"'532677#Óþ¦ÃÛ类JÂþ9½=pcB&02(6 J²þN5þdœýåþ^þÝ{tŽ>3‡��ÿ˜��Ѷ��)@�IY  �?3?39/93+�3910##!7!33!¼�´Óþºþì è¬Ë»ºþ !¼ýDý¼š`ýÍ3ý š�ÿ¶��H��)@  �GY�� �?3?39/3+�39910!33!!##!u�Óª®JÂþwþòã¨Àþ¦Ã¨þüuÓþdœþ-}þ²þNø��`��‘¶� ��@ JYJY�?+�?9/+104$!33! #"!3`8²‡ªþËþÁþCâ±µÌ!ž{ÕöpúJ´Ÿ’ðÿÿ�bÿìÃ�G����fÿìD¶��%�1@IY""IY�?3+�3?9/+�9/910#"'#"&54�!33327#"3267Dg(ɤÔG‚઻U$‹†¬êUO°/iý¶uÛájgo—PþÀºª¨¹¥í pû´ 'NRÙô¢µ¯[l{f��bÿìH�$�1�0@!�,FY%%FY �?3+�3?+�?9/991032673#"&'##"&5463236732654&#"ÁSVduB¤B+ij†žaÉpŠ›Œô™a“) L¦ôþ�]Åyohd«h+Tbz=þÁÕ¼€m~o°×`ÅdZŠŒbûŽHãµ$œnu£þتã��øÿìRË�-�:@ '',JY,JY,#IY,�?+�?+�9/+�9/99104654&##732654&#"'663232673# ò ›Ç˾Õup©¸GsÕ«ÉÆ®…lNXgo`¬f,À²þ¤'#U8ta¥‘dhv{K>®–™Ö$|ve\pŒÑþɱ��˜ÿìm\�*�8@ !'FY !!FY! FY �?+�?+�9/+�999/1032673# 74&##732654&#"'632é¦duB¦B+Ŷþ¿Vg¢kŠXVH‚H5°¡±ƒZ\dX—z=þÁÖ»ZTH‘c[DJ'T…{l–m��öþƒ3Ë�%�4@ JY%"JY"IY�?+�?+�?9/+�9910!#654!#732654&#"'6323#\¤LþèòÙ¬×raj²WGÎø¯ÈÁ¶‚€*œq¢dL+ѯ‘_e?7{‰®–˜Ð)ŽpfÁýç���˜þ‡\\�&�4@#FY"FYFY�?+�?+�?9/+�99103##66'4&##732654&#"'632î‘nªM"%^_¢kŠXVH‚H5°¡±ƒZah,™ýúy©P@‘c[DJ'T…{l–l��ÿ¾ÿé¼¶�$�'@ $$IY$IY�?3+�3?+�9/1032673#"&547! #"'53266Õªeq `¨h+Ư™±°þ°K°®€L143Si^‘˜¶üw9 oÑþ˳žA‡>þÜý›þÖ–XÐØ5�ÿ¢ÿìðP�.�)@**FY*" "FY�?+�33?+�9/10326673#"&547&#"#"'5326676632jªD[:B¨Hg¦zš¥u''M`RSTtŒd/'@[W@Wz¦„~x¨2im=þªœO–Ž>H$ =—íðÇY‘ L«»¾WýRH��Vÿì3¶��,@IY IY �?+�??39/+�9/1032673#"&547!#3!3‰¬gr`¨h-ð›³5ýl‘ª5ªƒ”…¬ß# tˆÑþ̲ž=XýP¶ý’nûâB���;ÿìmH��,@FYFY�?+�??39/+�9/10!#3!3326673#"&546býêi¨ê¦ab¨•ªD[:B¨Hg¦z›¥îþHþ5Ëý;H¨2im=þªœO˜ŒD���–ÿìNË��&@�IY�� IY IY�?+�?+�9/+10!#"�$32&&#"326!1+ þý½üþæÎeÝë½Bl¥U¨þ뻳¾Ö1þ“îyîþð‹'®òT˜/%Èþ›ÝÍÙÓ���bÿì\\��&@�FY�� FY FY�?+�?+�9/+10!#"&54$32&#"�3 !oí;þüäÔï *Äp¨J@Š˜ßþý•Œ"@þÁ?\þùðîÖÃ:¯&*…NþÓþ“¤?��ºÿìÕ¶��%@ ��IY IY�?+�?+�39/107!!32673#"&547º!Ù!þiÁ¬eq `¦f-ᛳº——üyH- oÑþ̲ž4as��Vÿì/H��%@ FY FY �?+�?+�39/10326673#"&547!7!!ªªD[:A¨Hg§x›¥uþÏ þÑw¨2im=þªN˜Œ>H ’’ýÍD���yÿì˜Ë�(�-@&JY JYJY�?+�?+�9/+�9104$32&&#"33#"3267#"&54675&&9�Ó‚½M[W‡Q†§‘ޏµÚÜ™Šn¶S¿×ÐêáÅk{-»ãHFvC3˜~tz ›v‚6&˜Q°±Ú"œÿÿ�?ÿìœZ‚���ÿ¾þy1¶� �+@ IY JYIY JY"�?+�?+�?+�?+103#"'53277#! #"'532661þè@o`F#,7MªþK°®€L143Si^‘˜¶úÝþÕnŽq‡þÜý›þÖ–XÐØ5��ÿ¢þyP�)�+@'FY'�FYFY FY"�?+�?+�?+�?+10%3#"'532677#&#"#"'5326676632?Ž>pbA(03(5 ¨Ï''M`RSTtŒd/'@[W@Wz¦„~x‹þÝ~qŽ>3‡º =—íðÇY‘ L«»¾Wÿÿÿ‹þ ¶&�$���g¬��ÿÿ�bþ `\&�D���g–��ÿÿÿ‹��ß&�$��fR� ´&�+5ÿÿ�bÿì`&�D��f¾��� ´&&&�+5ÿÿÿ‹��XÑ&�$��wîR� µ&�+55ÿÿ�bÿì&�D��w´��� µ,,&�+55ÿÿÿ‹��%Ñ&�$��xîR� µ&�+55ÿÿ�bÿì`&�D��x´��� µ,,&�+55ÿÿÿ‹�� /&�$��yîR� µ&�+55ÿÿ�bÿìÑÝ&�D��y´��� µ,,&�+55ÿÿÿ‹��Íb&�$��zîR� µ00&�+55ÿÿ�bÿì“&�D��z´��� µBB&�+55ÿÿÿ‹þ 8s&�$���'g¬��K�BR� ´&�+5ÿÿ�bþ `!&�D���'g–��K÷�� ´--&�+5ÿÿÿ‹��R&�$��{ðR� µ&�+55ÿÿ�bÿì`Á&�D��{º��� µ!!&�+55ÿÿÿ‹��P&�$��|ðR� µ&�+55ÿÿ�bÿì`Á&�D��|º��� µ))&�+55ÿÿÿ‹��PX&�$��}ðR� µ##&�+55ÿÿ�bÿì`&�D��}º��� µ55&�+55ÿÿÿ‹��»b&�$��~ðR� µ%%&�+55ÿÿ�bÿì…&�D��~º��� µ77&�+55ÿÿÿ‹þ N7&�$���'N�3Rg¬��� ´&�+5ÿÿ�bþ `å&�D���&N�g–��� ´$$&�+5ÿÿ�Vþ j¶&�(���g��ÿÿ�bþ ´\&�H���gu��ÿÿ�V��jß&�(��fðR� ´&�+5ÿÿ�bÿì´&�H��fƒ��� ´((&�+5ÿÿ�V��ƒ/&�(��R�TR� ´&�+5ÿÿ�bÿì Ý&�H��RÚ�� ´,,&�+5ÿÿ�V��VÑ&�(��wìR� µ&�+55ÿÿ�bÿìñ&�H��w‡��� µ..&�+55ÿÿ�V��jÑ&�(��xìR� µ&�+55ÿÿ�bÿì¾&�H��x‡��� µ..&�+55ÿÿ�V�� /&�(��yìR� µ&�+55ÿÿ�bÿì¤Ý&�H��y‡��� µ..&�+55ÿÿ�V��Ëb&�(��zìR� µ--&�+55ÿÿ�bÿìf&�H��z‡��� µDD&�+55ÿÿ�Vþ js&�(���'g��K�FR� ´&�+5ÿÿ�bþ Æ!&�H���'gu��KÐ�� ´//&�+5ÿÿÿÙ��)ß&�,��fR� ´&�+5ÿÿ�;��¦&�ó��f��� ´ &�+5ÿÿÿÙþ �¶&�,���g ��ÿÿÿúþ ß&�L���gN��ÿÿ�–þ ƒÍ&�2���g5��ÿÿ�bþ V&�R���g²��ÿÿ�–ÿìƒß&�2��fªR� ´!!&�+5ÿÿ�bÿð&�R��f¦��� ´!!&�+5ÿÿ�–ÿìãÑ&�2��wyR� µ''&�+55ÿÿ�bÿðý&�R��w“��� µ''&�+55ÿÿ�–ÿìƒÑ&�2��xuR� µ''&�+55ÿÿ�bÿð&�R��x“��� µ''&�+55ÿÿ�–ÿì’/&�2��yuR� µ''&�+55ÿÿ�bÿð°Ý&�R��y“��� µ''&�+55ÿÿ�–ÿìƒb&�2��zuR� µ==&�+55ÿÿ�bÿðr&�R��z“��� µ==&�+55ÿÿ�–þ ƒs&�2���'g5��K�ÍR� ´((&�+5ÿÿ�bþ !&�R���'g²��Kï��±-¸ÿh@ --��%((&�+5+5ÿÿ�–ÿì¦s&_���vR� ´//&�+5ÿÿ�bÿðH!&`���v9�� ´--&�+5ÿÿ�–ÿì¦s&_���C�hR� ´''&�+5ÿÿ�bÿðH!&`���C†�� ´%%&�+5ÿÿ�–ÿì¦ß&_��fªR� ´**&�+5ÿÿ�bÿðH&`��f¦��� ´**&�+5ÿÿ�–ÿì¦/&_��R�åR� ´//&�+5ÿÿ�bÿðHÝ&`��R�� ´--&�+5ÿÿ�–þ ¦&_���g5��ÿÿ�bþ Hð&`���g²��ÿÿ�¤þ ¶&�8���g��ÿÿ�qþ ^H&�X���gË��ÿÿ�¤ÿìß&�8��fmR� ´&�+5ÿÿ�qÿì^&�X��fÃ��� ´&�+5ÿÿ�¤ÿìås&a���vR� ´&&&�+5ÿÿ�qÿì×!&b���vT�� ´))&�+5ÿÿ�¤ÿìås&a���C�=R� ´&�+5ÿÿ�qÿì×!&b���CŽ�� ´!!&�+5ÿÿ�¤ÿìåß&a��fsR� ´!!&�+5ÿÿ�qÿì×&b��fË��� ´%%&�+5ÿÿ�¤ÿìå/&a��R�ÝR� ´&&&�+5ÿÿ�qÿì×Ý&b��R1�� ´))&�+5ÿÿ�¤þ å&a���g��ÿÿ�qþ ×ð&b���gË��ÿÿ�¼þ Ã¶&�<���gL��ÿÿÿ;þH&�\���gy��ÿÿ�¼��Ãß&�<��fªR� ´&�+5ÿÿÿ;þ&�\��f/��� ´&�+5ÿÿ�¼��Ã/&�<��R�R� ´&�+5ÿÿÿ;þÝ&�\��R—�� ´""&�+5ÿÿ�þ¼/&�Ó����B�Ã���üÙþ®!��� ³ �/3Í210#&&'53#&&'53ýPm;„ º(j^o;„ »,gÙ;¹?†©;¹?‘ž��üçÙ�j���@ ��� �/3Ì99//3103#&'#7667673#ýô®V/9a^xJ K—8.^+©+E`ö5•<9ml:@–/^>+u*��üVÙÿ7���@ ��� �/3Ì99//3103#&'#766&''3ýô®V/9a^xJ K—ÌCk‰8Aö5•<9ml:@–.=p]��üçÙ�Ý��"�@ ��� �/3Ì299//3103#&'#76676654&#"5632ýô®V/9a^xJ K—QED.(*BVXTHö5•<9ml:@– ^ (#N<9AE'��üéÙÿß��#�'@ ��  !�/3Ì9///333Ä10".#"#632326733#&'#76þø'JD?+5e:®*LD<&3 d@þW®W/9[b…ƒJ¹5%.5Û$';Û=3›:7qi?Ÿ�ý ÙÿbÁ� ��@ ���/2Ì9/Í210"&'33273673#þx“kWM”5p"¨’iI¬,¡ELÙŠvA6w}ƒ_n,z'�ý Ùÿ`Á���@  ���/3Ì9/Í210&'73"&'33273þ'rH‹1HVx“kWM”5p"¨Ým`bgþüŠvA6w}ƒ��ý Ùÿ`�� �@ �� �/3Ì29/Í21076654&#"5632"&'33273ýôEF.(%#:W\VH+x“kWM”5p"¨Ë` '(P=Ýþ¦ŠvA6w}ƒ�ÿTþH�¾���� ²� �/Ì2102654'3#"'7B9@)u"‚{A,$þ¸IEfT)p4qzk �ÿãþyy�‹� � ² ��/Í210%#"'53267yBqbA(7%-4;‹þÉolŽ3>�ÿãþyy�‹� � ² ��/Í210%#"'53267yBqbA(7%-4;‹þÉolŽ3>ÿÿ�ˆ��Þ¶��ÿY����bÿì q� ��@ KY &MY�?+�?+10#"&54632%"32654& ðŸ½Ï‹üžµÎþsh¨_zrg _qÃËþ¥±éÉÏK¹åW–þò£˜¨‰“��J��{\� �¶ �??9910!#667'3£| <W¸N¦‹?:ø9Gl)��ÿÕ��‹q��@ KY&LY�?+�3?+10!!7>54&#"'6632!%ü°®•y5WUJ—jRzÈn›¦BþêÑa…9ltg=JW?RodL˜…T‚À�ÿxþ“zr�(�-@KY && KY&& KY %�?+�?+�9/+�910#"'532654&##732654&#"'632zʳ|Ž|î¤Ò¯^ÒU¢´žƒ‹¥ÚtcPšbPÃå³Ä.œÙ §}…ËrO¤15ŸŠƒ‡®Œ\l6Bv®��ÿÇþ¨ß\� ��!@MY $�??39/33+�310%##!733!667'ÃÑN¢Lýy/ËËÑþ‹H@_9þþs{ÆüDNxãi‹@ý·�ÿÞþ“Ö]��-@��MY��LY KY%�?+�?+�9/+�9102#"'532654&#"'!!6ä¶×’þú±Á‰¤ª¿Ö“ƒ0bZJÅœ!ýöW$Ѳ¡ôyO¤fÀ¬~“ 9¬™þI�ÿÿ�oÿìDË��ê��ÿÿþ§Ê]��@ �$LY�?+�?310!7!�ý1{ýþ§!•‹úÕÿÿ�<ÿì2Í��Ü���þ“Ðr��(�-@ !MY!!KY& KY%�?+�?+�9/+�910#"'532##"&546632%"32>54&Ðp·þü¬ˆj†pÊú@ 3£c©¸ç´Çþ–¶mlL€Y)v±ÂþWþÙŒ"ž/,KWž™ÿåWâ±|„9jzZƒ™��ÿþ�=�:@(3$$FY-�: ( GY7(FY��?3+�3?3+�33?3+�3310"'53267!#"'53267#?6632&#"!76632&#"3#!G6=6DVãþè'¢„E8@0FXãÁ Î.£ (t +L=W]â-¢¢)q$-L=Y\ïîç+¡þs|:û¶½®t{:CBdÈ¥allŦf|lû¶Ã¨���RÝVÁ��1�%@,$�  )$�?3?3Ä2Í22109##33#7#%#"'53254&'&&54632&#"¶¼w²¹ÀªyÇþŽy~B^b4^iK~em^#PO5B7TsJå'nþGÑýÓ-ý/®yýÙÑfs!l(j,2%+\DYn%c#/+(3"0Yÿÿ�•þ´¶&�7����z?��ÿÿ�/þÛD&�W����z�Ù����5þb\� �+�.@ FY�FY!'FY!�?+�?+�?+�?9910%2654&#"%##"&54632373#"'532676Á]Ãyife°hú_´`ŒŸ‘÷—½X Cú-ðÒ¶Ž>´PŒŸ$w»#—ot©þ×£ãZylïÔhÂÀ¬ûtØÐFœ$2ˆŠ¦�ÿÿ�5þb!&‘��K�� ´22&�+5ÿÿ�5þbå&‘��N�� ´//&�+5ÿÿ�5þbß&‘��O#��� ´44&�+5ÿÿ�5þb!&‘��:{�� ´00&�+5��V��5¶�� ³��??1033V7¨þɶúJÿÿ�V��Qs&–���CþÊR� ´&�+5ÿÿ�V��Bs&–���vÿˆR� ´ &�+5ÿÿ�V��(s&–��Kÿ2R� ´ &�+5ÿÿ�V��)%&–���jÿ@R� µ&�+55ÿÿ�V��v/&–��RÿGR� ´ &�+5ÿÿ�V��¸&–��MÿIR� ´&�+5ÿÿ�V��A7&–��Nÿ&R� ´&�+5ÿÿÿµþH5¶&–���QF�ÿÿ�V��t1&–��O�fR� ´ &�+5ÿÿ�Vþd¶�&–����-/��ÿÿ�`��í �'–�¸���Týñÿ—ÿÿ�V��5¶–��ÿÿ�V��%&–���jÿ6R� µ&�+55ÿÿ�V��5¶–��ÿÿ�V��%&–���jÿ6R� µ&�+55ÿÿ�V��5¶–��ÿÿ�V��5¶–��ÿÿ�V�� ß&–��fôR� ´ &�+5ÿÿ�þ 5¶&–���gf���������¶2�I€��6$��$ÿq��7�)��9�)��:�)��<���Dÿ®��Fÿ…��Gÿ…��Hÿ…��JÿÃ��PÿÃ��QÿÃ��Rÿ…��SÿÃ��Tÿ…��UÿÃ��VÿÃ��XÿÃ��‚ÿq��ƒÿq��„ÿq��…ÿq��†ÿq��‡ÿq��Ÿ���¢ÿ…��£ÿ®��¤ÿ®��¥ÿ®��¦ÿ®��§ÿ®��¨ÿ®��©ÿ…��ªÿ…��«ÿ…��¬ÿ…��­ÿ…��´ÿ…��µÿ…��¶ÿ…��·ÿ…��¸ÿ…��ºÿ…��»ÿÃ��¼ÿÃ��½ÿÃ��¾ÿÃ��Âÿq��Ãÿ®��Äÿq��Åÿ®��Æÿq��Çÿ®��Éÿ…��Ëÿ…��Íÿ…��Ïÿ…��Ñÿ…��Óÿ…��Õÿ…��×ÿ…��Ùÿ…��Ûÿ…��Ýÿ…��ßÿÃ��áÿÃ��ãÿÃ��åÿÃ��úÿÃ�ÿÃ�ÿÃ� ÿÃ�ÿ…�ÿ…�ÿ…�ÿ…�ÿÃ�ÿÃ�ÿÃ�!ÿÃ�$�)�&�)�+ÿÃ�-ÿÃ�/ÿÃ�1ÿÃ�3ÿÃ�5ÿÃ�6�)�8��:��Cÿq�Dÿ®�Fÿ®�Hÿ…�JÿÃ�Vÿq�_ÿq�bÿq�iÿq�yÿ®�zÿ×�{ÿ×�~ÿ®�ÿÃ�‚ÿ×�ƒÿ×�„ÿ×�‡ÿ×�‰ÿ×�Œÿ®�ŽÿÃ�ÿ®�ÿ®�“ÿ®�™ÿ®�¤ÿ…�ªÿq�®ÿ…�µÿ…�Êÿ×�Îÿq�Ïÿ…�Õÿq�Øÿ…�Ûÿ…�Þÿ…�êÿ…�íÿ…�îÿÃ�òÿq�ú�)�ü�)�þ�)����WÿÃ�Xÿq�Yÿ®�`ÿ…�bÿÃ�jÿ…�rÿq�sÿq�}ÿì�ÿ…�…ÿ…�‡ÿ…�‰ÿ…�ÿ…�²ÿ…�´ÿ…�Îÿ…�Ïÿq�Ùÿq�Úÿ×�Ûÿq�Üÿ×�Ýÿq�Þÿ×�àÿ…�âÿ×�äÿ×�ðÿ…�òÿ…�ôÿ…� ÿq� ÿ…� ÿq� ÿ…�ÿ…�ÿq�ÿ…�ÿ…�ÿ…�ÿq�ÿq�ÿ®�ÿq� ÿ®�!ÿq�"ÿ®�#ÿq�%ÿq�&ÿ®�'ÿq�(ÿ®�)ÿq�*ÿ®�+ÿq�,ÿ®�-ÿq�.ÿ®�/ÿq�0ÿ®�1ÿq�2ÿ®�3ÿq�4ÿ®�6ÿ…�8ÿ…�:ÿ…�<ÿ…�@ÿ…�Bÿ…�Dÿ…�Jÿ…�Lÿ…�Nÿ…�Rÿ…�Tÿ…�Vÿ…�Xÿ…�Zÿ…�\ÿ…�^ÿ…�`ÿ…�bÿÃ�dÿÃ�fÿÃ�hÿÃ�jÿÃ�lÿÃ�nÿÃ�o��q��s���)� �$ÿq� �7�)� �9�)� �:�)� �<�� �Dÿ®� �Fÿ…� �Gÿ…� �Hÿ…� �JÿÃ� �PÿÃ� �QÿÃ� �Rÿ…� �SÿÃ� �Tÿ…� �UÿÃ� �VÿÃ� �XÿÃ� �‚ÿq� �ƒÿq� �„ÿq� �…ÿq� �†ÿq� �‡ÿq� �Ÿ�� �¢ÿ…� �£ÿ®� �¤ÿ®� �¥ÿ®� �¦ÿ®� �§ÿ®� �¨ÿ®� �©ÿ…� �ªÿ…� �«ÿ…� �¬ÿ…� �­ÿ…� �´ÿ…� �µÿ…� �¶ÿ…� �·ÿ…� �¸ÿ…� �ºÿ…� �»ÿÃ� �¼ÿÃ� �½ÿÃ� �¾ÿÃ� �Âÿq� �Ãÿ®� �Äÿq� �Åÿ®� �Æÿq� �Çÿ®� �Éÿ…� �Ëÿ…� �Íÿ…� �Ïÿ…� �Ñÿ…� �Óÿ…� �Õÿ…� �×ÿ…� �Ùÿ…� �Ûÿ…� �Ýÿ…� �ßÿÃ� �áÿÃ� �ãÿÃ� �åÿÃ� �úÿÃ� ÿÃ� ÿÃ�  ÿÃ� ÿ…� ÿ…� ÿ…� ÿ…� ÿÃ� ÿÃ� ÿÃ� !ÿÃ� $�)� &�)� +ÿÃ� -ÿÃ� /ÿÃ� 1ÿÃ� 3ÿÃ� 5ÿÃ� 6�)� 8�� :�� Cÿq� Dÿ®� Fÿ®� Hÿ…� JÿÃ� Vÿq� _ÿq� bÿq� iÿq� yÿ®� zÿ×� {ÿ×� ~ÿ®� ÿÃ� ‚ÿ×� ƒÿ×� „ÿ×� ‡ÿ×� ‰ÿ×� Œÿ®� ŽÿÃ� ÿ®� ÿ®� “ÿ®� ™ÿ®� ¤ÿ…� ªÿq� ®ÿ…� µÿ…� Êÿ×� Îÿq� Ïÿ…� Õÿq� Øÿ…� Ûÿ…� Þÿ…� êÿ…� íÿ…� îÿÃ� òÿq� ú�)� ü�)� þ�)� ��� WÿÃ� Xÿq� Yÿ®� `ÿ…� bÿÃ� jÿ…� rÿq� sÿq� }ÿì� ÿ…� …ÿ…� ‡ÿ…� ‰ÿ…� ÿ…� ²ÿ…� ´ÿ…� Îÿ…� Ïÿq� Ùÿq� Úÿ×� Ûÿq� Üÿ×� Ýÿq� Þÿ×� àÿ…� âÿ×� äÿ×� ðÿ…� òÿ…� ôÿ…�  ÿq�  ÿ…�  ÿq�  ÿ…� ÿ…� ÿq� ÿ…� ÿ…� ÿ…� ÿq� ÿq� ÿ®� ÿq�  ÿ®� !ÿq� "ÿ®� #ÿq� %ÿq� &ÿ®� 'ÿq� (ÿ®� )ÿq� *ÿ®� +ÿq� ,ÿ®� -ÿq� .ÿ®� /ÿq� 0ÿ®� 1ÿq� 2ÿ®� 3ÿq� 4ÿ®� 6ÿ…� 8ÿ…� :ÿ…� <ÿ…� @ÿ…� Bÿ…� Dÿ…� Jÿ…� Lÿ…� Nÿ…� Rÿ…� Tÿ…� Vÿ…� Xÿ…� Zÿ…� \ÿ…� ^ÿ…� `ÿ…� bÿÃ� dÿÃ� fÿÃ� hÿÃ� jÿÃ� lÿÃ� nÿÃ� o�� q�� s�� �)� �-�¸��&ÿš��*ÿš��2ÿš��4ÿš��7ÿq��8ÿ×��9ÿ…��:ÿ…��<ÿ…��‰ÿš��”ÿš��•ÿš��–ÿš��—ÿš��˜ÿš��šÿš��›ÿ×��œÿ×��ÿ×��žÿ×��Ÿÿ…��Èÿš��Êÿš��Ìÿš��Îÿš��Þÿš��àÿš��âÿš��äÿš�ÿš�ÿš�ÿš�ÿš�$ÿq�&ÿq�*ÿ×�,ÿ×�.ÿ×�0ÿ×�2ÿ×�4ÿ×�6ÿ…�8ÿ…�:ÿ…�Gÿš�fÿ®�mÿ®�qÿq�rÿ…�sÿš�uÿ…�xÿ…�…ÿ×�ÿq�Ÿÿš�¦ÿq�¸ÿš�»ÿš�¼ÿq�¾ÿ®�Áÿ\�Äÿq�Üÿš�áÿ…�äÿš�úÿ…�üÿ…�þÿ…��ÿ…�Tÿ…�_ÿš�aÿ×�lÿš�|ÿ\�~ÿš�€ÿ…�‚ÿ…�„ÿš�†ÿš�ˆÿš�Šÿš�Œÿš�©ÿq�ªÿš�±ÿš�³ÿš�µÿq�¶ÿš�·ÿ…�¹ÿ…�½ÿq�¾ÿš�¿ÿ\�Àÿ…�Áÿ\�Âÿ…�Åÿ…�Çÿ…�Ôÿ\�Õÿ…�ïÿš�ñÿš�óÿš�ýÿ\�þÿ…� ÿ…�ÿš�ÿ…�ÿš�ÿš�ÿq�ÿš�Iÿš�Kÿš�Mÿš�Oÿš�Qÿš�Sÿš�Uÿš�Wÿš�Yÿš�[ÿš�]ÿš�_ÿš�aÿ×�cÿ×�eÿ×�gÿ×�iÿ×�kÿ×�mÿ×�oÿ…�qÿ…�sÿ…�ÿq��7ÿ®�$ÿ®�&ÿ®�qÿ®�ÿ®�¦ÿ®�¼ÿ®�Äÿ®�Üÿ×�äÿ×�©ÿ®�ªÿ×�µÿ®�¶ÿ×�½ÿ®�¾ÿ×�ÿ®�ÿ×�ÿ®��&ÿš��*ÿš��2ÿš��4ÿš��7ÿq��8ÿ×��9ÿ…��:ÿ…��<ÿ…��‰ÿš��”ÿš��•ÿš��–ÿš��—ÿš��˜ÿš��šÿš��›ÿ×��œÿ×��ÿ×��žÿ×��Ÿÿ…��Èÿš��Êÿš��Ìÿš��Îÿš��Þÿš��àÿš��âÿš��äÿš�ÿš�ÿš�ÿš�ÿš�$ÿq�&ÿq�*ÿ×�,ÿ×�.ÿ×�0ÿ×�2ÿ×�4ÿ×�6ÿ…�8ÿ…�:ÿ…�Gÿš�fÿ®�mÿ®�qÿq�rÿ…�sÿš�uÿ…�xÿ…�…ÿ×�ÿq�Ÿÿš�¦ÿq�¸ÿš�»ÿš�¼ÿq�¾ÿ®�Áÿ\�Äÿq�Üÿš�áÿ…�äÿš�úÿ…�üÿ…�þÿ…��ÿ…�Tÿ…�_ÿš�aÿ×�lÿš�|ÿ\�~ÿš�€ÿ…�‚ÿ…�„ÿš�†ÿš�ˆÿš�Šÿš�Œÿš�©ÿq�ªÿš�±ÿš�³ÿš�µÿq�¶ÿš�·ÿ…�¹ÿ…�½ÿq�¾ÿš�¿ÿ\�Àÿ…�Áÿ\�Âÿ…�Åÿ…�Çÿ…�Ôÿ\�Õÿ…�ïÿš�ñÿš�óÿš�ýÿ\�þÿ…� ÿ…�ÿš�ÿ…�ÿš�ÿš�ÿq�ÿš�Iÿš�Kÿš�Mÿš�Oÿš�Qÿš�Sÿš�Uÿš�Wÿš�Yÿš�[ÿš�]ÿš�_ÿš�aÿ×�cÿ×�eÿ×�gÿ×�iÿ×�kÿ×�mÿ×�oÿ…�qÿ…�sÿ…�ÿq�$�ÿq�$� ÿq�$�&ÿ×�$�*ÿ×�$�- �$�2ÿ×�$�4ÿ×�$�7ÿq�$�9ÿ®�$�:ÿ®�$�<ÿ…�$�‰ÿ×�$�”ÿ×�$�•ÿ×�$�–ÿ×�$�—ÿ×�$�˜ÿ×�$�šÿ×�$�Ÿÿ…�$�Èÿ×�$�Êÿ×�$�Ìÿ×�$�Îÿ×�$�Þÿ×�$�àÿ×�$�âÿ×�$�äÿ×�$ÿ×�$ÿ×�$ÿ×�$ÿ×�$$ÿq�$&ÿq�$6ÿ®�$8ÿ…�$:ÿ…�$Gÿ×�$úÿ®�$üÿ®�$þÿ®�$�ÿ…�$ÿq�$ ÿq�$_ÿ×�$Iÿ×�$Kÿ×�$Mÿ×�$Oÿ×�$Qÿ×�$Sÿ×�$Uÿ×�$Wÿ×�$Yÿ×�$[ÿ×�$]ÿ×�$_ÿ×�$oÿ…�$qÿ…�$sÿ…�$ÿq�%�ÿ®�%�ÿ®�%�$ÿ×�%�7ÿÃ�%�9ÿì�%�:ÿì�%�;ÿ×�%�<ÿì�%�=ÿì�%�‚ÿ×�%�ƒÿ×�%�„ÿ×�%�…ÿ×�%�†ÿ×�%�‡ÿ×�%�Ÿÿì�%�Âÿ×�%�Äÿ×�%�Æÿ×�%$ÿÃ�%&ÿÃ�%6ÿì�%8ÿì�%:ÿì�%;ÿì�%=ÿì�%?ÿì�%Cÿ×�% ÿì�%úÿì�%üÿì�%þÿì�%�ÿì�%ÿ®�% ÿ®�%Xÿ×�%ÿ×�%ÿ×�%!ÿ×�%#ÿ×�%%ÿ×�%'ÿ×�%)ÿ×�%+ÿ×�%-ÿ×�%/ÿ×�%1ÿ×�%3ÿ×�%oÿì�%qÿì�%sÿì�%ÿÃ�&�&ÿ×�&�*ÿ×�&�2ÿ×�&�4ÿ×�&�‰ÿ×�&�”ÿ×�&�•ÿ×�&�–ÿ×�&�—ÿ×�&�˜ÿ×�&�šÿ×�&�Èÿ×�&�Êÿ×�&�Ìÿ×�&�Îÿ×�&�Þÿ×�&�àÿ×�&�âÿ×�&�äÿ×�&ÿ×�&ÿ×�&ÿ×�&ÿ×�&Gÿ×�&_ÿ×�&Iÿ×�&Kÿ×�&Mÿ×�&Oÿ×�&Qÿ×�&Sÿ×�&Uÿ×�&Wÿ×�&Yÿ×�&[ÿ×�&]ÿ×�&_ÿ×�'�ÿ®�'�ÿ®�'�$ÿ×�'�7ÿÃ�'�9ÿì�'�:ÿì�'�;ÿ×�'�<ÿì�'�=ÿì�'�‚ÿ×�'�ƒÿ×�'�„ÿ×�'�…ÿ×�'�†ÿ×�'�‡ÿ×�'�Ÿÿì�'�Âÿ×�'�Äÿ×�'�Æÿ×�'$ÿÃ�'&ÿÃ�'6ÿì�'8ÿì�':ÿì�';ÿì�'=ÿì�'?ÿì�'Cÿ×�' ÿì�'úÿì�'üÿì�'þÿì�'�ÿì�'ÿ®�' ÿ®�'Xÿ×�'ÿ×�'ÿ×�'!ÿ×�'#ÿ×�'%ÿ×�''ÿ×�')ÿ×�'+ÿ×�'-ÿ×�'/ÿ×�'1ÿ×�'3ÿ×�'oÿì�'qÿì�'sÿì�'ÿÃ�(�-�{�)�ÿ…�)�ÿ…�)�"�)�)�$ÿ×�)�‚ÿ×�)�ƒÿ×�)�„ÿ×�)�…ÿ×�)�†ÿ×�)�‡ÿ×�)�Âÿ×�)�Äÿ×�)�Æÿ×�)Cÿ×�)ÿ…�) ÿ…�)Xÿ×�)ÿ×�)ÿ×�)!ÿ×�)#ÿ×�)%ÿ×�)'ÿ×�))ÿ×�)+ÿ×�)-ÿ×�)/ÿ×�)1ÿ×�)3ÿ×�.�&ÿ×�.�*ÿ×�.�2ÿ×�.�4ÿ×�.�‰ÿ×�.�”ÿ×�.�•ÿ×�.�–ÿ×�.�—ÿ×�.�˜ÿ×�.�šÿ×�.�Èÿ×�.�Êÿ×�.�Ìÿ×�.�Îÿ×�.�Þÿ×�.�àÿ×�.�âÿ×�.�äÿ×�.ÿ×�.ÿ×�.ÿ×�.ÿ×�.Gÿ×�._ÿ×�.Iÿ×�.Kÿ×�.Mÿ×�.Oÿ×�.Qÿ×�.Sÿ×�.Uÿ×�.Wÿ×�.Yÿ×�.[ÿ×�.]ÿ×�._ÿ×�/�ÿ\�/� ÿ\�/�&ÿ×�/�*ÿ×�/�2ÿ×�/�4ÿ×�/�7ÿ×�/�8ÿì�/�9ÿ×�/�:ÿ×�/�<ÿÃ�/�‰ÿ×�/�”ÿ×�/�•ÿ×�/�–ÿ×�/�—ÿ×�/�˜ÿ×�/�šÿ×�/�›ÿì�/�œÿì�/�ÿì�/�žÿì�/�ŸÿÃ�/�Èÿ×�/�Êÿ×�/�Ìÿ×�/�Îÿ×�/�Þÿ×�/�àÿ×�/�âÿ×�/�äÿ×�/ÿ×�/ÿ×�/ÿ×�/ÿ×�/$ÿ×�/&ÿ×�/*ÿì�/,ÿì�/.ÿì�/0ÿì�/2ÿì�/4ÿì�/6ÿ×�/8ÿÃ�/:ÿÃ�/Gÿ×�/úÿ×�/üÿ×�/þÿ×�/�ÿÃ�/ÿ\�/ ÿ\�/_ÿ×�/aÿì�/Iÿ×�/Kÿ×�/Mÿ×�/Oÿ×�/Qÿ×�/Sÿ×�/Uÿ×�/Wÿ×�/Yÿ×�/[ÿ×�/]ÿ×�/_ÿ×�/aÿì�/cÿì�/eÿì�/gÿì�/iÿì�/kÿì�/mÿì�/oÿÃ�/qÿÃ�/sÿÃ�/ÿ×�2�ÿ®�2�ÿ®�2�$ÿ×�2�7ÿÃ�2�9ÿì�2�:ÿì�2�;ÿ×�2�<ÿì�2�=ÿì�2�‚ÿ×�2�ƒÿ×�2�„ÿ×�2�…ÿ×�2�†ÿ×�2�‡ÿ×�2�Ÿÿì�2�Âÿ×�2�Äÿ×�2�Æÿ×�2$ÿÃ�2&ÿÃ�26ÿì�28ÿì�2:ÿì�2;ÿì�2=ÿì�2?ÿì�2Cÿ×�2 ÿì�2úÿì�2üÿì�2þÿì�2�ÿì�2ÿ®�2 ÿ®�2Xÿ×�2ÿ×�2ÿ×�2!ÿ×�2#ÿ×�2%ÿ×�2'ÿ×�2)ÿ×�2+ÿ×�2-ÿ×�2/ÿ×�21ÿ×�23ÿ×�2oÿì�2qÿì�2sÿì�2ÿÃ�3�þö�3�þö�3�$ÿš�3�;ÿ×�3�=ÿì�3�‚ÿš�3�ƒÿš�3�„ÿš�3�…ÿš�3�†ÿš�3�‡ÿš�3�Âÿš�3�Äÿš�3�Æÿš�3;ÿì�3=ÿì�3?ÿì�3Cÿš�3þö�3 þö�3Xÿš�3ÿš�3ÿš�3!ÿš�3#ÿš�3%ÿš�3'ÿš�3)ÿš�3+ÿš�3-ÿš�3/ÿš�31ÿš�33ÿš�4�ÿ®�4�ÿ®�4�$ÿ×�4�7ÿÃ�4�9ÿì�4�:ÿì�4�;ÿ×�4�<ÿì�4�=ÿì�4�‚ÿ×�4�ƒÿ×�4�„ÿ×�4�…ÿ×�4�†ÿ×�4�‡ÿ×�4�Ÿÿì�4�Âÿ×�4�Äÿ×�4�Æÿ×�4$ÿÃ�4&ÿÃ�46ÿì�48ÿì�4:ÿì�4;ÿì�4=ÿì�4?ÿì�4Cÿ×�4 ÿì�4úÿì�4üÿì�4þÿì�4�ÿì�4ÿ®�4 ÿ®�4Xÿ×�4ÿ×�4ÿ×�4!ÿ×�4#ÿ×�4%ÿ×�4'ÿ×�4)ÿ×�4+ÿ×�4-ÿ×�4/ÿ×�41ÿ×�43ÿ×�4oÿì�4qÿì�4sÿì�4ÿÃ�7�ÿ…�7�ÿ®�7�ÿ…�7�"�)�7�$ÿq�7�&ÿ×�7�*ÿ×�7�2ÿ×�7�4ÿ×�7�7�)�7�Dÿ\�7�Fÿq�7�Gÿq�7�Hÿq�7�Jÿq�7�Pÿš�7�Qÿš�7�Rÿq�7�Sÿš�7�Tÿq�7�Uÿš�7�Vÿ…�7�Xÿš�7�Yÿ×�7�Zÿ×�7�[ÿ×�7�\ÿ×�7�]ÿ®�7�‚ÿq�7�ƒÿq�7�„ÿq�7�…ÿq�7�†ÿq�7�‡ÿq�7�‰ÿ×�7�”ÿ×�7�•ÿ×�7�–ÿ×�7�—ÿ×�7�˜ÿ×�7�šÿ×�7�¢ÿq�7�£ÿ\�7�¤ÿ\�7�¥ÿ\�7�¦ÿ\�7�§ÿ\�7�¨ÿ\�7�©ÿq�7�ªÿq�7�«ÿq�7�¬ÿq�7�­ÿq�7�´ÿq�7�µÿq�7�¶ÿq�7�·ÿq�7�¸ÿq�7�ºÿq�7�»ÿš�7�¼ÿš�7�½ÿš�7�¾ÿš�7�¿ÿ×�7�Âÿq�7�Ãÿ\�7�Äÿq�7�Åÿ\�7�Æÿq�7�Çÿ\�7�Èÿ×�7�Éÿq�7�Êÿ×�7�Ëÿq�7�Ìÿ×�7�Íÿq�7�Îÿ×�7�Ïÿq�7�Ñÿq�7�Óÿq�7�Õÿq�7�×ÿq�7�Ùÿq�7�Ûÿq�7�Ýÿq�7�Þÿ×�7�ßÿq�7�àÿ×�7�áÿq�7�âÿ×�7�ãÿq�7�äÿ×�7�åÿq�7�úÿš�7ÿš�7ÿš�7 ÿš�7ÿ×�7ÿq�7ÿ×�7ÿq�7ÿ×�7ÿq�7ÿ×�7ÿq�7ÿš�7ÿš�7ÿ…�7!ÿ…�7$�)�7&�)�7+ÿš�7-ÿš�7/ÿš�71ÿš�73ÿš�75ÿš�77ÿ×�7<ÿ®�7>ÿ®�7@ÿ®�7Cÿq�7Dÿ\�7Fÿ\�7Gÿ×�7Hÿq�7Jÿ…�7ûÿ×�7ýÿ×�7ÿ®�7ÿ®�7ÿ®�7ÿ…�7 ÿ…�7Wÿš�7Xÿq�7Yÿ\�7_ÿ×�7`ÿq�7bÿš�7ÿq�7ÿ\�7ÿq�7 ÿ\�7!ÿq�7"ÿ\�7#ÿq�7%ÿq�7&ÿ\�7'ÿq�7(ÿ\�7)ÿq�7*ÿ\�7+ÿq�7,ÿ\�7-ÿq�7.ÿ\�7/ÿq�70ÿ\�71ÿq�72ÿ\�73ÿq�74ÿ\�76ÿq�78ÿq�7:ÿq�7<ÿq�7@ÿq�7Bÿq�7Dÿq�7Iÿ×�7Jÿq�7Kÿ×�7Lÿq�7Mÿ×�7Nÿq�7Oÿ×�7Qÿ×�7Rÿq�7Sÿ×�7Tÿq�7Uÿ×�7Vÿq�7Wÿ×�7Xÿq�7Yÿ×�7Zÿq�7[ÿ×�7\ÿq�7]ÿ×�7^ÿq�7_ÿ×�7`ÿq�7bÿš�7dÿš�7fÿš�7hÿš�7jÿš�7lÿš�7nÿš�7pÿ×�7�)�8�ÿ×�8�ÿ×�8�$ÿì�8�‚ÿì�8�ƒÿì�8�„ÿì�8�…ÿì�8�†ÿì�8�‡ÿì�8�Âÿì�8�Äÿì�8�Æÿì�8Cÿì�8ÿ×�8 ÿ×�8Xÿì�8ÿì�8ÿì�8!ÿì�8#ÿì�8%ÿì�8'ÿì�8)ÿì�8+ÿì�8-ÿì�8/ÿì�81ÿì�83ÿì�9�ÿš�9�ÿš�9�"�)�9�$ÿ®�9�&ÿì�9�*ÿì�9�2ÿì�9�4ÿì�9�Dÿ×�9�Fÿ×�9�Gÿ×�9�Hÿ×�9�Jÿì�9�Pÿì�9�Qÿì�9�Rÿ×�9�Sÿì�9�Tÿ×�9�Uÿì�9�Vÿì�9�Xÿì�9�‚ÿ®�9�ƒÿ®�9�„ÿ®�9�…ÿ®�9�†ÿ®�9�‡ÿ®�9�‰ÿì�9�”ÿì�9�•ÿì�9�–ÿì�9�—ÿì�9�˜ÿì�9�šÿì�9�¢ÿ×�9�£ÿ×�9�¤ÿ×�9�¥ÿ×�9�¦ÿ×�9�§ÿ×�9�¨ÿ×�9�©ÿ×�9�ªÿ×�9�«ÿ×�9�¬ÿ×�9�­ÿ×�9�´ÿ×�9�µÿ×�9�¶ÿ×�9�·ÿ×�9�¸ÿ×�9�ºÿ×�9�»ÿì�9�¼ÿì�9�½ÿì�9�¾ÿì�9�Âÿ®�9�Ãÿ×�9�Äÿ®�9�Åÿ×�9�Æÿ®�9�Çÿ×�9�Èÿì�9�Éÿ×�9�Êÿì�9�Ëÿ×�9�Ìÿì�9�Íÿ×�9�Îÿì�9�Ïÿ×�9�Ñÿ×�9�Óÿ×�9�Õÿ×�9�×ÿ×�9�Ùÿ×�9�Ûÿ×�9�Ýÿ×�9�Þÿì�9�ßÿì�9�àÿì�9�áÿì�9�âÿì�9�ãÿì�9�äÿì�9�åÿì�9�úÿì�9ÿì�9ÿì�9 ÿì�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿì�9ÿì�9!ÿì�9+ÿì�9-ÿì�9/ÿì�91ÿì�93ÿì�95ÿì�9Cÿ®�9Dÿ×�9Fÿ×�9Gÿì�9Hÿ×�9Jÿì�9ÿš�9 ÿš�9Wÿì�9Xÿ®�9Yÿ×�9_ÿì�9`ÿ×�9bÿì�9ÿ®�9ÿ×�9ÿ®�9 ÿ×�9!ÿ®�9"ÿ×�9#ÿ®�9%ÿ®�9&ÿ×�9'ÿ®�9(ÿ×�9)ÿ®�9*ÿ×�9+ÿ®�9,ÿ×�9-ÿ®�9.ÿ×�9/ÿ®�90ÿ×�91ÿ®�92ÿ×�93ÿ®�94ÿ×�96ÿ×�98ÿ×�9:ÿ×�9<ÿ×�9@ÿ×�9Bÿ×�9Dÿ×�9Iÿì�9Jÿ×�9Kÿì�9Lÿ×�9Mÿì�9Nÿ×�9Oÿì�9Qÿì�9Rÿ×�9Sÿì�9Tÿ×�9Uÿì�9Vÿ×�9Wÿì�9Xÿ×�9Yÿì�9Zÿ×�9[ÿì�9\ÿ×�9]ÿì�9^ÿ×�9_ÿì�9`ÿ×�9bÿì�9dÿì�9fÿì�9hÿì�9jÿì�9lÿì�9nÿì�:�ÿš�:�ÿš�:�"�)�:�$ÿ®�:�&ÿì�:�*ÿì�:�2ÿì�:�4ÿì�:�Dÿ×�:�Fÿ×�:�Gÿ×�:�Hÿ×�:�Jÿì�:�Pÿì�:�Qÿì�:�Rÿ×�:�Sÿì�:�Tÿ×�:�Uÿì�:�Vÿì�:�Xÿì�:�‚ÿ®�:�ƒÿ®�:�„ÿ®�:�…ÿ®�:�†ÿ®�:�‡ÿ®�:�‰ÿì�:�”ÿì�:�•ÿì�:�–ÿì�:�—ÿì�:�˜ÿì�:�šÿì�:�¢ÿ×�:�£ÿ×�:�¤ÿ×�:�¥ÿ×�:�¦ÿ×�:�§ÿ×�:�¨ÿ×�:�©ÿ×�:�ªÿ×�:�«ÿ×�:�¬ÿ×�:�­ÿ×�:�´ÿ×�:�µÿ×�:�¶ÿ×�:�·ÿ×�:�¸ÿ×�:�ºÿ×�:�»ÿì�:�¼ÿì�:�½ÿì�:�¾ÿì�:�Âÿ®�:�Ãÿ×�:�Äÿ®�:�Åÿ×�:�Æÿ®�:�Çÿ×�:�Èÿì�:�Éÿ×�:�Êÿì�:�Ëÿ×�:�Ìÿì�:�Íÿ×�:�Îÿì�:�Ïÿ×�:�Ñÿ×�:�Óÿ×�:�Õÿ×�:�×ÿ×�:�Ùÿ×�:�Ûÿ×�:�Ýÿ×�:�Þÿì�:�ßÿì�:�àÿì�:�áÿì�:�âÿì�:�ãÿì�:�äÿì�:�åÿì�:�úÿì�:ÿì�:ÿì�: ÿì�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿì�:ÿì�:!ÿì�:+ÿì�:-ÿì�:/ÿì�:1ÿì�:3ÿì�:5ÿì�:Cÿ®�:Dÿ×�:Fÿ×�:Gÿì�:Hÿ×�:Jÿì�:ÿš�: ÿš�:Wÿì�:Xÿ®�:Yÿ×�:_ÿì�:`ÿ×�:bÿì�:ÿ®�:ÿ×�:ÿ®�: ÿ×�:!ÿ®�:"ÿ×�:#ÿ®�:%ÿ®�:&ÿ×�:'ÿ®�:(ÿ×�:)ÿ®�:*ÿ×�:+ÿ®�:,ÿ×�:-ÿ®�:.ÿ×�:/ÿ®�:0ÿ×�:1ÿ®�:2ÿ×�:3ÿ®�:4ÿ×�:6ÿ×�:8ÿ×�::ÿ×�:<ÿ×�:@ÿ×�:Bÿ×�:Dÿ×�:Iÿì�:Jÿ×�:Kÿì�:Lÿ×�:Mÿì�:Nÿ×�:Oÿì�:Qÿì�:Rÿ×�:Sÿì�:Tÿ×�:Uÿì�:Vÿ×�:Wÿì�:Xÿ×�:Yÿì�:Zÿ×�:[ÿì�:\ÿ×�:]ÿì�:^ÿ×�:_ÿì�:`ÿ×�:bÿì�:dÿì�:fÿì�:hÿì�:jÿì�:lÿì�:nÿì�;�&ÿ×�;�*ÿ×�;�2ÿ×�;�4ÿ×�;�‰ÿ×�;�”ÿ×�;�•ÿ×�;�–ÿ×�;�—ÿ×�;�˜ÿ×�;�šÿ×�;�Èÿ×�;�Êÿ×�;�Ìÿ×�;�Îÿ×�;�Þÿ×�;�àÿ×�;�âÿ×�;�äÿ×�;ÿ×�;ÿ×�;ÿ×�;ÿ×�;Gÿ×�;_ÿ×�;Iÿ×�;Kÿ×�;Mÿ×�;Oÿ×�;Qÿ×�;Sÿ×�;Uÿ×�;Wÿ×�;Yÿ×�;[ÿ×�;]ÿ×�;_ÿ×�<�ÿ…�<�ÿ…�<�"�)�<�$ÿ…�<�&ÿ×�<�*ÿ×�<�2ÿ×�<�4ÿ×�<�Dÿš�<�Fÿš�<�Gÿš�<�Hÿš�<�Jÿ×�<�PÿÃ�<�QÿÃ�<�Rÿš�<�SÿÃ�<�Tÿš�<�UÿÃ�<�Vÿ®�<�XÿÃ�<�]ÿ×�<�‚ÿ…�<�ƒÿ…�<�„ÿ…�<�…ÿ…�<�†ÿ…�<�‡ÿ…�<�‰ÿ×�<�”ÿ×�<�•ÿ×�<�–ÿ×�<�—ÿ×�<�˜ÿ×�<�šÿ×�<�¢ÿš�<�£ÿš�<�¤ÿš�<�¥ÿš�<�¦ÿš�<�§ÿš�<�¨ÿš�<�©ÿš�<�ªÿš�<�«ÿš�<�¬ÿš�<�­ÿš�<�´ÿš�<�µÿš�<�¶ÿš�<�·ÿš�<�¸ÿš�<�ºÿš�<�»ÿÃ�<�¼ÿÃ�<�½ÿÃ�<�¾ÿÃ�<�Âÿ…�<�Ãÿš�<�Äÿ…�<�Åÿš�<�Æÿ…�<�Çÿš�<�Èÿ×�<�Éÿš�<�Êÿ×�<�Ëÿš�<�Ìÿ×�<�Íÿš�<�Îÿ×�<�Ïÿš�<�Ñÿš�<�Óÿš�<�Õÿš�<�×ÿš�<�Ùÿš�<�Ûÿš�<�Ýÿš�<�Þÿ×�<�ßÿ×�<�àÿ×�<�áÿ×�<�âÿ×�<�ãÿ×�<�äÿ×�<�åÿ×�<�úÿÃ�<ÿÃ�<ÿÃ�< ÿÃ�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿÃ�<ÿÃ�<ÿ®�<!ÿ®�<+ÿÃ�<-ÿÃ�</ÿÃ�<1ÿÃ�<3ÿÃ�<5ÿÃ�<<ÿ×�<>ÿ×�<@ÿ×�<Cÿ…�<Dÿš�<Fÿš�<Gÿ×�<Hÿš�<Jÿ®�<ÿ…�< ÿ…�<WÿÃ�<Xÿ…�<Yÿš�<_ÿ×�<`ÿš�<bÿÃ�<ÿ…�<ÿš�<ÿ…�< ÿš�<!ÿ…�<"ÿš�<#ÿ…�<%ÿ…�<&ÿš�<'ÿ…�<(ÿš�<)ÿ…�<*ÿš�<+ÿ…�<,ÿš�<-ÿ…�<.ÿš�</ÿ…�<0ÿš�<1ÿ…�<2ÿš�<3ÿ…�<4ÿš�<6ÿš�<8ÿš�<:ÿš�<<ÿš�<@ÿš�<Bÿš�<Dÿš�<Iÿ×�<Jÿš�<Kÿ×�<Lÿš�<Mÿ×�<Nÿš�<Oÿ×�<Qÿ×�<Rÿš�<Sÿ×�<Tÿš�<Uÿ×�<Vÿš�<Wÿ×�<Xÿš�<Yÿ×�<Zÿš�<[ÿ×�<\ÿš�<]ÿ×�<^ÿš�<_ÿ×�<`ÿš�<bÿÃ�<dÿÃ�<fÿÃ�<hÿÃ�<jÿÃ�<lÿÃ�<nÿÃ�=�&ÿì�=�*ÿì�=�2ÿì�=�4ÿì�=�‰ÿì�=�”ÿì�=�•ÿì�=�–ÿì�=�—ÿì�=�˜ÿì�=�šÿì�=�Èÿì�=�Êÿì�=�Ìÿì�=�Îÿì�=�Þÿì�=�àÿì�=�âÿì�=�äÿì�=ÿì�=ÿì�=ÿì�=ÿì�=Gÿì�=_ÿì�=Iÿì�=Kÿì�=Mÿì�=Oÿì�=Qÿì�=Sÿì�=Uÿì�=Wÿì�=Yÿì�=[ÿì�=]ÿì�=_ÿì�>�-�¸�D�ÿì�D� ÿì�Dÿì�D ÿì�E�ÿì�E� ÿì�E�Yÿ×�E�Zÿ×�E�[ÿ×�E�\ÿ×�E�]ÿì�E�¿ÿ×�E7ÿ×�E<ÿì�E>ÿì�E@ÿì�Eûÿ×�Eýÿ×�Eÿì�E ÿì�Epÿ×�F��)�F� �)�F�)�F �)�H�ÿì�H� ÿì�H�Yÿ×�H�Zÿ×�H�[ÿ×�H�\ÿ×�H�]ÿì�H�¿ÿ×�H7ÿ×�H<ÿì�H>ÿì�H@ÿì�Hûÿ×�Hýÿ×�Hÿì�H ÿì�Hpÿ×�I��{�I� �{�I�{�I �{�K�ÿì�K� ÿì�Kÿì�K ÿì�N�Fÿ×�N�Gÿ×�N�Hÿ×�N�Rÿ×�N�Tÿ×�N�¢ÿ×�N�©ÿ×�N�ªÿ×�N�«ÿ×�N�¬ÿ×�N�­ÿ×�N�´ÿ×�N�µÿ×�N�¶ÿ×�N�·ÿ×�N�¸ÿ×�N�ºÿ×�N�Éÿ×�N�Ëÿ×�N�Íÿ×�N�Ïÿ×�N�Ñÿ×�N�Óÿ×�N�Õÿ×�N�×ÿ×�N�Ùÿ×�N�Ûÿ×�N�Ýÿ×�Nÿ×�Nÿ×�Nÿ×�Nÿ×�NHÿ×�N`ÿ×�N6ÿ×�N8ÿ×�N:ÿ×�N<ÿ×�N@ÿ×�NBÿ×�NDÿ×�NJÿ×�NLÿ×�NNÿ×�NRÿ×�NTÿ×�NVÿ×�NXÿ×�NZÿ×�N\ÿ×�N^ÿ×�N`ÿ×�P�ÿì�P� ÿì�Pÿì�P ÿì�Q�ÿì�Q� ÿì�Qÿì�Q ÿì�R�ÿì�R� ÿì�R�Yÿ×�R�Zÿ×�R�[ÿ×�R�\ÿ×�R�]ÿì�R�¿ÿ×�R7ÿ×�R<ÿì�R>ÿì�R@ÿì�Rûÿ×�Rýÿ×�Rÿì�R ÿì�Rpÿ×�S�ÿì�S� ÿì�S�Yÿ×�S�Zÿ×�S�[ÿ×�S�\ÿ×�S�]ÿì�S�¿ÿ×�S7ÿ×�S<ÿì�S>ÿì�S@ÿì�Sûÿ×�Sýÿ×�Sÿì�S ÿì�Spÿ×�U��R�U� �R�U�Dÿ×�U�Fÿ×�U�Gÿ×�U�Hÿ×�U�Jÿì�U�Rÿ×�U�Tÿ×�U�¢ÿ×�U�£ÿ×�U�¤ÿ×�U�¥ÿ×�U�¦ÿ×�U�§ÿ×�U�¨ÿ×�U�©ÿ×�U�ªÿ×�U�«ÿ×�U�¬ÿ×�U�­ÿ×�U�´ÿ×�U�µÿ×�U�¶ÿ×�U�·ÿ×�U�¸ÿ×�U�ºÿ×�U�Ãÿ×�U�Åÿ×�U�Çÿ×�U�Éÿ×�U�Ëÿ×�U�Íÿ×�U�Ïÿ×�U�Ñÿ×�U�Óÿ×�U�Õÿ×�U�×ÿ×�U�Ùÿ×�U�Ûÿ×�U�Ýÿ×�U�ßÿì�U�áÿì�U�ãÿì�U�åÿì�Uÿ×�Uÿ×�Uÿ×�Uÿ×�UDÿ×�UFÿ×�UHÿ×�U�R�U �R�UYÿ×�U`ÿ×�Uÿ×�U ÿ×�U"ÿ×�U&ÿ×�U(ÿ×�U*ÿ×�U,ÿ×�U.ÿ×�U0ÿ×�U2ÿ×�U4ÿ×�U6ÿ×�U8ÿ×�U:ÿ×�U<ÿ×�U@ÿ×�UBÿ×�UDÿ×�UJÿ×�ULÿ×�UNÿ×�URÿ×�UTÿ×�UVÿ×�UXÿ×�UZÿ×�U\ÿ×�U^ÿ×�U`ÿ×�W��)�W� �)�W�)�W �)�Y��R�Y� �R�Y�ÿ®�Y�ÿ®�Y�"�)�Y�R�Yÿ®�Y �R�Y ÿ®�Z��R�Z� �R�Z�ÿ®�Z�ÿ®�Z�"�)�Z�R�Zÿ®�Z �R�Z ÿ®�[�Fÿ×�[�Gÿ×�[�Hÿ×�[�Rÿ×�[�Tÿ×�[�¢ÿ×�[�©ÿ×�[�ªÿ×�[�«ÿ×�[�¬ÿ×�[�­ÿ×�[�´ÿ×�[�µÿ×�[�¶ÿ×�[�·ÿ×�[�¸ÿ×�[�ºÿ×�[�Éÿ×�[�Ëÿ×�[�Íÿ×�[�Ïÿ×�[�Ñÿ×�[�Óÿ×�[�Õÿ×�[�×ÿ×�[�Ùÿ×�[�Ûÿ×�[�Ýÿ×�[ÿ×�[ÿ×�[ÿ×�[ÿ×�[Hÿ×�[`ÿ×�[6ÿ×�[8ÿ×�[:ÿ×�[<ÿ×�[@ÿ×�[Bÿ×�[Dÿ×�[Jÿ×�[Lÿ×�[Nÿ×�[Rÿ×�[Tÿ×�[Vÿ×�[Xÿ×�[Zÿ×�[\ÿ×�[^ÿ×�[`ÿ×�\��R�\� �R�\�ÿ®�\�ÿ®�\�"�)�\�R�\ÿ®�\ �R�\ ÿ®�^�-�¸�‚�ÿq�‚� ÿq�‚�&ÿ×�‚�*ÿ×�‚�- �‚�2ÿ×�‚�4ÿ×�‚�7ÿq�‚�9ÿ®�‚�:ÿ®�‚�<ÿ…�‚�‰ÿ×�‚�”ÿ×�‚�•ÿ×�‚�–ÿ×�‚�—ÿ×�‚�˜ÿ×�‚�šÿ×�‚�Ÿÿ…�‚�Èÿ×�‚�Êÿ×�‚�Ìÿ×�‚�Îÿ×�‚�Þÿ×�‚�àÿ×�‚�âÿ×�‚�äÿ×�‚ÿ×�‚ÿ×�‚ÿ×�‚ÿ×�‚$ÿq�‚&ÿq�‚6ÿ®�‚8ÿ…�‚:ÿ…�‚Gÿ×�‚úÿ®�‚üÿ®�‚þÿ®�‚�ÿ…�‚ÿq�‚ ÿq�‚_ÿ×�‚Iÿ×�‚Kÿ×�‚Mÿ×�‚Oÿ×�‚Qÿ×�‚Sÿ×�‚Uÿ×�‚Wÿ×�‚Yÿ×�‚[ÿ×�‚]ÿ×�‚_ÿ×�‚oÿ…�‚qÿ…�‚sÿ…�‚ÿq�ƒ�ÿq�ƒ� ÿq�ƒ�&ÿ×�ƒ�*ÿ×�ƒ�- �ƒ�2ÿ×�ƒ�4ÿ×�ƒ�7ÿq�ƒ�9ÿ®�ƒ�:ÿ®�ƒ�<ÿ…�ƒ�‰ÿ×�ƒ�”ÿ×�ƒ�•ÿ×�ƒ�–ÿ×�ƒ�—ÿ×�ƒ�˜ÿ×�ƒ�šÿ×�ƒ�Ÿÿ…�ƒ�Èÿ×�ƒ�Êÿ×�ƒ�Ìÿ×�ƒ�Îÿ×�ƒ�Þÿ×�ƒ�àÿ×�ƒ�âÿ×�ƒ�äÿ×�ƒÿ×�ƒÿ×�ƒÿ×�ƒÿ×�ƒ$ÿq�ƒ&ÿq�ƒ6ÿ®�ƒ8ÿ…�ƒ:ÿ…�ƒGÿ×�ƒúÿ®�ƒüÿ®�ƒþÿ®�ƒ�ÿ…�ƒÿq�ƒ ÿq�ƒ_ÿ×�ƒIÿ×�ƒKÿ×�ƒMÿ×�ƒOÿ×�ƒQÿ×�ƒSÿ×�ƒUÿ×�ƒWÿ×�ƒYÿ×�ƒ[ÿ×�ƒ]ÿ×�ƒ_ÿ×�ƒoÿ…�ƒqÿ…�ƒsÿ…�ƒÿq�„�ÿq�„� ÿq�„�&ÿ×�„�*ÿ×�„�- �„�2ÿ×�„�4ÿ×�„�7ÿq�„�9ÿ®�„�:ÿ®�„�<ÿ…�„�‰ÿ×�„�”ÿ×�„�•ÿ×�„�–ÿ×�„�—ÿ×�„�˜ÿ×�„�šÿ×�„�Ÿÿ…�„�Èÿ×�„�Êÿ×�„�Ìÿ×�„�Îÿ×�„�Þÿ×�„�àÿ×�„�âÿ×�„�äÿ×�„ÿ×�„ÿ×�„ÿ×�„ÿ×�„$ÿq�„&ÿq�„6ÿ®�„8ÿ…�„:ÿ…�„Gÿ×�„úÿ®�„üÿ®�„þÿ®�„�ÿ…�„ÿq�„ ÿq�„_ÿ×�„Iÿ×�„Kÿ×�„Mÿ×�„Oÿ×�„Qÿ×�„Sÿ×�„Uÿ×�„Wÿ×�„Yÿ×�„[ÿ×�„]ÿ×�„_ÿ×�„oÿ…�„qÿ…�„sÿ…�„ÿq�…�ÿq�…� ÿq�…�&ÿ×�…�*ÿ×�…�- �…�2ÿ×�…�4ÿ×�…�7ÿq�…�9ÿ®�…�:ÿ®�…�<ÿ…�…�‰ÿ×�…�”ÿ×�…�•ÿ×�…�–ÿ×�…�—ÿ×�…�˜ÿ×�…�šÿ×�…�Ÿÿ…�…�Èÿ×�…�Êÿ×�…�Ìÿ×�…�Îÿ×�…�Þÿ×�…�àÿ×�…�âÿ×�…�äÿ×�…ÿ×�…ÿ×�…ÿ×�…ÿ×�…$ÿq�…&ÿq�…6ÿ®�…8ÿ…�…:ÿ…�…Gÿ×�…úÿ®�…üÿ®�…þÿ®�…�ÿ…�…ÿq�… ÿq�…_ÿ×�…Iÿ×�…Kÿ×�…Mÿ×�…Oÿ×�…Qÿ×�…Sÿ×�…Uÿ×�…Wÿ×�…Yÿ×�…[ÿ×�…]ÿ×�…_ÿ×�…oÿ…�…qÿ…�…sÿ…�…ÿq�†�ÿq�†� ÿq�†�&ÿ×�†�*ÿ×�†�- �†�2ÿ×�†�4ÿ×�†�7ÿq�†�9ÿ®�†�:ÿ®�†�<ÿ…�†�‰ÿ×�†�”ÿ×�†�•ÿ×�†�–ÿ×�†�—ÿ×�†�˜ÿ×�†�šÿ×�†�Ÿÿ…�†�Èÿ×�†�Êÿ×�†�Ìÿ×�†�Îÿ×�†�Þÿ×�†�àÿ×�†�âÿ×�†�äÿ×�†ÿ×�†ÿ×�†ÿ×�†ÿ×�†$ÿq�†&ÿq�†6ÿ®�†8ÿ…�†:ÿ…�†Gÿ×�†úÿ®�†üÿ®�†þÿ®�†�ÿ…�†ÿq�† ÿq�†_ÿ×�†Iÿ×�†Kÿ×�†Mÿ×�†Oÿ×�†Qÿ×�†Sÿ×�†Uÿ×�†Wÿ×�†Yÿ×�†[ÿ×�†]ÿ×�†_ÿ×�†oÿ…�†qÿ…�†sÿ…�†ÿq�‡�ÿq�‡� ÿq�‡�&ÿ×�‡�*ÿ×�‡�- �‡�2ÿ×�‡�4ÿ×�‡�7ÿq�‡�9ÿ®�‡�:ÿ®�‡�<ÿ…�‡�‰ÿ×�‡�”ÿ×�‡�•ÿ×�‡�–ÿ×�‡�—ÿ×�‡�˜ÿ×�‡�šÿ×�‡�Ÿÿ…�‡�Èÿ×�‡�Êÿ×�‡�Ìÿ×�‡�Îÿ×�‡�Þÿ×�‡�àÿ×�‡�âÿ×�‡�äÿ×�‡ÿ×�‡ÿ×�‡ÿ×�‡ÿ×�‡$ÿq�‡&ÿq�‡6ÿ®�‡8ÿ…�‡:ÿ…�‡Gÿ×�‡úÿ®�‡üÿ®�‡þÿ®�‡�ÿ…�‡ÿq�‡ ÿq�‡_ÿ×�‡Iÿ×�‡Kÿ×�‡Mÿ×�‡Oÿ×�‡Qÿ×�‡Sÿ×�‡Uÿ×�‡Wÿ×�‡Yÿ×�‡[ÿ×�‡]ÿ×�‡_ÿ×�‡oÿ…�‡qÿ…�‡sÿ…�‡ÿq�ˆ�-�{�‰�&ÿ×�‰�*ÿ×�‰�2ÿ×�‰�4ÿ×�‰�‰ÿ×�‰�”ÿ×�‰�•ÿ×�‰�–ÿ×�‰�—ÿ×�‰�˜ÿ×�‰�šÿ×�‰�Èÿ×�‰�Êÿ×�‰�Ìÿ×�‰�Îÿ×�‰�Þÿ×�‰�àÿ×�‰�âÿ×�‰�äÿ×�‰ÿ×�‰ÿ×�‰ÿ×�‰ÿ×�‰Gÿ×�‰_ÿ×�‰Iÿ×�‰Kÿ×�‰Mÿ×�‰Oÿ×�‰Qÿ×�‰Sÿ×�‰Uÿ×�‰Wÿ×�‰Yÿ×�‰[ÿ×�‰]ÿ×�‰_ÿ×�Š�-�{�‹�-�{�Œ�-�{��-�{�’�ÿ®�’�ÿ®�’�$ÿ×�’�7ÿÃ�’�9ÿì�’�:ÿì�’�;ÿ×�’�<ÿì�’�=ÿì�’�‚ÿ×�’�ƒÿ×�’�„ÿ×�’�…ÿ×�’�†ÿ×�’�‡ÿ×�’�Ÿÿì�’�Âÿ×�’�Äÿ×�’�Æÿ×�’$ÿÃ�’&ÿÃ�’6ÿì�’8ÿì�’:ÿì�’;ÿì�’=ÿì�’?ÿì�’Cÿ×�’ ÿì�’úÿì�’üÿì�’þÿì�’�ÿì�’ÿ®�’ ÿ®�’Xÿ×�’ÿ×�’ÿ×�’!ÿ×�’#ÿ×�’%ÿ×�’'ÿ×�’)ÿ×�’+ÿ×�’-ÿ×�’/ÿ×�’1ÿ×�’3ÿ×�’oÿì�’qÿì�’sÿì�’ÿÃ�”�ÿ®�”�ÿ®�”�$ÿ×�”�7ÿÃ�”�9ÿì�”�:ÿì�”�;ÿ×�”�<ÿì�”�=ÿì�”�‚ÿ×�”�ƒÿ×�”�„ÿ×�”�…ÿ×�”�†ÿ×�”�‡ÿ×�”�Ÿÿì�”�Âÿ×�”�Äÿ×�”�Æÿ×�”$ÿÃ�”&ÿÃ�”6ÿì�”8ÿì�”:ÿì�”;ÿì�”=ÿì�”?ÿì�”Cÿ×�” ÿì�”úÿì�”üÿì�”þÿì�”�ÿì�”ÿ®�” ÿ®�”Xÿ×�”ÿ×�”ÿ×�”!ÿ×�”#ÿ×�”%ÿ×�”'ÿ×�”)ÿ×�”+ÿ×�”-ÿ×�”/ÿ×�”1ÿ×�”3ÿ×�”oÿì�”qÿì�”sÿì�”ÿÃ�•�ÿ®�•�ÿ®�•�$ÿ×�•�7ÿÃ�•�9ÿì�•�:ÿì�•�;ÿ×�•�<ÿì�•�=ÿì�•�‚ÿ×�•�ƒÿ×�•�„ÿ×�•�…ÿ×�•�†ÿ×�•�‡ÿ×�•�Ÿÿì�•�Âÿ×�•�Äÿ×�•�Æÿ×�•$ÿÃ�•&ÿÃ�•6ÿì�•8ÿì�•:ÿì�•;ÿì�•=ÿì�•?ÿì�•Cÿ×�• ÿì�•úÿì�•üÿì�•þÿì�•�ÿì�•ÿ®�• ÿ®�•Xÿ×�•ÿ×�•ÿ×�•!ÿ×�•#ÿ×�•%ÿ×�•'ÿ×�•)ÿ×�•+ÿ×�•-ÿ×�•/ÿ×�•1ÿ×�•3ÿ×�•oÿì�•qÿì�•sÿì�•ÿÃ�–�ÿ®�–�ÿ®�–�$ÿ×�–�7ÿÃ�–�9ÿì�–�:ÿì�–�;ÿ×�–�<ÿì�–�=ÿì�–�‚ÿ×�–�ƒÿ×�–�„ÿ×�–�…ÿ×�–�†ÿ×�–�‡ÿ×�–�Ÿÿì�–�Âÿ×�–�Äÿ×�–�Æÿ×�–$ÿÃ�–&ÿÃ�–6ÿì�–8ÿì�–:ÿì�–;ÿì�–=ÿì�–?ÿì�–Cÿ×�– ÿì�–úÿì�–üÿì�–þÿì�–�ÿì�–ÿ®�– ÿ®�–Xÿ×�–ÿ×�–ÿ×�–!ÿ×�–#ÿ×�–%ÿ×�–'ÿ×�–)ÿ×�–+ÿ×�–-ÿ×�–/ÿ×�–1ÿ×�–3ÿ×�–oÿì�–qÿì�–sÿì�–ÿÃ�—�ÿ®�—�ÿ®�—�$ÿ×�—�7ÿÃ�—�9ÿì�—�:ÿì�—�;ÿ×�—�<ÿì�—�=ÿì�—�‚ÿ×�—�ƒÿ×�—�„ÿ×�—�…ÿ×�—�†ÿ×�—�‡ÿ×�—�Ÿÿì�—�Âÿ×�—�Äÿ×�—�Æÿ×�—$ÿÃ�—&ÿÃ�—6ÿì�—8ÿì�—:ÿì�—;ÿì�—=ÿì�—?ÿì�—Cÿ×�— ÿì�—úÿì�—üÿì�—þÿì�—�ÿì�—ÿ®�— ÿ®�—Xÿ×�—ÿ×�—ÿ×�—!ÿ×�—#ÿ×�—%ÿ×�—'ÿ×�—)ÿ×�—+ÿ×�—-ÿ×�—/ÿ×�—1ÿ×�—3ÿ×�—oÿì�—qÿì�—sÿì�—ÿÃ�˜�ÿ®�˜�ÿ®�˜�$ÿ×�˜�7ÿÃ�˜�9ÿì�˜�:ÿì�˜�;ÿ×�˜�<ÿì�˜�=ÿì�˜�‚ÿ×�˜�ƒÿ×�˜�„ÿ×�˜�…ÿ×�˜�†ÿ×�˜�‡ÿ×�˜�Ÿÿì�˜�Âÿ×�˜�Äÿ×�˜�Æÿ×�˜$ÿÃ�˜&ÿÃ�˜6ÿì�˜8ÿì�˜:ÿì�˜;ÿì�˜=ÿì�˜?ÿì�˜Cÿ×�˜ ÿì�˜úÿì�˜üÿì�˜þÿì�˜�ÿì�˜ÿ®�˜ ÿ®�˜Xÿ×�˜ÿ×�˜ÿ×�˜!ÿ×�˜#ÿ×�˜%ÿ×�˜'ÿ×�˜)ÿ×�˜+ÿ×�˜-ÿ×�˜/ÿ×�˜1ÿ×�˜3ÿ×�˜oÿì�˜qÿì�˜sÿì�˜ÿÃ�š�ÿ®�š�ÿ®�š�$ÿ×�š�7ÿÃ�š�9ÿì�š�:ÿì�š�;ÿ×�š�<ÿì�š�=ÿì�š�‚ÿ×�š�ƒÿ×�š�„ÿ×�š�…ÿ×�š�†ÿ×�š�‡ÿ×�š�Ÿÿì�š�Âÿ×�š�Äÿ×�š�Æÿ×�š$ÿÃ�š&ÿÃ�š6ÿì�š8ÿì�š:ÿì�š;ÿì�š=ÿì�š?ÿì�šCÿ×�š ÿì�šúÿì�šüÿì�šþÿì�š�ÿì�šÿ®�š ÿ®�šXÿ×�šÿ×�šÿ×�š!ÿ×�š#ÿ×�š%ÿ×�š'ÿ×�š)ÿ×�š+ÿ×�š-ÿ×�š/ÿ×�š1ÿ×�š3ÿ×�šoÿì�šqÿì�šsÿì�šÿÃ�›�ÿ×�›�ÿ×�›�$ÿì�›�‚ÿì�›�ƒÿì�›�„ÿì�›�…ÿì�›�†ÿì�›�‡ÿì�›�Âÿì�›�Äÿì�›�Æÿì�›Cÿì�›ÿ×�› ÿ×�›Xÿì�›ÿì�›ÿì�›!ÿì�›#ÿì�›%ÿì�›'ÿì�›)ÿì�›+ÿì�›-ÿì�›/ÿì�›1ÿì�›3ÿì�œ�ÿ×�œ�ÿ×�œ�$ÿì�œ�‚ÿì�œ�ƒÿì�œ�„ÿì�œ�…ÿì�œ�†ÿì�œ�‡ÿì�œ�Âÿì�œ�Äÿì�œ�Æÿì�œCÿì�œÿ×�œ ÿ×�œXÿì�œÿì�œÿì�œ!ÿì�œ#ÿì�œ%ÿì�œ'ÿì�œ)ÿì�œ+ÿì�œ-ÿì�œ/ÿì�œ1ÿì�œ3ÿì��ÿ×��ÿ×��$ÿì��‚ÿì��ƒÿì��„ÿì��…ÿì��†ÿì��‡ÿì��Âÿì��Äÿì��Æÿì�Cÿì�ÿ×� ÿ×�Xÿì�ÿì�ÿì�!ÿì�#ÿì�%ÿì�'ÿì�)ÿì�+ÿì�-ÿì�/ÿì�1ÿì�3ÿì�ž�ÿ×�ž�ÿ×�ž�$ÿì�ž�‚ÿì�ž�ƒÿì�ž�„ÿì�ž�…ÿì�ž�†ÿì�ž�‡ÿì�ž�Âÿì�ž�Äÿì�ž�Æÿì�žCÿì�žÿ×�ž ÿ×�žXÿì�žÿì�žÿì�ž!ÿì�ž#ÿì�ž%ÿì�ž'ÿì�ž)ÿì�ž+ÿì�ž-ÿì�ž/ÿì�ž1ÿì�ž3ÿì�Ÿ�ÿ…�Ÿ�ÿ…�Ÿ�"�)�Ÿ�$ÿ…�Ÿ�&ÿ×�Ÿ�*ÿ×�Ÿ�2ÿ×�Ÿ�4ÿ×�Ÿ�Dÿš�Ÿ�Fÿš�Ÿ�Gÿš�Ÿ�Hÿš�Ÿ�Jÿ×�Ÿ�PÿÃ�Ÿ�QÿÃ�Ÿ�Rÿš�Ÿ�SÿÃ�Ÿ�Tÿš�Ÿ�UÿÃ�Ÿ�Vÿ®�Ÿ�XÿÃ�Ÿ�]ÿ×�Ÿ�‚ÿ…�Ÿ�ƒÿ…�Ÿ�„ÿ…�Ÿ�…ÿ…�Ÿ�†ÿ…�Ÿ�‡ÿ…�Ÿ�‰ÿ×�Ÿ�”ÿ×�Ÿ�•ÿ×�Ÿ�–ÿ×�Ÿ�—ÿ×�Ÿ�˜ÿ×�Ÿ�šÿ×�Ÿ�¢ÿš�Ÿ�£ÿš�Ÿ�¤ÿš�Ÿ�¥ÿš�Ÿ�¦ÿš�Ÿ�§ÿš�Ÿ�¨ÿš�Ÿ�©ÿš�Ÿ�ªÿš�Ÿ�«ÿš�Ÿ�¬ÿš�Ÿ�­ÿš�Ÿ�´ÿš�Ÿ�µÿš�Ÿ�¶ÿš�Ÿ�·ÿš�Ÿ�¸ÿš�Ÿ�ºÿš�Ÿ�»ÿÃ�Ÿ�¼ÿÃ�Ÿ�½ÿÃ�Ÿ�¾ÿÃ�Ÿ�Âÿ…�Ÿ�Ãÿš�Ÿ�Äÿ…�Ÿ�Åÿš�Ÿ�Æÿ…�Ÿ�Çÿš�Ÿ�Èÿ×�Ÿ�Éÿš�Ÿ�Êÿ×�Ÿ�Ëÿš�Ÿ�Ìÿ×�Ÿ�Íÿš�Ÿ�Îÿ×�Ÿ�Ïÿš�Ÿ�Ñÿš�Ÿ�Óÿš�Ÿ�Õÿš�Ÿ�×ÿš�Ÿ�Ùÿš�Ÿ�Ûÿš�Ÿ�Ýÿš�Ÿ�Þÿ×�Ÿ�ßÿ×�Ÿ�àÿ×�Ÿ�áÿ×�Ÿ�âÿ×�Ÿ�ãÿ×�Ÿ�äÿ×�Ÿ�åÿ×�Ÿ�úÿÃ�ŸÿÃ�ŸÿÃ�Ÿ ÿÃ�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�ŸÿÃ�ŸÿÃ�Ÿÿ®�Ÿ!ÿ®�Ÿ+ÿÃ�Ÿ-ÿÃ�Ÿ/ÿÃ�Ÿ1ÿÃ�Ÿ3ÿÃ�Ÿ5ÿÃ�Ÿ<ÿ×�Ÿ>ÿ×�Ÿ@ÿ×�ŸCÿ…�ŸDÿš�ŸFÿš�ŸGÿ×�ŸHÿš�ŸJÿ®�Ÿÿ…�Ÿ ÿ…�ŸWÿÃ�ŸXÿ…�ŸYÿš�Ÿ_ÿ×�Ÿ`ÿš�ŸbÿÃ�Ÿÿ…�Ÿÿš�Ÿÿ…�Ÿ ÿš�Ÿ!ÿ…�Ÿ"ÿš�Ÿ#ÿ…�Ÿ%ÿ…�Ÿ&ÿš�Ÿ'ÿ…�Ÿ(ÿš�Ÿ)ÿ…�Ÿ*ÿš�Ÿ+ÿ…�Ÿ,ÿš�Ÿ-ÿ…�Ÿ.ÿš�Ÿ/ÿ…�Ÿ0ÿš�Ÿ1ÿ…�Ÿ2ÿš�Ÿ3ÿ…�Ÿ4ÿš�Ÿ6ÿš�Ÿ8ÿš�Ÿ:ÿš�Ÿ<ÿš�Ÿ@ÿš�ŸBÿš�ŸDÿš�ŸIÿ×�ŸJÿš�ŸKÿ×�ŸLÿš�ŸMÿ×�ŸNÿš�ŸOÿ×�ŸQÿ×�ŸRÿš�ŸSÿ×�ŸTÿš�ŸUÿ×�ŸVÿš�ŸWÿ×�ŸXÿš�ŸYÿ×�ŸZÿš�Ÿ[ÿ×�Ÿ\ÿš�Ÿ]ÿ×�Ÿ^ÿš�Ÿ_ÿ×�Ÿ`ÿš�ŸbÿÃ�ŸdÿÃ�ŸfÿÃ�ŸhÿÃ�ŸjÿÃ�ŸlÿÃ�ŸnÿÃ� �þö� �þö� �$ÿš� �;ÿ×� �=ÿì� �‚ÿš� �ƒÿš� �„ÿš� �…ÿš� �†ÿš� �‡ÿš� �Âÿš� �Äÿš� �Æÿš� ;ÿì� =ÿì� ?ÿì� Cÿš� þö�  þö� Xÿš� ÿš� ÿš� !ÿš� #ÿš� %ÿš� 'ÿš� )ÿš� +ÿš� -ÿš� /ÿš� 1ÿš� 3ÿš�¢�ÿì�¢� ÿì�¢ÿì�¢ ÿì�£�ÿì�£� ÿì�£ÿì�£ ÿì�¤�ÿì�¤� ÿì�¤ÿì�¤ ÿì�¥�ÿì�¥� ÿì�¥ÿì�¥ ÿì�¦�ÿì�¦� ÿì�¦ÿì�¦ ÿì�§�ÿì�§� ÿì�§ÿì�§ ÿì�ª�ÿì�ª� ÿì�ª�Yÿ×�ª�Zÿ×�ª�[ÿ×�ª�\ÿ×�ª�]ÿì�ª�¿ÿ×�ª7ÿ×�ª<ÿì�ª>ÿì�ª@ÿì�ªûÿ×�ªýÿ×�ªÿì�ª ÿì�ªpÿ×�«�ÿì�«� ÿì�«�Yÿ×�«�Zÿ×�«�[ÿ×�«�\ÿ×�«�]ÿì�«�¿ÿ×�«7ÿ×�«<ÿì�«>ÿì�«@ÿì�«ûÿ×�«ýÿ×�«ÿì�« ÿì�«pÿ×�¬�ÿì�¬� ÿì�¬�Yÿ×�¬�Zÿ×�¬�[ÿ×�¬�\ÿ×�¬�]ÿì�¬�¿ÿ×�¬7ÿ×�¬<ÿì�¬>ÿì�¬@ÿì�¬ûÿ×�¬ýÿ×�¬ÿì�¬ ÿì�¬pÿ×�­�ÿì�­� ÿì�­�Yÿ×�­�Zÿ×�­�[ÿ×�­�\ÿ×�­�]ÿì�­�¿ÿ×�­7ÿ×�­<ÿì�­>ÿì�­@ÿì�­ûÿ×�­ýÿ×�­ÿì�­ ÿì�­pÿ×�²�ÿì�²� ÿì�²�Yÿ×�²�Zÿ×�²�[ÿ×�²�\ÿ×�²�]ÿì�²�¿ÿ×�²7ÿ×�²<ÿì�²>ÿì�²@ÿì�²ûÿ×�²ýÿ×�²ÿì�² ÿì�²pÿ×�´�ÿì�´� ÿì�´�Yÿ×�´�Zÿ×�´�[ÿ×�´�\ÿ×�´�]ÿì�´�¿ÿ×�´7ÿ×�´<ÿì�´>ÿì�´@ÿì�´ûÿ×�´ýÿ×�´ÿì�´ ÿì�´pÿ×�µ�ÿì�µ� ÿì�µ�Yÿ×�µ�Zÿ×�µ�[ÿ×�µ�\ÿ×�µ�]ÿì�µ�¿ÿ×�µ7ÿ×�µ<ÿì�µ>ÿì�µ@ÿì�µûÿ×�µýÿ×�µÿì�µ ÿì�µpÿ×�¶�ÿì�¶� ÿì�¶�Yÿ×�¶�Zÿ×�¶�[ÿ×�¶�\ÿ×�¶�]ÿì�¶�¿ÿ×�¶7ÿ×�¶<ÿì�¶>ÿì�¶@ÿì�¶ûÿ×�¶ýÿ×�¶ÿì�¶ ÿì�¶pÿ×�¸�ÿ×�¸� ÿ×�¸ÿ×�¸ ÿ×�º�ÿì�º� ÿì�º�Yÿ×�º�Zÿ×�º�[ÿ×�º�\ÿ×�º�]ÿì�º�¿ÿ×�º7ÿ×�º<ÿì�º>ÿì�º@ÿì�ºûÿ×�ºýÿ×�ºÿì�º ÿì�ºpÿ×�¿��R�¿� �R�¿�ÿ®�¿�ÿ®�¿�"�)�¿�R�¿ÿ®�¿ �R�¿ ÿ®�À�ÿì�À� ÿì�À�Yÿ×�À�Zÿ×�À�[ÿ×�À�\ÿ×�À�]ÿì�À�¿ÿ×�À7ÿ×�À<ÿì�À>ÿì�À@ÿì�Àûÿ×�Àýÿ×�Àÿì�À ÿì�Àpÿ×�Á��R�Á� �R�Á�ÿ®�Á�ÿ®�Á�"�)�Á�R�Áÿ®�Á �R�Á ÿ®�Â�ÿq�Â� ÿq�Â�&ÿ×�Â�*ÿ×�Â�- �Â�2ÿ×�Â�4ÿ×�Â�7ÿq�Â�9ÿ®�Â�:ÿ®�Â�<ÿ…�Â�‰ÿ×�Â�”ÿ×�Â�•ÿ×�Â�–ÿ×�Â�—ÿ×�Â�˜ÿ×�Â�šÿ×�Â�Ÿÿ…�Â�Èÿ×�Â�Êÿ×�Â�Ìÿ×�Â�Îÿ×�Â�Þÿ×�Â�àÿ×�Â�âÿ×�Â�äÿ×�Âÿ×�Âÿ×�Âÿ×�Âÿ×�Â$ÿq�Â&ÿq�Â6ÿ®�Â8ÿ…�Â:ÿ…�ÂGÿ×�Âúÿ®�Âüÿ®�Âþÿ®�Â�ÿ…�Âÿq� ÿq�Â_ÿ×�ÂIÿ×�ÂKÿ×�ÂMÿ×�ÂOÿ×�ÂQÿ×�ÂSÿ×�ÂUÿ×�ÂWÿ×�ÂYÿ×�Â[ÿ×�Â]ÿ×�Â_ÿ×�Âoÿ…�Âqÿ…�Âsÿ…�Âÿq�Ã�ÿì�Ã� ÿì�Ãÿì�à ÿì�Ä�ÿq�Ä� ÿq�Ä�&ÿ×�Ä�*ÿ×�Ä�- �Ä�2ÿ×�Ä�4ÿ×�Ä�7ÿq�Ä�9ÿ®�Ä�:ÿ®�Ä�<ÿ…�Ä�‰ÿ×�Ä�”ÿ×�Ä�•ÿ×�Ä�–ÿ×�Ä�—ÿ×�Ä�˜ÿ×�Ä�šÿ×�Ä�Ÿÿ…�Ä�Èÿ×�Ä�Êÿ×�Ä�Ìÿ×�Ä�Îÿ×�Ä�Þÿ×�Ä�àÿ×�Ä�âÿ×�Ä�äÿ×�Äÿ×�Äÿ×�Äÿ×�Äÿ×�Ä$ÿq�Ä&ÿq�Ä6ÿ®�Ä8ÿ…�Ä:ÿ…�ÄGÿ×�Äúÿ®�Äüÿ®�Äþÿ®�Ä�ÿ…�Äÿq�Ä ÿq�Ä_ÿ×�ÄIÿ×�ÄKÿ×�ÄMÿ×�ÄOÿ×�ÄQÿ×�ÄSÿ×�ÄUÿ×�ÄWÿ×�ÄYÿ×�Ä[ÿ×�Ä]ÿ×�Ä_ÿ×�Äoÿ…�Äqÿ…�Äsÿ…�Äÿq�Å�ÿì�Å� ÿì�Åÿì�Å ÿì�Æ�ÿq�Æ� ÿq�Æ�&ÿ×�Æ�*ÿ×�Æ�- �Æ�2ÿ×�Æ�4ÿ×�Æ�7ÿq�Æ�9ÿ®�Æ�:ÿ®�Æ�<ÿ…�Æ�‰ÿ×�Æ�”ÿ×�Æ�•ÿ×�Æ�–ÿ×�Æ�—ÿ×�Æ�˜ÿ×�Æ�šÿ×�Æ�Ÿÿ…�Æ�Èÿ×�Æ�Êÿ×�Æ�Ìÿ×�Æ�Îÿ×�Æ�Þÿ×�Æ�àÿ×�Æ�âÿ×�Æ�äÿ×�Æÿ×�Æÿ×�Æÿ×�Æÿ×�Æ$ÿq�Æ&ÿq�Æ6ÿ®�Æ8ÿ…�Æ:ÿ…�ÆGÿ×�Æúÿ®�Æüÿ®�Æþÿ®�Æ�ÿ…�Æÿq�Æ ÿq�Æ_ÿ×�ÆIÿ×�ÆKÿ×�ÆMÿ×�ÆOÿ×�ÆQÿ×�ÆSÿ×�ÆUÿ×�ÆWÿ×�ÆYÿ×�Æ[ÿ×�Æ]ÿ×�Æ_ÿ×�Æoÿ…�Æqÿ…�Æsÿ…�Æÿq�Ç�ÿì�Ç� ÿì�Çÿì�Ç ÿì�È�&ÿ×�È�*ÿ×�È�2ÿ×�È�4ÿ×�È�‰ÿ×�È�”ÿ×�È�•ÿ×�È�–ÿ×�È�—ÿ×�È�˜ÿ×�È�šÿ×�È�Èÿ×�È�Êÿ×�È�Ìÿ×�È�Îÿ×�È�Þÿ×�È�àÿ×�È�âÿ×�È�äÿ×�Èÿ×�Èÿ×�Èÿ×�Èÿ×�ÈGÿ×�È_ÿ×�ÈIÿ×�ÈKÿ×�ÈMÿ×�ÈOÿ×�ÈQÿ×�ÈSÿ×�ÈUÿ×�ÈWÿ×�ÈYÿ×�È[ÿ×�È]ÿ×�È_ÿ×�Ê�&ÿ×�Ê�*ÿ×�Ê�2ÿ×�Ê�4ÿ×�Ê�‰ÿ×�Ê�”ÿ×�Ê�•ÿ×�Ê�–ÿ×�Ê�—ÿ×�Ê�˜ÿ×�Ê�šÿ×�Ê�Èÿ×�Ê�Êÿ×�Ê�Ìÿ×�Ê�Îÿ×�Ê�Þÿ×�Ê�àÿ×�Ê�âÿ×�Ê�äÿ×�Êÿ×�Êÿ×�Êÿ×�Êÿ×�ÊGÿ×�Ê_ÿ×�ÊIÿ×�ÊKÿ×�ÊMÿ×�ÊOÿ×�ÊQÿ×�ÊSÿ×�ÊUÿ×�ÊWÿ×�ÊYÿ×�Ê[ÿ×�Ê]ÿ×�Ê_ÿ×�Ì�&ÿ×�Ì�*ÿ×�Ì�2ÿ×�Ì�4ÿ×�Ì�‰ÿ×�Ì�”ÿ×�Ì�•ÿ×�Ì�–ÿ×�Ì�—ÿ×�Ì�˜ÿ×�Ì�šÿ×�Ì�Èÿ×�Ì�Êÿ×�Ì�Ìÿ×�Ì�Îÿ×�Ì�Þÿ×�Ì�àÿ×�Ì�âÿ×�Ì�äÿ×�Ìÿ×�Ìÿ×�Ìÿ×�Ìÿ×�ÌGÿ×�Ì_ÿ×�ÌIÿ×�ÌKÿ×�ÌMÿ×�ÌOÿ×�ÌQÿ×�ÌSÿ×�ÌUÿ×�ÌWÿ×�ÌYÿ×�Ì[ÿ×�Ì]ÿ×�Ì_ÿ×�Î�&ÿ×�Î�*ÿ×�Î�2ÿ×�Î�4ÿ×�Î�‰ÿ×�Î�”ÿ×�Î�•ÿ×�Î�–ÿ×�Î�—ÿ×�Î�˜ÿ×�Î�šÿ×�Î�Èÿ×�Î�Êÿ×�Î�Ìÿ×�Î�Îÿ×�Î�Þÿ×�Î�àÿ×�Î�âÿ×�Î�äÿ×�Îÿ×�Îÿ×�Îÿ×�Îÿ×�ÎGÿ×�Î_ÿ×�ÎIÿ×�ÎKÿ×�ÎMÿ×�ÎOÿ×�ÎQÿ×�ÎSÿ×�ÎUÿ×�ÎWÿ×�ÎYÿ×�Î[ÿ×�Î]ÿ×�Î_ÿ×�Ð�ÿ®�Ð�ÿ®�Ð�$ÿ×�Ð�7ÿÃ�Ð�9ÿì�Ð�:ÿì�Ð�;ÿ×�Ð�<ÿì�Ð�=ÿì�Ð�‚ÿ×�Ð�ƒÿ×�Ð�„ÿ×�Ð�…ÿ×�Ð�†ÿ×�Ð�‡ÿ×�Ð�Ÿÿì�Ð�Âÿ×�Ð�Äÿ×�Ð�Æÿ×�Ð$ÿÃ�Ð&ÿÃ�Ð6ÿì�Ð8ÿì�Ð:ÿì�Ð;ÿì�Ð=ÿì�Ð?ÿì�ÐCÿ×�Рÿì�Ðúÿì�Ðüÿì�Ðþÿì�Ð�ÿì�Ðÿ®�Ð ÿ®�ÐXÿ×�Ðÿ×�Ðÿ×�Ð!ÿ×�Ð#ÿ×�Ð%ÿ×�Ð'ÿ×�Ð)ÿ×�Ð+ÿ×�Ð-ÿ×�Ð/ÿ×�Ð1ÿ×�Ð3ÿ×�Ðoÿì�Ðqÿì�Ðsÿì�ÐÿÃ�Ñ��R�Ñ� �R�Ñ� ��Ñ�"�¤�Ñ�@��Ñ�E�=�Ñ�K�=�Ñ�N�=�Ñ�O�=�Ñ�`��Ñ�ç�=�Ñ�é�{�Ñ�R�Ñ �R�Ò�ÿ®�Ò�ÿ®�Ò�$ÿ×�Ò�7ÿÃ�Ò�9ÿì�Ò�:ÿì�Ò�;ÿ×�Ò�<ÿì�Ò�=ÿì�Ò�‚ÿ×�Ò�ƒÿ×�Ò�„ÿ×�Ò�…ÿ×�Ò�†ÿ×�Ò�‡ÿ×�Ò�Ÿÿì�Ò�Âÿ×�Ò�Äÿ×�Ò�Æÿ×�Ò$ÿÃ�Ò&ÿÃ�Ò6ÿì�Ò8ÿì�Ò:ÿì�Ò;ÿì�Ò=ÿì�Ò?ÿì�ÒCÿ×�Ò ÿì�Òúÿì�Òüÿì�Òþÿì�Ò�ÿì�Òÿ®�Ò ÿ®�ÒXÿ×�Òÿ×�Òÿ×�Ò!ÿ×�Ò#ÿ×�Ò%ÿ×�Ò'ÿ×�Ò)ÿ×�Ò+ÿ×�Ò-ÿ×�Ò/ÿ×�Ò1ÿ×�Ò3ÿ×�Òoÿì�Òqÿì�Òsÿì�ÒÿÃ�Ô�-�{�Õ�ÿì�Õ� ÿì�Õ�Yÿ×�Õ�Zÿ×�Õ�[ÿ×�Õ�\ÿ×�Õ�]ÿì�Õ�¿ÿ×�Õ7ÿ×�Õ<ÿì�Õ>ÿì�Õ@ÿì�Õûÿ×�Õýÿ×�Õÿì�Õ ÿì�Õpÿ×�Ö�-�{�×�ÿì�×� ÿì�×�Yÿ×�×�Zÿ×�×�[ÿ×�×�\ÿ×�×�]ÿì�×�¿ÿ×�×7ÿ×�×<ÿì�×>ÿì�×@ÿì�×ûÿ×�×ýÿ×�×ÿì�× ÿì�×pÿ×�Ø�-�{�Ù�ÿì�Ù� ÿì�Ù�Yÿ×�Ù�Zÿ×�Ù�[ÿ×�Ù�\ÿ×�Ù�]ÿì�Ù�¿ÿ×�Ù7ÿ×�Ù<ÿì�Ù>ÿì�Ù@ÿì�Ùûÿ×�Ùýÿ×�Ùÿì�Ù ÿì�Ùpÿ×�Ú�-�{�Û�ÿì�Û� ÿì�Û�Yÿ×�Û�Zÿ×�Û�[ÿ×�Û�\ÿ×�Û�]ÿì�Û�¿ÿ×�Û7ÿ×�Û<ÿì�Û>ÿì�Û@ÿì�Ûûÿ×�Ûýÿ×�Ûÿì�Û ÿì�Ûpÿ×�Ü�-�{�Ý�ÿì�Ý� ÿì�Ý�Yÿ×�Ý�Zÿ×�Ý�[ÿ×�Ý�\ÿ×�Ý�]ÿì�Ý�¿ÿ×�Ý7ÿ×�Ý<ÿì�Ý>ÿì�Ý@ÿì�Ýûÿ×�Ýýÿ×�Ýÿì�Ý ÿì�Ýpÿ×�ç�ÿì�ç� ÿì�çÿì�ç ÿì�ø�&ÿ×�ø�*ÿ×�ø�2ÿ×�ø�4ÿ×�ø�‰ÿ×�ø�”ÿ×�ø�•ÿ×�ø�–ÿ×�ø�—ÿ×�ø�˜ÿ×�ø�šÿ×�ø�Èÿ×�ø�Êÿ×�ø�Ìÿ×�ø�Îÿ×�ø�Þÿ×�ø�àÿ×�ø�âÿ×�ø�äÿ×�øÿ×�øÿ×�øÿ×�øÿ×�øGÿ×�ø_ÿ×�øIÿ×�øKÿ×�øMÿ×�øOÿ×�øQÿ×�øSÿ×�øUÿ×�øWÿ×�øYÿ×�ø[ÿ×�ø]ÿ×�ø_ÿ×�ù�Fÿ×�ù�Gÿ×�ù�Hÿ×�ù�Rÿ×�ù�Tÿ×�ù�¢ÿ×�ù�©ÿ×�ù�ªÿ×�ù�«ÿ×�ù�¬ÿ×�ù�­ÿ×�ù�´ÿ×�ù�µÿ×�ù�¶ÿ×�ù�·ÿ×�ù�¸ÿ×�ù�ºÿ×�ù�Éÿ×�ù�Ëÿ×�ù�Íÿ×�ù�Ïÿ×�ù�Ñÿ×�ù�Óÿ×�ù�Õÿ×�ù�×ÿ×�ù�Ùÿ×�ù�Ûÿ×�ù�Ýÿ×�ùÿ×�ùÿ×�ùÿ×�ùÿ×�ùHÿ×�ù`ÿ×�ù6ÿ×�ù8ÿ×�ù:ÿ×�ù<ÿ×�ù@ÿ×�ùBÿ×�ùDÿ×�ùJÿ×�ùLÿ×�ùNÿ×�ùRÿ×�ùTÿ×�ùVÿ×�ùXÿ×�ùZÿ×�ù\ÿ×�ù^ÿ×�ù`ÿ×�ú�Fÿ×�ú�Gÿ×�ú�Hÿ×�ú�Rÿ×�ú�Tÿ×�ú�¢ÿ×�ú�©ÿ×�ú�ªÿ×�ú�«ÿ×�ú�¬ÿ×�ú�­ÿ×�ú�´ÿ×�ú�µÿ×�ú�¶ÿ×�ú�·ÿ×�ú�¸ÿ×�ú�ºÿ×�ú�Éÿ×�ú�Ëÿ×�ú�Íÿ×�ú�Ïÿ×�ú�Ñÿ×�ú�Óÿ×�ú�Õÿ×�ú�×ÿ×�ú�Ùÿ×�ú�Ûÿ×�ú�Ýÿ×�úÿ×�úÿ×�úÿ×�úÿ×�úHÿ×�ú`ÿ×�ú6ÿ×�ú8ÿ×�ú:ÿ×�ú<ÿ×�ú@ÿ×�úBÿ×�úDÿ×�úJÿ×�úLÿ×�úNÿ×�úRÿ×�úTÿ×�úVÿ×�úXÿ×�úZÿ×�ú\ÿ×�ú^ÿ×�ú`ÿ×�û�ÿ\�û� ÿ\�û�&ÿ×�û�*ÿ×�û�2ÿ×�û�4ÿ×�û�7ÿ×�û�8ÿì�û�9ÿ×�û�:ÿ×�û�<ÿÃ�û�‰ÿ×�û�”ÿ×�û�•ÿ×�û�–ÿ×�û�—ÿ×�û�˜ÿ×�û�šÿ×�û�›ÿì�û�œÿì�û�ÿì�û�žÿì�û�ŸÿÃ�û�Èÿ×�û�Êÿ×�û�Ìÿ×�û�Îÿ×�û�Þÿ×�û�àÿ×�û�âÿ×�û�äÿ×�ûÿ×�ûÿ×�ûÿ×�ûÿ×�û$ÿ×�û&ÿ×�û*ÿì�û,ÿì�û.ÿì�û0ÿì�û2ÿì�û4ÿì�û6ÿ×�û8ÿÃ�û:ÿÃ�ûGÿ×�ûúÿ×�ûüÿ×�ûþÿ×�û�ÿÃ�ûÿ\�û ÿ\�û_ÿ×�ûaÿì�ûIÿ×�ûKÿ×�ûMÿ×�ûOÿ×�ûQÿ×�ûSÿ×�ûUÿ×�ûWÿ×�ûYÿ×�û[ÿ×�û]ÿ×�û_ÿ×�ûaÿì�ûcÿì�ûeÿì�ûgÿì�ûiÿì�ûkÿì�ûmÿì�ûoÿÃ�ûqÿÃ�ûsÿÃ�ûÿ×�ý�ÿ\�ý� ÿ\�ý�&ÿ×�ý�*ÿ×�ý�2ÿ×�ý�4ÿ×�ý�7ÿ×�ý�8ÿì�ý�9ÿ×�ý�:ÿ×�ý�<ÿÃ�ý�‰ÿ×�ý�”ÿ×�ý�•ÿ×�ý�–ÿ×�ý�—ÿ×�ý�˜ÿ×�ý�šÿ×�ý�›ÿì�ý�œÿì�ý�ÿì�ý�žÿì�ý�ŸÿÃ�ý�Èÿ×�ý�Êÿ×�ý�Ìÿ×�ý�Îÿ×�ý�Þÿ×�ý�àÿ×�ý�âÿ×�ý�äÿ×�ýÿ×�ýÿ×�ýÿ×�ýÿ×�ý$ÿ×�ý&ÿ×�ý*ÿì�ý,ÿì�ý.ÿì�ý0ÿì�ý2ÿì�ý4ÿì�ý6ÿ×�ý8ÿÃ�ý:ÿÃ�ýGÿ×�ýúÿ×�ýüÿ×�ýþÿ×�ý�ÿÃ�ýÿ\�ý ÿ\�ý_ÿ×�ýaÿì�ýIÿ×�ýKÿ×�ýMÿ×�ýOÿ×�ýQÿ×�ýSÿ×�ýUÿ×�ýWÿ×�ýYÿ×�ý[ÿ×�ý]ÿ×�ý_ÿ×�ýaÿì�ýcÿì�ýeÿì�ýgÿì�ýiÿì�ýkÿì�ýmÿì�ýoÿÃ�ýqÿÃ�ýsÿÃ�ýÿ×�ÿ�ÿ\�ÿ� ÿ\�ÿ�&ÿ×�ÿ�*ÿ×�ÿ�2ÿ×�ÿ�4ÿ×�ÿ�7ÿ×�ÿ�8ÿì�ÿ�9ÿ×�ÿ�:ÿ×�ÿ�<ÿÃ�ÿ�‰ÿ×�ÿ�”ÿ×�ÿ�•ÿ×�ÿ�–ÿ×�ÿ�—ÿ×�ÿ�˜ÿ×�ÿ�šÿ×�ÿ�›ÿì�ÿ�œÿì�ÿ�ÿì�ÿ�žÿì�ÿ�ŸÿÃ�ÿ�Èÿ×�ÿ�Êÿ×�ÿ�Ìÿ×�ÿ�Îÿ×�ÿ�Þÿ×�ÿ�àÿ×�ÿ�âÿ×�ÿ�äÿ×�ÿÿ×�ÿÿ×�ÿÿ×�ÿÿ×�ÿ$ÿ×�ÿ&ÿ×�ÿ*ÿì�ÿ,ÿì�ÿ.ÿì�ÿ0ÿì�ÿ2ÿì�ÿ4ÿì�ÿ6ÿ×�ÿ8ÿÃ�ÿ:ÿÃ�ÿGÿ×�ÿúÿ×�ÿüÿ×�ÿþÿ×�ÿ�ÿÃ�ÿÿ\�ÿ ÿ\�ÿ_ÿ×�ÿaÿì�ÿIÿ×�ÿKÿ×�ÿMÿ×�ÿOÿ×�ÿQÿ×�ÿSÿ×�ÿUÿ×�ÿWÿ×�ÿYÿ×�ÿ[ÿ×�ÿ]ÿ×�ÿ_ÿ×�ÿaÿì�ÿcÿì�ÿeÿì�ÿgÿì�ÿiÿì�ÿkÿì�ÿmÿì�ÿoÿÃ�ÿqÿÃ�ÿsÿÃ�ÿÿ×���R�� �R�� ���"���@���E�=��K�=��N�=��O�=��`���ç�=��é���R� �R�ÿ\� ÿ\�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7ÿ×�8ÿì�9ÿ×�:ÿ×�<ÿÃ�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�›ÿì�œÿì�ÿì�žÿì�ŸÿÃ�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿ×&ÿ×*ÿì,ÿì.ÿì0ÿì2ÿì4ÿì6ÿ×8ÿÃ:ÿÃGÿ×úÿ×üÿ×þÿ×�ÿÃÿ\ ÿ\_ÿ×aÿìIÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×aÿìcÿìeÿìgÿìiÿìkÿìmÿìoÿÃqÿÃsÿÃÿ×�ÿ\� ÿ\�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7ÿ×�8ÿì�9ÿ×�:ÿ×�<ÿÃ�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�›ÿì�œÿì�ÿì�žÿì�ŸÿÃ�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿ×&ÿ×*ÿì,ÿì.ÿì0ÿì2ÿì4ÿì6ÿ×8ÿÃ:ÿÃGÿ×úÿ×üÿ×þÿ×�ÿÃÿ\ ÿ\_ÿ×aÿìIÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×aÿìcÿìeÿìgÿìiÿìkÿìmÿìoÿÃqÿÃsÿÃÿ×�ÿì� ÿìÿì ÿì�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�-�{��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×$�ÿ…$�ÿ®$�ÿ…$�"�)$�$ÿq$�&ÿ×$�*ÿ×$�2ÿ×$�4ÿ×$�7�)$�Dÿ\$�Fÿq$�Gÿq$�Hÿq$�Jÿq$�Pÿš$�Qÿš$�Rÿq$�Sÿš$�Tÿq$�Uÿš$�Vÿ…$�Xÿš$�Yÿ×$�Zÿ×$�[ÿ×$�\ÿ×$�]ÿ®$�‚ÿq$�ƒÿq$�„ÿq$�…ÿq$�†ÿq$�‡ÿq$�‰ÿ×$�”ÿ×$�•ÿ×$�–ÿ×$�—ÿ×$�˜ÿ×$�šÿ×$�¢ÿq$�£ÿ\$�¤ÿ\$�¥ÿ\$�¦ÿ\$�§ÿ\$�¨ÿ\$�©ÿq$�ªÿq$�«ÿq$�¬ÿq$�­ÿq$�´ÿq$�µÿq$�¶ÿq$�·ÿq$�¸ÿq$�ºÿq$�»ÿš$�¼ÿš$�½ÿš$�¾ÿš$�¿ÿ×$�Âÿq$�Ãÿ\$�Äÿq$�Åÿ\$�Æÿq$�Çÿ\$�Èÿ×$�Éÿq$�Êÿ×$�Ëÿq$�Ìÿ×$�Íÿq$�Îÿ×$�Ïÿq$�Ñÿq$�Óÿq$�Õÿq$�×ÿq$�Ùÿq$�Ûÿq$�Ýÿq$�Þÿ×$�ßÿq$�àÿ×$�áÿq$�âÿ×$�ãÿq$�äÿ×$�åÿq$�úÿš$ÿš$ÿš$ ÿš$ÿ×$ÿq$ÿ×$ÿq$ÿ×$ÿq$ÿ×$ÿq$ÿš$ÿš$ÿ…$!ÿ…$$�)$&�)$+ÿš$-ÿš$/ÿš$1ÿš$3ÿš$5ÿš$7ÿ×$<ÿ®$>ÿ®$@ÿ®$Cÿq$Dÿ\$Fÿ\$Gÿ×$Hÿq$Jÿ…$ûÿ×$ýÿ×$ÿ®$ÿ®$ÿ®$ÿ…$ ÿ…$Wÿš$Xÿq$Yÿ\$_ÿ×$`ÿq$bÿš$ÿq$ÿ\$ÿq$ ÿ\$!ÿq$"ÿ\$#ÿq$%ÿq$&ÿ\$'ÿq$(ÿ\$)ÿq$*ÿ\$+ÿq$,ÿ\$-ÿq$.ÿ\$/ÿq$0ÿ\$1ÿq$2ÿ\$3ÿq$4ÿ\$6ÿq$8ÿq$:ÿq$<ÿq$@ÿq$Bÿq$Dÿq$Iÿ×$Jÿq$Kÿ×$Lÿq$Mÿ×$Nÿq$Oÿ×$Qÿ×$Rÿq$Sÿ×$Tÿq$Uÿ×$Vÿq$Wÿ×$Xÿq$Yÿ×$Zÿq$[ÿ×$\ÿq$]ÿ×$^ÿq$_ÿ×$`ÿq$bÿš$dÿš$fÿš$hÿš$jÿš$lÿš$nÿš$pÿ×$�)%��)%� �)%�)% �)&�ÿ…&�ÿ®&�ÿ…&�"�)&�$ÿq&�&ÿ×&�*ÿ×&�2ÿ×&�4ÿ×&�7�)&�Dÿ\&�Fÿq&�Gÿq&�Hÿq&�Jÿq&�Pÿš&�Qÿš&�Rÿq&�Sÿš&�Tÿq&�Uÿš&�Vÿ…&�Xÿš&�Yÿ×&�Zÿ×&�[ÿ×&�\ÿ×&�]ÿ®&�‚ÿq&�ƒÿq&�„ÿq&�…ÿq&�†ÿq&�‡ÿq&�‰ÿ×&�”ÿ×&�•ÿ×&�–ÿ×&�—ÿ×&�˜ÿ×&�šÿ×&�¢ÿq&�£ÿ\&�¤ÿ\&�¥ÿ\&�¦ÿ\&�§ÿ\&�¨ÿ\&�©ÿq&�ªÿq&�«ÿq&�¬ÿq&�­ÿq&�´ÿq&�µÿq&�¶ÿq&�·ÿq&�¸ÿq&�ºÿq&�»ÿš&�¼ÿš&�½ÿš&�¾ÿš&�¿ÿ×&�Âÿq&�Ãÿ\&�Äÿq&�Åÿ\&�Æÿq&�Çÿ\&�Èÿ×&�Éÿq&�Êÿ×&�Ëÿq&�Ìÿ×&�Íÿq&�Îÿ×&�Ïÿq&�Ñÿq&�Óÿq&�Õÿq&�×ÿq&�Ùÿq&�Ûÿq&�Ýÿq&�Þÿ×&�ßÿq&�àÿ×&�áÿq&�âÿ×&�ãÿq&�äÿ×&�åÿq&�úÿš&ÿš&ÿš& ÿš&ÿ×&ÿq&ÿ×&ÿq&ÿ×&ÿq&ÿ×&ÿq&ÿš&ÿš&ÿ…&!ÿ…&$�)&&�)&+ÿš&-ÿš&/ÿš&1ÿš&3ÿš&5ÿš&7ÿ×&<ÿ®&>ÿ®&@ÿ®&Cÿq&Dÿ\&Fÿ\&Gÿ×&Hÿq&Jÿ…&ûÿ×&ýÿ×&ÿ®&ÿ®&ÿ®&ÿ…& ÿ…&Wÿš&Xÿq&Yÿ\&_ÿ×&`ÿq&bÿš&ÿq&ÿ\&ÿq& ÿ\&!ÿq&"ÿ\&#ÿq&%ÿq&&ÿ\&'ÿq&(ÿ\&)ÿq&*ÿ\&+ÿq&,ÿ\&-ÿq&.ÿ\&/ÿq&0ÿ\&1ÿq&2ÿ\&3ÿq&4ÿ\&6ÿq&8ÿq&:ÿq&<ÿq&@ÿq&Bÿq&Dÿq&Iÿ×&Jÿq&Kÿ×&Lÿq&Mÿ×&Nÿq&Oÿ×&Qÿ×&Rÿq&Sÿ×&Tÿq&Uÿ×&Vÿq&Wÿ×&Xÿq&Yÿ×&Zÿq&[ÿ×&\ÿq&]ÿ×&^ÿq&_ÿ×&`ÿq&bÿš&dÿš&fÿš&hÿš&jÿš&lÿš&nÿš&pÿ×&�)'��)'� �)'�)' �)(�ÿ…(�ÿ®(�ÿ…(�"�)(�$ÿq(�&ÿ×(�*ÿ×(�2ÿ×(�4ÿ×(�7�)(�Dÿ\(�Fÿq(�Gÿq(�Hÿq(�Jÿq(�Pÿš(�Qÿš(�Rÿq(�Sÿš(�Tÿq(�Uÿš(�Vÿ…(�Xÿš(�Yÿ×(�Zÿ×(�[ÿ×(�\ÿ×(�]ÿ®(�‚ÿq(�ƒÿq(�„ÿq(�…ÿq(�†ÿq(�‡ÿq(�‰ÿ×(�”ÿ×(�•ÿ×(�–ÿ×(�—ÿ×(�˜ÿ×(�šÿ×(�¢ÿq(�£ÿ\(�¤ÿ\(�¥ÿ\(�¦ÿ\(�§ÿ\(�¨ÿ\(�©ÿq(�ªÿq(�«ÿq(�¬ÿq(�­ÿq(�´ÿq(�µÿq(�¶ÿq(�·ÿq(�¸ÿq(�ºÿq(�»ÿš(�¼ÿš(�½ÿš(�¾ÿš(�¿ÿ×(�Âÿq(�Ãÿ\(�Äÿq(�Åÿ\(�Æÿq(�Çÿ\(�Èÿ×(�Éÿq(�Êÿ×(�Ëÿq(�Ìÿ×(�Íÿq(�Îÿ×(�Ïÿq(�Ñÿq(�Óÿq(�Õÿq(�×ÿq(�Ùÿq(�Ûÿq(�Ýÿq(�Þÿ×(�ßÿq(�àÿ×(�áÿq(�âÿ×(�ãÿq(�äÿ×(�åÿq(�úÿš(ÿš(ÿš( ÿš(ÿ×(ÿq(ÿ×(ÿq(ÿ×(ÿq(ÿ×(ÿq(ÿš(ÿš(ÿ…(!ÿ…($�)(&�)(+ÿš(-ÿš(/ÿš(1ÿš(3ÿš(5ÿš(7ÿ×(<ÿ®(>ÿ®(@ÿ®(Cÿq(Dÿ\(Fÿ\(Gÿ×(Hÿq(Jÿ…(ûÿ×(ýÿ×(ÿ®(ÿ®(ÿ®(ÿ…( ÿ…(Wÿš(Xÿq(Yÿ\(_ÿ×(`ÿq(bÿš(ÿq(ÿ\(ÿq( ÿ\(!ÿq("ÿ\(#ÿq(%ÿq(&ÿ\('ÿq((ÿ\()ÿq(*ÿ\(+ÿq(,ÿ\(-ÿq(.ÿ\(/ÿq(0ÿ\(1ÿq(2ÿ\(3ÿq(4ÿ\(6ÿq(8ÿq(:ÿq(<ÿq(@ÿq(Bÿq(Dÿq(Iÿ×(Jÿq(Kÿ×(Lÿq(Mÿ×(Nÿq(Oÿ×(Qÿ×(Rÿq(Sÿ×(Tÿq(Uÿ×(Vÿq(Wÿ×(Xÿq(Yÿ×(Zÿq([ÿ×(\ÿq(]ÿ×(^ÿq(_ÿ×(`ÿq(bÿš(dÿš(fÿš(hÿš(jÿš(lÿš(nÿš(pÿ×(�)*�ÿ×*�ÿ×*�$ÿì*�‚ÿì*�ƒÿì*�„ÿì*�…ÿì*�†ÿì*�‡ÿì*�Âÿì*�Äÿì*�Æÿì*Cÿì*ÿ×* ÿ×*Xÿì*ÿì*ÿì*!ÿì*#ÿì*%ÿì*'ÿì*)ÿì*+ÿì*-ÿì*/ÿì*1ÿì*3ÿì,�ÿ×,�ÿ×,�$ÿì,�‚ÿì,�ƒÿì,�„ÿì,�…ÿì,�†ÿì,�‡ÿì,�Âÿì,�Äÿì,�Æÿì,Cÿì,ÿ×, ÿ×,Xÿì,ÿì,ÿì,!ÿì,#ÿì,%ÿì,'ÿì,)ÿì,+ÿì,-ÿì,/ÿì,1ÿì,3ÿì.�ÿ×.�ÿ×.�$ÿì.�‚ÿì.�ƒÿì.�„ÿì.�…ÿì.�†ÿì.�‡ÿì.�Âÿì.�Äÿì.�Æÿì.Cÿì.ÿ×. ÿ×.Xÿì.ÿì.ÿì.!ÿì.#ÿì.%ÿì.'ÿì.)ÿì.+ÿì.-ÿì./ÿì.1ÿì.3ÿì0�ÿ×0�ÿ×0�$ÿì0�‚ÿì0�ƒÿì0�„ÿì0�…ÿì0�†ÿì0�‡ÿì0�Âÿì0�Äÿì0�Æÿì0Cÿì0ÿ×0 ÿ×0Xÿì0ÿì0ÿì0!ÿì0#ÿì0%ÿì0'ÿì0)ÿì0+ÿì0-ÿì0/ÿì01ÿì03ÿì2�ÿ×2�ÿ×2�$ÿì2�‚ÿì2�ƒÿì2�„ÿì2�…ÿì2�†ÿì2�‡ÿì2�Âÿì2�Äÿì2�Æÿì2Cÿì2ÿ×2 ÿ×2Xÿì2ÿì2ÿì2!ÿì2#ÿì2%ÿì2'ÿì2)ÿì2+ÿì2-ÿì2/ÿì21ÿì23ÿì4�ÿ×4�ÿ×4�$ÿì4�‚ÿì4�ƒÿì4�„ÿì4�…ÿì4�†ÿì4�‡ÿì4�Âÿì4�Äÿì4�Æÿì4Cÿì4ÿ×4 ÿ×4Xÿì4ÿì4ÿì4!ÿì4#ÿì4%ÿì4'ÿì4)ÿì4+ÿì4-ÿì4/ÿì41ÿì43ÿì6�ÿš6�ÿš6�"�)6�$ÿ®6�&ÿì6�*ÿì6�2ÿì6�4ÿì6�Dÿ×6�Fÿ×6�Gÿ×6�Hÿ×6�Jÿì6�Pÿì6�Qÿì6�Rÿ×6�Sÿì6�Tÿ×6�Uÿì6�Vÿì6�Xÿì6�‚ÿ®6�ƒÿ®6�„ÿ®6�…ÿ®6�†ÿ®6�‡ÿ®6�‰ÿì6�”ÿì6�•ÿì6�–ÿì6�—ÿì6�˜ÿì6�šÿì6�¢ÿ×6�£ÿ×6�¤ÿ×6�¥ÿ×6�¦ÿ×6�§ÿ×6�¨ÿ×6�©ÿ×6�ªÿ×6�«ÿ×6�¬ÿ×6�­ÿ×6�´ÿ×6�µÿ×6�¶ÿ×6�·ÿ×6�¸ÿ×6�ºÿ×6�»ÿì6�¼ÿì6�½ÿì6�¾ÿì6�Âÿ®6�Ãÿ×6�Äÿ®6�Åÿ×6�Æÿ®6�Çÿ×6�Èÿì6�Éÿ×6�Êÿì6�Ëÿ×6�Ìÿì6�Íÿ×6�Îÿì6�Ïÿ×6�Ñÿ×6�Óÿ×6�Õÿ×6�×ÿ×6�Ùÿ×6�Ûÿ×6�Ýÿ×6�Þÿì6�ßÿì6�àÿì6�áÿì6�âÿì6�ãÿì6�äÿì6�åÿì6�úÿì6ÿì6ÿì6 ÿì6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿì6ÿì6!ÿì6+ÿì6-ÿì6/ÿì61ÿì63ÿì65ÿì6Cÿ®6Dÿ×6Fÿ×6Gÿì6Hÿ×6Jÿì6ÿš6 ÿš6Wÿì6Xÿ®6Yÿ×6_ÿì6`ÿ×6bÿì6ÿ®6ÿ×6ÿ®6 ÿ×6!ÿ®6"ÿ×6#ÿ®6%ÿ®6&ÿ×6'ÿ®6(ÿ×6)ÿ®6*ÿ×6+ÿ®6,ÿ×6-ÿ®6.ÿ×6/ÿ®60ÿ×61ÿ®62ÿ×63ÿ®64ÿ×66ÿ×68ÿ×6:ÿ×6<ÿ×6@ÿ×6Bÿ×6Dÿ×6Iÿì6Jÿ×6Kÿì6Lÿ×6Mÿì6Nÿ×6Oÿì6Qÿì6Rÿ×6Sÿì6Tÿ×6Uÿì6Vÿ×6Wÿì6Xÿ×6Yÿì6Zÿ×6[ÿì6\ÿ×6]ÿì6^ÿ×6_ÿì6`ÿ×6bÿì6dÿì6fÿì6hÿì6jÿì6lÿì6nÿì7��R7� �R7�ÿ®7�ÿ®7�"�)7�R7ÿ®7 �R7 ÿ®8�ÿ…8�ÿ…8�"�)8�$ÿ…8�&ÿ×8�*ÿ×8�2ÿ×8�4ÿ×8�Dÿš8�Fÿš8�Gÿš8�Hÿš8�Jÿ×8�PÿÃ8�QÿÃ8�Rÿš8�SÿÃ8�Tÿš8�UÿÃ8�Vÿ®8�XÿÃ8�]ÿ×8�‚ÿ…8�ƒÿ…8�„ÿ…8�…ÿ…8�†ÿ…8�‡ÿ…8�‰ÿ×8�”ÿ×8�•ÿ×8�–ÿ×8�—ÿ×8�˜ÿ×8�šÿ×8�¢ÿš8�£ÿš8�¤ÿš8�¥ÿš8�¦ÿš8�§ÿš8�¨ÿš8�©ÿš8�ªÿš8�«ÿš8�¬ÿš8�­ÿš8�´ÿš8�µÿš8�¶ÿš8�·ÿš8�¸ÿš8�ºÿš8�»ÿÃ8�¼ÿÃ8�½ÿÃ8�¾ÿÃ8�Âÿ…8�Ãÿš8�Äÿ…8�Åÿš8�Æÿ…8�Çÿš8�Èÿ×8�Éÿš8�Êÿ×8�Ëÿš8�Ìÿ×8�Íÿš8�Îÿ×8�Ïÿš8�Ñÿš8�Óÿš8�Õÿš8�×ÿš8�Ùÿš8�Ûÿš8�Ýÿš8�Þÿ×8�ßÿ×8�àÿ×8�áÿ×8�âÿ×8�ãÿ×8�äÿ×8�åÿ×8�úÿÃ8ÿÃ8ÿÃ8 ÿÃ8ÿ×8ÿš8ÿ×8ÿš8ÿ×8ÿš8ÿ×8ÿš8ÿÃ8ÿÃ8ÿ®8!ÿ®8+ÿÃ8-ÿÃ8/ÿÃ81ÿÃ83ÿÃ85ÿÃ8<ÿ×8>ÿ×8@ÿ×8Cÿ…8Dÿš8Fÿš8Gÿ×8Hÿš8Jÿ®8ÿ…8 ÿ…8WÿÃ8Xÿ…8Yÿš8_ÿ×8`ÿš8bÿÃ8ÿ…8ÿš8ÿ…8 ÿš8!ÿ…8"ÿš8#ÿ…8%ÿ…8&ÿš8'ÿ…8(ÿš8)ÿ…8*ÿš8+ÿ…8,ÿš8-ÿ…8.ÿš8/ÿ…80ÿš81ÿ…82ÿš83ÿ…84ÿš86ÿš88ÿš8:ÿš8<ÿš8@ÿš8Bÿš8Dÿš8Iÿ×8Jÿš8Kÿ×8Lÿš8Mÿ×8Nÿš8Oÿ×8Qÿ×8Rÿš8Sÿ×8Tÿš8Uÿ×8Vÿš8Wÿ×8Xÿš8Yÿ×8Zÿš8[ÿ×8\ÿš8]ÿ×8^ÿš8_ÿ×8`ÿš8bÿÃ8dÿÃ8fÿÃ8hÿÃ8jÿÃ8lÿÃ8nÿÃ9��R9� �R9�ÿ®9�ÿ®9�"�)9�R9ÿ®9 �R9 ÿ®:�ÿ…:�ÿ…:�"�):�$ÿ…:�&ÿ×:�*ÿ×:�2ÿ×:�4ÿ×:�Dÿš:�Fÿš:�Gÿš:�Hÿš:�Jÿ×:�PÿÃ:�QÿÃ:�Rÿš:�SÿÃ:�Tÿš:�UÿÃ:�Vÿ®:�XÿÃ:�]ÿ×:�‚ÿ…:�ƒÿ…:�„ÿ…:�…ÿ…:�†ÿ…:�‡ÿ…:�‰ÿ×:�”ÿ×:�•ÿ×:�–ÿ×:�—ÿ×:�˜ÿ×:�šÿ×:�¢ÿš:�£ÿš:�¤ÿš:�¥ÿš:�¦ÿš:�§ÿš:�¨ÿš:�©ÿš:�ªÿš:�«ÿš:�¬ÿš:�­ÿš:�´ÿš:�µÿš:�¶ÿš:�·ÿš:�¸ÿš:�ºÿš:�»ÿÃ:�¼ÿÃ:�½ÿÃ:�¾ÿÃ:�Âÿ…:�Ãÿš:�Äÿ…:�Åÿš:�Æÿ…:�Çÿš:�Èÿ×:�Éÿš:�Êÿ×:�Ëÿš:�Ìÿ×:�Íÿš:�Îÿ×:�Ïÿš:�Ñÿš:�Óÿš:�Õÿš:�×ÿš:�Ùÿš:�Ûÿš:�Ýÿš:�Þÿ×:�ßÿ×:�àÿ×:�áÿ×:�âÿ×:�ãÿ×:�äÿ×:�åÿ×:�úÿÃ:ÿÃ:ÿÃ: ÿÃ:ÿ×:ÿš:ÿ×:ÿš:ÿ×:ÿš:ÿ×:ÿš:ÿÃ:ÿÃ:ÿ®:!ÿ®:+ÿÃ:-ÿÃ:/ÿÃ:1ÿÃ:3ÿÃ:5ÿÃ:<ÿ×:>ÿ×:@ÿ×:Cÿ…:Dÿš:Fÿš:Gÿ×:Hÿš:Jÿ®:ÿ…: ÿ…:WÿÃ:Xÿ…:Yÿš:_ÿ×:`ÿš:bÿÃ:ÿ…:ÿš:ÿ…: ÿš:!ÿ…:"ÿš:#ÿ…:%ÿ…:&ÿš:'ÿ…:(ÿš:)ÿ…:*ÿš:+ÿ…:,ÿš:-ÿ…:.ÿš:/ÿ…:0ÿš:1ÿ…:2ÿš:3ÿ…:4ÿš:6ÿš:8ÿš::ÿš:<ÿš:@ÿš:Bÿš:Dÿš:Iÿ×:Jÿš:Kÿ×:Lÿš:Mÿ×:Nÿš:Oÿ×:Qÿ×:Rÿš:Sÿ×:Tÿš:Uÿ×:Vÿš:Wÿ×:Xÿš:Yÿ×:Zÿš:[ÿ×:\ÿš:]ÿ×:^ÿš:_ÿ×:`ÿš:bÿÃ:dÿÃ:fÿÃ:hÿÃ:jÿÃ:lÿÃ:nÿÃ;�&ÿì;�*ÿì;�2ÿì;�4ÿì;�‰ÿì;�”ÿì;�•ÿì;�–ÿì;�—ÿì;�˜ÿì;�šÿì;�Èÿì;�Êÿì;�Ìÿì;�Îÿì;�Þÿì;�àÿì;�âÿì;�äÿì;ÿì;ÿì;ÿì;ÿì;Gÿì;_ÿì;Iÿì;Kÿì;Mÿì;Oÿì;Qÿì;Sÿì;Uÿì;Wÿì;Yÿì;[ÿì;]ÿì;_ÿì=�&ÿì=�*ÿì=�2ÿì=�4ÿì=�‰ÿì=�”ÿì=�•ÿì=�–ÿì=�—ÿì=�˜ÿì=�šÿì=�Èÿì=�Êÿì=�Ìÿì=�Îÿì=�Þÿì=�àÿì=�âÿì=�äÿì=ÿì=ÿì=ÿì=ÿì=Gÿì=_ÿì=Iÿì=Kÿì=Mÿì=Oÿì=Qÿì=Sÿì=Uÿì=Wÿì=Yÿì=[ÿì=]ÿì=_ÿì?�&ÿì?�*ÿì?�2ÿì?�4ÿì?�‰ÿì?�”ÿì?�•ÿì?�–ÿì?�—ÿì?�˜ÿì?�šÿì?�Èÿì?�Êÿì?�Ìÿì?�Îÿì?�Þÿì?�àÿì?�âÿì?�äÿì?ÿì?ÿì?ÿì?ÿì?Gÿì?_ÿì?Iÿì?Kÿì?Mÿì?Oÿì?Qÿì?Sÿì?Uÿì?Wÿì?Yÿì?[ÿì?]ÿì?_ÿìC�ÿqC� ÿqC�&ÿ×C�*ÿ×C�- C�2ÿ×C�4ÿ×C�7ÿqC�9ÿ®C�:ÿ®C�<ÿ…C�‰ÿ×C�”ÿ×C�•ÿ×C�–ÿ×C�—ÿ×C�˜ÿ×C�šÿ×C�Ÿÿ…C�Èÿ×C�Êÿ×C�Ìÿ×C�Îÿ×C�Þÿ×C�àÿ×C�âÿ×C�äÿ×Cÿ×Cÿ×Cÿ×Cÿ×C$ÿqC&ÿqC6ÿ®C8ÿ…C:ÿ…CGÿ×Cúÿ®Cüÿ®Cþÿ®C�ÿ…CÿqC ÿqC_ÿ×CIÿ×CKÿ×CMÿ×COÿ×CQÿ×CSÿ×CUÿ×CWÿ×CYÿ×C[ÿ×C]ÿ×C_ÿ×Coÿ…Cqÿ…Csÿ…CÿqD�ÿìD� ÿìDÿìD ÿìE�-�{G�ÿ®G�ÿ®G�$ÿ×G�7ÿÃG�9ÿìG�:ÿìG�;ÿ×G�<ÿìG�=ÿìG�‚ÿ×G�ƒÿ×G�„ÿ×G�…ÿ×G�†ÿ×G�‡ÿ×G�ŸÿìG�Âÿ×G�Äÿ×G�Æÿ×G$ÿÃG&ÿÃG6ÿìG8ÿìG:ÿìG;ÿìG=ÿìG?ÿìGCÿ×G ÿìGúÿìGüÿìGþÿìG�ÿìGÿ®G ÿ®GXÿ×Gÿ×Gÿ×G!ÿ×G#ÿ×G%ÿ×G'ÿ×G)ÿ×G+ÿ×G-ÿ×G/ÿ×G1ÿ×G3ÿ×GoÿìGqÿìGsÿìGÿÃV�ÿqV� ÿqVfÿ×Vmÿ×VqÿqVrÿ…Vsÿ×Vuÿ®Vxÿ…VÿqV ÿqVTÿ…[�ÿ®[�ÿ®[Vÿ×[_ÿ×[bÿ×[dÿì[iÿ×[pÿì[qÿÃ[rÿì[tÿ×[uÿì[xÿì[ˆÿì[ÿ®[ ÿ®[Tÿì\�ÿ…\�ÿ…\Vÿ…\_ÿ…\bÿ…\fÿ×\iÿ…\mÿ×\sÿÃ\vÿì\yÿš\zÿ®\{ÿÃ\|ÿÃ\}ÿÃ\~ÿš\ÿÃ\‚ÿ®\„ÿÃ\†ÿÃ\‡ÿÃ\‰ÿÃ\Œÿš\Žÿš\ÿš\ÿš\’ÿÃ\“ÿš\•ÿÃ\–ÿÃ\˜ÿÃ\™ÿš\šÿÃ\›ÿÃ\ÿ…\ ÿ…\!ÿì]qÿ×]rÿì]xÿì]Tÿì^�ÿ×^� ÿ×^ÿ×^ ÿ×_�ÿq_� ÿq_fÿ×_mÿ×_qÿq_rÿ…_sÿ×_uÿ®_xÿ…_ÿq_ ÿq_Tÿ…`�ÿ®`�ÿ®`Vÿ×`_ÿ×`bÿ×`iÿ×`tÿ×`ÿ®` ÿ®a�ÿ…a�ÿ®a�ÿ…aVÿ\a_ÿ\abÿ\afÿÃaiÿ\amÿÃasÿšavÿÃayÿqazÿša{ÿša|ÿ®a}ÿša~ÿqa€ÿ×aÿÃa‚ÿša„ÿša†ÿ®a‡ÿša‰ÿšaŠÿ×aŒÿqaŽÿšaÿqaÿqa’ÿša“ÿqa”ÿ×a•ÿša–ÿša˜ÿša™ÿqašÿša›ÿšaÿ®aÿ®aÿ®aÿ…a ÿ…a!ÿÃaSÿ×b�ÿqb� ÿqbfÿ×bmÿ×bqÿqbrÿ…bsÿ×buÿ®bxÿ…bÿqb ÿqbTÿ…dfÿìdmÿìdsÿÃf�ÿ®f�ÿ®fVÿ×f_ÿ×fbÿ×fdÿìfiÿ×fpÿìfqÿÃfrÿìftÿ×fuÿìfxÿìfˆÿìfÿ®f ÿ®fTÿìhfÿ×hmÿ×hsÿÃhÿìh‘ÿìi�ÿqi� ÿqifÿ×imÿ×iqÿqirÿ…isÿ×iuÿ®ixÿ…iÿqi ÿqiTÿ…m�ÿ®m�ÿ®mVÿ×m_ÿ×mbÿ×mdÿìmiÿ×mpÿìmqÿÃmrÿìmtÿ×muÿìmxÿìmˆÿìmÿ®m ÿ®mTÿìo�þöo�þöoVÿšo_ÿšobÿšodÿìoiÿšotÿ×oˆÿ×oþöo þöq�ÿ…q�ÿ®q�ÿ…qVÿ\q_ÿ\qbÿ\qfÿÃqiÿ\qmÿÃqsÿšqvÿÃqyÿqqzÿšq{ÿšq|ÿ®q}ÿšq~ÿqq€ÿ×qÿÃq‚ÿšq„ÿšq†ÿ®q‡ÿšq‰ÿšqŠÿ×qŒÿqqŽÿšqÿqqÿqq’ÿšq“ÿqq”ÿ×q•ÿšq–ÿšq˜ÿšq™ÿqqšÿšq›ÿšqÿ®qÿ®qÿ®qÿ…q ÿ…q!ÿÃqSÿ×r�ÿ…r�ÿ…rVÿ…r_ÿ…rbÿ…rfÿ×riÿ…rmÿ×rsÿÃrvÿìryÿšrzÿ®r{ÿÃr|ÿÃr}ÿÃr~ÿšrÿÃr‚ÿ®r„ÿÃr†ÿÃr‡ÿÃr‰ÿÃrŒÿšrŽÿšrÿšrÿšr’ÿÃr“ÿšr•ÿÃr–ÿÃr˜ÿÃr™ÿšršÿÃr›ÿÃrÿ…r ÿ…r!ÿìs�ÿšs�ÿšsVÿ×s_ÿ×sbÿ×sdÿÃsiÿ×spÿìsqÿ®srÿÃstÿìsxÿÃsˆÿìsÿšs ÿšsTÿÃtfÿ×tmÿ×tsÿÃtÿìt‘ÿìu�ÿ…u�ÿ…uVÿ®u_ÿ®ubÿ®ufÿìuiÿ®umÿìuÿ…u ÿ…vqÿ×vrÿìvxÿìvTÿìx�ÿ…x�ÿ…xVÿ…x_ÿ…xbÿ…xfÿ×xiÿ…xmÿ×xsÿÃxvÿìxyÿšxzÿ®x{ÿÃx|ÿÃx}ÿÃx~ÿšxÿÃx‚ÿ®x„ÿÃx†ÿÃx‡ÿÃx‰ÿÃxŒÿšxŽÿšxÿšxÿšx’ÿÃx“ÿšx•ÿÃx–ÿÃx˜ÿÃx™ÿšxšÿÃx›ÿÃxÿ…x ÿ…x!ÿìyˆ�){�ÿì{� ÿì{ÿì{ ÿì|�ÿ®|� ÿ®|ÿì|‘ÿì|ÿ®| ÿ®~ˆ�)€�ÿ®€�ÿ®€ˆÿì€ÿ®€ ÿ®ƒ�ÿšƒyÿ׃~ÿ׃ÿ׃Œÿ׃ÿ׃ÿ׃ÿ׃‘ÿ׃“ÿ׃™ÿ׃ÿšƒÿšƒÿš„�ÿì„� ÿì„ÿì„ ÿì…�ÿ×…�ÿ×…ÿ×… ÿ׆�ÿ®†� ÿ®†ÿ솑ÿì†ÿ®† ÿ®‡yÿׇ~ÿׇŒÿׇÿׇÿׇ“ÿׇ™ÿ׈�ÿ…ˆ� ÿ…ˆyÿìˆ~ÿ숀ÿ׈Šÿ׈Œÿìˆÿ׈ÿìˆÿ숑ÿ׈“ÿ숙ÿìˆÿ…ˆ ÿ…Š�ÿ®Š�ÿ®ŠˆÿìŠÿ®Š ÿ®Œ�ÿìŒ� ÿ쌀ÿ׌Šÿ׌ÿìŒ ÿìŽ�ÿìŽ� ÿ쎀ÿ׎Šÿ׎ÿìŽ ÿì�ÿì�ÿìÿì ÿì“�ÿì“� ÿì“€ÿדŠÿדÿì“ ÿì”�ÿÔ�ÿ×”�ÿÔyÿ×”~ÿ×”ÿ×”Œÿ×”ÿ×”ÿ×”“ÿ×”™ÿ×”ÿ×”ÿ×”ÿ×”ÿÔ ÿ×�ÿ×—� ÿ×—ÿ×— ÿ×™�ÿì™� ÿ와ÿ×™Šÿ×™ÿì™ ÿì�ÿ®� ÿ®ÿ…¦ÿ…¨ÿ×¼ÿš½ÿ×ÁÿšÄÿ…Üÿ×Ýÿ×áÿ×äÿ×öÿ×ÿ® ÿ®nÿ®|ÿš€ÿ®‚ÿ®—ÿ®›ÿ®§ÿ®©ÿ…ªÿ×µÿš¶ÿ×·ÿš¸ÿ×¹ÿšºÿ×½ÿ…¾ÿ׿ÿšÀÿ×ÁÿšÂÿ×ÔÿšÕÿ×÷ÿ×øÿ×ùÿ×úÿ×ûÿ×üÿ×ýÿšþÿ×ÿ® ÿšÿÃÿšÿÃÿ…ÿמ�ÿ…ž�ÿ®ž�ÿ…žŸÿמ¤ÿšžªÿqž®ÿšžµÿšž¸ÿמ»ÿמ¼�)ž¾ÿ®žÌÿšžÍÿšžÎÿ…žÏÿqžÐÿמÑÿמÒÿšžÓÿšžÔÿšžÕÿ…žÖÿšž×ÿšžØÿqžÙÿšžÚÿšžÛÿqžÜÿ®žÝÿ®žÞÿqžßÿמàÿšžáÿšžâÿšžãÿšžäÿ®žåÿšžæÿšžçÿמèÿšžéÿÞêÿqžìÿšžíÿqžîÿ…žòÿ…žóÿšžõÿšžöÿ®ž÷ÿšžùÿšžÿ®žÿ®žÿ®žÿ…ž ÿ…žjÿqžkÿšžlÿמmÿמqÿšžrÿqžsÿ…žuÿšžwÿšžyÿšž}ÿšž~ÿמÿqžÿמƒÿמ„ÿמ…ÿqž†ÿמ‡ÿqžˆÿמ‰ÿqžŠÿמ‹ÿמŒÿמÿqž–ÿšžšÿšžžÿšž ÿמ¢ÿמ¤ÿšž¦ÿšžªÿ®ž¬ÿšž®ÿšž°ÿšž±ÿמ²ÿqž³ÿמ´ÿqžµ�)ž¶ÿ®ž¸ÿ®žºÿ®ž¼ÿמ¾ÿ®žÀÿšžÂÿšžÄÿšžÅÿšžÆÿqžÇÿšžÈÿqžËÿמÍÿšžÎÿšžÏÿ…žÑÿšžÓÿšžÕÿšž×ÿšžÙÿqžÛÿqžÝÿqžàÿqžæÿמèÿמêÿÞìÿšžîÿšžïÿמðÿqžñÿמòÿqžóÿמôÿqžöÿמøÿ®žúÿ®žüÿ®žþÿšž�ÿšžÿšžÿמÿמ ÿqž ÿqž ÿqž ÿqžÿšžÿšžÿšžÿ…žÿšžÿמÿqžÿ®žÿqžÿšžÿ…ŸŸÿן¸ÿן»ÿן¾ÿןáÿןlÿן~ÿן„ÿן†ÿןˆÿןŠÿןŒÿן±ÿן³ÿןÀÿןÂÿןÅÿןÇÿןÕÿןïÿןñÿןóÿןþÿן ÿן ÿןÿןÿןÿ× ÿ× ÿפ�ÿ®¤� ÿ®¤ÿ…¤¦ÿ…¤¨ÿפ¼ÿš¤½ÿפÁÿš¤Äÿ…¤ÜÿפÝÿפáÿפäÿפöÿפÿ®¤ ÿ®¤nÿ®¤|ÿš¤€ÿ®¤‚ÿ®¤—ÿ®¤›ÿ®¤§ÿ®¤©ÿ…¤ªÿפµÿš¤¶ÿפ·ÿš¤¸ÿפ¹ÿš¤ºÿפ½ÿ…¤¾ÿפ¿ÿš¤ÀÿפÁÿš¤ÂÿפÔÿš¤Õÿפ÷ÿפøÿפùÿפúÿפûÿפüÿפýÿš¤þÿפÿ®¤ ÿš¤ÿäÿš¤ÿäÿ…¤ÿ×¥�ÿ®¥� ÿ®¥ÿ…¥¦ÿ…¥¨ÿ×¥¼ÿš¥½ÿ×¥Áÿš¥Äÿ…¥Üÿ×¥Ýÿ×¥áÿ×¥äÿ×¥öÿ×¥ÿ®¥ ÿ®¥nÿ®¥|ÿš¥€ÿ®¥‚ÿ®¥—ÿ®¥›ÿ®¥§ÿ®¥©ÿ…¥ªÿ×¥µÿš¥¶ÿ×¥·ÿš¥¸ÿ×¥¹ÿš¥ºÿ×¥½ÿ…¥¾ÿ×¥¿ÿš¥Àÿ×¥Áÿš¥Âÿ×¥Ôÿš¥Õÿ×¥÷ÿ×¥øÿ×¥ùÿ×¥úÿ×¥ûÿ×¥üÿ×¥ýÿš¥þÿ×¥ÿ®¥ ÿš¥ÿÃ¥ÿš¥ÿÃ¥ÿ…¥ÿצ�ÿ®¦� ÿ®¦ÿ…¦¦ÿ…¦¨ÿצ¼ÿš¦½ÿצÁÿš¦Äÿ…¦ÜÿצÝÿצáÿצäÿצöÿצÿ®¦ ÿ®¦nÿ®¦|ÿš¦€ÿ®¦‚ÿ®¦—ÿ®¦›ÿ®¦§ÿ®¦©ÿ…¦ªÿצµÿš¦¶ÿצ·ÿš¦¸ÿצ¹ÿš¦ºÿצ½ÿ…¦¾ÿצ¿ÿš¦ÀÿצÁÿš¦ÂÿצÔÿš¦Õÿצ÷ÿצøÿצùÿצúÿצûÿצüÿצýÿš¦þÿצÿ®¦ ÿš¦ÿæÿš¦ÿæÿ…¦ÿ×§Ÿÿ×§¸ÿ×§»ÿ×§¾ÿ×§Áÿ×§áÿ×§lÿ×§|ÿ×§~ÿ×§„ÿ×§†ÿ×§ˆÿ×§Šÿ×§Œÿ×§±ÿ×§³ÿ×§¿ÿ×§Àÿ×§Áÿ×§Âÿ×§Åÿš§Çÿš§Ôÿ×§Õÿ×§ïÿ×§ñÿ×§óÿ×§ýÿ×§þÿ×§ ÿ×§ ÿ×§ÿ×§ÿ×§ÿ×§ÿì¨�ÿ…¨�ÿ…¨Ÿÿ쨤ÿš¨ªÿq¨®ÿš¨µÿš¨¸ÿ쨻ÿ쨾ÿèÉÿì¨Îÿ®¨ÏÿרÕÿ®¨ØÿרÛÿרÞÿרáÿרêÿרë�f¨íÿרîÿì¨òÿ®¨ô�f¨ÿ…¨ ÿ…¨jÿרlÿì¨rÿq¨sÿ®¨~ÿì¨ÿר„ÿ쨅ÿר†ÿ쨇ÿרˆÿ쨉ÿרŠÿ쨌ÿì¨ÿר˜�f¨¨�f¨±ÿ쨲ÿר³ÿ쨴ÿרÀÿרÂÿרÅÿרÆÿèÇÿרÈÿèÎÿš¨Ïÿ®¨ÕÿרÙÿq¨Ûÿq¨Ýÿq¨àÿרïÿì¨ðÿרñÿì¨òÿרóÿì¨ôÿרþÿר ÿq¨ ÿר ÿq¨ ÿרÿš¨ÿ®¨ÿì¨ÿרÿרÿš¨ÿ®ª�ÿqª� ÿqªÿšª¦ÿšª¼ÿqª¾ÿתÁÿšªÄÿšªÜÿתáÿתäÿתÿqª ÿqªnÿת|ÿšª€ÿ®ª‚ÿ®ª—ÿת›ÿת§ÿת©ÿšªªÿתµÿqª¶ÿת·ÿ…ª¹ÿ…ª½ÿšª¾ÿת¿ÿšªÀÿתÁÿšªÂÿתÅÿšªÇÿšªÔÿšªÕÿתáÿתãÿתýÿšªþÿתÿת ÿqªÿתÿqªÿתÿšªÿ׫�ÿ׫� ÿ׫ªÿì«Áÿ׫ÿ׫ ÿ׫rÿì«|ÿ׫¿ÿ׫Áÿ׫Åÿ׫Çÿ׫Ôÿ׫Ùÿì«Ûÿì«Ýÿì«ýÿ׬�ÿ®¬�ÿ®¬ÿ®¬ ÿ®¬€ÿ쬂ÿ쬷ÿ쬹ÿì¬ ÿ׬ÿ×­�ÿ…­�ÿ®­�ÿ…­Ÿÿ×­¤ÿš­ªÿq­®ÿš­µÿš­¸ÿ×­»ÿ×­¼�)­¾ÿ®­Ìÿš­Íÿš­Îÿ…­Ïÿq­Ðÿ×­Ñÿ×­Òÿš­Óÿš­Ôÿš­Õÿ…­Öÿš­×ÿš­Øÿq­Ùÿš­Úÿš­Ûÿq­Üÿ®­Ýÿ®­Þÿq­ßÿ×­àÿš­áÿš­âÿš­ãÿš­äÿ®­åÿš­æÿš­çÿ×­èÿš­éÿíêÿq­ìÿš­íÿq­îÿ…­òÿ…­óÿš­õÿš­öÿ®­÷ÿš­ùÿš­ÿ®­ÿ®­ÿ®­ÿ…­ ÿ…­jÿq­kÿš­lÿ×­mÿ×­qÿš­rÿq­sÿ…­uÿš­wÿš­yÿš­}ÿš­~ÿ×­ÿq­ÿ×­ƒÿ×­„ÿ×­…ÿq­†ÿ×­‡ÿq­ˆÿ×­‰ÿq­Šÿ×­‹ÿ×­Œÿ×­ÿq­–ÿš­šÿš­žÿš­ ÿ×­¢ÿ×­¤ÿš­¦ÿš­ªÿ®­¬ÿš­®ÿš­°ÿš­±ÿ×­²ÿq­³ÿ×­´ÿq­µ�)­¶ÿ®­¸ÿ®­ºÿ®­¼ÿ×­¾ÿ®­Àÿš­Âÿš­Äÿš­Åÿš­Æÿq­Çÿš­Èÿq­Ëÿ×­Íÿš­Îÿš­Ïÿ…­Ñÿš­Óÿš­Õÿš­×ÿš­Ùÿq­Ûÿq­Ýÿq­àÿq­æÿ×­èÿ×­êÿíìÿš­îÿš­ïÿ×­ðÿq­ñÿ×­òÿq­óÿ×­ôÿq­öÿ×­øÿ®­úÿ®­üÿ®­þÿš­�ÿš­ÿš­ÿ×­ÿ×­ ÿq­ ÿq­ ÿq­ ÿq­ÿš­ÿš­ÿš­ÿ…­ÿš­ÿ×­ÿq­ÿ®­ÿq­ÿš­ÿ…®£�á®ê�)®ÿ×®ÿ×°Ÿÿ×°¸ÿ×°»ÿ×°¾ÿ×°Áÿ×°áÿ×°lÿ×°|ÿ×°~ÿ×°„ÿ×°†ÿ×°ˆÿ×°Šÿ×°Œÿ×°±ÿ×°³ÿ×°¿ÿ×°Àÿ×°Áÿ×°Âÿ×°Åÿš°Çÿš°Ôÿ×°Õÿ×°ïÿ×°ñÿ×°óÿ×°ýÿ×°þÿ×° ÿ×° ÿ×°ÿ×°ÿ×°ÿ×°ÿì±�ÿ®±�ÿ®±ÿ®± ÿ®±€ÿ챂ÿì±·ÿì±¹ÿì± ÿ×±ÿ×´Ÿÿ×´¸ÿ×´»ÿ×´¾ÿ×´Áÿ×´áÿ×´lÿ×´|ÿ×´~ÿ×´„ÿ×´†ÿ×´ˆÿ×´Šÿ×´Œÿ×´±ÿ×´³ÿ×´¿ÿ×´Àÿ×´Áÿ×´Âÿ×´Åÿš´Çÿš´Ôÿ×´Õÿ×´ïÿ×´ñÿ×´óÿ×´ýÿ×´þÿ×´ ÿ×´ ÿ×´ÿ×´ÿ×´ÿ×´ÿì¸�ÿ®¸�ÿ®¸ÿ츤ÿ׸¦ÿ츨ÿ׸ªÿ׸®ÿ׸°ÿ׸±ÿ층ÿ׸¼ÿø½ÿ׸¿ÿ׸Áÿ׸Äÿì¸Çÿì¸Îÿì¸Õÿì¸òÿì¸ÿ®¸ ÿ®¸rÿ׸sÿì¸zÿì¸|ÿ׸€ÿ츂ÿ츟ÿ׸¡ÿ츩ÿ층ÿø·ÿ츹ÿ츻ÿ׸½ÿ츿ÿ׸Áÿ׸Êÿ׸Îÿ׸Ïÿì¸Ôÿ׸Ùÿ׸Ûÿ׸Ýÿ׸åÿ׸çÿì¸õÿì¸÷ÿ׸ùÿ׸ûÿ׸ýÿ׸ÿ׸ÿ׸ ÿ׸ÿ׸ÿ׸ÿì¸ÿì¸ÿ׸ÿìº�þöº�þöº¤ÿ…ºªÿšº®ÿ…º°ÿ׺µÿ…º¿ÿ׺ÎÿšºÕÿšºòÿšºþöº þöºrÿšºsÿšºvÿ캟ÿ׺»ÿ׺Êÿ׺Îÿ…ºÏÿšºÙÿšºÛÿšºÝÿšºåÿ׺ÿ׺ÿ׺ ÿ®º ÿ®ºÿ…ºÿšºÿ…ºÿš»Ÿÿ×»¸ÿ×»»ÿ×»¾ÿ×»áÿ×»lÿ×»~ÿ×»„ÿ×»†ÿ×»ˆÿ×»Šÿ×»Œÿ×»±ÿ×»³ÿ×»Àÿ×»Âÿ×»Åÿ×»Çÿ×»Õÿ×»ïÿ×»ñÿ×»óÿ×»þÿ×» ÿ×» ÿ×»ÿ×»ÿ×»ÿ×¼�ÿ…¼�ÿ®¼�ÿ…¼Ÿÿ×¼¤ÿš¼ªÿq¼®ÿš¼µÿš¼¸ÿ×¼»ÿ×¼¼�)¼¾ÿ®¼Ìÿš¼Íÿš¼Îÿ…¼Ïÿq¼Ðÿ×¼Ñÿ×¼Òÿš¼Óÿš¼Ôÿš¼Õÿ…¼Öÿš¼×ÿš¼Øÿq¼Ùÿš¼Úÿš¼Ûÿq¼Üÿ®¼Ýÿ®¼Þÿq¼ßÿ×¼àÿš¼áÿš¼âÿš¼ãÿš¼äÿ®¼åÿš¼æÿš¼çÿ×¼èÿš¼éÿüêÿq¼ìÿš¼íÿq¼îÿ…¼òÿ…¼óÿš¼õÿš¼öÿ®¼÷ÿš¼ùÿš¼ÿ®¼ÿ®¼ÿ®¼ÿ…¼ ÿ…¼jÿq¼kÿš¼lÿ×¼mÿ×¼qÿš¼rÿq¼sÿ…¼uÿš¼wÿš¼yÿš¼}ÿš¼~ÿ×¼ÿq¼ÿ×¼ƒÿ×¼„ÿ×¼…ÿq¼†ÿ×¼‡ÿq¼ˆÿ×¼‰ÿq¼Šÿ×¼‹ÿ×¼Œÿ×¼ÿq¼–ÿš¼šÿš¼žÿš¼ ÿ×¼¢ÿ×¼¤ÿš¼¦ÿš¼ªÿ®¼¬ÿš¼®ÿš¼°ÿš¼±ÿ×¼²ÿq¼³ÿ×¼´ÿq¼µ�)¼¶ÿ®¼¸ÿ®¼ºÿ®¼¼ÿ×¼¾ÿ®¼Àÿš¼Âÿš¼Äÿš¼Åÿš¼Æÿq¼Çÿš¼Èÿq¼Ëÿ×¼Íÿš¼Îÿš¼Ïÿ…¼Ñÿš¼Óÿš¼Õÿš¼×ÿš¼Ùÿq¼Ûÿq¼Ýÿq¼àÿq¼æÿ×¼èÿ×¼êÿüìÿš¼îÿš¼ïÿ×¼ðÿq¼ñÿ×¼òÿq¼óÿ×¼ôÿq¼öÿ×¼øÿ®¼úÿ®¼üÿ®¼þÿš¼�ÿš¼ÿš¼ÿ×¼ÿ×¼ ÿq¼ ÿq¼ ÿq¼ ÿq¼ÿš¼ÿš¼ÿš¼ÿ…¼ÿš¼ÿ×¼ÿq¼ÿ®¼ÿq¼ÿš¼ÿ…½�ÿ…½�ÿ…½Ÿÿ콤ÿš½ªÿq½®ÿš½µÿš½¸ÿì½»ÿì½¾ÿýÉÿì½Îÿ®½Ïÿ×½Õÿ®½Øÿ×½Ûÿ×½Þÿ×½áÿ×½êÿ×½ë�f½íÿ×½îÿì½òÿ®½ô�f½ÿ…½ ÿ…½jÿ×½lÿì½rÿq½sÿ®½~ÿì½ÿ×½„ÿì½…ÿ×½†ÿ콇ÿ×½ˆÿ콉ÿ×½Šÿ콌ÿì½ÿ×½˜�f½¨�f½±ÿì½²ÿ×½³ÿì½´ÿ×½Àÿ×½Âÿ×½Åÿ×½ÆÿýÇÿ×½ÈÿýÎÿš½Ïÿ®½Õÿ×½Ùÿq½Ûÿq½Ýÿq½àÿ×½ïÿì½ðÿ×½ñÿì½òÿ×½óÿì½ôÿ×½þÿ×½ ÿq½ ÿ×½ ÿq½ ÿ×½ÿš½ÿ®½ÿì½ÿ×½ÿ×½ÿš½ÿ®¾�ÿ®¾�ÿ®¾ÿ×¾¤ÿ×¾¦ÿ×¾¨ÿþªÿ×¾®ÿ×¾°ÿ×¾±ÿ×¾µÿ×¾¼ÿþ½ÿþ¿ÿ×¾Äÿ×¾Çÿ×¾Îÿì¾Õÿì¾òÿì¾ÿ®¾ ÿ®¾rÿ×¾sÿì¾zÿ×¾€ÿ쾂ÿ쾟ÿ×¾¡ÿ×¾©ÿ×¾µÿþ·ÿþ¹ÿþ»ÿ×¾½ÿ×¾Êÿ×¾Îÿ×¾Ïÿì¾Ùÿ×¾Ûÿ×¾Ýÿ×¾åÿ×¾çÿ×¾õÿ×¾÷ÿþùÿþûÿþÿ×¾ÿ×¾ ÿ×¾ÿ×¾ÿ×¾ÿì¾ÿ×¾ÿ×¾ÿ쿟ÿ׿¸ÿ׿»ÿ׿¾ÿ׿Áÿ׿áÿ׿lÿ׿|ÿ׿~ÿ׿„ÿ׿†ÿ׿ˆÿ׿Šÿ׿Œÿ׿±ÿ׿³ÿ׿¿ÿ׿Àÿ׿Áÿ׿Âÿ׿Åÿš¿Çÿš¿Ôÿ׿Õÿ׿ïÿ׿ñÿ׿óÿ׿ýÿ׿þÿ׿ ÿ׿ ÿ׿ÿ׿ÿ׿ÿ׿ÿìÀ£�áÀê�)Àÿ×Àÿ×ã�áÃê�)Ãÿ×Ãÿ×Ä�ÿ®Ä� ÿ®Äÿ…Ħÿ…Ĩÿ×ļÿšÄ½ÿ×ÄÁÿšÄÄÿ…ÄÜÿ×ÄÝÿ×Äáÿ×Ääÿ×Äöÿ×Äÿ®Ä ÿ®Änÿ®Ä|ÿšÄ€ÿ®Ä‚ÿ®Ä—ÿ®Ä›ÿ®Ä§ÿ®Ä©ÿ…Īÿ×ĵÿšÄ¶ÿ×Ä·ÿšÄ¸ÿ×ĹÿšÄºÿ׼ÿ…ľÿ×Ä¿ÿšÄÀÿ×ÄÁÿšÄÂÿ×ÄÔÿšÄÕÿ×Ä÷ÿ×Äøÿ×Äùÿ×Äúÿ×Äûÿ×Äüÿ×ÄýÿšÄþÿ×Äÿ®Ä ÿšÄÿÃÄÿšÄÿÃÄÿ…Äÿׯ�ÿ®Æ� ÿ®Æÿ…Ʀÿ…ƨÿׯ¼ÿšÆ½ÿׯÁÿšÆÄÿ…ÆÜÿׯÝÿׯáÿׯäÿׯöÿׯÿ®Æ ÿ®Ænÿ®Æ|ÿšÆ€ÿ®Æ‚ÿ®Æ—ÿ®Æ›ÿ®Æ§ÿ®Æ©ÿ…ƪÿׯµÿšÆ¶ÿׯ·ÿšÆ¸ÿׯ¹ÿšÆºÿׯ½ÿ…ƾÿׯ¿ÿšÆÀÿׯÁÿšÆÂÿׯÔÿšÆÕÿׯ÷ÿׯøÿׯùÿׯúÿׯûÿׯüÿׯýÿšÆþÿׯÿ®Æ ÿšÆÿÃÆÿšÆÿÃÆÿ…Æÿ×Ç�ÿ®Ç�ÿ®ÇÿìǤÿ×ǦÿìǨÿ×Ǫÿ×Ç®ÿ×ǰÿ×DZÿìǵÿ×ǼÿÃǽÿ×Ç¿ÿ×ÇÁÿ×ÇÄÿìÇÇÿìÇÎÿìÇÕÿìÇòÿìÇÿ®Ç ÿ®Çrÿ×ÇsÿìÇzÿìÇ|ÿ×Ç€ÿìÇ‚ÿìÇŸÿ×Ç¡ÿìÇ©ÿìǵÿÃÇ·ÿìǹÿìÇ»ÿ×ǽÿìÇ¿ÿ×ÇÁÿ×ÇÊÿ×ÇÎÿ×ÇÏÿìÇÔÿ×ÇÙÿ×ÇÛÿ×ÇÝÿ×Çåÿ×ÇçÿìÇõÿìÇ÷ÿ×Çùÿ×Çûÿ×Çýÿ×Çÿ×Çÿ×Ç ÿ×Çÿ×Çÿ×ÇÿìÇÿìÇÿ×ÇÿìÈ�ÿ®È�ÿ®ÈÿìȤÿ×ȦÿìȨÿ×Ȫÿ×È®ÿ×Ȱÿ×ȱÿìȵÿ×ȼÿÃȽÿ×È¿ÿ×ÈÁÿ×ÈÄÿìÈÇÿìÈÎÿìÈÕÿìÈòÿìÈÿ®È ÿ®Èrÿ×ÈsÿìÈzÿìÈ|ÿ×È€ÿìÈ‚ÿìÈŸÿ×È¡ÿìÈ©ÿìȵÿÃÈ·ÿìȹÿìÈ»ÿ×ȽÿìÈ¿ÿ×ÈÁÿ×ÈÊÿ×ÈÎÿ×ÈÏÿìÈÔÿ×ÈÙÿ×ÈÛÿ×ÈÝÿ×Èåÿ×ÈçÿìÈõÿìÈ÷ÿ×Èùÿ×Èûÿ×Èýÿ×Èÿ×Èÿ×È ÿ×Èÿ×Èÿ×ÈÿìÈÿìÈÿ×ÈÿìÊ�ÿìÊ� ÿìÊÿìÊ ÿìÌé�)Í�ÿšÍ�ÿ×Í�ÿšÍÎÿÃÍÏÿìÍÕÿÃÍØÿìÍÛÿìÍÞÿìÍêÿìÍíÿìÍòÿÃÍÿ×Íÿ×Íÿ×ÍÿšÍ ÿšÍjÿìÍsÿÃÍÿìÍ…ÿì͇ÿì͉ÿìÍÿìͲÿìÍ´ÿìÍÏÿÃÍàÿìÍðÿìÍòÿìÍôÿìÍ ÿìÍ ÿìÍÿÃÍÿìÍÿìÍÿÃÎ�ÿìÎ� ÿìÎÿìÎ ÿìÏ�ÿìÏ� ÿìÏÿìÏ ÿìÐÏÿ×ÐØÿ×ÐÛÿ×ÐÞÿ×Ðáÿ×Ðêÿ×Ðíÿ×Ðjÿ×Ðÿ×Ð…ÿ×Їÿ×Љÿ×Ðÿ×вÿ×дÿ×ÐÀÿ×ÐÂÿ×ÐÆÿ×ÐÈÿ×ÐÕÿ×Ðàÿ×Ððÿ×Ðòÿ×Ðôÿ×Ðþÿ×Ð ÿ×Ð ÿ×Ðÿ×Ðÿ×Ñé�)ÔÏÿ×ÔØÿ×ÔÛÿ×ÔÞÿ×Ôáÿ×Ôêÿ×Ôíÿ×Ôjÿ×Ôÿ×Ô…ÿ×Ô‡ÿ×Ô‰ÿ×Ôÿ×Ô²ÿ×Ô´ÿ×ÔÀÿ×ÔÂÿ×ÔÆÿ×ÔÈÿ×ÔÕÿ×Ôàÿ×Ôðÿ×Ôòÿ×Ôôÿ×Ôþÿ×Ô ÿ×Ô ÿ×Ôÿ×Ôÿר�ÿìØ� ÿìØÐÿרÜÿìØÝÿìØßÿרáÿìØäÿìØöÿìØÿìØ ÿìØ ÿרªÿìØ¶ÿìØ¼ÿר¾ÿìØÀÿìØÂÿìØËÿרÕÿìØæÿרøÿìØúÿìØüÿìØþÿìØÿרÿרÿìØÿìØÿìÚ�ÿìÚ� ÿìÚÐÿ×ÚÜÿìÚÝÿìÚßÿ×ÚáÿìÚäÿìÚöÿìÚÿìÚ ÿìÚ ÿ×ÚªÿìÚ¶ÿìÚ¼ÿ×Ú¾ÿìÚÀÿìÚÂÿìÚËÿ×ÚÕÿìÚæÿ×ÚøÿìÚúÿìÚüÿìÚþÿìÚÿ×Úÿ×ÚÿìÚÿìÚÿìÜ�ÿšÜ�ÿ×Ü�ÿšÜÎÿÃÜÏÿìÜÕÿÃÜØÿìÜÛÿìÜÞÿìÜêÿìÜíÿìÜòÿÃÜÿ×Üÿ×Üÿ×ÜÿšÜ ÿšÜjÿìÜsÿÃÜÿìÜ…ÿì܇ÿì܉ÿìÜÿìܲÿìÜ´ÿìÜÏÿÃÜàÿìÜðÿìÜòÿìÜôÿìÜ ÿìÜ ÿìÜÿÃÜÿìÜÿìÜÿÃÝ�ÿ®Ý�ÿ®ÝÎÿ×ÝÕÿ×Ýòÿ×Ýÿ®Ý ÿ®Ýsÿ×ÝÏÿ×Ýÿ×Ýÿ×Þ�ÿìÞ� ÿìÞÐÿ×ÞÜÿìÞÝÿìÞßÿ×ÞáÿìÞäÿìÞöÿìÞÿìÞ ÿìÞ ÿ×ÞªÿìÞ¶ÿìÞ¼ÿ×Þ¾ÿìÞÀÿìÞÂÿìÞËÿ×ÞÕÿìÞæÿ×ÞøÿìÞúÿìÞüÿìÞþÿìÞÿ×Þÿ×ÞÿìÞÿìÞÿìßÏÿ×ߨÿ×ßÛÿ×ßÞÿ×ßáÿ×ßêÿ×ßíÿ×ßjÿ×ßÿ×ß…ÿ×߇ÿ×߉ÿ×ßÿ×ß²ÿ×ß´ÿ×ßÀÿ×ßÂÿ×߯ÿ×ßÈÿ×ßÕÿ×ßàÿ×ßðÿ×ßòÿ×ßôÿ×ßþÿ×ß ÿ×ß ÿ×ßÿ×ßÿ×à�ÿìà� ÿìàÿìà ÿìã�ÿìã� ÿìãÿìã ÿìä�ÿ…ä� ÿ…äÐÿ×äÜÿšäÝÿÃäßÿ×äáÿ®ääÿšäöÿÃäÿ…ä ÿ…ämÿ×äÿ×äƒÿ×ä‹ÿ×ä ÿ×äªÿšä¶ÿšä¸ÿÃäºÿÃä¼ÿ×ä¾ÿšäÀÿ®äÂÿ®äÆÿ×äÈÿ×äËÿ×äÕÿ®äæÿ×äêÿ×äøÿÃäúÿÃäüÿÃäþÿ®äÿ×äÿ×äÿšäÿšäÿšæ�ÿ…æ� ÿ…æÐÿ׿ÜÿšæÝÿÃæßÿ׿áÿ®æäÿšæöÿÃæÿ…æ ÿ…æmÿ׿ÿ׿ƒÿ׿‹ÿ׿ ÿ׿ªÿšæ¶ÿšæ¸ÿÃæºÿÃæ¼ÿ׿¾ÿšæÀÿ®æÂÿ®æÆÿ׿Èÿ׿Ëÿ׿Õÿ®ææÿ׿êÿ׿øÿÃæúÿÃæüÿÃæþÿ®æÿ׿ÿ׿ÿšæÿšæÿšç�ÿìç� ÿìçÐÿ×çÜÿìçÝÿìçßÿ×çáÿìçäÿìçöÿìçÿìç ÿìç ÿ×çªÿìç¶ÿìç¼ÿ×ç¾ÿìçÀÿìçÂÿìçËÿ×çÕÿìçæÿ×çøÿìçúÿìçüÿìçþÿìçÿ×çÿ×çÿìçÿìçÿìè�ÿìè� ÿìèÐÿ×èÜÿìèÝÿìèßÿ×èáÿìèäÿìèöÿìèÿìè ÿìè ÿ×èªÿìè¶ÿìè¼ÿ×è¾ÿìèÀÿìèÂÿìèËÿ×èÕÿìèæÿ×èøÿìèúÿìèüÿìèþÿìèÿ×èÿ×èÿìèÿìèÿìê�ÿìê� ÿìêÿìê ÿìë�ÿìë� ÿìëÿìë ÿìëÿ×ëÿ×ì�ÿšì�ÿ×ì�ÿšìÎÿÃìÏÿììÕÿÃìØÿììÛÿììÞÿììêÿììíÿììòÿÃìÿ×ìÿ×ìÿ×ìÿšì ÿšìjÿììsÿÃìÿìì…ÿìì‡ÿìì‰ÿììÿìì²ÿìì´ÿììÏÿÃìàÿììðÿììòÿììôÿìì ÿìì ÿììÿÃìÿììÿììÿÃò�ÿ…ò� ÿ…òÐÿ×òÜÿšòÝÿÃòßÿ×òáÿ®òäÿšòöÿÃòÿ…ò ÿ…òmÿ×òÿ×òƒÿ×ò‹ÿ×ò ÿ×òªÿšò¶ÿšò¸ÿÃòºÿÃò¼ÿ×ò¾ÿšòÀÿ®òÂÿ®òÆÿ×òÈÿ×òËÿ×òÕÿ®òæÿ×òêÿ×òøÿÃòúÿÃòüÿÃòþÿ®òÿ×òÿ×òÿšòÿšòÿšó�ÿ…ó� ÿ…óÐÿ×óÜÿšóÝÿÃóßÿ×óáÿ®óäÿšóöÿÃóÿ…ó ÿ…ómÿ×óÿ×óƒÿ×ó‹ÿ×ó ÿ×óªÿšó¶ÿšó¸ÿÃóºÿÃó¼ÿ×ó¾ÿšóÀÿ®óÂÿ®óÆÿ×óÈÿ×óËÿ×óÕÿ®óæÿ×óêÿ×óøÿÃóúÿÃóüÿÃóþÿ®óÿ×óÿ×óÿšóÿšóÿšô�ÿìô� ÿìôÿìô ÿìôÿ×ôÿ×õÏÿ×õØÿ×õÛÿ×õÞÿ×õáÿ×õêÿ×õíÿ×õjÿ×õÿ×õ…ÿ×õ‡ÿ×õ‰ÿ×õÿ×õ²ÿ×õ´ÿ×õÀÿ×õÂÿ×õÆÿ×õÈÿ×õÕÿ×õàÿ×õðÿ×õòÿ×õôÿ×õþÿ×õ ÿ×õ ÿ×õÿ×õÿ×ö�ÿ®ö�ÿ®öÎÿ×öÕÿ×öòÿ×öÿ®ö ÿ®ösÿ×öÏÿ×öÿ×öÿ×ø�ÿ…ø�ÿ®ø�ÿ…øŸÿ×ø¤ÿšøªÿqø®ÿšøµÿšø¸ÿ×ø»ÿ×ø¼�)ø¾ÿ®øÌÿšøÍÿšøÎÿ…øÏÿqøÐÿ×øÑÿ×øÒÿšøÓÿšøÔÿšøÕÿ…øÖÿšø×ÿšøØÿqøÙÿšøÚÿšøÛÿqøÜÿ®øÝÿ®øÞÿqøßÿ×øàÿšøáÿšøâÿšøãÿšøäÿ®øåÿšøæÿšøçÿ×øèÿšøéÿÃøêÿqøìÿšøíÿqøîÿ…øòÿ…øóÿšøõÿšøöÿ®ø÷ÿšøùÿšøÿ®øÿ®øÿ®øÿ…ø ÿ…øjÿqøkÿšølÿ×ømÿ×øqÿšørÿqøsÿ…øuÿšøwÿšøyÿšø}ÿšø~ÿ×øÿqøÿ×øƒÿ×ø„ÿ×ø…ÿqø†ÿ×ø‡ÿqøˆÿ×ø‰ÿqøŠÿ×ø‹ÿ×øŒÿ×øÿqø–ÿšøšÿšøžÿšø ÿ×ø¢ÿ×ø¤ÿšø¦ÿšøªÿ®ø¬ÿšø®ÿšø°ÿšø±ÿ×ø²ÿqø³ÿ×ø´ÿqøµ�)ø¶ÿ®ø¸ÿ®øºÿ®ø¼ÿ×ø¾ÿ®øÀÿšøÂÿšøÄÿšøÅÿšøÆÿqøÇÿšøÈÿqøËÿ×øÍÿšøÎÿšøÏÿ…øÑÿšøÓÿšøÕÿšø×ÿšøÙÿqøÛÿqøÝÿqøàÿqøæÿ×øèÿ×øêÿÃøìÿšøîÿšøïÿ×øðÿqøñÿ×øòÿqøóÿ×øôÿqøöÿ×øøÿ®øúÿ®øüÿ®øþÿšø�ÿšøÿšøÿ×øÿ×ø ÿqø ÿqø ÿqø ÿqøÿšøÿšøÿšøÿ…øÿšøÿ×øÿqøÿ®øÿqøÿšøÿ…ù�ÿšù�ÿ×ù�ÿšùÎÿÃùÏÿìùÕÿÃùØÿìùÛÿìùÞÿìùêÿìùíÿìùòÿÃùÿ×ùÿ×ùÿ×ùÿšù ÿšùjÿìùsÿÃùÿìù…ÿìù‡ÿìù‰ÿìùÿìù²ÿìù´ÿìùÏÿÃùàÿìùðÿìùòÿìùôÿìù ÿìù ÿìùÿÃùÿìùÿìùÿÃú�ÿšú�ÿšú�"�)ú�$ÿ®ú�&ÿìú�*ÿìú�2ÿìú�4ÿìú�Dÿ×ú�Fÿ×ú�Gÿ×ú�Hÿ×ú�Jÿìú�Pÿìú�Qÿìú�Rÿ×ú�Sÿìú�Tÿ×ú�Uÿìú�Vÿìú�Xÿìú�‚ÿ®ú�ƒÿ®ú�„ÿ®ú�…ÿ®ú�†ÿ®ú�‡ÿ®ú�‰ÿìú�”ÿìú�•ÿìú�–ÿìú�—ÿìú�˜ÿìú�šÿìú�¢ÿ×ú�£ÿ×ú�¤ÿ×ú�¥ÿ×ú�¦ÿ×ú�§ÿ×ú�¨ÿ×ú�©ÿ×ú�ªÿ×ú�«ÿ×ú�¬ÿ×ú�­ÿ×ú�´ÿ×ú�µÿ×ú�¶ÿ×ú�·ÿ×ú�¸ÿ×ú�ºÿ×ú�»ÿìú�¼ÿìú�½ÿìú�¾ÿìú�Âÿ®ú�Ãÿ×ú�Äÿ®ú�Åÿ×ú�Æÿ®ú�Çÿ×ú�Èÿìú�Éÿ×ú�Êÿìú�Ëÿ×ú�Ìÿìú�Íÿ×ú�Îÿìú�Ïÿ×ú�Ñÿ×ú�Óÿ×ú�Õÿ×ú�×ÿ×ú�Ùÿ×ú�Ûÿ×ú�Ýÿ×ú�Þÿìú�ßÿìú�àÿìú�áÿìú�âÿìú�ãÿìú�äÿìú�åÿìú�úÿìúÿìúÿìú ÿìúÿìúÿ×úÿìúÿ×úÿìúÿ×úÿìúÿ×úÿìúÿìúÿìú!ÿìú+ÿìú-ÿìú/ÿìú1ÿìú3ÿìú5ÿìúCÿ®úDÿ×úFÿ×úGÿìúHÿ×úJÿìúÿšú ÿšúWÿìúXÿ®úYÿ×ú_ÿìú`ÿ×úbÿìúÿ®úÿ×úÿ®ú ÿ×ú!ÿ®ú"ÿ×ú#ÿ®ú%ÿ®ú&ÿ×ú'ÿ®ú(ÿ×ú)ÿ®ú*ÿ×ú+ÿ®ú,ÿ×ú-ÿ®ú.ÿ×ú/ÿ®ú0ÿ×ú1ÿ®ú2ÿ×ú3ÿ®ú4ÿ×ú6ÿ×ú8ÿ×ú:ÿ×ú<ÿ×ú@ÿ×úBÿ×úDÿ×úIÿìúJÿ×úKÿìúLÿ×úMÿìúNÿ×úOÿìúQÿìúRÿ×úSÿìúTÿ×úUÿìúVÿ×úWÿìúXÿ×úYÿìúZÿ×ú[ÿìú\ÿ×ú]ÿìú^ÿ×ú_ÿìú`ÿ×úbÿìúdÿìúfÿìúhÿìújÿìúlÿìúnÿìû��Rû� �Rû�ÿ®û�ÿ®û�"�)û�Rûÿ®û �Rû ÿ®ü�ÿšü�ÿšü�"�)ü�$ÿ®ü�&ÿìü�*ÿìü�2ÿìü�4ÿìü�Dÿ×ü�Fÿ×ü�Gÿ×ü�Hÿ×ü�Jÿìü�Pÿìü�Qÿìü�Rÿ×ü�Sÿìü�Tÿ×ü�Uÿìü�Vÿìü�Xÿìü�‚ÿ®ü�ƒÿ®ü�„ÿ®ü�…ÿ®ü�†ÿ®ü�‡ÿ®ü�‰ÿìü�”ÿìü�•ÿìü�–ÿìü�—ÿìü�˜ÿìü�šÿìü�¢ÿ×ü�£ÿ×ü�¤ÿ×ü�¥ÿ×ü�¦ÿ×ü�§ÿ×ü�¨ÿ×ü�©ÿ×ü�ªÿ×ü�«ÿ×ü�¬ÿ×ü�­ÿ×ü�´ÿ×ü�µÿ×ü�¶ÿ×ü�·ÿ×ü�¸ÿ×ü�ºÿ×ü�»ÿìü�¼ÿìü�½ÿìü�¾ÿìü�Âÿ®ü�Ãÿ×ü�Äÿ®ü�Åÿ×ü�Æÿ®ü�Çÿ×ü�Èÿìü�Éÿ×ü�Êÿìü�Ëÿ×ü�Ìÿìü�Íÿ×ü�Îÿìü�Ïÿ×ü�Ñÿ×ü�Óÿ×ü�Õÿ×ü�×ÿ×ü�Ùÿ×ü�Ûÿ×ü�Ýÿ×ü�Þÿìü�ßÿìü�àÿìü�áÿìü�âÿìü�ãÿìü�äÿìü�åÿìü�úÿìüÿìüÿìü ÿìüÿìüÿ×üÿìüÿ×üÿìüÿ×üÿìüÿ×üÿìüÿìüÿìü!ÿìü+ÿìü-ÿìü/ÿìü1ÿìü3ÿìü5ÿìüCÿ®üDÿ×üFÿ×üGÿìüHÿ×üJÿìüÿšü ÿšüWÿìüXÿ®üYÿ×ü_ÿìü`ÿ×übÿìüÿ®üÿ×üÿ®ü ÿ×ü!ÿ®ü"ÿ×ü#ÿ®ü%ÿ®ü&ÿ×ü'ÿ®ü(ÿ×ü)ÿ®ü*ÿ×ü+ÿ®ü,ÿ×ü-ÿ®ü.ÿ×ü/ÿ®ü0ÿ×ü1ÿ®ü2ÿ×ü3ÿ®ü4ÿ×ü6ÿ×ü8ÿ×ü:ÿ×ü<ÿ×ü@ÿ×üBÿ×üDÿ×üIÿìüJÿ×üKÿìüLÿ×üMÿìüNÿ×üOÿìüQÿìüRÿ×üSÿìüTÿ×üUÿìüVÿ×üWÿìüXÿ×üYÿìüZÿ×ü[ÿìü\ÿ×ü]ÿìü^ÿ×ü_ÿìü`ÿ×übÿìüdÿìüfÿìühÿìüjÿìülÿìünÿìý��Rý� �Rý�ÿ®ý�ÿ®ý�"�)ý�Rýÿ®ý �Rý ÿ®þ�ÿšþ�ÿšþ�"�)þ�$ÿ®þ�&ÿìþ�*ÿìþ�2ÿìþ�4ÿìþ�Dÿ×þ�Fÿ×þ�Gÿ×þ�Hÿ×þ�Jÿìþ�Pÿìþ�Qÿìþ�Rÿ×þ�Sÿìþ�Tÿ×þ�Uÿìþ�Vÿìþ�Xÿìþ�‚ÿ®þ�ƒÿ®þ�„ÿ®þ�…ÿ®þ�†ÿ®þ�‡ÿ®þ�‰ÿìþ�”ÿìþ�•ÿìþ�–ÿìþ�—ÿìþ�˜ÿìþ�šÿìþ�¢ÿ×þ�£ÿ×þ�¤ÿ×þ�¥ÿ×þ�¦ÿ×þ�§ÿ×þ�¨ÿ×þ�©ÿ×þ�ªÿ×þ�«ÿ×þ�¬ÿ×þ�­ÿ×þ�´ÿ×þ�µÿ×þ�¶ÿ×þ�·ÿ×þ�¸ÿ×þ�ºÿ×þ�»ÿìþ�¼ÿìþ�½ÿìþ�¾ÿìþ�Âÿ®þ�Ãÿ×þ�Äÿ®þ�Åÿ×þ�Æÿ®þ�Çÿ×þ�Èÿìþ�Éÿ×þ�Êÿìþ�Ëÿ×þ�Ìÿìþ�Íÿ×þ�Îÿìþ�Ïÿ×þ�Ñÿ×þ�Óÿ×þ�Õÿ×þ�×ÿ×þ�Ùÿ×þ�Ûÿ×þ�Ýÿ×þ�Þÿìþ�ßÿìþ�àÿìþ�áÿìþ�âÿìþ�ãÿìþ�äÿìþ�åÿìþ�úÿìþÿìþÿìþ ÿìþÿìþÿ×þÿìþÿ×þÿìþÿ×þÿìþÿ×þÿìþÿìþÿìþ!ÿìþ+ÿìþ-ÿìþ/ÿìþ1ÿìþ3ÿìþ5ÿìþCÿ®þDÿ×þFÿ×þGÿìþHÿ×þJÿìþÿšþ ÿšþWÿìþXÿ®þYÿ×þ_ÿìþ`ÿ×þbÿìþÿ®þÿ×þÿ®þ ÿ×þ!ÿ®þ"ÿ×þ#ÿ®þ%ÿ®þ&ÿ×þ'ÿ®þ(ÿ×þ)ÿ®þ*ÿ×þ+ÿ®þ,ÿ×þ-ÿ®þ.ÿ×þ/ÿ®þ0ÿ×þ1ÿ®þ2ÿ×þ3ÿ®þ4ÿ×þ6ÿ×þ8ÿ×þ:ÿ×þ<ÿ×þ@ÿ×þBÿ×þDÿ×þIÿìþJÿ×þKÿìþLÿ×þMÿìþNÿ×þOÿìþQÿìþRÿ×þSÿìþTÿ×þUÿìþVÿ×þWÿìþXÿ×þYÿìþZÿ×þ[ÿìþ\ÿ×þ]ÿìþ^ÿ×þ_ÿìþ`ÿ×þbÿìþdÿìþfÿìþhÿìþjÿìþlÿìþnÿìÿ��Rÿ� �Rÿ�ÿ®ÿ�ÿ®ÿ�"�)ÿ�Rÿÿ®ÿ �Rÿ ÿ®��ÿ…��ÿ…��"�)��$ÿ…��&ÿ×��*ÿ×��2ÿ×��4ÿ×��Dÿš��Fÿš��Gÿš��Hÿš��Jÿ×��PÿÃ��QÿÃ��Rÿš��SÿÃ��Tÿš��UÿÃ��Vÿ®��XÿÃ��]ÿ×��‚ÿ…��ƒÿ…��„ÿ…��…ÿ…��†ÿ…��‡ÿ…��‰ÿ×��”ÿ×��•ÿ×��–ÿ×��—ÿ×��˜ÿ×��šÿ×��¢ÿš��£ÿš��¤ÿš��¥ÿš��¦ÿš��§ÿš��¨ÿš��©ÿš��ªÿš��«ÿš��¬ÿš��­ÿš��´ÿš��µÿš��¶ÿš��·ÿš��¸ÿš��ºÿš��»ÿÃ��¼ÿÃ��½ÿÃ��¾ÿÃ��Âÿ…��Ãÿš��Äÿ…��Åÿš��Æÿ…��Çÿš��Èÿ×��Éÿš��Êÿ×��Ëÿš��Ìÿ×��Íÿš��Îÿ×��Ïÿš��Ñÿš��Óÿš��Õÿš��×ÿš��Ùÿš��Ûÿš��Ýÿš��Þÿ×��ßÿ×��àÿ×��áÿ×��âÿ×��ãÿ×��äÿ×��åÿ×��úÿÃ�ÿÃ�ÿÃ� ÿÃ�ÿ×�ÿš�ÿ×�ÿš�ÿ×�ÿš�ÿ×�ÿš�ÿÃ�ÿÃ�ÿ®�!ÿ®�+ÿÃ�-ÿÃ�/ÿÃ�1ÿÃ�3ÿÃ�5ÿÃ�<ÿ×�>ÿ×�@ÿ×�Cÿ…�Dÿš�Fÿš�Gÿ×�Hÿš�Jÿ®�ÿ…� ÿ…�WÿÃ�Xÿ…�Yÿš�_ÿ×�`ÿš�bÿÃ�ÿ…�ÿš�ÿ…� ÿš�!ÿ…�"ÿš�#ÿ…�%ÿ…�&ÿš�'ÿ…�(ÿš�)ÿ…�*ÿš�+ÿ…�,ÿš�-ÿ…�.ÿš�/ÿ…�0ÿš�1ÿ…�2ÿš�3ÿ…�4ÿš�6ÿš�8ÿš�:ÿš�<ÿš�@ÿš�Bÿš�Dÿš�Iÿ×�Jÿš�Kÿ×�Lÿš�Mÿ×�Nÿš�Oÿ×�Qÿ×�Rÿš�Sÿ×�Tÿš�Uÿ×�Vÿš�Wÿ×�Xÿš�Yÿ×�Zÿš�[ÿ×�\ÿš�]ÿ×�^ÿš�_ÿ×�`ÿš�bÿÃ�dÿÃ�fÿÃ�hÿÃ�jÿÃ�lÿÃ�nÿÃ��R� �R�ÿ®�ÿ®�"�)�Rÿ® �R ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�$ÿq�7�)�9�)�:�)�<��Dÿ®�Fÿ…�Gÿ…�Hÿ…�JÿÃ�PÿÃ�QÿÃ�Rÿ…�SÿÃ�Tÿ…�UÿÃ�VÿÃ�XÿÃ�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�Ÿ��¢ÿ…�£ÿ®�¤ÿ®�¥ÿ®�¦ÿ®�§ÿ®�¨ÿ®�©ÿ…�ªÿ…�«ÿ…�¬ÿ…�­ÿ…�´ÿ…�µÿ…�¶ÿ…�·ÿ…�¸ÿ…�ºÿ…�»ÿÃ�¼ÿÃ�½ÿÃ�¾ÿÃ�Âÿq�Ãÿ®�Äÿq�Åÿ®�Æÿq�Çÿ®�Éÿ…�Ëÿ…�Íÿ…�Ïÿ…�Ñÿ…�Óÿ…�Õÿ…�×ÿ…�Ùÿ…�Ûÿ…�Ýÿ…�ßÿÃ�áÿÃ�ãÿÃ�åÿÃ�úÿÃÿÃÿà ÿÃÿ…ÿ…ÿ…ÿ…ÿÃÿÃÿÃ!ÿÃ$�)&�)+ÿÃ-ÿÃ/ÿÃ1ÿÃ3ÿÃ5ÿÃ6�)8�:�CÿqDÿ®Fÿ®Hÿ…JÿÃVÿq_ÿqbÿqiÿqyÿ®zÿ×{ÿ×~ÿ®ÿÂÿ׃ÿׄÿׇÿ׉ÿ׌ÿ®ŽÿÃÿ®ÿ®“ÿ®™ÿ®¤ÿ…ªÿq®ÿ…µÿ…Êÿ×ÎÿqÏÿ…ÕÿqØÿ…Ûÿ…Þÿ…êÿ…íÿ…îÿÃòÿqú�)ü�)þ�)��WÿÃXÿqYÿ®`ÿ…bÿÃjÿ…rÿqsÿq}ÿìÿ……ÿ…‡ÿ…‰ÿ…ÿ…²ÿ…´ÿ…Îÿ…ÏÿqÙÿqÚÿ×ÛÿqÜÿ×ÝÿqÞÿ×àÿ…âÿ×äÿ×ðÿ…òÿ…ôÿ… ÿq ÿ… ÿq ÿ…ÿ…ÿqÿ…ÿ…ÿ…ÿqÿqÿ®ÿq ÿ®!ÿq"ÿ®#ÿq%ÿq&ÿ®'ÿq(ÿ®)ÿq*ÿ®+ÿq,ÿ®-ÿq.ÿ®/ÿq0ÿ®1ÿq2ÿ®3ÿq4ÿ®6ÿ…8ÿ…:ÿ…<ÿ…@ÿ…Bÿ…Dÿ…Jÿ…Lÿ…Nÿ…Rÿ…Tÿ…Vÿ…Xÿ…Zÿ…\ÿ…^ÿ…`ÿ…bÿÃdÿÃfÿÃhÿÃjÿÃlÿÃnÿÃo�q�s��)�$ÿq�7�)�9�)�:�)�<��Dÿ®�Fÿ…�Gÿ…�Hÿ…�JÿÃ�PÿÃ�QÿÃ�Rÿ…�SÿÃ�Tÿ…�UÿÃ�VÿÃ�XÿÃ�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�Ÿ��¢ÿ…�£ÿ®�¤ÿ®�¥ÿ®�¦ÿ®�§ÿ®�¨ÿ®�©ÿ…�ªÿ…�«ÿ…�¬ÿ…�­ÿ…�´ÿ…�µÿ…�¶ÿ…�·ÿ…�¸ÿ…�ºÿ…�»ÿÃ�¼ÿÃ�½ÿÃ�¾ÿÃ�Âÿq�Ãÿ®�Äÿq�Åÿ®�Æÿq�Çÿ®�Éÿ…�Ëÿ…�Íÿ…�Ïÿ…�Ñÿ…�Óÿ…�Õÿ…�×ÿ…�Ùÿ…�Ûÿ…�Ýÿ…�ßÿÃ�áÿÃ�ãÿÃ�åÿÃ�úÿÃÿÃÿà ÿÃÿ…ÿ…ÿ…ÿ…ÿÃÿÃÿÃ!ÿÃ$�)&�)+ÿÃ-ÿÃ/ÿÃ1ÿÃ3ÿÃ5ÿÃ6�)8�:�CÿqDÿ®Fÿ®Hÿ…JÿÃVÿq_ÿqbÿqiÿqyÿ®zÿ×{ÿ×~ÿ®ÿÂÿ׃ÿׄÿׇÿ׉ÿ׌ÿ®ŽÿÃÿ®ÿ®“ÿ®™ÿ®¤ÿ…ªÿq®ÿ…µÿ…Êÿ×ÎÿqÏÿ…ÕÿqØÿ…Ûÿ…Þÿ…êÿ…íÿ…îÿÃòÿqú�)ü�)þ�)��WÿÃXÿqYÿ®`ÿ…bÿÃjÿ…rÿqsÿq}ÿìÿ……ÿ…‡ÿ…‰ÿ…ÿ…²ÿ…´ÿ…Îÿ…ÏÿqÙÿqÚÿ×ÛÿqÜÿ×ÝÿqÞÿ×àÿ…âÿ×äÿ×ðÿ…òÿ…ôÿ… ÿq ÿ… ÿq ÿ…ÿ…ÿqÿ…ÿ…ÿ…ÿqÿqÿ®ÿq ÿ®!ÿq"ÿ®#ÿq%ÿq&ÿ®'ÿq(ÿ®)ÿq*ÿ®+ÿq,ÿ®-ÿq.ÿ®/ÿq0ÿ®1ÿq2ÿ®3ÿq4ÿ®6ÿ…8ÿ…:ÿ…<ÿ…@ÿ…Bÿ…Dÿ…Jÿ…Lÿ…Nÿ…Rÿ…Tÿ…Vÿ…Xÿ…Zÿ…\ÿ…^ÿ…`ÿ…bÿÃdÿÃfÿÃhÿÃjÿÃlÿÃnÿÃo�q�s��)�&ÿš�*ÿš�2ÿš�4ÿš�7ÿq�8ÿ×�9ÿ…�:ÿ…�<ÿ…�‰ÿš�”ÿš�•ÿš�–ÿš�—ÿš�˜ÿš�šÿš�›ÿ×�œÿ×�ÿ×�žÿ×�Ÿÿ…�Èÿš�Êÿš�Ìÿš�Îÿš�Þÿš�àÿš�âÿš�äÿšÿšÿšÿšÿš$ÿq&ÿq*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ…8ÿ…:ÿ…Gÿšfÿ®mÿ®qÿqrÿ…sÿšuÿ…xÿ……ÿ×ÿqŸÿš¦ÿq¸ÿš»ÿš¼ÿq¾ÿ®Áÿ\ÄÿqÜÿšáÿ…äÿšúÿ…üÿ…þÿ…�ÿ…Tÿ…_ÿšaÿ×lÿš|ÿ\~ÿš€ÿ…‚ÿ…„ÿš†ÿšˆÿšŠÿšŒÿš©ÿqªÿš±ÿš³ÿšµÿq¶ÿš·ÿ…¹ÿ…½ÿq¾ÿš¿ÿ\Àÿ…Áÿ\Âÿ…Åÿ…Çÿ…Ôÿ\Õÿ…ïÿšñÿšóÿšýÿ\þÿ… ÿ…ÿšÿ…ÿšÿšÿqÿšIÿšKÿšMÿšOÿšQÿšSÿšUÿšWÿšYÿš[ÿš]ÿš_ÿšaÿ×cÿ×eÿ×gÿ×iÿ×kÿ×mÿ×oÿ…qÿ…sÿ…ÿq �$ÿq �7�) �9�) �:�) �<� �Dÿ® �Fÿ… �Gÿ… �Hÿ… �Jÿà �Pÿà �Qÿà �Rÿ… �Sÿà �Tÿ… �Uÿà �Vÿà �Xÿà �‚ÿq �ƒÿq �„ÿq �…ÿq �†ÿq �‡ÿq �Ÿ� �¢ÿ… �£ÿ® �¤ÿ® �¥ÿ® �¦ÿ® �§ÿ® �¨ÿ® �©ÿ… �ªÿ… �«ÿ… �¬ÿ… �­ÿ… �´ÿ… �µÿ… �¶ÿ… �·ÿ… �¸ÿ… �ºÿ… �»ÿà �¼ÿà �½ÿà �¾ÿà �Âÿq �Ãÿ® �Äÿq �Åÿ® �Æÿq �Çÿ® �Éÿ… �Ëÿ… �Íÿ… �Ïÿ… �Ñÿ… �Óÿ… �Õÿ… �×ÿ… �Ùÿ… �Ûÿ… �Ýÿ… �ßÿà �áÿà �ãÿà �åÿà �úÿà ÿà ÿà  ÿà ÿ… ÿ… ÿ… ÿ… ÿà ÿà ÿà !ÿà $�) &�) +ÿà -ÿà /ÿà 1ÿà 3ÿà 5ÿà 6�) 8� :� Cÿq Dÿ® Fÿ® Hÿ… Jÿà Vÿq _ÿq bÿq iÿq yÿ® zÿ× {ÿ× ~ÿ® ÿà ‚ÿ× ƒÿ× „ÿ× ‡ÿ× ‰ÿ× Œÿ® Žÿà ÿ® ÿ® “ÿ® ™ÿ® ¤ÿ… ªÿq ®ÿ… µÿ… Êÿ× Îÿq Ïÿ… Õÿq Øÿ… Ûÿ… Þÿ… êÿ… íÿ… îÿà òÿq ú�) ü�) þ�) �� Wÿà Xÿq Yÿ® `ÿ… bÿà jÿ… rÿq sÿq }ÿì ÿ… …ÿ… ‡ÿ… ‰ÿ… ÿ… ²ÿ… ´ÿ… Îÿ… Ïÿq Ùÿq Úÿ× Ûÿq Üÿ× Ýÿq Þÿ× àÿ… âÿ× äÿ× ðÿ… òÿ… ôÿ…  ÿq  ÿ…  ÿq  ÿ… ÿ… ÿq ÿ… ÿ… ÿ… ÿq ÿq ÿ® ÿq  ÿ® !ÿq "ÿ® #ÿq %ÿq &ÿ® 'ÿq (ÿ® )ÿq *ÿ® +ÿq ,ÿ® -ÿq .ÿ® /ÿq 0ÿ® 1ÿq 2ÿ® 3ÿq 4ÿ® 6ÿ… 8ÿ… :ÿ… <ÿ… @ÿ… Bÿ… Dÿ… Jÿ… Lÿ… Nÿ… Rÿ… Tÿ… Vÿ… Xÿ… Zÿ… \ÿ… ^ÿ… `ÿ… bÿà dÿà fÿà hÿà jÿà lÿà nÿà o� q� s� �) �&ÿš �*ÿš �2ÿš �4ÿš �7ÿq �8ÿ× �9ÿ… �:ÿ… �<ÿ… �‰ÿš �”ÿš �•ÿš �–ÿš �—ÿš �˜ÿš �šÿš �›ÿ× �œÿ× �ÿ× �žÿ× �Ÿÿ… �Èÿš �Êÿš �Ìÿš �Îÿš �Þÿš �àÿš �âÿš �äÿš ÿš ÿš ÿš ÿš $ÿq &ÿq *ÿ× ,ÿ× .ÿ× 0ÿ× 2ÿ× 4ÿ× 6ÿ… 8ÿ… :ÿ… Gÿš fÿ® mÿ® qÿq rÿ… sÿš uÿ… xÿ… …ÿ× ÿq Ÿÿš ¦ÿq ¸ÿš »ÿš ¼ÿq ¾ÿ® Áÿ\ Äÿq Üÿš áÿ… äÿš úÿ… üÿ… þÿ… �ÿ… Tÿ… _ÿš aÿ× lÿš |ÿ\ ~ÿš €ÿ… ‚ÿ… „ÿš †ÿš ˆÿš Šÿš Œÿš ©ÿq ªÿš ±ÿš ³ÿš µÿq ¶ÿš ·ÿ… ¹ÿ… ½ÿq ¾ÿš ¿ÿ\ Àÿ… Áÿ\ Âÿ… Åÿ… Çÿ… Ôÿ\ Õÿ… ïÿš ñÿš óÿš ýÿ\ þÿ…  ÿ… ÿš ÿ… ÿš ÿš ÿq ÿš Iÿš Kÿš Mÿš Oÿš Qÿš Sÿš Uÿš Wÿš Yÿš [ÿš ]ÿš _ÿš aÿ× cÿ× eÿ× gÿ× iÿ× kÿ× mÿ× oÿ… qÿ… sÿ… ÿq!qÿ×!rÿì!xÿì!TÿìS�ÿÃS�ÿÃSÿÃS ÿÃT�ÿ…T�ÿ…TVÿ…T_ÿ…Tbÿ…Tfÿ×Tiÿ…Tmÿ×TsÿÃTvÿìTyÿšTzÿ®T{ÿÃT|ÿÃT}ÿÃT~ÿšTÿÃT‚ÿ®T„ÿÃT†ÿÃT‡ÿÃT‰ÿÃTŒÿšTŽÿšTÿšTÿšT’ÿÃT“ÿšT•ÿÃT–ÿÃT˜ÿÃT™ÿšTšÿÃT›ÿÃTÿ…T ÿ…T!ÿìX�ÿqX� ÿqX�&ÿ×X�*ÿ×X�- X�2ÿ×X�4ÿ×X�7ÿqX�9ÿ®X�:ÿ®X�<ÿ…X�‰ÿ×X�”ÿ×X�•ÿ×X�–ÿ×X�—ÿ×X�˜ÿ×X�šÿ×X�Ÿÿ…X�Èÿ×X�Êÿ×X�Ìÿ×X�Îÿ×X�Þÿ×X�àÿ×X�âÿ×X�äÿ×Xÿ×Xÿ×Xÿ×Xÿ×X$ÿqX&ÿqX6ÿ®X8ÿ…X:ÿ…XGÿ×Xúÿ®Xüÿ®Xþÿ®X�ÿ…XÿqX ÿqX_ÿ×XIÿ×XKÿ×XMÿ×XOÿ×XQÿ×XSÿ×XUÿ×XWÿ×XYÿ×X[ÿ×X]ÿ×X_ÿ×Xoÿ…Xqÿ…Xsÿ…XÿqY�ÿìY� ÿìYÿìY ÿìZ�ÿ®Z�ÿ®ZVÿ×Z_ÿ×Zbÿ×ZdÿìZiÿ×ZpÿìZqÿÃZrÿìZtÿ×ZuÿìZxÿìZˆÿìZÿ®Z ÿ®ZTÿì`�I�R`�W�R`�Y�f`�Z�f`�[�f`�\�f`�¿�f`%�R`'�R`7�f`û�f`ý�f`4�R`5�R`]�R`^�R`p�f`�R`�Rb�I�fb�W�fb�Y�fb�Z�fb�[�fb�\�fb�¿�fb%�fb'�fb7�fbû�fbý�fb4�fb5�fb]�fb^�fbp�fb�fb�fj�ÿìj� ÿìjÿìj ÿìl�ÿ®l�ÿ®lÿìl¤ÿ×l¦ÿìl¨ÿ×lªÿ×l®ÿ×l°ÿ×l±ÿìlµÿ×l¼ÿÃl½ÿ×l¿ÿ×lÁÿ×lÄÿìlÇÿìlÎÿìlÕÿìlòÿìlÿ®l ÿ®lrÿ×lsÿìlzÿìl|ÿ×l€ÿìl‚ÿìlŸÿ×l¡ÿìl©ÿìlµÿÃl·ÿìl¹ÿìl»ÿ×l½ÿìl¿ÿ×lÁÿ×lÊÿ×lÎÿ×lÏÿìlÔÿ×lÙÿ×lÛÿ×lÝÿ×låÿ×lçÿìlõÿìl÷ÿ×lùÿ×lûÿ×lýÿ×lÿ×lÿ×l ÿ×lÿ×lÿ×lÿìlÿìlÿ×lÿìm�ÿ®m�ÿ®mÎÿ×mÕÿ×mòÿ×mÿ®m ÿ®msÿ×mÏÿ×mÿ×mÿ×n�ÿ®n� ÿ®nÿ×n¦ÿ×n¼ÿ®nÁÿ®nÄÿ×nÜÿ×näÿ×nÿ®n ÿ®n|ÿ®n€ÿÃn‚ÿÃn©ÿ×nªÿ×nµÿ®n¶ÿ×n·ÿÃn¹ÿÃn½ÿ×n¾ÿ×n¿ÿ®nÁÿ®nÔÿ®nýÿ®n ÿšnÿšnÿ×nÿ×o�ÿ…o� ÿ…oÐÿ×oÜÿšoÝÿÃoßÿ×oáÿ®oäÿšoöÿÃoÿ…o ÿ…omÿ×oÿ×oƒÿ×o‹ÿ×o ÿ×oªÿšo¶ÿšo¸ÿÃoºÿÃo¼ÿ×o¾ÿšoÀÿ®oÂÿ®oÆÿ×oÈÿ×oËÿ×oÕÿ®oæÿ×oêÿ×oøÿÃoúÿÃoüÿÃoþÿ®oÿ×oÿ×oÿšoÿšoÿšpŸÿ×p¸ÿ×p»ÿ×p¾ÿ×páÿ×plÿ×p~ÿ×p„ÿ×p†ÿ×pˆÿ×pŠÿ×pŒÿ×p±ÿ×p³ÿ×pÀÿ×pÂÿ×pÅÿ×pÇÿ×pÕÿ×pïÿ×pñÿ×póÿ×pþÿ×p ÿ×p ÿ×pÿ×pÿ×pÿ×r�ÿqr� ÿqrÿšr¦ÿšr¼ÿqr¾ÿ×rÁÿšrÄÿšrÜÿ×ráÿ×räÿ×rÿqr ÿqrnÿ×r|ÿšr€ÿ®r‚ÿ®r—ÿ×r›ÿ×r§ÿ×r©ÿšrªÿ×rµÿqr¶ÿ×r·ÿ…r¹ÿ…r½ÿšr¾ÿ×r¿ÿšrÀÿ×rÁÿšrÂÿ×rÅÿšrÇÿšrÔÿšrÕÿ×ráÿ×rãÿ×rýÿšrþÿ×rÿ×r ÿqrÿ×rÿqrÿ×rÿšrÿ×s�ÿqs� ÿqsÏÿ×sØÿ×sÛÿ×sÜÿšsÝÿÃsÞÿ×sáÿÃsäÿšsêÿ×síÿ×söÿÃsÿqs ÿqsjÿ×smÿ×s}ÿìsÿ×sÿ×sƒÿ×s…ÿ×s‡ÿ×s‰ÿ×s‹ÿ×sÿ×sªÿšs²ÿ×s´ÿ×s¶ÿšs¸ÿ×sºÿ×s¾ÿšsÀÿÃsÂÿÃsÆÿ×sÈÿ×sÕÿÃsàÿ×sðÿ×sòÿ×sôÿ×søÿÃsúÿÃsüÿÃsþÿÃs ÿ×s ÿ×sÿ…sÿ…sÿ×sÿšsÿ×t�ÿqt� ÿqtÿšt¦ÿšt¼ÿqt¾ÿ×tÁÿštÄÿštÜÿ×táÿ×täÿ×tÿqt ÿqtnÿ×t|ÿšt€ÿ®t‚ÿ®t—ÿ×t›ÿ×t§ÿ×t©ÿštªÿ×tµÿqt¶ÿ×t·ÿ…t¹ÿ…t½ÿšt¾ÿ×t¿ÿštÀÿ×tÁÿštÂÿ×tÅÿštÇÿštÔÿštÕÿ×táÿ×tãÿ×týÿštþÿ×tÿ×t ÿqtÿ×tÿqtÿ×tÿštÿ×u�ÿqu� ÿquÏÿ×uØÿ×uÛÿ×uÜÿšuÝÿÃuÞÿ×uáÿÃuäÿšuêÿ×uíÿ×uöÿÃuÿqu ÿqujÿ×umÿ×u}ÿìuÿ×uÿ×uƒÿ×u…ÿ×u‡ÿ×u‰ÿ×u‹ÿ×uÿ×uªÿšu²ÿ×u´ÿ×u¶ÿšu¸ÿ×uºÿ×u¾ÿšuÀÿÃuÂÿÃuÆÿ×uÈÿ×uÕÿÃuàÿ×uðÿ×uòÿ×uôÿ×uøÿÃuúÿÃuüÿÃuþÿÃu ÿ×u ÿ×uÿ…uÿ…uÿ×uÿšuÿ×v ÿìvÿìx ÿìxÿìz�ÿ®z�ÿ®zÿ®z ÿ®z€ÿìz‚ÿìz·ÿìz¹ÿìz ÿ×zÿ×|�ÿq|�ÿq|¤ÿÃ|ªÿ®|®ÿÃ|µÿÃ|Îÿ×|Õÿ×|òÿ×|ÿq| ÿq|rÿ®|sÿ×|ÎÿÃ|Ïÿ×|Ùÿ®|Ûÿ®|Ýÿ®| ÿ®| ÿ®|ÿÃ|ÿ×|ÿÃ|ÿ×}�ÿì}� ÿì}Ðÿ×}Üÿì}Ýÿì}ßÿ×}áÿì}äÿì}öÿì}ÿì} ÿì} ÿ×}ªÿì}¶ÿì}¼ÿ×}¾ÿì}Àÿì}Âÿì}Ëÿ×}Õÿì}æÿ×}øÿì}úÿì}üÿì}þÿì}ÿ×}ÿ×}ÿì}ÿì}ÿì~�ÿ®~�ÿ®~ÿì~¤ÿ×~¦ÿì~¨ÿ×~ªÿ×~®ÿ×~°ÿ×~±ÿì~µÿ×~¼ÿÃ~½ÿ×~¿ÿ×~Áÿ×~Äÿì~Çÿì~Îÿì~Õÿì~òÿì~ÿ®~ ÿ®~rÿ×~sÿì~zÿì~|ÿ×~€ÿì~‚ÿì~Ÿÿ×~¡ÿì~©ÿì~µÿÃ~·ÿì~¹ÿì~»ÿ×~½ÿì~¿ÿ×~Áÿ×~Êÿ×~Îÿ×~Ïÿì~Ôÿ×~Ùÿ×~Ûÿ×~Ýÿ×~åÿ×~çÿì~õÿì~÷ÿ×~ùÿ×~ûÿ×~ýÿ×~ÿ×~ÿ×~ ÿ×~ÿ×~ÿ×~ÿì~ÿì~ÿ×~ÿì�ÿì� ÿìÐÿ×ÜÿìÝÿìßÿ×áÿìäÿìöÿìÿì ÿì ÿתÿì¶ÿì¼ÿ×¾ÿìÀÿìÂÿìËÿ×Õÿìæÿ×øÿìúÿìüÿìþÿìÿ×ÿ×ÿìÿìÿì€�ÿ…€�ÿ…€Ÿÿ쀤ÿš€ªÿq€®ÿš€µÿš€¸ÿ쀻ÿ쀾ÿÀÉÿì€Îÿ®€Ïÿ×€Õÿ®€Øÿ×€Ûÿ×€Þÿ×€áÿ×€êÿ×€ë�f€íÿ×€îÿì€òÿ®€ô�f€ÿ…€ ÿ…€jÿ×€lÿì€rÿq€sÿ®€~ÿì€ÿ×€„ÿ쀅ÿ×€†ÿ쀇ÿ×€ˆÿ쀉ÿ×€Šÿ쀌ÿì€ÿ×€˜�f€¨�f€±ÿ쀲ÿ×€³ÿ쀴ÿ×€Àÿ×€Âÿ×€Åÿ×€ÆÿÀÇÿ×€ÈÿÀÎÿš€Ïÿ®€Õÿ×€Ùÿq€Ûÿq€Ýÿq€àÿ×€ïÿì€ðÿ×€ñÿì€òÿ×€óÿì€ôÿ×€þÿ×€ ÿq€ ÿ×€ ÿq€ ÿ×€ÿš€ÿ®€ÿì€ÿ×€ÿ×€ÿš€ÿ®�ÿ®�ÿ®Îÿ×Õÿ×òÿ×ÿ® ÿ®sÿ×Ïÿ×ÿ×ÿׂ�ÿ…‚�ÿ…‚Ÿÿ삤ÿš‚ªÿq‚®ÿš‚µÿš‚¸ÿì‚»ÿ삾ÿÂÉÿì‚Îÿ®‚ÏÿׂÕÿ®‚ØÿׂÛÿׂÞÿׂáÿׂêÿׂë�f‚íÿׂîÿì‚òÿ®‚ô�f‚ÿ…‚ ÿ…‚jÿׂlÿì‚rÿq‚sÿ®‚~ÿì‚ÿׂ„ÿì‚…ÿׂ†ÿ삇ÿׂˆÿ삉ÿׂŠÿ삌ÿì‚ÿׂ˜�f‚¨�f‚±ÿ삲ÿׂ³ÿì‚´ÿׂÀÿׂÂÿׂÅÿׂÆÿÂÇÿׂÈÿÂÎÿš‚Ïÿ®‚ÕÿׂÙÿq‚Ûÿq‚Ýÿq‚àÿׂïÿì‚ðÿׂñÿì‚òÿׂóÿì‚ôÿׂþÿׂ ÿq‚ ÿׂ ÿq‚ ÿׂÿš‚ÿ®‚ÿì‚ÿׂÿׂÿš‚ÿ®ƒ�ÿ®ƒ�ÿ®ƒÎÿ׃Õÿ׃òÿ׃ÿ®ƒ ÿ®ƒsÿ׃Ïÿ׃ÿ׃ÿׄ�ÿ®„�ÿ®„ÎÿׄÕÿׄòÿׄÿ®„ ÿ®„sÿׄÏÿׄÿׄÿ×…�ÿ®…�ÿ®…Îÿ×…Õÿ×…òÿ×…ÿ®… ÿ®…sÿ×…Ïÿ×…ÿ×…ÿ׆�ÿ®†�ÿ®†ÿ솤ÿ׆¦ÿ솨ÿ׆ªÿ׆®ÿ׆°ÿ׆±ÿ솵ÿ׆¼ÿƽÿ׆¿ÿ׆Áÿ׆Äÿì†Çÿì†Îÿì†Õÿì†òÿì†ÿ®† ÿ®†rÿ׆sÿì†zÿì†|ÿ׆€ÿ솂ÿ솟ÿ׆¡ÿ솩ÿ솵ÿÆ·ÿ솹ÿ솻ÿ׆½ÿ솿ÿ׆Áÿ׆Êÿ׆Îÿ׆Ïÿì†Ôÿ׆Ùÿ׆Ûÿ׆Ýÿ׆åÿ׆çÿì†õÿì†÷ÿ׆ùÿ׆ûÿ׆ýÿ׆ÿ׆ÿ׆ ÿ׆ÿ׆ÿ׆ÿì†ÿì†ÿ׆ÿì‡�ÿì‡� ÿì‡ÐÿׇÜÿì‡Ýÿì‡ßÿׇáÿì‡äÿì‡öÿì‡ÿì‡ ÿ쇠ÿׇªÿ쇶ÿ쇼ÿׇ¾ÿì‡Àÿì‡Âÿì‡ËÿׇÕÿì‡æÿׇøÿì‡úÿì‡üÿì‡þÿì‡ÿׇÿׇÿì‡ÿì‡ÿìˆ�ÿ®ˆ�ÿ®ˆÿ숤ÿ׈¦ÿ숨ÿ׈ªÿ׈®ÿ׈°ÿ׈±ÿ숵ÿ׈¼ÿȽÿ׈¿ÿ׈Áÿ׈ÄÿìˆÇÿìˆÎÿìˆÕÿìˆòÿìˆÿ®ˆ ÿ®ˆrÿ׈sÿìˆzÿìˆ|ÿ׈€ÿ숂ÿ숟ÿ׈¡ÿ숩ÿ숵ÿÈ·ÿ숹ÿ숻ÿ׈½ÿ숿ÿ׈Áÿ׈Êÿ׈Îÿ׈ÏÿìˆÔÿ׈Ùÿ׈Ûÿ׈Ýÿ׈åÿ׈çÿìˆõÿìˆ÷ÿ׈ùÿ׈ûÿ׈ýÿ׈ÿ׈ÿ׈ ÿ׈ÿ׈ÿ׈ÿìˆÿìˆÿ׈ÿì‰�ÿì‰� ÿì‰Ðÿ׉Üÿì‰Ýÿì‰ßÿ׉áÿì‰äÿì‰öÿì‰ÿì‰ ÿ쉠ÿ׉ªÿ쉶ÿ쉼ÿ׉¾ÿì‰Àÿì‰Âÿì‰Ëÿ׉Õÿì‰æÿ׉øÿì‰úÿì‰üÿì‰þÿì‰ÿ׉ÿ׉ÿì‰ÿì‰ÿìŠ�ÿ®Š�ÿ®Šÿ스ÿ׊¦ÿ슨ÿ׊ªÿ׊®ÿ׊°ÿ׊±ÿ습ÿ׊¼ÿʽÿ׊¿ÿ׊Áÿ׊ÄÿìŠÇÿìŠÎÿìŠÕÿìŠòÿìŠÿ®Š ÿ®Šrÿ׊sÿìŠzÿìŠ|ÿ׊€ÿ슂ÿ슟ÿ׊¡ÿ슩ÿ습ÿÊ·ÿ승ÿ슻ÿ׊½ÿ슿ÿ׊Áÿ׊Êÿ׊Îÿ׊ÏÿìŠÔÿ׊Ùÿ׊Ûÿ׊Ýÿ׊åÿ׊çÿìŠõÿìŠ÷ÿ׊ùÿ׊ûÿ׊ýÿ׊ÿ׊ÿ׊ ÿ׊ÿ׊ÿ׊ÿìŠÿìŠÿ׊ÿì‹�ÿ®‹�ÿ®‹Îÿ׋Õÿ׋òÿ׋ÿ®‹ ÿ®‹sÿ׋Ïÿ׋ÿ׋ÿ׌Ÿÿ׌¸ÿ׌»ÿ׌¾ÿ׌áÿ׌lÿ׌~ÿ׌„ÿ׌†ÿ׌ˆÿ׌Šÿ׌Œÿ׌±ÿ׌³ÿ׌Àÿ׌Âÿ׌Åÿ׌Çÿ׌Õÿ׌ïÿ׌ñÿ׌óÿ׌þÿ׌ ÿ׌ ÿ׌ÿ׌ÿ׌ÿו£�á•ê�)•ÿוÿ×–�ÿì–� ÿì–ÿì– ÿì—�ÿ®—� ÿ®—ÿ×—¦ÿ×—¼ÿ®—Áÿ®—Äÿ×—Üÿ×—äÿ×—ÿ®— ÿ®—|ÿ®—€ÿׂÿשÿ×—ªÿ×—µÿ®—¶ÿ×—·ÿ×¹ÿ×½ÿ×—¾ÿ×—¿ÿ®—Áÿ®—Ôÿ®—ýÿ®— ÿš—ÿš—ÿ×—ÿט�ÿ…˜� ÿ…˜ÐÿטÜÿš˜ÝÿØßÿטáÿ®˜äÿš˜öÿØÿ…˜ ÿ…˜mÿטÿטƒÿט‹ÿט ÿטªÿš˜¶ÿš˜¸ÿغÿؼÿט¾ÿš˜Àÿ®˜Âÿ®˜ÆÿטÈÿטËÿטÕÿ®˜æÿטêÿטøÿØúÿØüÿØþÿ®˜ÿטÿטÿš˜ÿš˜ÿš™�þö™�þö™¤ÿ…™ªÿš™®ÿ…™°ÿ×™µÿ…™¿ÿ×™Îÿš™Õÿš™òÿš™þö™ þö™rÿš™sÿš™vÿ왟ÿ×™»ÿ×™Êÿ×™Îÿ…™Ïÿš™Ùÿš™Ûÿš™Ýÿš™åÿ×™ÿ×™ÿ×™ ÿ®™ ÿ®™ÿ…™ÿš™ÿ…™ÿšš�ÿìš� ÿìšÐÿךÜÿìšÝÿìšßÿךáÿìšäÿìšöÿìšÿìš ÿìš ÿךªÿìš¶ÿìš¼ÿך¾ÿìšÀÿìšÂÿìšËÿךÕÿìšæÿךøÿìšúÿìšüÿìšþÿìšÿךÿךÿìšÿìšÿì›�ÿš›�ÿ×›�ÿš›�)›Ÿÿ×›¤ÿ®›¦�)›ªÿ…›®ÿ®›µÿ®›¸ÿ×›»ÿ×›¼�)›¾ÿÛÄ�)›ÌÿÛÍÿÛÎÿš›Ïÿ®›Ðÿ×›Ñÿ×›ÒÿÛÓÿÛÔÿÛÕÿš›ÖÿÛ×ÿÛØÿ®›ÙÿÛÚÿÛÛÿ®›Þÿ®›ßÿ×›àÿÛáÿš›âÿÛãÿÛåÿÛæÿÛçÿ×›èÿÛêÿ®›ë�)›ìÿÛíÿ®›îÿÛòÿš›óÿÛô�)›õÿÛ÷ÿÛùÿÛÿ×›ÿ×›ÿ×›ÿš› ÿš›jÿ®›kÿÛlÿ×›qÿÛrÿ…›sÿš›uÿÛwÿ×›yÿÛ}ÿÛ~ÿ×›ÿ®›„ÿ×›…ÿ®›†ÿ×›‡ÿ®›ˆÿ×›‰ÿ®›Šÿ×›Œÿ×›ÿ®›–ÿÛ˜�)›šÿÛžÿÛ ÿ×›¢ÿ×›¤ÿÛ¦ÿÛ¨�)›©�)›¬ÿÛ®ÿÛ°ÿÛ±ÿ×›²ÿ®›³ÿ×›´ÿ®›µ�)›¼ÿ×›½�)›Àÿš›Âÿš›ÄÿÛÅÿ×›ÆÿÛÇÿ×›ÈÿÛËÿ×›ÍÿÛÎÿ®›Ïÿš›ÑÿÛÓÿÛÕÿš›×ÿÛÙÿ…›Ûÿ…›Ýÿ…›àÿ®›æÿ×›èÿ×›ìÿÛîÿÛïÿ×›ðÿ®›ñÿ×›òÿ®›óÿ×›ôÿ®›öÿ×›þÿš›�ÿÛÿÛÿ×›ÿ×› ÿš› ÿ®› ÿš› ÿ®›ÿ×›ÿ×›ÿ®›ÿš›ÿÛÿ×›ÿ®›�)›ÿ®›ÿ®›ÿšœ�ÿÜ�ÿÜÎÿÜÏÿלÕÿÜØÿלÛÿלÞÿלêÿלíÿלòÿÜÿÜ ÿÜjÿלsÿÜÿל…ÿל‡ÿל‰ÿלÿל²ÿל´ÿלÏÿÜàÿלðÿלòÿלôÿל ÿל ÿלÿÜÿלÿלÿÃ�ÿÃ� ÿÃÿã�f¦ÿüÿÃÁÿ®ÄÿÃÜÿ×áÿ×äÿ×ÿà ÿÃ|ÿ®€ÿÂÿéÿêÿ×µÿöÿ×·ÿ×¹ÿ×½ÿþÿ׿ÿ®Àÿ×Áÿ®Âÿ×Ôÿ®Õÿ×ýÿ®þÿ× ÿ×ÿÃÿ×ÿÃÿÃÿמ�ÿÞ� ÿÞÿÞ ÿÞÿמÿןŸÿן£�៸ÿן»ÿן¾ÿßÜÿןáÿ®Ÿäÿןlÿן{�=Ÿ}ÿìŸ~ÿן„ÿן†ÿןˆÿןŠÿןŒÿןªÿן±ÿן³ÿן¶ÿן¾ÿןÀÿ®ŸÂÿ®ŸÅÿ߯ÿןÇÿßÈÿןÕÿ®Ÿïÿןñÿןóÿןþÿ®Ÿÿןÿןÿןÿ× Ïÿì Øÿì Ûÿì Þÿì áÿì êÿì íÿì jÿì ÿì …ÿì ‡ÿì ‰ÿì ÿì ²ÿì ´ÿì Àÿì Âÿì Õÿì àÿì ðÿì òÿì ôÿì þÿì  ÿì  ÿì ÿ× ÿ× ÿì ÿì¡�ÿ®¡�ÿ®¡ÿ®¡ ÿ®¡€ÿì¡‚ÿì¡·ÿ졹ÿì¡ ÿסÿ×¢é�)£Ÿÿ×££�᣸ÿ×£»ÿ×£¾ÿãÜÿ×£áÿ®£äÿ×£lÿ×£{�=£}ÿì£~ÿ×£„ÿ×£†ÿ×£ˆÿ×£Šÿ×£Œÿ×£ªÿ×£±ÿ×£³ÿ×£¶ÿ×£¾ÿ×£Àÿ®£Âÿ®£ÅÿãÆÿ×£ÇÿãÈÿ×£Õÿ®£ïÿ×£ñÿ×£óÿ×£þÿ®£ÿ×£ÿ×£ÿ×£ÿפÏÿì¤Øÿì¤Ûÿì¤Þÿì¤áÿì¤êÿì¤íÿì¤jÿì¤ÿ줅ÿ줇ÿ줉ÿì¤ÿ줲ÿ줴ÿì¤Àÿì¤Âÿì¤Õÿì¤àÿì¤ðÿì¤òÿì¤ôÿì¤þÿì¤ ÿì¤ ÿì¤ÿפÿפÿì¤ÿ쥟ÿ×¥¸ÿ×¥»ÿ×¥¾ÿ×¥Áÿ×¥áÿ×¥lÿ×¥|ÿ×¥~ÿ×¥„ÿ×¥†ÿ×¥ˆÿ×¥Šÿ×¥Œÿ×¥±ÿ×¥³ÿ×¥¿ÿ×¥Àÿ×¥Áÿ×¥Âÿ×¥Åÿš¥Çÿš¥Ôÿ×¥Õÿ×¥ïÿ×¥ñÿ×¥óÿ×¥ýÿ×¥þÿ×¥ ÿ×¥ ÿ×¥ÿ×¥ÿ×¥ÿ×¥ÿì¦ÏÿצØÿצÛÿצÞÿצáÿצêÿצíÿצjÿצÿצ…ÿצ‡ÿצ‰ÿצÿצ²ÿצ´ÿצÀÿצÂÿצÆÿצÈÿצÕÿצàÿצðÿצòÿצôÿצþÿצ ÿצ ÿצÿצÿ×§Ÿÿ×§¸ÿ×§»ÿ×§¾ÿ×§Áÿ×§áÿ×§lÿ×§|ÿ×§~ÿ×§„ÿ×§†ÿ×§ˆÿ×§Šÿ×§Œÿ×§±ÿ×§³ÿ×§¿ÿ×§Àÿ×§Áÿ×§Âÿ×§Åÿš§Çÿš§Ôÿ×§Õÿ×§ïÿ×§ñÿ×§óÿ×§ýÿ×§þÿ×§ ÿ×§ ÿ×§ÿ×§ÿ×§ÿ×§ÿì¨ÏÿרØÿרÛÿרÞÿרáÿרêÿרíÿרjÿרÿר…ÿר‡ÿר‰ÿרÿר²ÿר´ÿרÀÿרÂÿרÆÿרÈÿרÕÿרàÿרðÿרòÿרôÿרþÿר ÿר ÿרÿרÿשŸÿש¸ÿש»ÿש¾ÿשÁÿשáÿשlÿש|ÿש~ÿש„ÿש†ÿשˆÿשŠÿשŒÿש±ÿש³ÿש¿ÿשÀÿשÁÿשÂÿשÅÿš©Çÿš©ÔÿשÕÿשïÿשñÿשóÿשýÿשþÿש ÿש ÿשÿשÿשÿשÿìªÏÿתØÿתÛÿתÞÿתáÿתêÿתíÿתjÿתÿת…ÿת‡ÿת‰ÿתÿת²ÿת´ÿתÀÿתÂÿתÆÿתÈÿתÕÿתàÿתðÿתòÿתôÿתþÿת ÿת ÿתÿתÿ׫£�á«ê�)«ÿ׫ÿ׬�ÿì¬� ÿì¬ÿì¬ ÿì­�ÿš­�ÿ×­�ÿš­�)­Ÿÿ×­¤ÿ®­¦�)­ªÿ…­®ÿ®­µÿ®­¸ÿ×­»ÿ×­¼�)­¾ÿíÄ�)­ÌÿíÍÿíÎÿš­Ïÿ®­Ðÿ×­Ñÿ×­ÒÿíÓÿíÔÿíÕÿš­Öÿí×ÿíØÿ®­ÙÿíÚÿíÛÿ®­Þÿ®­ßÿ×­àÿíáÿš­âÿíãÿíåÿíæÿíçÿ×­èÿíêÿ®­ë�)­ìÿííÿ®­îÿíòÿš­óÿíô�)­õÿí÷ÿíùÿíÿ×­ÿ×­ÿ×­ÿš­ ÿš­jÿ®­kÿílÿ×­qÿírÿ…­sÿš­uÿíwÿ×­yÿí}ÿí~ÿ×­ÿ®­„ÿ×­…ÿ®­†ÿ×­‡ÿ®­ˆÿ×­‰ÿ®­Šÿ×­Œÿ×­ÿ®­–ÿí˜�)­šÿížÿí ÿ×­¢ÿ×­¤ÿí¦ÿí¨�)­©�)­¬ÿí®ÿí°ÿí±ÿ×­²ÿ®­³ÿ×­´ÿ®­µ�)­¼ÿ×­½�)­Àÿš­Âÿš­ÄÿíÅÿ×­ÆÿíÇÿ×­ÈÿíËÿ×­ÍÿíÎÿ®­Ïÿš­ÑÿíÓÿíÕÿš­×ÿíÙÿ…­Ûÿ…­Ýÿ…­àÿ®­æÿ×­èÿ×­ìÿíîÿíïÿ×­ðÿ®­ñÿ×­òÿ®­óÿ×­ôÿ®­öÿ×­þÿš­�ÿíÿíÿ×­ÿ×­ ÿš­ ÿ®­ ÿš­ ÿ®­ÿ×­ÿ×­ÿ®­ÿš­ÿíÿ×­ÿ®­�)­ÿ®­ÿ®­ÿš®�ÿš®�ÿ×®�ÿš®ÎÿîÏÿì®ÕÿîØÿì®Ûÿì®Þÿì®êÿì®íÿì®òÿîÿ×®ÿ×®ÿ×®ÿš® ÿš®jÿì®sÿîÿì®…ÿ쮇ÿ쮉ÿì®ÿ쮲ÿì®´ÿì®Ïÿîàÿì®ðÿì®òÿì®ôÿì® ÿì® ÿì®ÿîÿì®ÿì®ÿï�ÿ\¯� ÿ\¯ÿš¯£�f¯¦ÿš¯¼ÿH¯Áÿ…¯Äÿš¯Üÿ®¯áÿׯäÿ®¯ÿ\¯ ÿ\¯|ÿ…¯€ÿq¯‚ÿq¯©ÿš¯ªÿ®¯µÿH¯¶ÿ®¯·ÿš¯¹ÿš¯½ÿš¯¾ÿ®¯¿ÿ…¯ÀÿׯÁÿ…¯ÂÿׯÅÿïÆÿׯÇÿïÈÿׯÔÿ…¯Õÿׯýÿ…¯þÿׯ ÿH¯ÿ®¯ÿH¯ÿ®¯ÿš¯ÿ®°�ÿq°� ÿq°Üÿš°áÿ×°äÿš°ÿq° ÿq°mÿ×°ÿ×°ƒÿ×°‹ÿ×°ªÿš°¶ÿš°¸ÿ×°ºÿ×°¾ÿš°Àÿ×°Âÿ×°Æÿ×°Èÿ×°Õÿ×°þÿ×°ÿq°ÿq°ÿš±ÿ×±¦ÿ×±¼ÿñÄÿ×±€ÿ챂ÿ챩ÿ×±µÿñ·ÿì±¹ÿì±½ÿ×± ÿ×±ÿ×±ÿײ�ÿì²� ÿì²ÐÿײÜÿì²Ýÿì²ßÿײáÿì²äÿì²öÿì²ÿì² ÿì² ÿײªÿì²¶ÿì²¼ÿײ¾ÿì²Àÿì²Âÿì²ËÿײÕÿì²æÿײøÿì²úÿì²üÿì²þÿì²ÿײÿײÿì²ÿì²ÿ쳟ÿ׳¸ÿ׳»ÿ׳¾ÿ׳áÿ׳lÿ׳~ÿ׳„ÿ׳†ÿ׳ˆÿ׳Šÿ׳Œÿ׳±ÿ׳³ÿ׳Àÿ׳Âÿ׳Åÿ׳Çÿ׳Õÿ׳ïÿ׳ñÿ׳óÿ׳þÿ׳ ÿ׳ ÿ׳ÿ׳ÿ׳ÿ×µ�ÿ…µ�ÿ®µ�ÿ…µŸÿ×µ¤ÿšµªÿqµ®ÿšµµÿšµ¸ÿ×µ»ÿ×µ¼�)µ¾ÿ®µÌÿšµÍÿšµÎÿ…µÏÿqµÐÿ×µÑÿ×µÒÿšµÓÿšµÔÿšµÕÿ…µÖÿšµ×ÿšµØÿqµÙÿšµÚÿšµÛÿqµÜÿ®µÝÿ®µÞÿqµßÿ×µàÿšµáÿšµâÿšµãÿšµäÿ®µåÿšµæÿšµçÿ×µèÿšµéÿõêÿqµìÿšµíÿqµîÿ…µòÿ…µóÿšµõÿšµöÿ®µ÷ÿšµùÿšµÿ®µÿ®µÿ®µÿ…µ ÿ…µjÿqµkÿšµlÿ×µmÿ×µqÿšµrÿqµsÿ…µuÿšµwÿšµyÿšµ}ÿšµ~ÿ×µÿqµÿ×µƒÿ×µ„ÿ×µ…ÿqµ†ÿ×µ‡ÿqµˆÿ×µ‰ÿqµŠÿ×µ‹ÿ×µŒÿ×µÿqµ–ÿšµšÿšµžÿšµ ÿ×µ¢ÿ×µ¤ÿšµ¦ÿšµªÿ®µ¬ÿšµ®ÿšµ°ÿšµ±ÿ×µ²ÿqµ³ÿ×µ´ÿqµµ�)µ¶ÿ®µ¸ÿ®µºÿ®µ¼ÿ×µ¾ÿ®µÀÿšµÂÿšµÄÿšµÅÿšµÆÿqµÇÿšµÈÿqµËÿ×µÍÿšµÎÿšµÏÿ…µÑÿšµÓÿšµÕÿšµ×ÿšµÙÿqµÛÿqµÝÿqµàÿqµæÿ×µèÿ×µêÿõìÿšµîÿšµïÿ×µðÿqµñÿ×µòÿqµóÿ×µôÿqµöÿ×µøÿ®µúÿ®µüÿ®µþÿšµ�ÿšµÿšµÿ×µÿ×µ ÿqµ ÿqµ ÿqµ ÿqµÿšµÿšµÿšµÿ…µÿšµÿ×µÿqµÿ®µÿqµÿšµÿ…¶�ÿš¶�ÿ×¶�ÿš¶ÎÿöÏÿì¶ÕÿöØÿì¶Ûÿì¶Þÿì¶êÿì¶íÿì¶òÿöÿ×¶ÿ×¶ÿ×¶ÿš¶ ÿš¶jÿì¶sÿöÿì¶…ÿ춇ÿ춉ÿì¶ÿì¶²ÿì¶´ÿì¶Ïÿöàÿì¶ðÿì¶òÿì¶ôÿì¶ ÿì¶ ÿì¶ÿöÿì¶ÿì¶ÿ÷�ÿ…·�ÿ…·Ÿÿ×·¤ÿ®·ªÿ…·®ÿ®·µÿ®·¸ÿ×·»ÿ×·¾ÿ÷Êÿ®·Ìÿ÷Íÿ÷Îÿš·Ïÿš·Òÿ÷Óÿ÷Ôÿ÷Õÿš·Öÿ÷×ÿ÷Øÿš·Ùÿ÷Úÿ÷Ûÿš·Þÿš·àÿ÷áÿ®·âÿ÷ãÿ÷åÿ÷æÿ÷èÿ÷éÿ×·êÿš·ë�)·ìÿ÷íÿš·îÿ®·òÿš·óÿ÷ô�)·õÿ÷÷ÿ÷ùÿ÷ÿ…· ÿ…·jÿš·kÿ÷lÿ×·qÿ÷rÿ…·sÿš·uÿ÷wÿ×·yÿ÷}ÿ×·~ÿ×·ÿš·„ÿ×·…ÿš·†ÿ×·‡ÿš·ˆÿ×·‰ÿš·Šÿ×·Œÿ×·ÿš·–ÿ÷˜�)·šÿ÷žÿ÷¤ÿ÷¦ÿ÷¨�)·¬ÿ÷®ÿ÷°ÿ÷±ÿ×·²ÿš·³ÿ×·´ÿš·Àÿ®·Âÿ®·Äÿ÷Æÿ®·Èÿ®·Íÿ÷Îÿ®·Ïÿš·Ñÿ÷Óÿ÷Õÿ®·×ÿ÷Ùÿ…·Úÿ®·Ûÿ…·Üÿ®·Ýÿ…·Þÿ®·àÿš·áÿì·âÿ®·ãÿì·äÿ®·ìÿ÷îÿ÷ïÿ×·ðÿš·ñÿ×·òÿš·óÿ×·ôÿš·þÿ®·�ÿ÷ÿ÷ ÿ®· ÿš· ÿ®· ÿš·ÿ×·ÿ×·ÿ®·ÿš·ÿ÷ÿ×·ÿš·ÿì·ÿš·ÿ®·ÿš¸�ÿ®¸�ÿ®¸Îÿì¸Õÿì¸òÿì¸ÿ®¸ ÿ®¸sÿì¸Ïÿì¸ÿì¸ÿì¹�ÿ…¹�ÿ…¹Ÿÿ×¹¤ÿ®¹ªÿ…¹®ÿ®¹µÿ®¹¸ÿ×¹»ÿ×¹¾ÿùÊÿ®¹ÌÿùÍÿùÎÿš¹Ïÿš¹ÒÿùÓÿùÔÿùÕÿš¹Öÿù×ÿùØÿš¹ÙÿùÚÿùÛÿš¹Þÿš¹àÿùáÿ®¹âÿùãÿùåÿùæÿùèÿùéÿ×¹êÿš¹ë�)¹ìÿùíÿš¹îÿ®¹òÿš¹óÿùô�)¹õÿù÷ÿùùÿùÿ…¹ ÿ…¹jÿš¹kÿùlÿ×¹qÿùrÿ…¹sÿš¹uÿùwÿ×¹yÿù}ÿ×¹~ÿ×¹ÿš¹„ÿ×¹…ÿš¹†ÿ×¹‡ÿš¹ˆÿ×¹‰ÿš¹Šÿ×¹Œÿ×¹ÿš¹–ÿù˜�)¹šÿùžÿù¤ÿù¦ÿù¨�)¹¬ÿù®ÿù°ÿù±ÿ×¹²ÿš¹³ÿ×¹´ÿš¹Àÿ®¹Âÿ®¹ÄÿùÆÿ®¹Èÿ®¹ÍÿùÎÿ®¹Ïÿš¹ÑÿùÓÿùÕÿ®¹×ÿùÙÿ…¹Úÿ®¹Ûÿ…¹Üÿ®¹Ýÿ…¹Þÿ®¹àÿš¹áÿì¹âÿ®¹ãÿì¹äÿ®¹ìÿùîÿùïÿ×¹ðÿš¹ñÿ×¹òÿš¹óÿ×¹ôÿš¹þÿ®¹�ÿùÿù ÿ®¹ ÿš¹ ÿ®¹ ÿš¹ÿ×¹ÿ×¹ÿ®¹ÿš¹ÿùÿ×¹ÿš¹ÿì¹ÿš¹ÿ®¹ÿšº�ÿ®º�ÿ®ºÎÿìºÕÿìºòÿìºÿ®º ÿ®ºsÿìºÏÿìºÿìºÿ컟ÿ×»£�Ỹÿ×»»ÿ×»¾ÿûÜÿ×»áÿ®»äÿ×»lÿ×»{�=»}ÿì»~ÿ×»„ÿ×»†ÿ×»ˆÿ×»Šÿ×»Œÿ×»ªÿ×»±ÿ×»³ÿ×»¶ÿ×»¾ÿ×»Àÿ®»Âÿ®»ÅÿûÆÿ×»ÇÿûÈÿ×»Õÿ®»ïÿ×»ñÿ×»óÿ×»þÿ®»ÿ×»ÿ×»ÿ×»ÿ×¼Ïÿì¼Øÿì¼Ûÿì¼Þÿì¼áÿì¼êÿì¼íÿì¼jÿì¼ÿì¼…ÿ켇ÿ켉ÿì¼ÿì¼²ÿì¼´ÿì¼Àÿì¼Âÿì¼Õÿì¼àÿì¼ðÿì¼òÿì¼ôÿì¼þÿì¼ ÿì¼ ÿì¼ÿ×¼ÿ×¼ÿì¼ÿì½£�á½ê�)½ÿ×½ÿ×¾�ÿì¾� ÿì¾ÿì¾ ÿì¿£�á¿ê�)¿ÿ׿ÿ×À�ÿìÀ� ÿìÀÿìÀ ÿìÃ�ÿÃÃ� ÿÃÃÿ׿ÿ×üÿ…ÃÁÿ®ÃÄÿ×ÃÜÿ×ÃÝÿìÃáÿìÃäÿ×ÃöÿìÃÿÃà ÿÃÃ|ÿ®Ã€ÿÃÂÿÃéÿ×êÿ×õÿ…öÿ×÷ÿšÃ¹ÿšÃ½ÿ×þÿ×ÿÿ®ÃÀÿìÃÁÿ®ÃÂÿìÃÔÿ®ÃÕÿìÃøÿìÃúÿìÃüÿìÃýÿ®Ãþÿìà ÿ®Ãÿ×Ãÿ®Ãÿ×Ãÿ×Ãÿ×Ä�ÿšÄ� ÿšÄÜÿ×ÄÝÿ×Ääÿ×Äöÿ×ÄÿšÄ ÿšÄªÿ×Ķÿ×ĸÿ×ĺÿ׾ÿ×Äøÿ×Äúÿ×Äüÿ×Äÿ®Äÿ®Äÿ׿ÿ×Å€ÿìÅ‚ÿìŵÿ×Å·ÿìŹÿìÅ ÿìÅÿìÆ�ÿìÆ� ÿìÆÿìÆ ÿìǼÿ×Ç€ÿìÇ‚ÿìǵÿ×Ç·ÿìǹÿìÇ ÿìÇÿìÈ�ÿìÈ� ÿìÈÿìÈ ÿìÊŸÿ×ʸÿ×Ê»ÿ×ʾÿ×ÊÁÿ×Êáÿ×Êlÿ×Ê|ÿ×Ê~ÿ×Ê„ÿ×ʆÿ×ʈÿ×ÊŠÿ×ÊŒÿ×ʱÿ×ʳÿ×Ê¿ÿ×ÊÀÿ×ÊÁÿ×ÊÂÿ×ÊÅÿšÊÇÿšÊÔÿ×ÊÕÿ×Êïÿ×Êñÿ×Êóÿ×Êýÿ×Êþÿ×Ê ÿ×Ê ÿ×Êÿ×Êÿ×Êÿ×ÊÿìËÏÿ×ËØÿ×ËÛÿ×ËÞÿ×Ëáÿ×Ëêÿ×Ëíÿ×Ëjÿ×Ëÿ×Ë…ÿסÿ×ˉÿ×Ëÿ×˲ÿ×Ë´ÿ×ËÀÿ×ËÂÿ×ËÆÿ×ËÈÿ×ËÕÿ×Ëàÿ×Ëðÿ×Ëòÿ×Ëôÿ×Ëþÿ×Ë ÿ×Ë ÿ×Ëÿ×Ëÿ×Ì�ÿÃÌ� ÿÃÌ£�f̼ÿ×̾ÿ×ÌÁÿ®ÌÜÿÃÌáÿ×ÌäÿÃÌÿÃÌ ÿÃÌmÿìÌ|ÿ®Ì€ÿ×ÌÿìÌ‚ÿ×̃ÿìÌ‹ÿì̪ÿÃ̵ÿ×̶ÿÃÌ·ÿ×̸ÿì̹ÿ×̺ÿì̾ÿÃÌ¿ÿ®ÌÀÿ×ÌÁÿ®ÌÂÿ×ÌÅÿÃÌÆÿ×ÌÇÿÃÌÈÿ×ÌÔÿ®ÌÕÿ×Ìýÿ®Ìþÿ×Ì ÿ×ÌÿÃÌÿ×ÌÿÃÌÿÃÍáÿ×ÍÀÿ×ÍÂÿ×ÍÕÿ×Íþÿ×Σ�áÎê�)Îÿ×Îÿ×Ï�ÿìÏ� ÿìÏÿìÏ ÿìÒ£�áÒê�)Òÿ×Òÿ×Ó�ÿìÓ� ÿìÓÿìÓ ÿìÖ£�áÖê�)Öÿ×Öÿ××�ÿì×� ÿì×ÿì× ÿìÙ�ÿqÙ� ÿqÙÿšÙ¦ÿšÙ¼ÿqÙ¾ÿ×ÙÁÿšÙÄÿšÙÜÿ×Ùáÿ×Ùäÿ×ÙÿqÙ ÿqÙnÿ×Ù|ÿšÙ€ÿ®Ù‚ÿ®Ù—ÿ×Ù›ÿ×Ù§ÿ×Ù©ÿšÙªÿ×ÙµÿqÙ¶ÿ×Ù·ÿ…Ù¹ÿ…Ù½ÿšÙ¾ÿ×Ù¿ÿšÙÀÿ×ÙÁÿšÙÂÿ×ÙÅÿšÙÇÿšÙÔÿšÙÕÿ×Ùáÿ×Ùãÿ×ÙýÿšÙþÿ×Ùÿ×Ù ÿqÙÿ×ÙÿqÙÿ×ÙÿšÙÿ×Ú�ÿìÚ� ÿìÚÿìÚ ÿìÛ�ÿqÛ� ÿqÛÿšÛ¦ÿšÛ¼ÿqÛ¾ÿ×ÛÁÿšÛÄÿšÛÜÿ×Ûáÿ×Ûäÿ×ÛÿqÛ ÿqÛnÿ×Û|ÿšÛ€ÿ®Û‚ÿ®Û—ÿ×Û›ÿ×Û§ÿ×Û©ÿšÛªÿ×ÛµÿqÛ¶ÿ×Û·ÿ…Û¹ÿ…Û½ÿšÛ¾ÿ×Û¿ÿšÛÀÿ×ÛÁÿšÛÂÿ×ÛÅÿšÛÇÿšÛÔÿšÛÕÿ×Ûáÿ×Ûãÿ×ÛýÿšÛþÿ×Ûÿ×Û ÿqÛÿ×ÛÿqÛÿ×ÛÿšÛÿ×Ü�ÿìÜ� ÿìÜÿìÜ ÿìÞ�ÿìÞ� ÿìÞÿìÞ ÿìà�ÿìà� ÿìàÿìà ÿìá�ÿ®á�ÿ®áÿìá¤ÿ×á¦ÿìá¨ÿ×áªÿ×á®ÿ×á°ÿ×á±ÿìáµÿ×á¼ÿÃá½ÿ×á¿ÿ×áÁÿ×áÄÿìáÇÿìáÎÿìáÕÿìáòÿìáÿ®á ÿ®árÿ×ásÿìázÿìá|ÿ×á€ÿìá‚ÿìáŸÿ×á¡ÿìá©ÿìáµÿÃá·ÿìá¹ÿìá»ÿ×á½ÿìá¿ÿ×áÁÿ×áÊÿ×áÎÿ×áÏÿìáÔÿ×áÙÿ×áÛÿ×áÝÿ×áåÿ×áçÿìáõÿìá÷ÿ×áùÿ×áûÿ×áýÿ×áÿ×áÿ×á ÿ×áÿ×áÿ×áÿìáÿìáÿ×áÿìâ�ÿìâ� ÿìâÐÿ×âÜÿìâÝÿìâßÿ×âáÿìâäÿìâöÿìâÿìâ ÿìâ ÿ×âªÿìâ¶ÿìâ¼ÿ×â¾ÿìâÀÿìâÂÿìâËÿ×âÕÿìâæÿ×âøÿìâúÿìâüÿìâþÿìâÿ×âÿ×âÿìâÿìâÿìã�ÿ®ã�ÿ®ãÿìã¤ÿ×ã¦ÿìã¨ÿ×ãªÿ×ã®ÿ×ã°ÿ×ã±ÿìãµÿ×ã¼ÿÃã½ÿ×ã¿ÿ×ãÁÿ×ãÄÿìãÇÿìãÎÿìãÕÿìãòÿìãÿ®ã ÿ®ãrÿ×ãsÿìãzÿìã|ÿ×ã€ÿìã‚ÿìãŸÿ×ã¡ÿìã©ÿìãµÿÃã·ÿìã¹ÿìã»ÿ×ã½ÿìã¿ÿ×ãÁÿ×ãÊÿ×ãÎÿ×ãÏÿìãÔÿ×ãÙÿ×ãÛÿ×ãÝÿ×ãåÿ×ãçÿìãõÿìã÷ÿ×ãùÿ×ãûÿ×ãýÿ×ãÿ×ãÿ×ã ÿ×ãÿ×ãÿ×ãÿìãÿìãÿ×ãÿìä�ÿìä� ÿìäÐÿ×äÜÿìäÝÿìäßÿ×äáÿìääÿìäöÿìäÿìä ÿìä ÿ×äªÿìä¶ÿìä¼ÿ×ä¾ÿìäÀÿìäÂÿìäËÿ×äÕÿìäæÿ×äøÿìäúÿìäüÿìäþÿìäÿ×äÿ×äÿìäÿìäÿìåŸÿ×å¸ÿ×å»ÿ×å¾ÿ×åÁÿ×åáÿ×ålÿ×å|ÿ×å~ÿ×å„ÿ×å†ÿ×åˆÿ×åŠÿ×åŒÿ×å±ÿ×å³ÿ×å¿ÿ×åÀÿ×åÁÿ×åÂÿ×åÅÿšåÇÿšåÔÿ×åÕÿ×åïÿ×åñÿ×åóÿ×åýÿ×åþÿ×å ÿ×å ÿ×åÿ×åÿ×åÿ×åÿìæÏÿ׿Øÿ׿Ûÿ׿Þÿ׿áÿ׿êÿ׿íÿ׿jÿ׿ÿ׿…ÿ׿‡ÿ׿‰ÿ׿ÿ׿²ÿ׿´ÿ׿Àÿ׿Âÿ׿Æÿ׿Èÿ׿Õÿ׿àÿ׿ðÿ׿òÿ׿ôÿ׿þÿ׿ ÿ׿ ÿ׿ÿ׿ÿ×ç�ÿ®ç�ÿ®çÿ®ç ÿ®ç€ÿìç‚ÿìç·ÿìç¹ÿìç ÿ×çÿ×èé�)é�ÿìé� ÿìéÿìé ÿìéÿ×éÿ×ï�ÿ®ï�ÿ®ïÿìï¤ÿ×ï¦ÿìï¨ÿ×ïªÿ×ï®ÿ×ï°ÿ×ï±ÿìïµÿ×ï¼ÿÃï½ÿ×ï¿ÿ×ïÁÿ×ïÄÿìïÇÿìïÎÿìïÕÿìïòÿìïÿ®ï ÿ®ïrÿ×ïsÿìïzÿìï|ÿ×ï€ÿìï‚ÿìïŸÿ×ï¡ÿìï©ÿìïµÿÃï·ÿìï¹ÿìï»ÿ×ï½ÿìï¿ÿ×ïÁÿ×ïÊÿ×ïÎÿ×ïÏÿìïÔÿ×ïÙÿ×ïÛÿ×ïÝÿ×ïåÿ×ïçÿìïõÿìï÷ÿ×ïùÿ×ïûÿ×ïýÿ×ïÿ×ïÿ×ï ÿ×ïÿ×ïÿ×ïÿìïÿìïÿ×ïÿìð�ÿìð� ÿìðÐÿ×ðÜÿìðÝÿìðßÿ×ðáÿìðäÿìðöÿìðÿìð ÿìð ÿ×ðªÿìð¶ÿìð¼ÿ×ð¾ÿìðÀÿìðÂÿìðËÿ×ðÕÿìðæÿ×ðøÿìðúÿìðüÿìðþÿìðÿ×ðÿ×ðÿìðÿìðÿìñ�ÿ®ñ�ÿ®ñÿìñ¤ÿ×ñ¦ÿìñ¨ÿ×ñªÿ×ñ®ÿ×ñ°ÿ×ñ±ÿìñµÿ×ñ¼ÿÃñ½ÿ×ñ¿ÿ×ñÁÿ×ñÄÿìñÇÿìñÎÿìñÕÿìñòÿìñÿ®ñ ÿ®ñrÿ×ñsÿìñzÿìñ|ÿ×ñ€ÿìñ‚ÿìñŸÿ×ñ¡ÿìñ©ÿìñµÿÃñ·ÿìñ¹ÿìñ»ÿ×ñ½ÿìñ¿ÿ×ñÁÿ×ñÊÿ×ñÎÿ×ñÏÿìñÔÿ×ñÙÿ×ñÛÿ×ñÝÿ×ñåÿ×ñçÿìñõÿìñ÷ÿ×ñùÿ×ñûÿ×ñýÿ×ñÿ×ñÿ×ñ ÿ×ñÿ×ñÿ×ñÿìñÿìñÿ×ñÿìò�ÿìò� ÿìòÐÿ×òÜÿìòÝÿìòßÿ×òáÿìòäÿìòöÿìòÿìò ÿìò ÿ×òªÿìò¶ÿìò¼ÿ×ò¾ÿìòÀÿìòÂÿìòËÿ×òÕÿìòæÿ×òøÿìòúÿìòüÿìòþÿìòÿ×òÿ×òÿìòÿìòÿìó�ÿ®ó�ÿ®óÿìó¤ÿ×ó¦ÿìó¨ÿ×óªÿ×ó®ÿ×ó°ÿ×ó±ÿìóµÿ×ó¼ÿÃó½ÿ×ó¿ÿ×óÁÿ×óÄÿìóÇÿìóÎÿìóÕÿìóòÿìóÿ®ó ÿ®órÿ×ósÿìózÿìó|ÿ×ó€ÿìó‚ÿìóŸÿ×ó¡ÿìó©ÿìóµÿÃó·ÿìó¹ÿìó»ÿ×ó½ÿìó¿ÿ×óÁÿ×óÊÿ×óÎÿ×óÏÿìóÔÿ×óÙÿ×óÛÿ×óÝÿ×óåÿ×óçÿìóõÿìó÷ÿ×óùÿ×óûÿ×óýÿ×óÿ×óÿ×ó ÿ×óÿ×óÿ×óÿìóÿìóÿ×óÿìô�ÿìô� ÿìôÐÿ×ôÜÿìôÝÿìôßÿ×ôáÿìôäÿìôöÿìôÿìô ÿìô ÿ×ôªÿìô¶ÿìô¼ÿ×ô¾ÿìôÀÿìôÂÿìôËÿ×ôÕÿìôæÿ×ôøÿìôúÿìôüÿìôþÿìôÿ×ôÿ×ôÿìôÿìôÿìõ�ÿ®õ�ÿ®õÿìõ¤ÿ×õ¦ÿìõ¨ÿ×õªÿ×õ®ÿ×õ°ÿ×õ±ÿìõµÿ×õ¼ÿÃõ½ÿ×õ¿ÿ×õÁÿ×õÄÿìõÇÿìõÎÿìõÕÿìõòÿìõÿ®õ ÿ®õrÿ×õsÿìõzÿìõ|ÿ×õ€ÿìõ‚ÿìõŸÿ×õ¡ÿìõ©ÿìõµÿÃõ·ÿìõ¹ÿìõ»ÿ×õ½ÿìõ¿ÿ×õÁÿ×õÊÿ×õÎÿ×õÏÿìõÔÿ×õÙÿ×õÛÿ×õÝÿ×õåÿ×õçÿìõõÿìõ÷ÿ×õùÿ×õûÿ×õýÿ×õÿ×õÿ×õ ÿ×õÿ×õÿ×õÿìõÿìõÿ×õÿìö�ÿìö� ÿìöÐÿ×öÜÿìöÝÿìößÿ×öáÿìöäÿìööÿìöÿìö ÿìö ÿ×öªÿìö¶ÿìö¼ÿ×ö¾ÿìöÀÿìöÂÿìöËÿ×öÕÿìöæÿ×öøÿìöúÿìöüÿìöþÿìöÿ×öÿ×öÿìöÿìöÿì÷�ÿ…÷�ÿ…÷Ÿÿì÷¤ÿš÷ªÿq÷®ÿš÷µÿš÷¸ÿì÷»ÿì÷¾ÿÃ÷Éÿì÷Îÿ®÷Ïÿ×÷Õÿ®÷Øÿ×÷Ûÿ×÷Þÿ×÷áÿ×÷êÿ×÷ë�f÷íÿ×÷îÿì÷òÿ®÷ô�f÷ÿ…÷ ÿ…÷jÿ×÷lÿì÷rÿq÷sÿ®÷~ÿì÷ÿ×÷„ÿì÷…ÿ×÷†ÿì÷‡ÿ×÷ˆÿì÷‰ÿ×÷Šÿì÷Œÿì÷ÿ×÷˜�f÷¨�f÷±ÿì÷²ÿ×÷³ÿì÷´ÿ×÷Àÿ×÷Âÿ×÷Åÿ×÷ÆÿÃ÷Çÿ×÷ÈÿÃ÷Îÿš÷Ïÿ®÷Õÿ×÷Ùÿq÷Ûÿq÷Ýÿq÷àÿ×÷ïÿì÷ðÿ×÷ñÿì÷òÿ×÷óÿì÷ôÿ×÷þÿ×÷ ÿq÷ ÿ×÷ ÿq÷ ÿ×÷ÿš÷ÿ®÷ÿì÷ÿ×÷ÿ×÷ÿš÷ÿ®ø�ÿ®ø�ÿ®øÎÿ×øÕÿ×øòÿ×øÿ®ø ÿ®øsÿ×øÏÿ×øÿ×øÿ×ù�ÿ…ù�ÿ…ùŸÿìù¤ÿšùªÿqù®ÿšùµÿšù¸ÿìù»ÿìù¾ÿÃùÉÿìùÎÿ®ùÏÿ×ùÕÿ®ùØÿ×ùÛÿ×ùÞÿ×ùáÿ×ùêÿ×ùë�fùíÿ×ùîÿìùòÿ®ùô�fùÿ…ù ÿ…ùjÿ×ùlÿìùrÿqùsÿ®ù~ÿìùÿ×ù„ÿìù…ÿ×ù†ÿìù‡ÿ×ùˆÿìù‰ÿ×ùŠÿìùŒÿìùÿ×ù˜�fù¨�fù±ÿìù²ÿ×ù³ÿìù´ÿ×ùÀÿ×ùÂÿ×ùÅÿ×ùÆÿÃùÇÿ×ùÈÿÃùÎÿšùÏÿ®ùÕÿ×ùÙÿqùÛÿqùÝÿqùàÿ×ùïÿìùðÿ×ùñÿìùòÿ×ùóÿìùôÿ×ùþÿ×ù ÿqù ÿ×ù ÿqù ÿ×ùÿšùÿ®ùÿìùÿ×ùÿ×ùÿšùÿ®ú�ÿ®ú�ÿ®úÎÿ×úÕÿ×úòÿ×úÿ®ú ÿ®úsÿ×úÏÿ×úÿ×úÿ×û�ÿ…û�ÿ…ûŸÿìû¤ÿšûªÿqû®ÿšûµÿšû¸ÿìû»ÿìû¾ÿÃûÉÿìûÎÿ®ûÏÿ×ûÕÿ®ûØÿ×ûÛÿ×ûÞÿ×ûáÿ×ûêÿ×ûë�fûíÿ×ûîÿìûòÿ®ûô�fûÿ…û ÿ…ûjÿ×ûlÿìûrÿqûsÿ®û~ÿìûÿ×û„ÿìû…ÿ×û†ÿìû‡ÿ×ûˆÿìû‰ÿ×ûŠÿìûŒÿìûÿ×û˜�fû¨�fû±ÿìû²ÿ×û³ÿìû´ÿ×ûÀÿ×ûÂÿ×ûÅÿ×ûÆÿÃûÇÿ×ûÈÿÃûÎÿšûÏÿ®ûÕÿ×ûÙÿqûÛÿqûÝÿqûàÿ×ûïÿìûðÿ×ûñÿìûòÿ×ûóÿìûôÿ×ûþÿ×û ÿqû ÿ×û ÿqû ÿ×ûÿšûÿ®ûÿìûÿ×ûÿ×ûÿšûÿ®ü�ÿ®ü�ÿ®üÎÿ×üÕÿ×üòÿ×üÿ®ü ÿ®üsÿ×üÏÿ×üÿ×üÿ×ÿ�ÿ…ÿ�ÿ®ÿ�ÿ…ÿŸÿ×ÿ¤ÿšÿªÿqÿ®ÿšÿµÿšÿ¸ÿ×ÿ»ÿ×ÿ¼�)ÿ¾ÿ®ÿÌÿšÿÍÿšÿÎÿ…ÿÏÿqÿÐÿ×ÿÑÿ×ÿÒÿšÿÓÿšÿÔÿšÿÕÿ…ÿÖÿšÿ×ÿšÿØÿqÿÙÿšÿÚÿšÿÛÿqÿÜÿ®ÿÝÿ®ÿÞÿqÿßÿ×ÿàÿšÿáÿšÿâÿšÿãÿšÿäÿ®ÿåÿšÿæÿšÿçÿ×ÿèÿšÿéÿÃÿêÿqÿìÿšÿíÿqÿîÿ…ÿòÿ…ÿóÿšÿõÿšÿöÿ®ÿ÷ÿšÿùÿšÿÿ®ÿÿ®ÿÿ®ÿÿ…ÿ ÿ…ÿjÿqÿkÿšÿlÿ×ÿmÿ×ÿqÿšÿrÿqÿsÿ…ÿuÿšÿwÿšÿyÿšÿ}ÿšÿ~ÿ×ÿÿqÿÿ×ÿƒÿ×ÿ„ÿ×ÿ…ÿqÿ†ÿ×ÿ‡ÿqÿˆÿ×ÿ‰ÿqÿŠÿ×ÿ‹ÿ×ÿŒÿ×ÿÿqÿ–ÿšÿšÿšÿžÿšÿ ÿ×ÿ¢ÿ×ÿ¤ÿšÿ¦ÿšÿªÿ®ÿ¬ÿšÿ®ÿšÿ°ÿšÿ±ÿ×ÿ²ÿqÿ³ÿ×ÿ´ÿqÿµ�)ÿ¶ÿ®ÿ¸ÿ®ÿºÿ®ÿ¼ÿ×ÿ¾ÿ®ÿÀÿšÿÂÿšÿÄÿšÿÅÿšÿÆÿqÿÇÿšÿÈÿqÿËÿ×ÿÍÿšÿÎÿšÿÏÿ…ÿÑÿšÿÓÿšÿÕÿšÿ×ÿšÿÙÿqÿÛÿqÿÝÿqÿàÿqÿæÿ×ÿèÿ×ÿêÿÃÿìÿšÿîÿšÿïÿ×ÿðÿqÿñÿ×ÿòÿqÿóÿ×ÿôÿqÿöÿ×ÿøÿ®ÿúÿ®ÿüÿ®ÿþÿšÿ�ÿšÿÿšÿÿ×ÿÿ×ÿ ÿqÿ ÿqÿ ÿqÿ ÿqÿÿšÿÿšÿÿšÿÿ…ÿÿšÿÿ×ÿÿqÿÿ®ÿÿqÿÿšÿÿ…��ÿš��ÿ×��ÿš�ÎÿÃ�Ïÿì�ÕÿÃ�Øÿì�Ûÿì�Þÿì�êÿì�íÿì�òÿÃ�ÿ×�ÿ×�ÿ×�ÿš� ÿš�jÿì�sÿÃ�ÿì�…ÿì�‡ÿì�‰ÿì�ÿì�²ÿì�´ÿì�ÏÿÃ�àÿì�ðÿì�òÿì�ôÿì� ÿì� ÿì�ÿÃ�ÿì�ÿì�ÿÃ�ÿš�ÿ×�ÿš�)Ÿÿפÿ®¦�)ªÿ…®ÿ®µÿ®¸ÿ×»ÿ×¼�)¾ÿÃÄ�)ÌÿÃÍÿÃÎÿšÏÿ®Ðÿ×Ñÿ×ÒÿÃÓÿÃÔÿÃÕÿšÖÿÃ×ÿÃØÿ®ÙÿÃÚÿÃÛÿ®Þÿ®ßÿ×àÿÃáÿšâÿÃãÿÃåÿÃæÿÃçÿ×èÿÃêÿ®ë�)ìÿÃíÿ®îÿÃòÿšóÿÃô�)õÿÃ÷ÿÃùÿÃÿ×ÿ×ÿ×ÿš ÿšjÿ®kÿÃlÿ×qÿÃrÿ…sÿšuÿÃwÿ×yÿÃ}ÿÃ~ÿ×ÿ®„ÿ×…ÿ®†ÿׇÿ®ˆÿ׉ÿ®Šÿ׌ÿ×ÿ®–ÿØ�)šÿÞÿàÿ×¢ÿפÿæÿè�)©�)¬ÿîÿðÿñÿײÿ®³ÿ×´ÿ®µ�)¼ÿ×½�)ÀÿšÂÿšÄÿÃÅÿׯÿÃÇÿ×ÈÿÃËÿ×ÍÿÃÎÿ®ÏÿšÑÿÃÓÿÃÕÿš×ÿÃÙÿ…Ûÿ…Ýÿ…àÿ®æÿ×èÿ×ìÿÃîÿÃïÿ×ðÿ®ñÿ×òÿ®óÿ×ôÿ®öÿ×þÿš�ÿÃÿÃÿ×ÿ× ÿš ÿ® ÿš ÿ®ÿ×ÿ×ÿ®ÿšÿÃÿ×ÿ®�)ÿ®ÿ®ÿš�ÿÃ�ÿÃÎÿÃÏÿ×ÕÿÃØÿ×Ûÿ×Þÿ×êÿ×íÿ×òÿÃÿà ÿÃjÿ×sÿÃÿ×…ÿׇÿ׉ÿ×ÿײÿ×´ÿ×ÏÿÃàÿ×ðÿ×òÿ×ôÿ× ÿ× ÿ×ÿÃÿ×ÿ×ÿßÿ×£�á¸ÿ×»ÿ×¾ÿÃÜÿ×áÿ®äÿ×lÿ×{�=}ÿì~ÿׄÿ׆ÿ׈ÿ׊ÿ׌ÿתÿ×±ÿ׳ÿ×¶ÿ×¾ÿ×Àÿ®Âÿ®ÅÿÃÆÿ×ÇÿÃÈÿ×Õÿ®ïÿ×ñÿ×óÿ×þÿ®ÿ×ÿ×ÿ×ÿ×ÏÿìØÿìÛÿìÞÿìáÿìêÿìíÿìjÿìÿì…ÿì‡ÿì‰ÿìÿì²ÿì´ÿìÀÿìÂÿìÕÿìàÿìðÿìòÿìôÿìþÿì ÿì ÿìÿ×ÿ×ÿìÿìŸÿ׸ÿ×»ÿ×¾ÿ×Áÿ×áÿ×lÿ×|ÿ×~ÿׄÿ׆ÿ׈ÿ׊ÿ׌ÿ×±ÿ׳ÿ׿ÿ×Àÿ×Áÿ×Âÿ×ÅÿšÇÿšÔÿ×Õÿ×ïÿ×ñÿ×óÿ×ýÿ×þÿ× ÿ× ÿ×ÿ×ÿ×ÿ×ÿìÏÿìØÿìÛÿìÞÿìáÿìêÿìíÿìjÿìÿì…ÿì‡ÿì‰ÿìÿì²ÿì´ÿìÀÿìÂÿìÕÿìàÿìðÿìòÿìôÿìþÿì ÿì ÿìÿ×ÿ×ÿìÿì �ÿš � ÿš ÿ® ¦ÿ® ¨ÿà ªÿà °ÿà ¼ÿq ½ÿà ¿ÿà Áÿà Äÿ® Ðÿ× Üÿà ßÿ× áÿ× äÿà ÿš  ÿš rÿà vÿ× |ÿà €ÿà ‚ÿà Ÿÿà  ÿ× ©ÿ® ªÿà µÿq ¶ÿà ·ÿà ¹ÿà »ÿà ¼ÿ× ½ÿ® ¾ÿà ¿ÿà Àÿ× Áÿà Âÿ× Êÿà Ëÿ× Ôÿà Õÿ× Ùÿà Ûÿà Ýÿà åÿà æÿ× ÷ÿà ùÿà ûÿà ýÿà þÿ× ÿà ÿ× ÿà ÿ×  ÿ× ÿ× ÿ× ÿ× ÿ® ÿà �ÿš � ÿš Ðÿ× Üÿà Ýÿ× ßÿ× áÿ× äÿà öÿ× ÿš  ÿš  ÿ× ªÿà ¶ÿà ¼ÿ× ¾ÿà Àÿ× Âÿ× Ëÿ× Õÿ× æÿ× øÿ× úÿ× üÿ× þÿ× ÿ× ÿ× ÿš ÿš ÿà �ÿš � ÿš ÿ® ¦ÿ® ¨ÿà ªÿà °ÿà ¼ÿq ½ÿà ¿ÿà Áÿà Äÿ® Ðÿ× Üÿà ßÿ× áÿ× äÿà ÿš  ÿš rÿà vÿ× |ÿà €ÿà ‚ÿà Ÿÿà  ÿ× ©ÿ® ªÿà µÿq ¶ÿà ·ÿà ¹ÿà »ÿà ¼ÿ× ½ÿ® ¾ÿà ¿ÿà Àÿ× Áÿà Âÿ× Êÿà Ëÿ× Ôÿà Õÿ× Ùÿà Ûÿà Ýÿà åÿà æÿ× ÷ÿà ùÿà ûÿà ýÿà þÿ× ÿà ÿ× ÿà ÿ×  ÿ× ÿ× ÿ× ÿ× ÿ® ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿã�áê�)ÿ×ÿ×�ÿì� ÿìÿì ÿì�ÿš� ÿšÿ®¦ÿ®¨ÿêÿðÿüÿq½ÿÿÿÃÁÿÃÄÿ®Ðÿ×ÜÿÃßÿ×áÿ×äÿÃÿš ÿšrÿÃvÿ×|ÿÀÿÂÿßÿàÿשÿ®ªÿõÿq¶ÿ÷ÿùÿûÿüÿ×½ÿ®¾ÿÿÿÃÀÿ×ÁÿÃÂÿ×ÊÿÃËÿ×ÔÿÃÕÿ×ÙÿÃÛÿÃÝÿÃåÿÃæÿ×÷ÿÃùÿÃûÿÃýÿÃþÿ×ÿÃÿ×ÿÃÿ× ÿ×ÿ×ÿ×ÿ×ÿ®ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃ�ÿš� ÿšÿ®¦ÿ®¨ÿêÿðÿüÿq½ÿÿÿÃÁÿÃÄÿ®Ðÿ×ÜÿÃßÿ×áÿ×äÿÃÿš ÿšrÿÃvÿ×|ÿÀÿÂÿßÿàÿשÿ®ªÿõÿq¶ÿ÷ÿùÿûÿüÿ×½ÿ®¾ÿÿÿÃÀÿ×ÁÿÃÂÿ×ÊÿÃËÿ×ÔÿÃÕÿ×ÙÿÃÛÿÃÝÿÃåÿÃæÿ×÷ÿÃùÿÃûÿÃýÿÃþÿ×ÿÃÿ×ÿÃÿ× ÿ×ÿ×ÿ×ÿ×ÿ®ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃ�ÿ®�ÿ®ªÿì°ÿ×¼ÿ׿ÿ×ÿ® ÿ®rÿì€ÿì‚ÿìŸÿ×µÿ×·ÿì¹ÿì»ÿ×Êÿ×ÙÿìÛÿìÝÿìåÿ×ÿ×ÿ×�ÿ×� ÿ×ÐÿìÝÿìßÿìöÿìÿ× ÿ× ÿì¼ÿìËÿìæÿìøÿìúÿìüÿìÿìÿìÿ×ÿ×�ÿ®� ÿ®ÿæÿêÿ×°ÿ×¼ÿÿÿ×Áÿ×ÄÿÃÜÿ×äÿ×ÿ® ÿ®rÿ×|ÿ×€ÿׂÿןÿשÿêÿ×µÿöÿ×·ÿ×¹ÿ×»ÿ×½ÿþÿ׿ÿ×Áÿ×Êÿ×Ôÿ×Ùÿ×Ûÿ×Ýÿ×åÿ×ýÿ×ÿ×ÿ× ÿ×ÿ×ÿÃÿ×�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃáÿ×Àÿ×Âÿ×Õÿ×þÿ×£�áê�)ÿ×ÿ×�ÿì� ÿìÿì ÿì�ÿq� ÿq�&ÿ×�*ÿ×�- �2ÿ×�4ÿ×�7ÿq�9ÿ®�:ÿ®�<ÿ…�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�Ÿÿ…�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿq&ÿq6ÿ®8ÿ…:ÿ…Gÿ×úÿ®üÿ®þÿ®�ÿ…ÿq ÿq_ÿ×Iÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×oÿ…qÿ…sÿ…ÿq�ÿì� ÿìÿì ÿì�ÿq� ÿq�&ÿ×�*ÿ×�- �2ÿ×�4ÿ×�7ÿq�9ÿ®�:ÿ®�<ÿ…�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�Ÿÿ…�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿq&ÿq6ÿ®8ÿ…:ÿ…Gÿ×úÿ®üÿ®þÿ®�ÿ…ÿq ÿq_ÿ×Iÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×oÿ…qÿ…sÿ…ÿq �ÿì � ÿì ÿì  ÿì!�ÿq!� ÿq!�&ÿ×!�*ÿ×!�- !�2ÿ×!�4ÿ×!�7ÿq!�9ÿ®!�:ÿ®!�<ÿ…!�‰ÿ×!�”ÿ×!�•ÿ×!�–ÿ×!�—ÿ×!�˜ÿ×!�šÿ×!�Ÿÿ…!�Èÿ×!�Êÿ×!�Ìÿ×!�Îÿ×!�Þÿ×!�àÿ×!�âÿ×!�äÿ×!ÿ×!ÿ×!ÿ×!ÿ×!$ÿq!&ÿq!6ÿ®!8ÿ…!:ÿ…!Gÿ×!úÿ®!üÿ®!þÿ®!�ÿ…!ÿq! ÿq!_ÿ×!Iÿ×!Kÿ×!Mÿ×!Oÿ×!Qÿ×!Sÿ×!Uÿ×!Wÿ×!Yÿ×![ÿ×!]ÿ×!_ÿ×!oÿ…!qÿ…!sÿ…!ÿq"�ÿì"� ÿì"ÿì" ÿì#�ÿq#� ÿq#�&ÿ×#�*ÿ×#�- #�2ÿ×#�4ÿ×#�7ÿq#�9ÿ®#�:ÿ®#�<ÿ…#�‰ÿ×#�”ÿ×#�•ÿ×#�–ÿ×#�—ÿ×#�˜ÿ×#�šÿ×#�Ÿÿ…#�Èÿ×#�Êÿ×#�Ìÿ×#�Îÿ×#�Þÿ×#�àÿ×#�âÿ×#�äÿ×#ÿ×#ÿ×#ÿ×#ÿ×#$ÿq#&ÿq#6ÿ®#8ÿ…#:ÿ…#Gÿ×#úÿ®#üÿ®#þÿ®#�ÿ…#ÿq# ÿq#_ÿ×#Iÿ×#Kÿ×#Mÿ×#Oÿ×#Qÿ×#Sÿ×#Uÿ×#Wÿ×#Yÿ×#[ÿ×#]ÿ×#_ÿ×#oÿ…#qÿ…#sÿ…#ÿq$�ÿì$� ÿì$ÿì$ ÿì%�ÿq%� ÿq%�&ÿ×%�*ÿ×%�- %�2ÿ×%�4ÿ×%�7ÿq%�9ÿ®%�:ÿ®%�<ÿ…%�‰ÿ×%�”ÿ×%�•ÿ×%�–ÿ×%�—ÿ×%�˜ÿ×%�šÿ×%�Ÿÿ…%�Èÿ×%�Êÿ×%�Ìÿ×%�Îÿ×%�Þÿ×%�àÿ×%�âÿ×%�äÿ×%ÿ×%ÿ×%ÿ×%ÿ×%$ÿq%&ÿq%6ÿ®%8ÿ…%:ÿ…%Gÿ×%úÿ®%üÿ®%þÿ®%�ÿ…%ÿq% ÿq%_ÿ×%Iÿ×%Kÿ×%Mÿ×%Oÿ×%Qÿ×%Sÿ×%Uÿ×%Wÿ×%Yÿ×%[ÿ×%]ÿ×%_ÿ×%oÿ…%qÿ…%sÿ…%ÿq&�ÿì&� ÿì&ÿì& ÿì'�ÿq'� ÿq'�&ÿ×'�*ÿ×'�- '�2ÿ×'�4ÿ×'�7ÿq'�9ÿ®'�:ÿ®'�<ÿ…'�‰ÿ×'�”ÿ×'�•ÿ×'�–ÿ×'�—ÿ×'�˜ÿ×'�šÿ×'�Ÿÿ…'�Èÿ×'�Êÿ×'�Ìÿ×'�Îÿ×'�Þÿ×'�àÿ×'�âÿ×'�äÿ×'ÿ×'ÿ×'ÿ×'ÿ×'$ÿq'&ÿq'6ÿ®'8ÿ…':ÿ…'Gÿ×'úÿ®'üÿ®'þÿ®'�ÿ…'ÿq' ÿq'_ÿ×'Iÿ×'Kÿ×'Mÿ×'Oÿ×'Qÿ×'Sÿ×'Uÿ×'Wÿ×'Yÿ×'[ÿ×']ÿ×'_ÿ×'oÿ…'qÿ…'sÿ…'ÿq(�ÿì(� ÿì(ÿì( ÿì)�ÿq)� ÿq)�&ÿ×)�*ÿ×)�- )�2ÿ×)�4ÿ×)�7ÿq)�9ÿ®)�:ÿ®)�<ÿ…)�‰ÿ×)�”ÿ×)�•ÿ×)�–ÿ×)�—ÿ×)�˜ÿ×)�šÿ×)�Ÿÿ…)�Èÿ×)�Êÿ×)�Ìÿ×)�Îÿ×)�Þÿ×)�àÿ×)�âÿ×)�äÿ×)ÿ×)ÿ×)ÿ×)ÿ×)$ÿq)&ÿq)6ÿ®)8ÿ…):ÿ…)Gÿ×)úÿ®)üÿ®)þÿ®)�ÿ…)ÿq) ÿq)_ÿ×)Iÿ×)Kÿ×)Mÿ×)Oÿ×)Qÿ×)Sÿ×)Uÿ×)Wÿ×)Yÿ×)[ÿ×)]ÿ×)_ÿ×)oÿ…)qÿ…)sÿ…)ÿq*�ÿì*� ÿì*ÿì* ÿì+�ÿq+� ÿq+�&ÿ×+�*ÿ×+�- +�2ÿ×+�4ÿ×+�7ÿq+�9ÿ®+�:ÿ®+�<ÿ…+�‰ÿ×+�”ÿ×+�•ÿ×+�–ÿ×+�—ÿ×+�˜ÿ×+�šÿ×+�Ÿÿ…+�Èÿ×+�Êÿ×+�Ìÿ×+�Îÿ×+�Þÿ×+�àÿ×+�âÿ×+�äÿ×+ÿ×+ÿ×+ÿ×+ÿ×+$ÿq+&ÿq+6ÿ®+8ÿ…+:ÿ…+Gÿ×+úÿ®+üÿ®+þÿ®+�ÿ…+ÿq+ ÿq+_ÿ×+Iÿ×+Kÿ×+Mÿ×+Oÿ×+Qÿ×+Sÿ×+Uÿ×+Wÿ×+Yÿ×+[ÿ×+]ÿ×+_ÿ×+oÿ…+qÿ…+sÿ…+ÿq,�ÿì,� ÿì,ÿì, ÿì-�ÿq-� ÿq-�&ÿ×-�*ÿ×-�- -�2ÿ×-�4ÿ×-�7ÿq-�9ÿ®-�:ÿ®-�<ÿ…-�‰ÿ×-�”ÿ×-�•ÿ×-�–ÿ×-�—ÿ×-�˜ÿ×-�šÿ×-�Ÿÿ…-�Èÿ×-�Êÿ×-�Ìÿ×-�Îÿ×-�Þÿ×-�àÿ×-�âÿ×-�äÿ×-ÿ×-ÿ×-ÿ×-ÿ×-$ÿq-&ÿq-6ÿ®-8ÿ…-:ÿ…-Gÿ×-úÿ®-üÿ®-þÿ®-�ÿ…-ÿq- ÿq-_ÿ×-Iÿ×-Kÿ×-Mÿ×-Oÿ×-Qÿ×-Sÿ×-Uÿ×-Wÿ×-Yÿ×-[ÿ×-]ÿ×-_ÿ×-oÿ…-qÿ…-sÿ…-ÿq.�ÿì.� ÿì.ÿì. ÿì/�ÿq/� ÿq/�&ÿ×/�*ÿ×/�- /�2ÿ×/�4ÿ×/�7ÿq/�9ÿ®/�:ÿ®/�<ÿ…/�‰ÿ×/�”ÿ×/�•ÿ×/�–ÿ×/�—ÿ×/�˜ÿ×/�šÿ×/�Ÿÿ…/�Èÿ×/�Êÿ×/�Ìÿ×/�Îÿ×/�Þÿ×/�àÿ×/�âÿ×/�äÿ×/ÿ×/ÿ×/ÿ×/ÿ×/$ÿq/&ÿq/6ÿ®/8ÿ…/:ÿ…/Gÿ×/úÿ®/üÿ®/þÿ®/�ÿ…/ÿq/ ÿq/_ÿ×/Iÿ×/Kÿ×/Mÿ×/Oÿ×/Qÿ×/Sÿ×/Uÿ×/Wÿ×/Yÿ×/[ÿ×/]ÿ×/_ÿ×/oÿ…/qÿ…/sÿ…/ÿq0�ÿì0� ÿì0ÿì0 ÿì1�ÿq1� ÿq1�&ÿ×1�*ÿ×1�- 1�2ÿ×1�4ÿ×1�7ÿq1�9ÿ®1�:ÿ®1�<ÿ…1�‰ÿ×1�”ÿ×1�•ÿ×1�–ÿ×1�—ÿ×1�˜ÿ×1�šÿ×1�Ÿÿ…1�Èÿ×1�Êÿ×1�Ìÿ×1�Îÿ×1�Þÿ×1�àÿ×1�âÿ×1�äÿ×1ÿ×1ÿ×1ÿ×1ÿ×1$ÿq1&ÿq16ÿ®18ÿ…1:ÿ…1Gÿ×1úÿ®1üÿ®1þÿ®1�ÿ…1ÿq1 ÿq1_ÿ×1Iÿ×1Kÿ×1Mÿ×1Oÿ×1Qÿ×1Sÿ×1Uÿ×1Wÿ×1Yÿ×1[ÿ×1]ÿ×1_ÿ×1oÿ…1qÿ…1sÿ…1ÿq2�ÿì2� ÿì2ÿì2 ÿì3�ÿq3� ÿq3�&ÿ×3�*ÿ×3�- 3�2ÿ×3�4ÿ×3�7ÿq3�9ÿ®3�:ÿ®3�<ÿ…3�‰ÿ×3�”ÿ×3�•ÿ×3�–ÿ×3�—ÿ×3�˜ÿ×3�šÿ×3�Ÿÿ…3�Èÿ×3�Êÿ×3�Ìÿ×3�Îÿ×3�Þÿ×3�àÿ×3�âÿ×3�äÿ×3ÿ×3ÿ×3ÿ×3ÿ×3$ÿq3&ÿq36ÿ®38ÿ…3:ÿ…3Gÿ×3úÿ®3üÿ®3þÿ®3�ÿ…3ÿq3 ÿq3_ÿ×3Iÿ×3Kÿ×3Mÿ×3Oÿ×3Qÿ×3Sÿ×3Uÿ×3Wÿ×3Yÿ×3[ÿ×3]ÿ×3_ÿ×3oÿ…3qÿ…3sÿ…3ÿq4�ÿì4� ÿì4ÿì4 ÿì5�-�{6�ÿì6� ÿì6�Yÿ×6�Zÿ×6�[ÿ×6�\ÿ×6�]ÿì6�¿ÿ×67ÿ×6<ÿì6>ÿì6@ÿì6ûÿ×6ýÿ×6ÿì6 ÿì6pÿ×7�-�{8�ÿì8� ÿì8�Yÿ×8�Zÿ×8�[ÿ×8�\ÿ×8�]ÿì8�¿ÿ×87ÿ×8<ÿì8>ÿì8@ÿì8ûÿ×8ýÿ×8ÿì8 ÿì8pÿ×9�-�{:�ÿì:� ÿì:�Yÿ×:�Zÿ×:�[ÿ×:�\ÿ×:�]ÿì:�¿ÿ×:7ÿ×:<ÿì:>ÿì:@ÿì:ûÿ×:ýÿ×:ÿì: ÿì:pÿ×;�-�{<�ÿì<� ÿì<�Yÿ×<�Zÿ×<�[ÿ×<�\ÿ×<�]ÿì<�¿ÿ×<7ÿ×<<ÿì<>ÿì<@ÿì<ûÿ×<ýÿ×<ÿì< ÿì<pÿ×=�-�{>�ÿì>� ÿì>�Yÿ×>�Zÿ×>�[ÿ×>�\ÿ×>�]ÿì>�¿ÿ×>7ÿ×><ÿì>>ÿì>@ÿì>ûÿ×>ýÿ×>ÿì> ÿì>pÿ×?�-�{@�ÿì@� ÿì@�Yÿ×@�Zÿ×@�[ÿ×@�\ÿ×@�]ÿì@�¿ÿ×@7ÿ×@<ÿì@>ÿì@@ÿì@ûÿ×@ýÿ×@ÿì@ ÿì@pÿ×A�-�{B�ÿìB� ÿìB�Yÿ×B�Zÿ×B�[ÿ×B�\ÿ×B�]ÿìB�¿ÿ×B7ÿ×B<ÿìB>ÿìB@ÿìBûÿ×Býÿ×BÿìB ÿìBpÿ×C�-�{D�ÿìD� ÿìD�Yÿ×D�Zÿ×D�[ÿ×D�\ÿ×D�]ÿìD�¿ÿ×D7ÿ×D<ÿìD>ÿìD@ÿìDûÿ×Dýÿ×DÿìD ÿìDpÿ×I�ÿ®I�ÿ®I�$ÿ×I�7ÿÃI�9ÿìI�:ÿìI�;ÿ×I�<ÿìI�=ÿìI�‚ÿ×I�ƒÿ×I�„ÿ×I�…ÿ×I�†ÿ×I�‡ÿ×I�ŸÿìI�Âÿ×I�Äÿ×I�Æÿ×I$ÿÃI&ÿÃI6ÿìI8ÿìI:ÿìI;ÿìI=ÿìI?ÿìICÿ×I ÿìIúÿìIüÿìIþÿìI�ÿìIÿ®I ÿ®IXÿ×Iÿ×Iÿ×I!ÿ×I#ÿ×I%ÿ×I'ÿ×I)ÿ×I+ÿ×I-ÿ×I/ÿ×I1ÿ×I3ÿ×IoÿìIqÿìIsÿìIÿÃJ�ÿìJ� ÿìJ�Yÿ×J�Zÿ×J�[ÿ×J�\ÿ×J�]ÿìJ�¿ÿ×J7ÿ×J<ÿìJ>ÿìJ@ÿìJûÿ×Jýÿ×JÿìJ ÿìJpÿ×K�ÿ®K�ÿ®K�$ÿ×K�7ÿÃK�9ÿìK�:ÿìK�;ÿ×K�<ÿìK�=ÿìK�‚ÿ×K�ƒÿ×K�„ÿ×K�…ÿ×K�†ÿ×K�‡ÿ×K�ŸÿìK�Âÿ×K�Äÿ×K�Æÿ×K$ÿÃK&ÿÃK6ÿìK8ÿìK:ÿìK;ÿìK=ÿìK?ÿìKCÿ×K ÿìKúÿìKüÿìKþÿìK�ÿìKÿ®K ÿ®KXÿ×Kÿ×Kÿ×K!ÿ×K#ÿ×K%ÿ×K'ÿ×K)ÿ×K+ÿ×K-ÿ×K/ÿ×K1ÿ×K3ÿ×KoÿìKqÿìKsÿìKÿÃL�ÿìL� ÿìL�Yÿ×L�Zÿ×L�[ÿ×L�\ÿ×L�]ÿìL�¿ÿ×L7ÿ×L<ÿìL>ÿìL@ÿìLûÿ×Lýÿ×LÿìL ÿìLpÿ×M�ÿ®M�ÿ®M�$ÿ×M�7ÿÃM�9ÿìM�:ÿìM�;ÿ×M�<ÿìM�=ÿìM�‚ÿ×M�ƒÿ×M�„ÿ×M�…ÿ×M�†ÿ×M�‡ÿ×M�ŸÿìM�Âÿ×M�Äÿ×M�Æÿ×M$ÿÃM&ÿÃM6ÿìM8ÿìM:ÿìM;ÿìM=ÿìM?ÿìMCÿ×M ÿìMúÿìMüÿìMþÿìM�ÿìMÿ®M ÿ®MXÿ×Mÿ×Mÿ×M!ÿ×M#ÿ×M%ÿ×M'ÿ×M)ÿ×M+ÿ×M-ÿ×M/ÿ×M1ÿ×M3ÿ×MoÿìMqÿìMsÿìMÿÃO�ÿ®O�ÿ®O�$ÿ×O�7ÿÃO�9ÿìO�:ÿìO�;ÿ×O�<ÿìO�=ÿìO�‚ÿ×O�ƒÿ×O�„ÿ×O�…ÿ×O�†ÿ×O�‡ÿ×O�ŸÿìO�Âÿ×O�Äÿ×O�Æÿ×O$ÿÃO&ÿÃO6ÿìO8ÿìO:ÿìO;ÿìO=ÿìO?ÿìOCÿ×O ÿìOúÿìOüÿìOþÿìO�ÿìOÿ®O ÿ®OXÿ×Oÿ×Oÿ×O!ÿ×O#ÿ×O%ÿ×O'ÿ×O)ÿ×O+ÿ×O-ÿ×O/ÿ×O1ÿ×O3ÿ×OoÿìOqÿìOsÿìOÿÃQ�ÿ®Q�ÿ®Q�$ÿ×Q�7ÿÃQ�9ÿìQ�:ÿìQ�;ÿ×Q�<ÿìQ�=ÿìQ�‚ÿ×Q�ƒÿ×Q�„ÿ×Q�…ÿ×Q�†ÿ×Q�‡ÿ×Q�ŸÿìQ�Âÿ×Q�Äÿ×Q�Æÿ×Q$ÿÃQ&ÿÃQ6ÿìQ8ÿìQ:ÿìQ;ÿìQ=ÿìQ?ÿìQCÿ×Q ÿìQúÿìQüÿìQþÿìQ�ÿìQÿ®Q ÿ®QXÿ×Qÿ×Qÿ×Q!ÿ×Q#ÿ×Q%ÿ×Q'ÿ×Q)ÿ×Q+ÿ×Q-ÿ×Q/ÿ×Q1ÿ×Q3ÿ×QoÿìQqÿìQsÿìQÿÃS�ÿ®S�ÿ®S�$ÿ×S�7ÿÃS�9ÿìS�:ÿìS�;ÿ×S�<ÿìS�=ÿìS�‚ÿ×S�ƒÿ×S�„ÿ×S�…ÿ×S�†ÿ×S�‡ÿ×S�ŸÿìS�Âÿ×S�Äÿ×S�Æÿ×S$ÿÃS&ÿÃS6ÿìS8ÿìS:ÿìS;ÿìS=ÿìS?ÿìSCÿ×S ÿìSúÿìSüÿìSþÿìS�ÿìSÿ®S ÿ®SXÿ×Sÿ×Sÿ×S!ÿ×S#ÿ×S%ÿ×S'ÿ×S)ÿ×S+ÿ×S-ÿ×S/ÿ×S1ÿ×S3ÿ×SoÿìSqÿìSsÿìSÿÃU�ÿ®U�ÿ®U�$ÿ×U�7ÿÃU�9ÿìU�:ÿìU�;ÿ×U�<ÿìU�=ÿìU�‚ÿ×U�ƒÿ×U�„ÿ×U�…ÿ×U�†ÿ×U�‡ÿ×U�ŸÿìU�Âÿ×U�Äÿ×U�Æÿ×U$ÿÃU&ÿÃU6ÿìU8ÿìU:ÿìU;ÿìU=ÿìU?ÿìUCÿ×U ÿìUúÿìUüÿìUþÿìU�ÿìUÿ®U ÿ®UXÿ×Uÿ×Uÿ×U!ÿ×U#ÿ×U%ÿ×U'ÿ×U)ÿ×U+ÿ×U-ÿ×U/ÿ×U1ÿ×U3ÿ×UoÿìUqÿìUsÿìUÿÃX�I�RX�W�RX�Y�fX�Z�fX�[�fX�\�fX�¿�fX%�RX'�RX7�fXû�fXý�fX4�RX5�RX]�RX^�RXp�fX�RX�RZ�I�RZ�W�RZ�Y�fZ�Z�fZ�[�fZ�\�fZ�¿�fZ%�RZ'�RZ7�fZû�fZý�fZ4�RZ5�RZ]�RZ^�RZp�fZ�RZ�R\�I�R\�W�R\�Y�f\�Z�f\�[�f\�\�f\�¿�f\%�R\'�R\7�f\û�f\ý�f\4�R\5�R\]�R\^�R\p�f\�R\�R^�I�R^�W�R^�Y�f^�Z�f^�[�f^�\�f^�¿�f^%�R^'�R^7�f^û�f^ý�f^4�R^5�R^]�R^^�R^p�f^�R^�R`�I�R`�W�R`�Y�f`�Z�f`�[�f`�\�f`�¿�f`%�R`'�R`7�f`û�f`ý�f`4�R`5�R`]�R`^�R`p�f`�R`�Ra�ÿ×a�ÿ×a�$ÿìa�‚ÿìa�ƒÿìa�„ÿìa�…ÿìa�†ÿìa�‡ÿìa�Âÿìa�Äÿìa�ÆÿìaCÿìaÿ×a ÿ×aXÿìaÿìaÿìa!ÿìa#ÿìa%ÿìa'ÿìa)ÿìa+ÿìa-ÿìa/ÿìa1ÿìa3ÿìf�I�ff�W�ff�Y�ff�Z�ff�[�ff�\�ff�¿�ff%�ff'�ff7�ffû�ffý�ff4�ff5�ff]�ff^�ffp�ff�ff�fh�I�fh�W�fh�Y�fh�Z�fh�[�fh�\�fh�¿�fh%�fh'�fh7�fhû�fhý�fh4�fh5�fh]�fh^�fhp�fh�fh�fj�I�fj�W�fj�Y�fj�Z�fj�[�fj�\�fj�¿�fj%�fj'�fj7�fjû�fjý�fj4�fj5�fj]�fj^�fjp�fj�fj�fl�I�fl�W�fl�Y�fl�Z�fl�[�fl�\�fl�¿�fl%�fl'�fl7�flû�flý�fl4�fl5�fl]�fl^�flp�fl�fl�fn�I�fn�W�fn�Y�fn�Z�fn�[�fn�\�fn�¿�fn%�fn'�fn7�fnû�fný�fn4�fn5�fn]�fn^�fnp�fn�fn�fo�ÿ…o�ÿ…o�"�)o�$ÿ…o�&ÿ×o�*ÿ×o�2ÿ×o�4ÿ×o�Dÿšo�Fÿšo�Gÿšo�Hÿšo�Jÿ×o�PÿÃo�QÿÃo�Rÿšo�SÿÃo�Tÿšo�UÿÃo�Vÿ®o�XÿÃo�]ÿ×o�‚ÿ…o�ƒÿ…o�„ÿ…o�…ÿ…o�†ÿ…o�‡ÿ…o�‰ÿ×o�”ÿ×o�•ÿ×o�–ÿ×o�—ÿ×o�˜ÿ×o�šÿ×o�¢ÿšo�£ÿšo�¤ÿšo�¥ÿšo�¦ÿšo�§ÿšo�¨ÿšo�©ÿšo�ªÿšo�«ÿšo�¬ÿšo�­ÿšo�´ÿšo�µÿšo�¶ÿšo�·ÿšo�¸ÿšo�ºÿšo�»ÿÃo�¼ÿÃo�½ÿÃo�¾ÿÃo�Âÿ…o�Ãÿšo�Äÿ…o�Åÿšo�Æÿ…o�Çÿšo�Èÿ×o�Éÿšo�Êÿ×o�Ëÿšo�Ìÿ×o�Íÿšo�Îÿ×o�Ïÿšo�Ñÿšo�Óÿšo�Õÿšo�×ÿšo�Ùÿšo�Ûÿšo�Ýÿšo�Þÿ×o�ßÿ×o�àÿ×o�áÿ×o�âÿ×o�ãÿ×o�äÿ×o�åÿ×o�úÿÃoÿÃoÿÃo ÿÃoÿ×oÿšoÿ×oÿšoÿ×oÿšoÿ×oÿšoÿÃoÿÃoÿ®o!ÿ®o+ÿÃo-ÿÃo/ÿÃo1ÿÃo3ÿÃo5ÿÃo<ÿ×o>ÿ×o@ÿ×oCÿ…oDÿšoFÿšoGÿ×oHÿšoJÿ®oÿ…o ÿ…oWÿÃoXÿ…oYÿšo_ÿ×o`ÿšobÿÃoÿ…oÿšoÿ…o ÿšo!ÿ…o"ÿšo#ÿ…o%ÿ…o&ÿšo'ÿ…o(ÿšo)ÿ…o*ÿšo+ÿ…o,ÿšo-ÿ…o.ÿšo/ÿ…o0ÿšo1ÿ…o2ÿšo3ÿ…o4ÿšo6ÿšo8ÿšo:ÿšo<ÿšo@ÿšoBÿšoDÿšoIÿ×oJÿšoKÿ×oLÿšoMÿ×oNÿšoOÿ×oQÿ×oRÿšoSÿ×oTÿšoUÿ×oVÿšoWÿ×oXÿšoYÿ×oZÿšo[ÿ×o\ÿšo]ÿ×o^ÿšo_ÿ×o`ÿšobÿÃodÿÃofÿÃohÿÃojÿÃolÿÃonÿÃp��Rp� �Rp�ÿ®p�ÿ®p�"�)p�Rpÿ®p �Rp ÿ®q�ÿ…q�ÿ…q�"�)q�$ÿ…q�&ÿ×q�*ÿ×q�2ÿ×q�4ÿ×q�Dÿšq�Fÿšq�Gÿšq�Hÿšq�Jÿ×q�PÿÃq�QÿÃq�Rÿšq�SÿÃq�Tÿšq�UÿÃq�Vÿ®q�XÿÃq�]ÿ×q�‚ÿ…q�ƒÿ…q�„ÿ…q�…ÿ…q�†ÿ…q�‡ÿ…q�‰ÿ×q�”ÿ×q�•ÿ×q�–ÿ×q�—ÿ×q�˜ÿ×q�šÿ×q�¢ÿšq�£ÿšq�¤ÿšq�¥ÿšq�¦ÿšq�§ÿšq�¨ÿšq�©ÿšq�ªÿšq�«ÿšq�¬ÿšq�­ÿšq�´ÿšq�µÿšq�¶ÿšq�·ÿšq�¸ÿšq�ºÿšq�»ÿÃq�¼ÿÃq�½ÿÃq�¾ÿÃq�Âÿ…q�Ãÿšq�Äÿ…q�Åÿšq�Æÿ…q�Çÿšq�Èÿ×q�Éÿšq�Êÿ×q�Ëÿšq�Ìÿ×q�Íÿšq�Îÿ×q�Ïÿšq�Ñÿšq�Óÿšq�Õÿšq�×ÿšq�Ùÿšq�Ûÿšq�Ýÿšq�Þÿ×q�ßÿ×q�àÿ×q�áÿ×q�âÿ×q�ãÿ×q�äÿ×q�åÿ×q�úÿÃqÿÃqÿÃq ÿÃqÿ×qÿšqÿ×qÿšqÿ×qÿšqÿ×qÿšqÿÃqÿÃqÿ®q!ÿ®q+ÿÃq-ÿÃq/ÿÃq1ÿÃq3ÿÃq5ÿÃq<ÿ×q>ÿ×q@ÿ×qCÿ…qDÿšqFÿšqGÿ×qHÿšqJÿ®qÿ…q ÿ…qWÿÃqXÿ…qYÿšq_ÿ×q`ÿšqbÿÃqÿ…qÿšqÿ…q ÿšq!ÿ…q"ÿšq#ÿ…q%ÿ…q&ÿšq'ÿ…q(ÿšq)ÿ…q*ÿšq+ÿ…q,ÿšq-ÿ…q.ÿšq/ÿ…q0ÿšq1ÿ…q2ÿšq3ÿ…q4ÿšq6ÿšq8ÿšq:ÿšq<ÿšq@ÿšqBÿšqDÿšqIÿ×qJÿšqKÿ×qLÿšqMÿ×qNÿšqOÿ×qQÿ×qRÿšqSÿ×qTÿšqUÿ×qVÿšqWÿ×qXÿšqYÿ×qZÿšq[ÿ×q\ÿšq]ÿ×q^ÿšq_ÿ×q`ÿšqbÿÃqdÿÃqfÿÃqhÿÃqjÿÃqlÿÃqnÿÃr��Rr� �Rr�ÿ®r�ÿ®r�"�)r�Rrÿ®r �Rr ÿ®s�ÿ…s�ÿ…s�"�)s�$ÿ…s�&ÿ×s�*ÿ×s�2ÿ×s�4ÿ×s�Dÿšs�Fÿšs�Gÿšs�Hÿšs�Jÿ×s�PÿÃs�QÿÃs�Rÿšs�SÿÃs�Tÿšs�UÿÃs�Vÿ®s�XÿÃs�]ÿ×s�‚ÿ…s�ƒÿ…s�„ÿ…s�…ÿ…s�†ÿ…s�‡ÿ…s�‰ÿ×s�”ÿ×s�•ÿ×s�–ÿ×s�—ÿ×s�˜ÿ×s�šÿ×s�¢ÿšs�£ÿšs�¤ÿšs�¥ÿšs�¦ÿšs�§ÿšs�¨ÿšs�©ÿšs�ªÿšs�«ÿšs�¬ÿšs�­ÿšs�´ÿšs�µÿšs�¶ÿšs�·ÿšs�¸ÿšs�ºÿšs�»ÿÃs�¼ÿÃs�½ÿÃs�¾ÿÃs�Âÿ…s�Ãÿšs�Äÿ…s�Åÿšs�Æÿ…s�Çÿšs�Èÿ×s�Éÿšs�Êÿ×s�Ëÿšs�Ìÿ×s�Íÿšs�Îÿ×s�Ïÿšs�Ñÿšs�Óÿšs�Õÿšs�×ÿšs�Ùÿšs�Ûÿšs�Ýÿšs�Þÿ×s�ßÿ×s�àÿ×s�áÿ×s�âÿ×s�ãÿ×s�äÿ×s�åÿ×s�úÿÃsÿÃsÿÃs ÿÃsÿ×sÿšsÿ×sÿšsÿ×sÿšsÿ×sÿšsÿÃsÿÃsÿ®s!ÿ®s+ÿÃs-ÿÃs/ÿÃs1ÿÃs3ÿÃs5ÿÃs<ÿ×s>ÿ×s@ÿ×sCÿ…sDÿšsFÿšsGÿ×sHÿšsJÿ®sÿ…s ÿ…sWÿÃsXÿ…sYÿšs_ÿ×s`ÿšsbÿÃsÿ…sÿšsÿ…s ÿšs!ÿ…s"ÿšs#ÿ…s%ÿ…s&ÿšs'ÿ…s(ÿšs)ÿ…s*ÿšs+ÿ…s,ÿšs-ÿ…s.ÿšs/ÿ…s0ÿšs1ÿ…s2ÿšs3ÿ…s4ÿšs6ÿšs8ÿšs:ÿšs<ÿšs@ÿšsBÿšsDÿšsIÿ×sJÿšsKÿ×sLÿšsMÿ×sNÿšsOÿ×sQÿ×sRÿšsSÿ×sTÿšsUÿ×sVÿšsWÿ×sXÿšsYÿ×sZÿšs[ÿ×s\ÿšs]ÿ×s^ÿšs_ÿ×s`ÿšsbÿÃsdÿÃsfÿÃshÿÃsjÿÃslÿÃsnÿÃt��Rt� �Rt�ÿ®t�ÿ®t�"�)t�Rtÿ®t �Rt ÿ®��{� �{�{ �{�ÿ…�ÿ®�ÿ…�"�)�$ÿq�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7�)�Dÿ\�Fÿq�Gÿq�Hÿq�Jÿq�Pÿš�Qÿš�Rÿq�Sÿš�Tÿq�Uÿš�Vÿ…�Xÿš�Yÿ×�Zÿ×�[ÿ×�\ÿ×�]ÿ®�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�¢ÿq�£ÿ\�¤ÿ\�¥ÿ\�¦ÿ\�§ÿ\�¨ÿ\�©ÿq�ªÿq�«ÿq�¬ÿq�­ÿq�´ÿq�µÿq�¶ÿq�·ÿq�¸ÿq�ºÿq�»ÿš�¼ÿš�½ÿš�¾ÿš�¿ÿ×�Âÿq�Ãÿ\�Äÿq�Åÿ\�Æÿq�Çÿ\�Èÿ×�Éÿq�Êÿ×�Ëÿq�Ìÿ×�Íÿq�Îÿ×�Ïÿq�Ñÿq�Óÿq�Õÿq�×ÿq�Ùÿq�Ûÿq�Ýÿq�Þÿ×�ßÿq�àÿ×�áÿq�âÿ×�ãÿq�äÿ×�åÿq�úÿšÿšÿš ÿšÿ×ÿqÿ×ÿqÿ×ÿqÿ×ÿqÿšÿšÿ…!ÿ…$�)&�)+ÿš-ÿš/ÿš1ÿš3ÿš5ÿš7ÿ×<ÿ®>ÿ®@ÿ®CÿqDÿ\Fÿ\Gÿ×HÿqJÿ…ûÿ×ýÿ×ÿ®ÿ®ÿ®ÿ… ÿ…WÿšXÿqYÿ\_ÿ×`ÿqbÿšÿqÿ\ÿq ÿ\!ÿq"ÿ\#ÿq%ÿq&ÿ\'ÿq(ÿ\)ÿq*ÿ\+ÿq,ÿ\-ÿq.ÿ\/ÿq0ÿ\1ÿq2ÿ\3ÿq4ÿ\6ÿq8ÿq:ÿq<ÿq@ÿqBÿqDÿqIÿ×JÿqKÿ×LÿqMÿ×NÿqOÿ×Qÿ×RÿqSÿ×TÿqUÿ×VÿqWÿ×XÿqYÿ×Zÿq[ÿ×\ÿq]ÿ×^ÿq_ÿ×`ÿqbÿšdÿšfÿšhÿšjÿšlÿšnÿšpÿ×�)��)� �)�) �)�����>��������9��������� �9��������B�������%�H��������S������� �m��������y�������R�ˆ��������Ú������ ��î������ �. ������ �.8�������*f�� ���r�� ���� �� �� ��J �� �� 6�� ��j�� ��‚�� ��¤ �� ��(D�� � �8l�� � �\¤�� � �\��� ��T\Digitized data copyright © 2010-2011, Google Corporation.Open SansItalicAscender - Open Sans Italic Build 100Version 1.10OpenSans-ItalicOpen Sans is a trademark of Google and may be registered in certain jurisdictions.Ascender Corporationhttp://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0�D�i�g�i�t�i�z�e�d� �d�a�t�a� �c�o�p�y�r�i�g�h�t� �©� �2�0�1�0�-�2�0�1�1�,� �G�o�o�g�l�e� �C�o�r�p�o�r�a�t�i�o�n�.�O�p�e�n� �S�a�n�s�I�t�a�l�i�c�A�s�c�e�n�d�e�r� �-� �O�p�e�n� �S�a�n�s� �I�t�a�l�i�c� �B�u�i�l�d� �1�0�0�V�e�r�s�i�o�n� �1�.�1�0�O�p�e�n�S�a�n�s�-�I�t�a�l�i�c�O�p�e�n� �S�a�n�s� �i�s� �a� �t�r�a�d�e�m�a�r�k� �o�f� �G�o�o�g�l�e� �a�n�d� �m�a�y� �b�e� �r�e�g�i�s�t�e�r�e�d� �i�n� �c�e�r�t�a�i�n� �j�u�r�i�s�d�i�c�t�i�o�n�s�.�A�s�c�e�n�d�e�r� �C�o�r�p�o�r�a�t�i�o�n�h�t�t�p�:�/�/�w�w�w�.�a�s�c�e�n�d�e�r�c�o�r�p�.�c�o�m�/�h�t�t�p�:�/�/�w�w�w�.�a�s�c�e�n�d�e�r�c�o�r�p�.�c�o�m�/�t�y�p�e�d�e�s�i�g�n�e�r�s�.�h�t�m�l�L�i�c�e�n�s�e�d� �u�n�d�e�r� �t�h�e� �A�p�a�c�h�e� �L�i�c�e�n�s�e�,� �V�e�r�s�i�o�n� �2�.�0�h�t�t�p�:�/�/�w�w�w�.�a�p�a�c�h�e�.�o�r�g�/�l�i�c�e�n�s�e�s�/�L�I�C�E�N�S�E�-�2�.�0�����ÿô��ÿf�f��������������������ª      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«.notdefnullnonmarkingreturnspaceexclamquotedbl numbersigndollarpercent ampersand quotesingle parenleft parenrightasteriskpluscommahyphenperiodslashzeroonetwothreefourfivesixseveneightninecolon semicolonlessequalgreaterquestionatABCDEFGHI.altJKLMNOPQRSTUVWXYZ bracketleft backslash bracketright asciicircum underscoregraveabcdefghijklmnopqrstuvwxyz braceleftbar braceright asciitildenonbreakingspace exclamdowncentsterlingcurrencyyen brokenbarsectiondieresis copyright ordfeminine guillemotleft logicalnotuni00AD registered overscoredegree plusminus twosuperior threesuperioracutemu paragraphperiodcenteredcedilla onesuperior ordmasculineguillemotright onequarteronehalf threequarters questiondownAgraveAacute AcircumflexAtilde AdieresisAringAECcedillaEgraveEacute Ecircumflex Edieresis Igrave.alt Iacute.altIcircumflex.alt Idieresis.altEthNtildeOgraveOacute OcircumflexOtilde OdieresismultiplyOslashUgraveUacute Ucircumflex UdieresisYacuteThorn germandblsagraveaacute acircumflexatilde adieresisaringaeccedillaegraveeacute ecircumflex edieresisigraveiacute icircumflex idieresisethntildeograveoacute ocircumflexotilde odieresisdivideoslashugraveuacute ucircumflex udieresisyacutethorn ydieresisAmacronamacronAbreveabreveAogonekaogonekCacutecacute Ccircumflex ccircumflexCdotcdotCcaronccaronDcarondcaronDcroatdcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGbrevegbreveGdotgdot Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbar Itilde.altitilde Imacron.altimacron Ibreve.altibreve Iogonek.altiogonekIdotaccent.altdotlessiIJ.altij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotLslashlslashNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautOEoeRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflexScedillascedillaScaronscaron Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflex YdieresisZacutezacute Zdotaccent zdotaccentZcaronzcaronlongsflorin Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute Scommaaccent scommaaccent circumflexcaronmacronbreve dotaccentringogonektilde hungarumlauttonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos.alt Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIota.altKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9Iotadieresis.altUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronpirhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055.alt afii10056.alt afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygraveendashemdash afii00208 underscoredbl quoteleft quoterightquotesinglbase quotereversed quotedblleft quotedblright quotedblbasedagger daggerdblbulletellipsis perthousandminutesecond guilsinglleftguilsinglright exclamdblfraction nsuperiorfranc afii08941pesetaEuro afii61248 afii61289 afii61352 trademarkOmega estimated oneeighth threeeighths fiveeighths seveneighths partialdiffDeltaproduct summationminusradicalinfinityintegral approxequalnotequal lessequal greaterequallozengeuniFB01uniFB02 cyrillicbrevedotlessjcaroncommaaccent commaaccentcommaaccentrotate zerosuperior foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperioruni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni1F4Duni02F3 dasiaoxiauniFB03uniFB04OhornohornUhornuhornuni0300uni0301uni0303hookdotbelowuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BF uni04C0.altuni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CE uni04CF.altuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7 uni1EC8.altuni1EC9 uni1ECA.altuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABuni030Fcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCcyrillicbighookLCone.pnumzero.osone.ostwo.osthree.osfour.osfive.ossix.osseven.oseight.osnine.osffuni2120Tcedillatcedillag.altgcircumflex.alt gbreve.altgdot.altgcommaaccent.altIIgraveIacute Icircumflex IdieresisItildeImacronIbreveIogonek IdotaccentIJ IotatonosIota Iotadieresis afii10055 afii10056uni04C0uni04CFuni1EC8uni1ECA����� � �ÿÿ����� ���������©������������� �4�6�latn���MOL �ROM ���ÿÿ����ÿÿ����ÿÿ���������� �nä�latn���MOL �(ROM �B��ÿÿ� ��� ���������ÿÿ� ��� � ��������ÿÿ� ��� � �������liga�°liga�¶liga�¼lnum�Âlnum�Èlnum�Îlocl�Ôlocl�Úonum�àonum�èonum�ðpnum�øpnum�þpnumsalt saltsaltss01"ss01*ss012ss02:ss02@ss02Fss03Lss03Rss03Xtnum^tnumftnumn���� ���� ���� ������������������������������������������������������������������������������������������������������������������������������ ���&�.�6�>�F�N�V�^�����P�����z�����ª�����Æ�����î�����ô������������2����H���‘’“”•���J�J���ß�ß��á�á��ã�ã��å�å���.��,�Ž���‘�ê�ì�î�ð�ò�ôZgw¡¢ÉØEG��–©����� ƒ„…†‡ˆ‰Š‹Œ��������� ƒ…†‡ˆ‰Š‹Œ„���������‚‚� ���‚������ ������������ƒŒ��������‚��� �‚����������ƒŒ����� !��$%��IJ���6���� ���"�(^��I�O]��I�L��I5��O4��L���I������������^���������V0‚R *†H†÷  ‚C0‚?1 0 +�0a +‚7 S0Q0, +‚7¢€�<�<�<�O�b�s�o�l�e�t�e�>�>�>0!0 +�ëU¼Îßaz£€Æ²IVv« ‚]0‚z0‚b 8%×úøa¯žôç&µÖZÕ0  *†H†÷ �0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0 070615000000Z 120614235959Z0\1 0 UUS10U VeriSign, Inc.1402U+VeriSign Time Stamping Services Signer - G20Ÿ0  *†H†÷ ��0‰�ĵòR¼ˆ†`)J[/K‘k‡‘ó5TX5êÑ6^bMRQ4qÂ{f‰ÈÝ*Äj ö7Ù˜t‘ö’®°µv–ñ©JcEG.k ’NK+ŒîXJ‹Ôä,ø‚ªXÙÍBó-ÀuÞ«ÇŽšlL•ÞÛïgárÂIž`<áâ¾£cxi{­-�£Ä0Á04+(0&0$+0†http://ocsp.verisign.com0 Uÿ0�03U,0*0( & $†"http://crl.verisign.com/tss-ca.crl0U%ÿ 0 +0UÿÀ0U0¤010 UTSA1-20  *†H†÷ �‚�PÅKÈ$€ßä $ÂÞ±¡¡¦‚- ƒ7 ‚,°ZaµØþˆÛñ‘‘³V@¦ë’¾89°u6t:˜Oä7º™‰Ê•B°¹Ç WàúÕdB5NÑ3¢ÈMª'Çòá†L8MƒxÆüSàëà‡Ý¤–ž^ ˜â¥¾¿‚…Ã`áß­(ØÇ¥KdÚÇ[½¬9Õ8"¡3‹/Ššë¼!?DA µe$¼HÓD€ë¡ÏÉ´ÏTÇ£€\ùy>]r}ˆž,C¢ÊSÎ}=ö*:¸O”�¥m ƒ]ù^Sô³WpÃûõ­• ÞÄ€`É+n†ñëôx'ÑÅî4[^¹I2ò30‚Ä0‚- G¿•ßRFC÷ÛmH 1¤0  *†H†÷ �0‹1 0 UZA10U Western Cape10U Durbanville10 U Thawte10U Thawte Certification10UThawte Timestamping CA0 031204000000Z 131203235959Z0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0‚"0  *†H†÷ �‚�0‚ ‚�©Ê²¤ÌÍ ¯ }‰¬‡uð´NñßÁ¿ga½£dÚ»ùÊ3«„0‰X~ŒÛkÝ6ž¿Ñìxòw¦~o<¿“¯ ºhôl”ʽR-«H=õ¶Õ]_Ÿú/k¤÷£š¦ÈáLRã`ì@~¹ Þ?Ǵ߇½_zj1.™¨G Î1s W-Íx43•™¹Þh/ªæãŠŒ*Ë!‡f½ƒXWou¿<ª&‡]Ê<Ÿ„êTÁ nÄþÅJݹ—"|Û>'ÑxìŸ1Éñæ"ÛijGCš_ ä^õî|ñ}«bõM ÞÐ"V¨•Í®ˆv®îº óäMÙ ûh ®;³‡Á»�£Û0Ø04+(0&0$+0†http://ocsp.verisign.com0Uÿ0ÿ�0AU:0806 4 2†0http://crl.verisign.com/ThawteTimestampingCA.crl0U% 0 +0Uÿ0$U0¤010U TSA2048-1-530  *†H†÷ ��JkùêXÂD1‰y™+–¿‚¬ÖLͰŠXnß)£^ÈÊ“çR ïG'/�8°äÉ“NšÔ"b÷?7!Op1€ñ‹8‡³èè—�þÏU–N$Ò©'Nz®·aAó*ÎçÉÙ^Ý»+…>µµÙáWÿ¾´Å~õÏ žð—þ+Ó;R8'÷?J0‚ü0‚e eR&á².áY)…¬"ç\0  *†H†÷ �0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority0 090521000000Z 190520235959Z0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0‚"0  *†H†÷ �‚�0‚ ‚�¾g´`ªIoV|fÉ^† Õñ¬§qƒŽ‹‰øˆ‰º-„!•äÑœPLûÒ"½Úò²5;à ûü.Z¿‰|=;%öóX{œôµÆ ¸€Î¾'tag'MjåìaXy£à'°áM4+G D¹Þf$fŠÍOºÅ8ÈTáröfuj¹IhÏ8y ª0¨Û,`Hž×ª©ƒ×8‘09–:|@T¶­à/ƒÜ¨R>³×+ý!¶§\£ ©¦P4.M§ÎÉ^%ÔŒ¼ón|)¼]ü1‡ZÕŒ…gXˆ ¿5ðê+£!çöƒå¨í`x^{`ƒýW ]A cT`ÖC!ï�£‚Û0‚×0Uÿ0ÿ�0pU i0g0e `†H†øE0V0(+https://www.verisign.com/cps0*+0https://www.verisign.com/rpa0Uÿ0m+ a0_¡] [0Y0W0U image/gif0!00+åÓ†¬ŽkÃÏ€jÔH,{.0%#http://logo.verisign.com/vslogo.gif0U%0++04+(0&0$+0†http://ocsp.verisign.com01U*0(0& $ "† http://crl.verisign.com/pca3.crl0)U"0 ¤010UClass3CA2048-1-550U—Ðk¨&pÈ¡?”-Ä5›¤¡ò0  *†H†÷ ��‹ÀÝ”ØA¢ai°¨xÇ0Æ<~B÷$¶äƒsœ¡âú/ëÀÊDçràP¶U ƒn–’äšQj´71Ü¥-ëŒ�ÇOçM2º…øN¾úgUeðj¾zÊd8xEv1ó†z`³]ö‹fv‚Yáƒå½I¥8VåÞAwX0‚0‚û fãðgyÊmPSoˆƒ0  *†H†÷ �0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0 100729000000Z 120808235959Z0Ð1 0 UUS10U Massachusetts10 UWoburn10U Monotype Imaging Inc.1>0<U 5Digital ID Class 3 - Microsoft Software Validation v210U Type Operations10UMonotype Imaging Inc.0Ÿ0  *†H†÷ ��0‰�”D •i|U ÐÛ25ŠL3«^ ¡L×*‡8ט¥@ðI "SOÂC¦Ê‹©VïnH¨9c;$¹˜ÏÊ5}rãGWýyËŠJç@p-5c®€ÏįØû÷Éü‰Ø×¤ Û ò¢ò{ïÍuÁ÷ePd"½}¼­¸KÌXEMÑYLM�£‚ƒ0‚0 U0�0Uÿ€0DU=0;09 7 5†3http://csc3-2009-2-crl.verisign.com/CSC3-2009-2.crl0DU =0;09 `†H†øE0*0(+https://www.verisign.com/rpa0U% 0 +0u+i0g0$+0†http://ocsp.verisign.com0?+0†3http://csc3-2009-2-aia.verisign.com/CSC3-2009-2.cer0U#0€—Ðk¨&pÈ¡?”-Ä5›¤¡ò0 `†H†øB0 +‚70�ÿ0  *†H†÷ �‚�Næ"‡ßgAâÒî~ΙÖc½ðµ“åjrbáõÒ<8î¨=_ºG‚_[KIô ú“ ÐVD¢ˆóû®÷ 5Þ< ¬D”`E*›þ›oL;±4gp†ÿZ9\Zãl‚«5|eKý˜mµ”Iœˆp¾=±b•´Û´ÔÚèA~þ}¹¤’ënò"ŠÆw6MŠZ S1Ó+(¯RázkµwD½ ­ô]%,ãÍŠ0>KœyʦN® ÂÌ$ Á”‚öñº¶›šØ\<ñê'M<‰o3ŠÓ†ÞéX3u=ë“iâDoN�lÏÕ…ÚV¦š¦?ËL!hò`ºáè]9!2í1‚g0‚c0Ë0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CAfãðgyÊmPSoˆƒ0 +� p0 +‚7 10�0 *†H†÷  1  +‚70 +‚7 10  +‚70# *†H†÷  1Œý Ì¿&.:m ìQ؈~) €0  *†H†÷ �€føé޶Jô®ß›�4Úåž®‘‘¤íüÅÒŠ³€Îzp2g¥+=¬”›®¶=¨DcõíDn»ž’ÚÜ®SÇî­ Êvò+DЫ#ë¾Àfðô΀Ol�Ñ‚­ ïa_FíJ Ôñqù(ôl'R‡Í-éW¸IØ?ëËR›lü¡‚0‚{ *†H†÷  1‚l0‚h0g0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA8%×úøa¯žôç&µÖZÕ0 +� ]0 *†H†÷  1  *†H†÷ 0 *†H†÷  1 110505165510Z0# *†H†÷  1ÀïB¼ànÀ&á6á÷¼ˆûu0  *†H†÷ �€3„–AÙóÕɾܱaÖÃoüI(*l*ø ò›¡¢ø¦|Ç ÖÐ/Mqy¬)Ìü)bîíÐuº'︓E½JíNÌ|:ž"M&Íí.¢ƒÛ ñ†OÖÁ÷¶›º�"d�>ð�Gÿ…ö�5�5×ÿËV�5JÿÚ�5ð�{ÿÅç�5Íÿ… �5 �5+ÿÕ×�{�5¤�5XÿÕ?�¨ƒ�º‡�{Ùÿ‹‡�²ËÿÃÿŃ�ºÙ�Z�'Õ�%ð�`î�wÙ�Zöÿ¾�;´�7�'´�ZÕ�%®�dð�`‹�%“ÿ¤áÿ¼f�f´�\®�Z¸�^Ãÿ¾´�Zò�Zþ�^î�w�Zjþüw�w�Zð�`î�w®�Zî�w�ZV�5ð�¨�5å�{=�)ÿÅÿŦþ¾ªÿÃ�5ð�¨Ã�5É��5ÿ…¼�5ö�5�5Ãÿ;V�5ðÿƒÅ�) �5 �5Ã�5ÿà �5š�5×�{�5¤�5å�{?�¨É�‡�{Ùÿ‹Ý�5b�¸�5V�5‘�¨×�5¼�5å� �5çÿƒÁ�Z®�m–�ZÕ�Ï�Zu�Z®ÿì1�Õ�oÕ�o �%ìÿÅT�î�%®�ZÕ�%Ãÿ¼Ý�Z=�%'ÿs�Z?ÿœö�jÃ�¤=�j^�jd�\m�j�hÝ�®�%¨ÿìu�ZÕ�%Õ�Ý�ZÉ�`�%`�%`þøçÿÅç�1Õ�% �%'ÿsÕ�j�5´�%'�¸N�}'�¸N�}'�¸N�}ƒ�º'ÿs×�)®�)®�)3ÿ,¸�s¸�j9ÿš¸�ßw�sw�júÿš �¾!�;ã�hª� ¾�s �É �ɘ�H˜��}��ýì�…h��hÿôÙ�9h�)�{��)Õ�þ�jËÿÃô�m �a �\ �f �”¦�L×ÿËî�Á �{h�md�b¨�…L��h�mh�mh�mh�mª�qHÿ%Hÿ%o=`þú�î��{�Õ�b�/�^�Z�{�R�Z������������ª�����V��y��H��š���Í������������T��T`þú¸�j‹�uš�¸�^ �5=�%ÿ…Á�Z;þîªÿš��f�ÿ%�ÿ%-�{%�Z3�°�j��üï��ý¯��ü€��ý¤��übV�5 �5u�ZÕ�o1�¤�f�{Ó�=�5��%Zÿ…¤ÿƒd�5°�%×ÿª¢ÿšá�5 �%Åÿ°1ÿ¤‡�²w�w×�{®�Z3�¸^�f3�¸^�f î�{}�Z�{�Z1�®�Z1�¤�få�{Ý�Zß�hH-q?oZo;é�)¦�)‘�5�j¼�5…�s¤�5Ãÿ¼�´ÿôT�5–�%Zÿƒ®ÿìÅ�)1��5å�%Ã�5´�Zç�5‹�%{�¨/�J!�5H�%)�5°�%B�5é�%×�{ç�Zå�{Ý�Z?�¨h�%ƒ�º�fƒ�LÿÇÿ‹´ÿœ²�¨u�\ü�¸�¤b�¸Õ�¤b�5Õ�%;�=J�;�=J�ÿÅðÿƒ®ÿì‰�5ô�%ÿÃFÿÅš�5î�%!�5H�%b�¸Ã�¤‘�5²�ÿÅÿ…Á�Zÿ…Á�Z)ÿ…ú�ZV�5u�ZH�Du�3H�Du�3ðÿƒ®ÿìÅ�)1�qÿü`ÿ¦ �5Õ�o �5Õ�o×�{®�Z×�{®�Z×�{®�Zå�Ý�É�'ÿsÉ�'ÿsÉ�'ÿsb�¸Ã�¤�5´�%×�5m�j�´ÿô‡ÿ‹ËÿœÙÿ‹?ÿœ¤�=Á�Zé�=��Zá�Ň�‰B�ƒÓ�²ÕÿÃ+ÿÅß�5/�%ã�{ �Zª�¨b�^¸�D�'5ÿËÿÅÿ…Á�Zÿ…Á�Zÿ…Á�Zÿ…Á�Zÿ…Á�Zÿ…Á�Zÿ…Á�Zÿ…Á�Zÿ…Á�Zÿ…Á�Zÿ…Á�Zÿ…Á�ZV�5u�ZV�5u�ZV�5u�ZV�5u�ZV�5u�ZV�5u�ZV�5u�ZV�5u�ZÿÅ`�%ÿÅ`ÿß×�{®�Z×�{®�Z×�{®�Z×�{®�Z×�{®�Z×�{®�Z×�{®�Z-�{%�Z-�{%�Z-�{%�Z-�{%�Z-�{%�Z‡�Õ�o‡�Õ�o3�°�j3�°�j3�°�j3�°�j3�°�jƒ�º'ÿsƒ�º'ÿsƒ�º'ÿsÁ�6��ü`��ü¨��ûå��ü¨��ü¨��ý��ý��ý��üþ¦ÿFVÿ`Vÿ`Ù�q¦�Z�'`ÿìhÿ¯hÿÉNÿã…�Z`�}�?š�E´ÿ%á�ž?�H�VÁ�Á�Á�Á�Á�“�5“�5“�5“�5“�5“�5“�5“�5“ÿ¸“�59�5%�"“�5�5�5�5�5�5�5ÿñ���������� ����°�€��0�H�I�~�Ë�Ï'2a’¡°ðÿ7¼ÇÉÝó #ŠŒ¡ªÎÒÖ O_†‘¿Ï?…ÇÊñùM   " & 0 3 : < D p y  ¤ § ¬!!!! !"!&!.!^"""""""+"H"`"e%Êûþÿÿýÿÿ��� �I�J� �Ì�Ð(3b’ ¯ðú7¼ÆÉØó� #„ŒŽ£«ÑÖ�P`ˆ’ÀÐ�>€ ÈËòM �   & 0 2 9 < D p t  £ § «!!!! !"!&!.!["""""""+"H"`"d%Êû�þÿÿüÿÿÿã��ÿãÿÂ��ÿÂ��ÿÂ��ÿ°�¿�²�aÿI����ÿ–þ…þ„þvÿhÿcÿbÿ]�gÿD��ýÏ����ýÍþ‚þ��ýš��þ ��þ ��þ äXäãzä}��ä}��ã âBáïáîáíáêáááàáÛáÚáÓáËáÈá™ávát��áá á ânàþàûàôàÈà%à"àààààßçßÐßÍÜi��OS����®�����ª���®���À�����������ð�����������������������à���ê��������0��L��\����������p��r��������������������������������`��������������������������������������<������–—˜™š›�ëœ�í�ïž�ñŸ�ó &'()*+,-./0123456789:;<=>?@AIJ$%TUVWXY¡\]^_`abcdef¢hijklmnopqrstuv£hœžŸ ¤¥£¤¥¦§ijêëìíîïðñòóôõkö÷“”•–—˜™šøù¦ÊËÌÍÎÏÐÑÒÓÔÕÖ×§¨F©�opqrstu45]^��@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! �, °`E°% Fa#E#aH-, EhD-,E#F`° a °F`°&#HH-,E#F#a° ` °&a° a°&#HH-,E#F`°@a °f`°&#HH-,E#F#a°@` °&a°@a°&#HH-, <�<-, E# °ÍD# ¸ZQX# °D#Y °íQX# °MD#Y °&QX# ° D#Y!!-, EhD °` E°FvhŠE`D-,± C#Ce -,�± C#C -,�°(#p±(>°(#p±(E:±� -, E°%Ead°PQXED!!Y-,I°#D-, E°�C`D-,°C°Ce -, i°@a°�‹ ±,ÀŠŒ¸�b`+ d#da\X°aY-,ŠEŠŠ‡°+°)#D°)zä-,Ee°,#DE°+#D-,KRXED!!Y-,KQXED!!Y-,°%# Šõ�°`#íì-,°%# Šõ�°a#íì-,°%õ�íì-,°C°RX!!!!!F#F`ŠŠF# FŠ`Ša¸ÿ€b# #б ŠpE` °�PX°a¸ÿº‹°FŒY°`h:Y-, E°%FRK°Q[X°%F ha°%°%?#!8!Y-, E°%FPX°%F ha°%°%?#!8!Y-,�°C°C -,!! d#d‹¸@�b-,!°€QX d#d‹¸ �b²�@/+Y°`-,!°ÀQX d#d‹¸Ub²�€/+Y°`-, d#d‹¸@�b`#!-,KSXа%Id#Ei°@‹a°€b° aj°#D#°ö!#Š 9/Y-,KSX °%Idi °&°%Id#a°€b° aj°#D°&°öа#D°ö°#D°íа& 9# 9//Y-,E#E`#E`#E`#vh°€b -,°H+-, E°�TX°@D E°@aD!!Y-,E±0/E#Ea`°`iD-,KQX°/#p°#B!!Y-,KQX °%EiSXD!!Y!!Y-,E°C°�`c°`iD-,°/ED-,E# EŠ`D-,F#F`ŠŠF# FŠ`Ša¸ÿ€b# #б ŠpE` °�PX°a¸ÿ€‹°ŒYh:-,K#QX¹�3ÿà±4 ³3�4�YDD-,°CX°&EŠXdf°`d° `f X!°@Y°aY#XeY°)#D#°)à!!!!!Y-,°CTXKS#KQZX8!!Y!!!!Y-,°CX°%Ed° `f X!°@Y°a#XeY°)#D°%°% XY°%°% F°%#B<°%°%°%°% F°%°`#B< X�Y°%°%°)à°) EeD°%°%°)à°%°% XY°%°%CH°%°%°%°%°`CH!Y!!!!!!!-,°% F°%#B°%°%EH!!!!-,°% °%°%CH!!!-,E# E °�P X#e#Y#h °@PX!°@Y#XeYŠ`D-,KS#KQZX EŠ`D!!Y-,KTX EŠ`D!!Y-,KS#KQZX8!!Y-,°�!KTX8!!Y-,°CTX°F+!!!!Y-,°CTX°G+!!!Y-,°CTX°H+!!!!Y-,°CTX°I+!!!Y-, Š#KSŠKQZX#8!!Y-,�°%I°�SX °@8!Y-,F#F`#Fa#  FŠa¸ÿ€bб@@ŠpE`h:-, Š#IdŠ#SX<!Y-,KRX}zY-,°�KKTB-,±�B±#ˆQ±@ˆSZX¹�� ˆTX²C`BY±$ˆQX¹ ��@ˆTX²C`B±$ˆTX² C`B�KKRX²C`BY¹@��€ˆTX²C`BY¹@��€c¸�ˆTX²C`BY¹@��c¸�ˆTX²C`BY±&ˆQX¹@��c¸�ˆTX²@C`BY¹@��c¸�ˆTX²€C`BYYYYYY±�CTX@ @@ @  ±CTX²@º�� �³  ±€CRX²@¸€± @²@º€� @Y¹@��€ˆU¹@��c¸�ˆUZX³ � ³ � YYYBBBBB-,Eh#KQX# E d°@PX|YhŠ`YD-,°�°%°%°#>�°#>± ° #eB° #B°#?�°#?± °#eB°#B°-,°€°CP°°CT[X!#° ÉŠíY-,°Y+-,Šå-@’ !H U UHUÿPLOMdNLd&4U%3$UÿÿÿMLdLLF 3UU3U?¯KFËFÛF#3"UO"3U3UU3U¯Ï0�U3�Uo��¯�ï��€¸±TS++K¸ÿRK° P[°ˆ°%S°ˆ°@QZ°ˆ°�UZ[X±ŽY…�BK°2SX° YK°dSX°±�BYss++ss++++s+s^st++++t+++++++++++++^�����N¶��u¶Í��������������^��{��ÿì����ÿì����ÿì��þÿì��¶�ü”ÿëþoÿçþ¿ÿé�þ¼������������������������������������������������������������å�ö�+�Ó�Ç�ö�ö�í�ß�²��TþV�������K�h�¾)”ü1Qy¤»Íéþ8Y•ë'j½Ù/„¶ã$F•J˜Ñ8^¥Ðö  L g Ÿ Î @ ‰ Ç  . ` ˆ Ï ú  J f z • ° Ä Þ $ p ¥ ò?†ü6]’ÃÖ&Z‘Û)R—Ó-rÓýGY£ßßUò:V²ÛWÓòúsŽÅð&pŠÊñú&GsºÐåûK\m~¡² /@Rct…—áò%6HrÈÙêû UÁÑáñ#¯»ËÛëü /A¤´ÄÔäô  K ­ ½ Í Þ î! >Y>–>É??l?ª?²@@\@¤@ùAAsAÀAÈAØBB?B|B¦B®B¶B¾BÆBÎBÖC.C6C}C·DDeD¬DöE3EvEÁFF#FŠFšFÛFãFëFýGGqGÃGËGÜGìH/HQHrHƒH“H¤HµHÇHÙHêHûI II'IMIeI}I”I®IÔIûJ"JTJ¥JÅJÕKiKqKyK›KÇKÓKèLL^LÄM2M•MøNEN®NòNúODO[OrO‰O OçPP1P[PvP•PòQQ‚QÀQëRR@RLRXR}R¡R»RÖRñS SSSSÖSðT@T‡T‡T‡T‡T‡T‡T‡T‡T‡T‡T‡T‡T‡T‡U£V�VVVVÄW"W3WDWPW\WhW–WÌX\XÛY.Y{YÁZZZ$Z-ZUZnZZZ Z°[[X[§[û\W\«\â]]h]¯^^U^¸__©`;`C`K`™`âaaSaeawaƒaañbJc�c¨d:d™dÕe e=ebee³eÕf¨g/gˆgåh,h†hÑi/i_iiÖjjbjÛjçjók)k^k”kËl lQl‚l´lîm$m[mmÙn"n›oo&o2o^oÅoÍo÷p4pmp¥pÜqqgq¥qêr2rzr¬râsDs t trtzt‹tœtãu'uku´u÷v2vmv£váw)wnwµw½wÎwÞwðxx xx"x2xxÈxÚxëxýyy!y2yvy¹yÊyÚyìyýzz z(z0zBzSzezwzˆz˜zªz»zÍzÞzð{{'{L{^{p{½| |Q|™|Õ}}C}K}¢~~^~¶W¨€€N€•€Ý!^›íõ‚G‚ ‚¬‚¸‚ɂڂì‚þƒƒ"ƒ4ƒFƒXƒjƒƒ“ƒ¥ƒ·ƒÉƒÛƒíƒÿ„„#„8„L„X„d„u„†„—„§„¹„˄݄ï………%…7…L…`…q…‚…Ž…š…¦…²…Ã…Ô…æ…ø† ††.†@†R†d†y††ž†®†¿†Ï†à†ñ‡‡‡‡*‡6‡B‡S‡d‡u‡…‡–‡¦‡·‡È‡Ù‡é‡õˆˆ ˆˆ*ˆ;ˆLˆ\ˆhˆ‘ˆÆˆü‰?‰¡‰ÒŠŠCŠžŠ¿Šà‹‹ ‹>‹_‹™‹ëŒ&ŒiŒqŒ’ŒšŒïaÏÛçŽAŽQŽaŽrނޖާޏŽÉŽÛŽìŽý*6KSem‡ ¬����Á�� ¶��� ³��/2/310!!7!!ÁIü·hyý‡¶úJhæ���ÿ凶���"@   TY �?+�?9/_^]10#!4632#"&‡ñTý’kaDRnYIRåÑú½ZlLEWlK���ɦº¶��� ´�?3Í210#!#%˜ÄGª—ÇJ¶ýðýð���)��5¶���;@ �0@  �?3?399//]333333333310!!####7!7#7!333337# Eþ×tÜuÂs×qîFü!wÙuÇt×tïýÅEÄNèÎþh˜þh˜ÎèÑ—þi—þiÑèè���3ÿ‰`��%�+�C@%+ %&OY  PY@H,��?99//3Í++�33+�3910#7&'&&546773&'6654&' ûò'Œ,Ñ’ÆÎG£œþß#‹!¦j„n?ƒ€BþH?N1#>EPð¸×ÇÉ J k B=´|­Õ——DèA þÕ3aþý H>.;=D+Ý´‰REB‹f’„ð‚¾ÞR£¹þó8Õ¨Ÿ¿TKOCUËp_Ä¡k¼¼—Þ ���ÿìPË�'�5@QY   !%OY% OY �?+�?+�9/_^]+�910#"'32654!#732654&#"'6632P½§ƒ‡”þì½ï”TÄ]žªþþŠ.I§ÀVN†™|ÙŠ¾Ø}œÔ¡y…ÒuO 23q¬Ý|nCJdÌQAµ��ÿç��P¶� ��%@PY �??9/33+�3910#!!7!3!7667#ª?þÛ?ý²07êþ1: 8#aþÇ/þÑ/êüiø:Ô#?vþŒ���ÿìh¶��&@�PY��NY OY�?+�?+�9/+102#"&'32654&#"'!!6o·ÔþîÁuË=®²š®smfohÏó7þXHšÎ¸§ú‡.# cŽ{^^!NÝþúþÛ��XÿìÍ��&�-@  PY OYOY�?+�?+�9/+�9104$32&#"3632#"&2654&#"XzÕ,Ï}b3TkÂïBsÄ«ŽïžÔغc}CEjýRI1-H9$H­þئMFMFŸ–“—7þ\,A D€ÖkÕá¼þ¨Üóþí(.ºZ«8Ë´÷þ¹ög«YAJÞ ��ÿ…��‹¼���@ LY�??39/+�910!!!!&55Jþ'¬þÀþu“þ×' ,A¼\þ¤¼úD`^ƒz$x†þ��5��ã¶����5@MY  LYLY�?+�?+�9/_^]+�9102!!32654##32654##åꦙdwþÎþòýö5·•yyª˜Ê´u…·¥¶£¢–à nåø¶ý½a[‰üHtg¢��{ÿì7Í��@ �LY LY �?+�?+10"327#"�$32&&‰„Ó€‰–’ÀÇÉþþæÐ\â}ÂovjŠË¢þŵ§¢MþüM+¹å-<ú;&���5��+¶� ��@ LYLY�?+�?+10#!! �2654&##+Äþüþ=5Œ'üÛ”Ü|‘ˆsÊþèþcضþâüh˜'¾š¡üH���5��œ¶� �.@ LY  LY LY�?+�?+�9/_^]+10!!!!!!!füÏ526þ�CÝ7þ#P�¶þþ¿þþ‡��5��˜¶� �@ LYLY�?+�?9/+10!!!!!!fþÏ5.6þOÙ8þ'¶þþ‡ý��{ÿìmÍ��&@�LY�� LY LY�?+�?+�9/+10!# �$32&&#"327!ò3¢†å€þýþæÖ„þÚÀsJ”W™ñ‰‘–L^Bþõ5ý.!& ±ëcû(0¦þͱ¬¡+��5��¤¶� �@LYL �?3?39/]+10!!!!!!!oþΆþ)†þÏ52y×y1wý‰¶ýÃ=�ÿÅ��¶� �@  �?33?3310!!77'7!Lýy$»É˜%‡%¾Éœ°R²R°°RüNR��þ¾þR²¶� �· �LY�#�?+�?10"'53267!‡^]XLc{%1þÑ4÷þRýyƒdúqõà��5��¤¶� �#@  �?3?9339310!!!!7!uþ®Ó}mþÏ52˜ž™iý®PFýö¶ýBÍñýD���5��œ¶��·�LY��?+�?103!!552ÿ��6¶ûJÿ����5��¶��@  �?33?3910!!67#!#!!F'§þËþè‘54ýÇþç= 6 þí5”DqEúJ´÷Âû“mXþâý ¶û»��5��¶��@   �?3?39910!!#!!36!ßþªþœ "‘þí5eT 6™R تýP¶ûËLÏ���{ÿì˜Í� ��@ LY LY�?+�?+10# �$32�%"32654&˜Æþ¨àÿ�þáÆ\äÿýÐyÊs…yyÇp‚ªþåþAä' ½îþà·þÀ»“–®>ÆŽ›��5��¦¶���@ �LY LY�?+�?9/+1032654##�!#!!2 8Ž£ÃJ@þ­þÖVmþÏ5RòøŠt´¬ñþíýø¶Õ���{þ¤˜Í���!@LYLY�?+�3?+�Æ10!# �$32�%"32654&˜õÔþþ™²ÿ�þáÆ\äÿýÐyÊs…yyÇp‚ªþÄþ!bþwH' ½îþà·þÀ»“–®>ÆŽ›��5��¬¶���&@ � MY��   LY �?+�?39/+�91032654&##!!2!Nƒ’_fJ‰wþÏ5gíþ´Ï-ruRRýyý϶ÌÅžã7ý“1���)ÿìVË�#� @ LYLY�?+�?+�9910#"'32654&&'&&546632&#"²þØý݇Á¥p~2cŠp|å•Ù´mœ„SjBsyx°ÑóZlUJ+A8JcÂpËqcéJZJ=[KPÁ��¨��Ѷ��@ LY�?+�3?10!!!!!;þÏþþ 7ò7þŸ´þþ���ÿìš¶��@  LY�?+�?310�!"&547!3267šÉ9þ¾þþÔóÄ1½{ ¶üNþöþòãÂHB›üiJ3²™˜•���¸��q¶� �¶ �??391067!!!N °<ýþ²'3+Ý>púJ¶ü-Xg/���¸��ç¶��@ �?3?33910!!3667!367!!'47î.Eþ¼þ®-Zq +XJ5ýyþ¦ú’™ý1¶üâ4ò5@é3ü—’~ÜúJÑ‹XF��ÿ‹��y¶� �@   �?3?3910!!!!!dþµ¬þtþª@î@™kXýßýáüºýúý+���º��?¶��@ ���??393310!!!svVý–wþÑwì8fPüyýÑ/‡��ÿÃ��°¶� �$@LYLY�?+�9?+�910!!7!!!˜ü+& ýá5¨)üòTÉí�Êü��ÿÛþ¼1¶��µ'�?3?310!!#3´þ'}Ù-×þÝ×þ¼úÓú¬��Ý��Á¶�� ³�??10!áàþõÙ¶úJ¶�ÿwþ¼Ë¶��µ�'�?3?3103#7!!\Õ#×-Ùþƒþ)qTÓù��'¾�� ´��?Í29103#k“ß®þn¶üJyý‡�ÿFþ¼šÿH�� ´��/]310!7!{üË6þ¼Œ�üÙ¦!�� ²€�/Í10#&&'5!¦¸G‡$7$OÙE©E”›���Zÿìžs�� �%@  GY�GY��??+�?+�?9910"&54632373#7#'26654&#"–“©êŒa'9èîå†CE€NM?D{ICÕÀÆgÅTPû¢‘¥ó†å‘G]ìtXX���%ÿìh��!�+@  � � �GY�GY�?+�?+�??99//102#"'##!366"326654-’©ˆîÂR:çJ->)+N-D|PJACzKsؾ¾þУþà¶ubHô‚ç‡P`Šñu°��Zÿìòs��@  GY�GY��?+�?+10"&54$32&&#"3267úÉו­¶’\6hBU‰M[QL‚E˜×ÄÔ[½Hå"€ß€`a/#öO��Zÿìú��#�'@ ��GY�GY��??+�?+�?9910"&546323767!#7#726654&#"–“©êRw5L-þ¶åG›!B}LKAD{IÖÁÄeÇJZnUfùì‘WNó‰äƒP`ìt°���ZÿìBs��!�.@KY  �JY FY �?+�?+�9/_^]+10"32654"&54$32!#3267¸Xœ-›­þóÒ襲±Æþ²þÊ3g`WŽe¬š saS_üRâÎÏU³£Ž»Ë[i&0ãV��ÿ%þÛ��*@ GY�FY�GY��?+�?+�?+�9910"'53267#?6632&#"3#-hF=6=\Ì£·)ðƒhPE@9F Û1Ü×MþòPZÅ‘TT¾¯1àPA>åüþ��ÿþ¢s�(�4�>�4@:KY()&5KY&(JY(.KY�?+�?+�?+�/339/+10#"'!"&5467&5467&&5463232654&'"32654¢#®òÐ;(T<;‰£•þÈþÞÐ鉒JYfA>ûØSSþVjm—šAi‡M_gK]\¦)4BÃã 3"xÓà—„f“0/VGg/1~ZÇéûžVBsZR'*È™sw™uu���%��m��@ GY � �?3??+�910!!654#"!!36632ÑþÓ‰l\–+bþÓJ-'*A>˜dŠ—D3{äÌþ1µÂÔM]§›Ik���%��� ��@  IY�  �???+104632#"!!B_WILX\—þÓí-dWY>:Pcû^��þøþ� ��@IY��GY��?+�??+10"'5327!4632#"ZhF=5‰$ý.þ÷MX_WILX\—þòªªû)þPWY>:Pc��%��ð��@  ���?3??910!!!!3˜Xþþ°·xJþÓJ-”^þýš¤Hþ¤ýJ)Lf���%��œ�� ³��??10!!!RþÓJ-��%��Õs�&�%@%� !�GY�"�??2+�3?339910236632!654#"!654#"!336LÛ+D¹iˆ…þÓŠb\•,`þÓ‰b\–+bþÓíæ’sänvª˜LhýƒD3{àÎþ/D3{äÌþ1^Ïä��%��ms��@ GY  �?3??+�910!!654#"!33632ÑþÓ‰l\–+bþÓíæ’ÑŠ—D3{äÌþ1^Ïä§›Ik��ZÿìTs� ��@ GYGY�?+�?+104#"3266%#"&54$32%KwK–KxC/þö·ÃéµÄä¸Åzì}¹{ÛÓþº³ëÃÕM·ì�ÿ¼þhs��!�'@ �GY�GY��?+�?+�99??102#"&'#!336"326654-’©‰îSx3 HþÓVæ Š:DMJACzKs×Á¿þŸÏJYŸˆþ¬Jª¿ô‡áˆP`Šñu°��Zþžs��$�'@ � GY�GY��?+�?+�??9910"&&54632373!667#726654&#"‡XˆMêŒV…:9èþªþÓ/4:HŽ=@IKAD{II_¶€ÆgÅKYù¶Úï¹^LóŒÞ†P`ìtXX���%��ªs��@ �  �???29102&#"!336J;%B-7t¯%jþÓíæ“s þÞ·«þ ^Ïä��ÿì s�"� @ FY FY�?+�?+�9910#"&'532654&'&&54632&#"=ùÞkŸE¢PfJ^y`ÝÍÉ¢cŒv9F@X{nq¼É#øZA8+D4D‡\ª»_×T3+';-?”���^ÿìoL��'@@ FY�GY�?+�?+�33Í10%27#"&547#?3!!Aao›– s˜Ä„Â12þæsß#á5~„2>“TìîåýåJ��oÿì²^��@  �GY �??+�?3910!3267!#7##"&547-‰l\–+b-íæ‘ÒŠ’^ýsD3{äÌÏû¢Ï㥜]x���f��^� �¶ �??391067!!!Ç7%)Cý¤þ½Š'--™FRû¢^ý¶…b���}��¶^��@ � �?33?3910!!3>7!3767!!47X>=éþ¼3VçH IÛ3ýîþ¶ \²ýã^þ§«+æý莛PÐ2ïû¢›¹��ÿœ��¤^� �@ �  �?3?3910!!!!{áAsôbþ-ôþº}þøþ¢=!þ²NýÏýÓVþª��ÿsþ‘^��@  � GY �?+�?3/310!3667!!"'532677f'80)GýX±þÎZ9D0T6^ýúz«3‰Uûþ´ð `e1�ÿÑ��Ç^� �$@FYFY�?+�9?+�910!!7!7!!ìüå#?þs3î+ýÊ·´ÁéÈýS�ÿøþ¼d¶�'�@  '�?3?3933104#726766333#"&54776ÉÑ-z=&º±T1ZQB-çUS$C:5§­'?ŒåSaª‹áAJþ×ÏqN,E²6(ây9D¸E�Çþ/¢�� ²���?Í103#ÇÛÛø!��ÿœþ¼á¶�&�@ %&'�?3?393310 3"##56676675&5477654�T'Ñ-z>%»±-]XBŒo¨%II)¶ø8D¹E‹åTaþ䪌âAI)on 3¦+E³6(á���m'%}��(@ �?_¯�/]q333}/3310"56323267#"&'&&V6}6e›@jV@^03z=g™;daYL B7çm$=<çm(&��ÿ¶þ%^���!@��� ��  TY �?+�/9/_^]103!#"&54632¶òžþ¬oo^DQlZIS^ü/D\kLFUmJ���¤ÿì;Ë��*@ PY @ OY¸ÿÀµH�?Í+2+�?Í2+10#7&&546773&&#"3267?¼1†Š}ç˜!¼#vk\5hB…¦ZRK‚Eˆ£Ò$ÈžÁDÉžž3æ"þâÁ``/#öG ��ÿô��ÙÍ��-@ QY ��OY�NY�?+�?+�9/3+�3102&#"!!!!7677#73766hîq`K`/+-þÕ*§7ü1Ä0À-À1)òÍVèDO]åÜTÃKþüö0ØhÜ÷ÅÏ��s�þ#ª��'�.@  %%?%_%%¯%Ï%ï%%�/]3Æ291047'76327'#"''7&732654&#"¾6“[ji[–55}’_esT}‘6ÏmPQoqONoÓf_“57Ynk\}‘}33{‘}]hMonNPnp���X�� ¶��>@ RY�  RY  �??39/3+�3Ä_^]29+�310!3#3#!7#737#73!`s7þË'üü%ü/þÝ/ü%üü'ÄÀ)hNý²Š²ÝÝ²Š²ë�Çþ/¢���¶���?/99//103#3#ÇÛÛÛÛüÑþüÑ���ÿì)�*�5�@1+1( "�?3?3910467&&54632&#"#"'532654&'&&6654–_k*8êĬ¬R“‡QWòÁ&4üÛÌ„¬­c[B^u€d3>Wd15øY”D"e9•«XÁTj+B'jżs#e:¡¹KàiF9'C.9”5[66U._0m��øF� ��´ �/333104632#"%4632#"UM…NR‡’TNADOQ‡fNPlIWnNP84IW��}ÿì\Ë��&�6�G@2���p€àð    Ÿ ï ÿ  #3+#�?3?39/]q39/]q310"3267#"&54632&4$32#"$732$54$#"¤ohoh+ƒ;ƒƒÄÞëÐŽŽKrü~È^ÊÇ]ËÅþ¤ÎÏþ¢Ã££§¡¡þ妤þã¢ò’†Š¿9õÖáüH®:þéÈ^ÊÆþŸÉÇþ¤ÌÏZƤþ椦££¦¥þæ��˜ðmÇ���@ � �?3ÄÔ2Ä9910"&546632373#7#'26654#"qgr\šdx<'šžšP1-N5XIeðŒ€uÞxgZýB\h—SLjÀwo��H�Z�� �=@ ��   0  �/]3339/]33393333310%H‘¿þé‡öÊç’¾þéˆöË9Ï›þ²þ¢gÍÏ›þ²þ¢gÍ���m�ø%?��@?/Ÿ¿ßï�/]q3Ä10%#!5!%Ûý#¸ølÛÿÿ�)¨j¢�����}ÿì\Ë� ��&�6�9@     �   #3+#�?3?399//]3]39/3310###!232654&##4$32#"$732$54$#"ÕXVÅþŠCæ4­¹þL3HRGQ5ý\È^ÊÇ]ËÅþ¤ÎÏþ¢Ã££§¡¡þ妤þ㢃V!þ˜)þ×j‡þî>=;;þôÈ^ÊÆþŸÉÇþ¤ÌÏZƤþ椦££¦¥þæ��ÿú3Ý��@/?o¯ï�/]310!7!ûô- É���¤XË���@ p _  �?3Ä]]21046632#"&&732654&#"¤]¢[]¡\\ ^] ]¾[ABZ[AA[q]¡\^ \] [Z ^?[\>?^_���m��%î� ��@ �/33333/310!5!3!!#5!Ûþ’nÛoþ‘Ûþ’¸¢ÛqþÛþ“þËÛÛ���;J+Ë��@   �?3?3910!7%>54&#"'6632!Ãýx#o<3#VfdJ¤n{“,`[J¦Û[>8*(Rž9<~aFjef_���\9-É�%�'@¿Ïß # !�?3?39/]3910#"&'532654&##732654&#"'632-fsšÖ²K2}Lb4>"ZTa4-VfRŽ«‚™öWu!ƒœÀH=:%3 89((B–\o�ãÙ!�� ²€�/Í1067!#ãOO.ØVÑòX×8Á>��ÿ¼þ¾^�� @ GY �??+�??399103267!#7##"&'#!!¨?0Z‘-b-íåu•3MEþÐV-‡\TØÌ×ì\þüPù°3úû��ÿÿ�ƒ)å}���jD�ÿ1þ����@   �?33/99|/10#"'53254&'73¦—VB?>f>O`¹'út~¨R"- šH1���Jß¶� �¶ � �??99103#67'ϺöT@ƒf¶ü”mb-Q¥���¢ð)Ç� �� ´ �?3Ä210#"&54632"32654)a¨p†ˆÐ­‰þá@WP?Tª€Ñi–ˆÅô“³no«zk�����H7ö�� �E@' ��    Oo¯Ïï�/]3339/33393333310'7'77þo¿‡öÊþþn¾ˆöËþ1›N^gþ3þ1›N^gþ3ÿÿ�a��J¶�'Å���&�{à�<ý·� ³�?55�ÿÿ�a��œ¶�'�tqý·�&�{à�Å���²��?5�ÿÿ�…��‡É�'J���'<Zý·�u)�� ³�?55��ÿ¼þy^��&�&@� $$TY$ "�?3?+�9/_^]103267#"&546676677#"&54632¦spzE…2q‘\Ýà±Î9n†]F`o^DQmYIS^Ni–LSV7v DÝ}¯R…sY>Y>!s\kLFVlJÿÿÿ…��‹s&�$���C�R�³&�+5ÿÿÿ…�� s&�$���v�üR�³&�+5ÿÿÿ…��Ós&�$��K�sR�³&�+5ÿÿÿ…��`&�$��R�…R�³&�+5ÿÿÿ…��½V&�$���j�wR� ´#&�+55ÿÿÿ…��‹ &�$��P5X� ³%�?55��ÿ…��o¶���H@' LY   LY   LYLY�?+�?+�3?99//+�9/_^]+10!!!!!!!!!#9üÏJþVÛþ¸šP6þ�CÝ7þ"O�ýPþ”\þ¤¶þþ¿þþ‡`Xý¨ÿÿ�{þ7Í&�&����z3��ÿÿ�5��œs&�(���CÿäR�³ &�+5ÿÿ�5��œs&�(���v�}R�³&�+5ÿÿ�5��œs&�(��K�9R�³&�+5ÿÿ�5��œV&�(���j�)R� ´&�+55ÿÿÿÅ��s&�,���CÿR�³ &�+5ÿÿÿÅ��ôs&�,���vÿäR�³&�+5ÿÿÿÅ��Òs&�,��KÿrR�³&�+5ÿÿÿÅ��ÀV&�,���jÿzR� ´&�+55��%��+¶� ��-@LY LY LY�?+�?+�9/3+�310#!#73! �2654&##3#+Äþüþ=}7‹ƒŒ'üÛ”Ü|‘ˆsKí7îGþèþcØTþdþâüh˜'¾š¡þšþþ¬ÿÿ�5��`&�1��R�øR�³&�+5ÿÿ�{ÿì˜s&�2���C�wR�³&�+5ÿÿ�{ÿì˜s&�2���vR�³$&�+5ÿÿ�{ÿì˜s&�2��K�¼R�³"&�+5ÿÿ�{ÿì˜`&�2��R�ÍR�³%&�+5ÿÿ�{ÿì˜V&�2���j�¼R� ´/&�+55�� š� �@ /�_��ß���/]107'¬þÕ˜-1™þÏ-•þÏþÓ–Ó-šþÕ+–þÏþј-þÕ˜��dÿª²���&� @ #MY#LY�?+�?+�9910#"''7&5$327%"&'326˜Æþ¨à´|lšybÆ\ä¶{i—u[ýÎ~ÎrTE®ý³;^|ÌqªþåþAäG‰s—ŠÛ ½îLƒu‘†Y·þ¾¿ Eò+þÑPý%²=�ÿÿ�ÿìšs&�8���C�DR�³&�+5ÿÿ�ÿìšs&�8���vR�³&�+5ÿÿ�ÿìšs&�8��K�¤R�³&�+5ÿÿ�ÿìšV&�8���j�¨R� ´(&�+55ÿÿ�º��?s&�<���v�{R�³&�+5��5��w¶� ��@ LY LY  �??99//++10�!#!!3232654##wþ«þØV>þÏ522#òøýd7‹§ÃJ'óþñþÛ¶åÕþ'…w´��þüþ�:�,@& )�)GY��FY05GY0�?+�?+�?+�9102#"'532654&'&&54676654&#"#"'53276$NÐöbI.,9kK÷Ö»n€‚e^5R^FZuBMOU`xþ+ЭZF=6…%þ/´–rœJ*55+S‡SªÈ=ðNB5(F>HtDTzB%N9>Igxû=Ƶò²·àÌ�ÿÿ�Zÿìž!&�D���C¿��³"&�+5ÿÿ�Zÿìž!&�D���vs��³)&�+5ÿÿ�Zÿìž &�D��K�ÿ�³'&�+5ÿÿ�Zÿìž&�D��R��³*&�+5ÿÿ�Zÿìž&�D���j��� ´4&�+55ÿÿ�Zÿìž²&�D��Põ�� ´$&�+55��ZÿìÇs�,�:�C�T@0 >"KY> >>�;JY4GY�'FY� -GY �??+�3+�?+�?+�?9/_^]+�910"&'#7##"&546323736632!#3267%26654&#""32654¶m/¼G—\’£‰éŽ`29¼,™\³þ²þË4ohB¬L³üšH|JBEDxFAqXœ-›­6<^‘XMÚ»È_ËMWZ1>§Š»Ë`g.(ãVóˆéP`Šé}T\» saS_�ÿÿ�Zþòs&�F����z‡��ÿÿ�ZÿìB!&�H���C«��³#&�+5ÿÿ�ZÿìB!&�H���v-��³*&�+5ÿÿ�ZÿìM!&�H��Kí��³(&�+5ÿÿ�ZÿìB&�H���jà�� ´5&�+55ÿÿ�%��C!&�ó���Cþ���³&�+5ÿÿ�%��P!&�ó���vÿ@���³ &�+5ÿÿ�$��M!&�ó��Kþí���³ &�+5ÿÿ�%��7&�ó���jþñ��� ´&�+55��Hÿì…'��,�7@�&FYFY�?+�?Æ29/+�99910&'77#"&546323&''26654&#"j"Zv†bíLÀQ@‹þô½Øâè•S‰.açXnBqAMFIr?N".º=Nƒ˜jkÿ�—ùþ‘ÇÜÎ¥˜>?Õaƒ“ü!oºdM[j´kQ[�ÿÿ�%��¬&�Q��R+��³ &�+5ÿÿ�ZÿìT!&�R���C§��³&�+5ÿÿ�Zÿìh!&�R���vX��³"&�+5ÿÿ�ZÿìU!&�R��Kõ��³ &�+5ÿÿ�Zÿì€&�R��Rÿ��³#&�+5ÿÿ�ZÿìT&�R���jõ�� ´-&�+55��m�Ý%Ç����3@$Ÿ �P`p�?/Ÿ¿ßï�/]q3Ä]2Ä]2105!4632#"&4632#"&m¸ý—JBBIJAAKJBCHJAAKdÛÛïLKNIFRNKMQGFQN���+ÿšf“���%� @"FYFY�?+�?+�9910#"''7&54$327%"&255Tþö·{fmwHµƒ`F‘TBþBQEm#¬ržþš 1¸Óþº³5‡l”k•ÕM·8Xnik6}Ö~ ÅýP ¹þDÿÿ�oÿì²!&�X���C§��³&�+5ÿÿ�oÿì²!&�X���v��³&�+5ÿÿ�oÿì²!&�X��K��³&�+5ÿÿ�oÿì²&�X���j�� ´*&�+55ÿÿÿsþ‘!&�\���v��³&�+5�ÿ¼þh��#�'@ ���GY�GY�?+�?+�??99102#"'#!!36"326654&-–¥Šâ‰²] HþÓ³-B3ƒGvICHExKKsÕÃÇþ”¼£Ÿˆþ¬�þ̓•ªô‚ïP`‚íXXÿÿÿsþ‘&�\���j©�� ´*&�+55ÿÿÿ…��¬þ&�$��M�“R�³&�+5ÿÿ�Zÿ재&�D��M#��³$&�+5ÿÿÿ…��×}&�$��N�\R�³&�+5ÿÿ�Zÿìž+&�D��Nï��³#&�+5ÿÿÿ…þ‹¼&�$���Q7��ÿÿ�Zþžs&�D���Q ��ÿÿ�{ÿì7s&�&���v�üR�³"&�+5ÿÿ�Zÿì+!&�F���v��³"&�+5ÿÿ�{ÿì7s&�&��K�¬R�³ &�+5ÿÿ�Zÿì&!&�F��KÆ��³ &�+5ÿÿ�{ÿì7f&�&��OÅR�³#&�+5ÿÿ�Zÿìò&�F��O�Ñ���³#&�+5ÿÿ�{ÿì7s&�&��L�‰R�³$&�+5ÿÿ�Zÿì]!&�F��L»��³$&�+5ÿÿ�5��+s&�'��L�\R�³&�+5ÿÿ�Zÿì¬&�G���8'��ÿÿ�%��+¶�’����Zÿìq��+�J@)� KY � �%GY��� � �� �GY�??+�?9/_^]+�9/3+�3991023467!7!7!3##7##"&54626654&#"TX}- þÙ)'#-#š)œüåM“U˜®ŒâQDtLAIGzGL5EPl…Ç¡¡ÇûT‘ZKÔõN¯üªwàlHXzÊoXX�ÿÿ�5��œþ&�(��M�VR�³&�+5ÿÿ�ZÿìB¬&�H��M ��³%&�+5ÿÿ�5��œ}&�(��N�R�³&�+5ÿÿ�Zÿìc+&�H��Nè��³$&�+5ÿÿ�5��œI&�(��O-5�³&�+5ÿÿ�ZÿìB&�H��O�ú���³+&�+5ÿÿ�5þœ¶&�(���Q��ÿÿ�Zþ(Bs&�H���QÁ�ÿÿ�5��Ïs&�(��L�-R�³&�+5ÿÿ�Zÿì•!&�H��Ló��³,&�+5ÿÿ�{ÿìms&�*��K�¼R�³$&�+5ÿÿÿþ¦!&�J��KF��³E&�+5ÿÿ�{ÿìm}&�*��N�œR�³ &�+5ÿÿÿþ¢+&�J��N��³A&�+5ÿÿ�{ÿìmf&�*��OÇR�³'&�+5ÿÿÿþ¢&�J��O#���³H&�+5ÿÿ�{þ;mÍ&�*���9��ÿÿÿþ¢!&�J��:w��³C&�+5ÿÿ�5��¤s&�+��K�¸R�³&�+5ÿÿ�%��®ª&�K��K�N‰�³ &�+5��5��-¶���3@� LYL  �?3?39/]+�9/39103#!!!#737!!7!7!{²)´áþΆþ)†þÏâ±)³(2)×)1þV%þ)%ôÇûÓwý‰-ÇÂÂÂýô´���%��`� �<@!   KYGY�  � �?3?9/]+�9/3+�3910!!654#"!#737!!!36632ÑþÓ}j]—+VþÓþ¢+ #-!++þÕ&*>•dŒ•PD3{çÊþo¬Ç¡¡Ç¤}N\¨™Of�ÿÿÿÅ��`&�,��RÿR�³&�+5ÿÿ�%��|&�ó��Rþû���³ &�+5ÿÿÿÅ��²þ&�,��Mÿ™R�³&�+5ÿÿ�%��¬&�ó��Mþÿ���³&�+5ÿÿÿÅ��Ø}&�,��Nÿ]R�³&�+5ÿÿ�%��S+&�ó��NþØ���³&�+5ÿÿÿÅþ¶&�,���Q�¨��ÿÿÿ´þ&�L���Q`�ÿÿÿÅ��f&�,��O�sR�³&�+5��%��?^�� ³�??10!!!RþÓí-^�ÿÿÿÅþR¶�&�,����-��ÿÿ�%þï�&�L����M`��ÿÿþ¾þRµs&�-��KÿUR�³&�+5ÿÿþúþD!&7��Kþä���³&�+5ÿÿ�5þ;¤¶&�.���9�´��ÿÿ�%þ;ð&�N���9{���%��ð^��@ � �?3?3910!!!!3˜Xþþ°·xJþÓí-G ^þýš¤Hþ¤^þÁx*ÿÿ�5��Ás&�/���vÿ±R�³&�+5ÿÿ�%��­¬&�O���vÿ‹�³ &�+5ÿÿ�5þ;œ¶&�/���9B�ÿÿÿþ;œ&�O���9ÿ"��ÿÿ�5��¦·&�/��8!ÿ£�²�?5�ÿÿ�%��N&�O���8�É��ÿÿ�5��¶&�/���Ouýpÿÿ�%��®�&�O���O!ý8�����ž¶� �'@   �� LY��?+�?939103'7!7!9g:fÝ’/kŽjþÉV�6áÊw²þN˧þhÿ������Ù� �@ � ��??939107!'7!/@jçŠþ×cjyÊs…' ½îþþ¿þþ‡ ”'·þÀ»“–���ZÿìÛs�#�0�9�K@)� 4KY4 44� 1JY $GY +GY�FY��?+�?+�?+�?+�9/_^]+�9910"&'#"&54$32632!#3267"3254&%"32654®t¸.?¨{»ÝвÑhšõ±Æþ²þË3f`WŽe]¹ýhGpEIImŒJˆX›-›¬MDDMêÈØI´  £Ž»Ë[i&0ã.(‘yè~[b Ê`evaS_ÿÿ�5��²s&�5���v�¢R�³&�+5ÿÿ�%��ø!&�U���vè��³&�+5ÿÿ�5þ;¬¶&�5���9�°��ÿÿÿ¡þ;ªs&�U���9ÿ&��ÿÿ�5��äs&�5��L�BR�³!&�+5ÿÿ�%��,!&�U��LŠ��³&�+5ÿÿ�)ÿìxs&�6���v�hR�³,&�+5ÿÿ�ÿìè!&�V���vØ��³+&�+5ÿÿ�)ÿìds&�6��K�R�³*&�+5ÿÿ�ÿìÔ!&�V��Kÿt���³)&�+5ÿÿ�)þVË&�6����zd��ÿÿ�þ s&�V����z/��ÿÿ�)ÿì“s&�6��LÿñR�³.&�+5ÿÿ�ÿì"!&�V��L€��³-&�+5ÿÿ�þ;Ѷ&�7���9�ÿÿ�8þ;oL&�W���9½�ÿÿ�¨��Ñs&�7��L�R�³&�+5ÿÿ�^ÿì‡(&�W���8���¦��Ѷ��$@�  LY �?+�3?9/333103#!#73!!!îë7ê}þÏ}á7ßLþ 7ò7þŸRþý¬Tþbþþ���ÿìoL� �9@ KY @FY�GY�?+�?+�33Í9/3+�310%27#"&5477#737#?3!!3#Aao›– !…)…)˜Ä„Â12þæ)é)ëß#á5~„2>”ÆÁ“TìîåÁÆ”Jÿÿ�ÿìš`&�8��R�ÍR�³&�+5ÿÿ�oÿì²&�X��R1��³ &�+5ÿÿ�ÿìšþ&�8��M�ÙR�³&�+5ÿÿ�oÿ첬&�X��M9��³&�+5ÿÿ�ÿìš}&�8��N�¢R�³&�+5ÿÿ�oÿì²+&�X��N��³&�+5ÿÿ�ÿìš&�8��P� R� ´&�+55ÿÿ�oÿì²²&�X��P��� ´&�+55ÿÿ�ÿìûs&�8��S?R� ´&&�+55ÿÿ�oÿìR!&�X��S�–��� ´(&�+55ÿÿ�þš¶&�8���Qm��ÿÿ�oþ²^&�X���Q´��ÿÿ�¸��çs&�:��KR�³%&�+5ÿÿ�}��¶!&�Z��K�É���³%&�+5ÿÿ�º��?s&�<��K�R�³&�+5ÿÿÿsþ‘!&�\��K©��³&�+5ÿÿ�º��?V&�<���j�#R� ´&�+55ÿÿÿÃ��°s&�=���v�jR�³&�+5ÿÿÿÑ�� !&�]���vù��³&�+5ÿÿÿÃ��°f&�=��OR�³&�+5ÿÿÿÑ��Ç&�]��O�…���³&�+5ÿÿÿÃ��°s&�=��L� R�³&�+5ÿÿÿÑ��2!&�]��L��³&�+5�ÿ þÁ��@ GY ��GY��?+�?+10"'53276632&#"HhF=6ˆ$*ïkPE@9EþìMþòªþÀ­1àPAúëþ��ÿáþ…Ë��/@ FY�GY��GY��?+�?+�9/+�9910"'5327#?6632&#"3#hF=6ˆ$º¨½)ðƒhPE@9F Û1ÜÄMþòªq•PT¾¯1àPA>åübþ��ÿ…��)ª���&�.�,@*LY** '`$$��?3?3Ì9Î]29/+10!!!&54632%667!#4&#"326!&'=M›þ×þ+ªþÀÛˆonþÃ$…*V:ÄT× 7**600*7 +5®C ¶pDúþJþ¶B0Bn€‚Çy/ 1p&þÛ-33--33þöojþª5U¥���ZÿìѪ� ��!�4�B�Q@/¿� @ H)@-3").):Pc�× ²� ��@    �/3Ì]210#"&546324&#"326 ŒrnЉon7**600*7Çk…om€‚k-33--44��ÿTþ�ò���� ³ ��/?3103327#"&5467òwH ,:TPfw_x`h2°kXRŠM��;×��!@� � � o€ �/Ì]99//3310".#"#3232673b1K@;!1±;Þ1MB:"0¬BÙ!'!835!' 37þË�Ù¼!���´ €�/3Í21067!#%67!#ru1+Û`²ru1+Û`²ò€¯5½E€¯5½E��5Ùç^��±�/Í1067!#5LJx†´ø˜ÎƧ�Vø‡´����¶ �/333Ì910667!#4632#"%4632#"…IZ”þÑMD<=GJy'MD<=GJy‡*¸K¤NP93IWnNP93IW�ÿÿÿ…��‹õ&�$��Tþÿ—� ·2>�+5�ÿÿ�ƒ)å}���jDÿÿ�"��/õ�'�(�“��Týíÿ—� ·2>�+5�ÿÿ�"��7õ�'�+�“��Týíÿ—� ·2>�+5�ÿÿ�"��ƒõ�'�,��Týíÿ—� ·2>�+5�ÿÿ�>ÿìõ�&�2w�Tþ ÿ—� ·2 >�+5�ÿÿ�"��võ�'�<7��Týíÿ—� · 2 >�+5�ÿÿ�>��'õ�'v�š��Tþ ÿ—� ·#2##>�+5�ÿÿ�Gÿìx´&†��Uþñ��� µ.&�+555ÿÿÿ…��‹¼�$��ÿÿ�5��ã¶�%����5��˜¶��·LY�?+�?10!!!!bþÓ5.6þ�¶þ�ÿÿÿË��b¼(��ÿÿ�5��œ¶�(��ÿÿÿÃ��°¶�=��ÿÿ�5��¤¶�+����{ÿì°Í����.@LY  LYLY�?+�?+�9/_^]+10!7%# �$3 �%"32654&5þD5\ºþ¥çþôþÓÉ_ä"ýÅ„Ët’}ˆÆj‹fþþ!þòþVã3 ²áþÍ1¨þÁÁŽ¥©8ÅŽ§�ÿÿÿÅ��¶�,��ÿÿ�5��¤¶�.���ÿ…��R¼� �µ��?2?910#!!&5{ìV‹þÙ; /*þT¼úDDqáOWüÿÿ�5��¶�0��ÿÿ�5��¶�1���ÿÕ��h¶��� �.@�LY � �� LY LY �?+�?+�9/_^]+10!!!!!œ8ýeb75üÉ“5ü{3wþ=þüHÿ���ÿÿ�{ÿì˜Í�2����5��š¶��@ LY�?+�?310!!!!!dþÏ�þ3ÿ�þÏ50´ûL¶ÿÿ�5��¦¶�3���ÿÕ��ž¶� �*@�LY �� LY��?+�9?+�99910#77!!!+1þ¾1Š6ýÑ>þ T5òúáéþþþ)ÿ�ÿÿ�¨��Ѷ�7��ÿÿ�º��?¶�<����{ÿìHË��� �<@! MY�  @   MYP`��?Í]+�99?Í_^]+�99107.5�%7!�6654&'!-1“ÙwtY%%èüþ…þ®1d°¹vpþã¨Àvoß tב? ´´ûÓþáþ»áРǦm ©p€ÿÿÿ‹��y¶�;����²��Ó¶��%@MYO �??339/]3+�310#!#"&547!33!3267!Ç\þã_Þù^'eop¤¤“—"f)om²þN²Ï¸Yl¸þ!D1bRüøŠ›ãýúþ�ÿÃ��Í� �!@�LY LY �?3+�3?+10"!!&&54$3 �!!$4&To±oLO;ýž5uewºH×�#ØÉl7ýw;ƒ†Ë~ù™ÏKþì?õ—ßeºþìôöþ—bþüã©‘š�ÿÿÿÅ��ÀV&�,���jÿzR� ´&�+55ÿÿ�º��?V&�<���j�#R� ´&�+55ÿÿ�ZÿìÙ^&~��TD��³7&�+5ÿÿ�'ÿì/^&‚��Tý��³.&�+5ÿÿ�%þm^&„��TB��³&�+5ÿÿ�`ÿìÿ^&†��Tÿ���³&�+5ÿÿ�wÿì“´&’��U �� µ1&�+555��ZÿìÙs�!�/�+@�)GY HY"GY�?3++�?+�?99106673327#"'##"&546322654&#"w;þ-]C=$?fÉ-Q‘\‘ †ò–Y}3þŸMtC@AGyHAÇ)WSñxþÀA î¥_FÕÀÔ[ÃN^ý VP`ˆáxc\�ÿ¾þÏ��+�0@$%%$GY%% ��GY�� GY �?+�?+�?9/+�9102#"&'!66"32654&##732654&#Ì๧}à—OˆLrþÐY.ýÐSf®h6q‚ic/3;n|Jº¤°Æ¬…•ïw!*ýÝcÛÍîx|üà#—~\\òˆ|DK��;þ^��· �??33310!7!3>!}þ¾3„Œ'-- CýÙbjþùPþÆþ°¥)i_>ü µþî���7ÿìÏ��'�$@"�� FY�GY�?+�?+�93310&54632&#"�!"&542654'—ðÌpÅcw‚CP.héþÈþíÊíøáe‡f~£Mž~ž¦¿5<×f@802U»úþñþÀÓ¶Ì#ý}¾”“Q+ÖƒU]���'ÿì/s�&�-@%%JY%% FY FY �?+�?+�9/+�910#"3267#"&5%5&&54632&&#"33ú”xŠZV\§^ òÎîHPVöÝõž\\‹Hahad‡ÝMD8=)1öO›‹5qGž®VÞ.$@=40��Zþ…}�!�)@" FY��?+�99/_^]33104%##7!�!6654&'&&Z}öNfî2 'þçþÖˆV\ja1WcþÅwM8Q›Š¶œ Ò ß¶âþ×ö|Lk*2ObB[Ÿ\wu-"4%FÇ��%þms��@ GY �??+�??910!654#"!33632hþÓòl\–+bþÓíæ’ÑŠ—þyD3{äÌþ1^Ïä§›Ik��dÿì‹+����&@FY� FY �FY��?+�?+�9/+10"&546632�'267!"!654&ìÀÈ …­?3AþÜùþ×^ý˜a…$þ›ýéâ���\þ…9�0�9@1�00�JY00'!%$%$FY%��?+�99/_^]9/+�93310#"!6654&'&&5%5&&5467##7!#"33PF³ÌFlxW-WcþÅqO;J˜‹k`k›¨¹:51ñ-†ÕjusfÁsDU4[Ÿ\qz.&3"Fº=^rPp”* ßÒ@xSPTÿÿ�ZÿìTs�R����^ÿìÝ^��"@ FY�GY�?+�?+�33?10%27#"&546!!#77!#–ABd”ƒŒ rþî¼þοðÄž/ðlÛ!Ù7€yF3ü‡y“Råýã(H��ÿ¾þhs���"@ GYGY�?+�?+�?910#"'!>32"326654&h‚ðš£_ HþÕä.†ÖÃÜþNWj"71dEuLBÁÒþ´·|R°þ®:ØÞoæ£þøXømXb���Zþ…òs� �!@!  GY�?+�/_^]3310!6654&'&&54$32&#"‹Ndja1WcþÅqO>Gˆ£ ©©—\~bTˆOçPj-2ObB[Ÿ\qz.(7 GÆ”¾K¬Hå9pÀ��Zÿì;^���@ FY GY�?+�?+�310#"&54$3!!%"3254'JW•σÉé 1ÏA/þä%5þ•i›R–t’)Nmò¨[ìÐÓ;¨ß-ÊxÕ„ÍÃof��^ÿì%^��@  FY�GY�?+�?+�310%27#"&547!77!!…Aao›” sþå¾ì1þ²sß#á5}…2>“RåýåJ���wÿì“^��@ � GY��?+�?310"&547!3 !ÉÔ+5-P›ñƶFP`ý˜=<œ}þµþaþÿ‡���ZþÇ��#�(@ FY �""�GY"�?+�99?+�?Æ10%>54#"&&5476632Df–Q^Y(ýÛi½ß¬ÁÀ…uTDn/̯¸Î©þÅÏ`ãŒÝƒ²°û3âøÆËO °‡ømxÒ¼æÈÑþ¼¹ þ&�þüþìs��(@�HY� HY�?+�?+�??9102!327#"&'!&&#"'63|@LýÂJ 01-:ab~ƒ)þþ¸5 **"'4esŒ£·Ñüúþ1:;î!‹ ý¼}NMAî'���wþ�� @�  GY �?3+�3??3?106!!&&547!“þäĵ-œþ¾ûcþàbÑÛ+[WúÅ ±¶þ`þ-òþ1ÏƪFP`ý˜='4&##!!!!32¤uNPDIL% ?FéþÏ�þ 7è8þª9ò²Æ4b &þ'L©F =6ý^´þþþð®œ'Bþé Lÿÿ�5��˜s&a���v�}R�³&�+5��{ÿì7Í��.@LY  �LY LY�?+�?+�9/_^]+10"!!!27#"�$32&&‰’Û95ýñ’À»ÕþþæÐ\âzÌhvH™Ë¿¬þþËMþüM+¹å18ú)8ÿÿ�)ÿìVË�6��ÿÿÿÅ��¶�,��ÿÿÿÅ��ÀV&�,���jÿzR� ´&�+55ÿÿþ¾þR²¶�-���ÿÃÿì?¶��$�0@�$LY��LY LYLY�?+�?+�?+�9/+1032�!!# #"'53266!32654&##DíüþÄþÖþJþþO¤“µ–P?5/ATPiGLÿ�w‰_am…ÎÁóþý´þÀýþþïuþ?Œ'ÀûHyuJQ���5��¶���)@ LY  LY�?+�??39/3+�310�!!!!!!!3232654##þÇþÜþ@‰þmŠþÏ52w‘y1wVæòýI}…Àmúôþú‡ýy¶ýÏ1ýÏÌþEyn¢��¨��h¶��#@ LY   LY �?+�3?39/+104&##!!!!32!67JKÙþÏ�þ 7è8þª9áÃÄTþËd 'D7ý^´þþþð´ >pþ^×2ÿÿ�5��s&´���v�ÛR�³&�+5ÿÿ�ÿì…‘&½��6�/R�³&�+5��5þVš¶� �@ � U LY �?3+�??310!!!!!!j4ÿ�Ì�0þÊþ\þÑ\þ¶ûL´úJþVª�ÿÿÿ…��‹¼�$����5�� ¶� ��&@ LY  LYLY�?+�?+�9/+102!!!!2654##dîüþÄþÕþN566ýøA%‹’ÁlT…ÑÈíÿ¶þþÍýyujªþw�ÿÿ�5��ã¶�%��ÿÿ�5��˜¶a���ÿ;þV¶� ��$@ ULY� �LY �?+�33?+�?31036�!3!!!!5«yþÿ�º‘þÑ\üð]þÓ ×Ó¨Ê÷NoûLýTªþV^ý˜þ¶²�ÿÿ�5��œ¶�(���ÿƒ��–¶��!@ �� �?33?339310!!!!!!îþé/ ’%’Hýº'þÄþî˜þÜ—ýÕþª ªýT¬ýT¬ý-ýÏý1Íý3���)ÿì°Ë�'�-@MY $$MY$ MY �?+�?+�9/+�910!"&'32654&##73 54&#"'6632°Ûŧþ¾þÍ‹ÏN¶Ùª¬š¥3_^ª¾mû—{Æmw©Ã ¨ƒàò$+cys]WòÈHR{Ñ[GXœ��5��¶��@    �?3?39910!!7'!!¦ürþËþì^d#ýþ‰5“A¢úJ¾ØûÙ¶ýLþæF�ÿÿ�5��‘&²��6�áR�³&�+5��5��¶� �@  �?3?393310!!!!!`þ®þð˜þÏ52’XýºÏý1¶ýT¬ý-�ÿÃÿìš¶��@�LY LY�?+�?+�?10 #"'53266!!#O¤“µ–P?5/ATPiG]þÈþÑ�´þÀýþþïuþ?Œ'ÀúJ´�ÿÿ�5��¶�0��ÿÿ�5��¤¶�+��ÿÿ�{ÿì˜Í�2��ÿÿ�5��š¶n��ÿÿ�5��¦¶�3��ÿÿ�{ÿì7Í�&��ÿÿ�¨��Ѷ�7����ÿì…¶��@ � �LY��?+�?39310"'32667!67!ò|WQu0J?1þã3š.XNý¦„’¬  '"AQý‰]:n'yûúášIÿÿ�{ÿìHËs��ÿÿÿ‹��y¶�;����5þVš¶� �@ U LY�?+�3??310%!!!!!hþÑ\ü/54ÿ�Ì�0þþöý`ª¶ûL´û@��¸��m¶��@ LY ��??39/+10!#"&547!327!w»¡§Â p1l ™ƒ¤‹2þÊ1Vª˜9;%ýü.$ƒJúJ���5��¶� �@ �LY��?+�3?33103!!!!!55:ÿ�}�;ÿ�}�9þ˶ûL´ûL´úJ���5þV¶��@ U LY�?+�33??3310%!!!!!!!áþÑ\ù¶5:ÿ�}�;ÿ�}�9þþöý`ª¶ûL´ûL´û@��¨��'¶� ��&@�LY�� LY  LY�?+�?+�9/+1032!!!!32654##úCðúþÄþÖþIþþ 7’ÿ�d‹’Ál…ÒÇíÿ´ûHujª��5��á¶����"@ LY  LY�?+�??39/+10!!!!!!3232654##¬þÇ59ýRþÄþÖþh52w%ðùýiE‹’ÁM¶ü6íÿ¶ýÏÒþKujª��5��R¶� ��@LY LY�?+�?9/+10!!!3232654##RþÄþÖþI52wCðúýJd‹’Álìíÿ¶ýÏÒþKujª���ÿì¦Í��.@LY   LY�LY�?+�?+�9/_^]+10%267!7!75!"'63 �#"&'…©Ú<ýå5þþA}„PÐèÆþ«ãyÁS­î·½þ83öZþÛþðþôþEå%.`��5ÿìÍÍ��#�2@ LY    LYLY�?+�?+�??_^]9/+10#"�47!!!!�32�%"32654&Ͱþ½Üñþïþì†þÏ52yPeõñ ýëk´fwjk²erªþéþDë'2,ý‰¶ýà 4þà·þÀ»“–®;É‹ž�ÿƒ��ò¶� ��$@��MY  LY �?+�?39/+�310!&&54$!!!#"33þaþ•kY3æþÊþÏw‰‡…m^l1ýÏ‘DkßúúJ1‡roO[ÿÿ�Zÿìžs�D����mÿìô#��&�$@  FYGY�?+�?9/+�910�76$%36632#"&%26654#"mãH +þÎé†ZBª`Ž›”þ÷«ÉÔºBsGs8†<ÍK#g 9(þþ-/W’tTV½³Åþ»¬öyÑk™ZRsOà��ZÿìZs�� �*�-@$$JY$$��!JY�JY�?+�?+�9/+�9102#"&54$32654&#"32654&Ç»ØyyYZuÛ”ãú™…[YU\apŸUŒ'ÉW_Bs£Šp…rTj£YéÛÊC¶ýh iuVDBA¿‚nFE05��ÿì¢s�&� @"$FYFY�?+�?+�9910632327#"&54>7>54#"¦·Ð³ÂBp”RdA‡¿^µr¸ÊFoŒEƒ:yr˜X’_{T;#$*dZã/'§–]|S6,",VK���Zÿì)��%�1@  FY GYGY�?+�?+�9/+�9310#"&546323754&#"'63 267&&#" þáÄÏáyݹ`|Œja?ºýŽj“!YGs‰L þäþJâéѺ·$Ͼ)î1ú¸àÙBWݺXc�ÿÿ�ZÿìBs�H���ÿìÿìÃs�9�=@ 7KY99'GY"-  GY2�?3+�3?3+�3??9/3+�310##"'7326654&#"56323!36�32&#"327#"&55#!+˜9þãÊM:/5TƒHHA-LLf¯Ê¦d!d9$ÊGA45}¡HA-LIi±È¬`þßÃßø ï€Þ`aôäÌÑþ/ã ïþðÏ`aôßË-þ=��ÿìüs�$�-@#$$#JY$$ FY FY�?+�?+�9/+�9102654&#"'632!"&'53 54##7ÏŒtDVE©MHàØÁÑ~†¾þáþöeÊE°º°°-°;D.4-"ÛVއhŠ$8ª´À(#øX‘uÓÿÿ�oÿì²^�X��ÿÿ�oÿìñ?&�X��6��³&�+5��%��ã^� �@  ��?3?393310!!!!‹Xþ!þ®ÿ�uþÓï.o^ýÏýÓ!ýß^ýø��ÿÅÿì¾s��@GY HY �?+�?+�?10!!&#"#"'53267>32ÛþÓ¼(CZJH#C[`Y<%/2O&fp…À‰W¾Eu?‹Ögµ‡Nøio/Òu9����3^��@ �   �?33?3910#&'!!667!!ÏR*þìè?ªþäï–; B-¬ëþÛ'>”OýúøËhüÕ^þ#TG<˜Oåû¢���%��Ë^� �@ FY   �?3?39/+10!!!!!D]]Z-îþÓ`þ¦bþÑï^þR®û¢Íþ3^ÿÿ�ZÿìTs�R��ÿÿ�%��ms�Q��ÿÿÿ¼þhs�S��ÿÿ�Zÿìòs�F��ÿÿ�%��Õs�P��ÿÿÿsþ‘^�\����ZþÁ���#�-@� #FY� GY�?+�99?3+�3??10!&&54$7!>54&'ÌÜ¢þ×Ñ`þÝhÊæ©+ÌZýVV]RË]‰Q[Odö¿ÁþÙ¦þ&äù½¿/¦ ®ûÉgˆ œjÃþ— jÁr`ÿÿÿœ��¤^�[����jþo²^��%@ "FYGY�?+�?+�??39103267!327!&'##"&5473‰l\–+b-¢>'3‰þé`a ŽÆ‰˜^ýsD3{äÌÏýAýu‘-‡È¥œD‘\��¤�� ^��@�GY ��??29/+�910!32677!!7##"&547ô->>9b--îþÓ@+€¤‡š^þÝD2?@ÅÔû¢/´¬–O†���jÿì^�%�%@%  !!GY�?3+�3??3399103267!#7##"'##"&547!3267¦‰c]”+c-îå‘ÑÛ+‘Õ…’…-‰b\’/`^ýsF1{æÊÏû¢Ïãã㦛Ik}ýsD3{ÝÑÑ��jþo^�.�/@. $"FY**GY�?3+�3?+�??3399103267!327!&'##"'##"&547!3267¦‰c]”+c-¢=)1‰þéag‘ÃÛ+‘Õ…’…-‰b\’/`^ýsF1{æÊÏýAýu‘/‰Ìã㦛Ik}ýsD3{ÝÑÑ��\ÿìò^���&@JY FY JY �?+�?+�9/+10632#"&547!732654#"cX¦½}ï«ÃÌXþ 1êLCVg–FZ^þ:¥Šx¶e¦¡Fn’åüß=BYI{ N���jÿìJ^��� �"@JY JY �?+�??39/+10632# 47"32654!!5bbi‹®sܘþz‡%9J>4IY²þÓî-^þ:¤…y¸hEWfpý}C5=>]Iwþ%^���hÿì^���@JY  JY �?+�?9/+10632#"&547!"32654ÓVƒ¦¼|ï«Ã͇-FZNBVf˜¦‰yµe§ Aswý}g%>AZH{���ÿìƒs��&@KY��GY� GY�?+�?+�9/+102#"'53267!7!754&#"'6éÃבþ쿘h4xNqŒ!þŒ+dWQ9]9P¤sê×Õþ¾¯7ü'u{Ë[c ÝR��%ÿìTs�� �*@ FY  GYGY�?+�?+�??9/+10#"&55#!!36$32"326654T‡ÿ¯ÂØÏbþÑï0]Á9ÇÂÖþNDnD‰@nB¸Óþº³âÊ5þ3^þRÖíê ‚èy¹}ß{Å��ÿìÿìƒ^��#�.@�!!�KY!!JY HY�?+�?+�?9/+�910"#"'53267767&5463!!"33‡9R.Bšo[<(%*5@_ÿäÓëþÒ[O]FM]; Odtø-"3mAª²Éû¢ çWI7Cÿÿ�ZÿìB&�H���jÖ�� ´5&�+55��%þ`�*�H@($ &'&KY�'' ) GY�    $)�$GY�?+�??9/_^]+�9/3+�3910!!36632#"'53267654#"!#737!{++þ×&,>•dŒ•’)É£hF=5=]Žj\•,XþÓü + #-sÇ»fN\¨™OfýLÀ·òQYœD3{ãÎþo¬Ç¡�ÿÿ�ÿì!&Í���v��³/&�+5��Zÿìòs��&@KY� GY�GY��?+�?+�9/+10"&54$32&&#"!!3267úÈØ— ¯¶’\6hB[‹"o+þ™®L‚E˜ØÅÜZ´Hå"srËÁ/#öOÿÿ�ÿì s�V��ÿÿ�%���L��ÿÿ�%��7&�ó���jþñ��� ´&�+55ÿÿþøþ�M���ÿÅÿì{s�(�5�:@!)JY &&GY&HY 1JY �?+�?+�?+�9/_^]+10632#"&547'"#"&'532677>32"32654?ln¥½|ï¬ÃÌZ-CYGDHgŒe3;!%/3M#+QŽÌÎEZLCVg˜¥Šyµe¦¡Ik’@‡Ùè®[ øgp‡ûÒ\:ý¢[#=BYI{���1ÿì{^��$�-@JYFY JY �?+�??39/+�Ä+10632#"&547!!!!!"32654?tf¥½|ï¬ÃÌþÚcþÑð/\'Z-EZLCVg˜¥Šyµe¦¡=]þ3^þR®ý}[#=BYI{ÿÿ�%��`�é��ÿÿ�%��ã!&Ô���v����³&�+5ÿÿÿsþ‘?&�\��6›��³&�+5��jþ²^���$@� GY�??+�??399103!3267!#7##"&547T1`þçLÛ‰l\–+b-íæ‘Ò‰˜)þ=YñýsD3{äÌÏû¢Ï㥜D‘\���5��×ì��@ LY�?+�?Æ10!!!…Buþ�ÿ�þÓ5¶6ýÌûH¶��%��5��@ GY�?+�?Æ10!!!!ÁþI¸þÓíÍ@füš^1�ÿÿ�¸��çs&�:���C%R�³ &�+5ÿÿ�}��¶!&�Z���Cs��³ &�+5ÿÿ�¸��çs&�:���v×R�³'&�+5ÿÿ�}��¶!&�Z���v;���³'&�+5ÿÿ�¸��çV&�:���j}R� ´2&�+55ÿÿ�}��¶&�Z���j�¼��� ´2&�+55ÿÿ�º��?s&�<���Cÿ»R�³ &�+5ÿÿÿsþ‘!&�\���CÿM���³&�+5��)´®š��±��/3107!)1T1´ææ���)´…š��±��/3107!)1+1´ææ�ÿÿ�)´…š��ÿÿÿ,þ2·ÿÓ�'�B��‹�Bÿæÿv�0²�¸ÿÀ³45H¸ÿÀ³./H¸ÿÀ@ %)HÐÀ�]5]+++55��sÁZ¶�� ³�€�?Í10'673{gŸá[gÁãüÕþà���jÁR¶�� ³€�?Î10#6JgŸâYj¶ãüÎ'�ÿšþø�î�� ²€��/Í10%#6ygŸáXjîãüÏ'��ßÁþ¶�� ³€�?Í10#'7ø ÑC ¶hh—Ž Ò��sÁ¶�� �µ�€ �?3Í210'673!'6739gŸâabý%gŸá[gÁãüãþîãüÕþà���jÁ¶���µ €�?3Î210#6!#66JgŸâYjÛgŸá/}¶ãüÎ'ãü5tL�ÿšþøB�î���´ €��/2Í210%#6!#66ygŸáXjÝ d£á2yîãüÏ'Ýþþ9{B���¾�� � �'@ � ��??9/333993310%!7!%Ùþɘþæ�þÎ4'"C üB¼ñŸþa��;��!��E@!�        ��??99//3339933333993310%%!7777!%%XD4þÉþã…þ¼47Zþ½37…D3þÈ/òþ‰wòãÓñvþŠñÓ��h®¶)� �@ ?Ÿ�/]Í1046632#"&hU›ix}·u…®j°a†|±È†ÿÿ�ÿåÝ9�&����'�1����b����sÿî uË� ���&�4�@�N�9@5520LLL?�";;+E�?333?3??9/]39/]33310"3254#"&54632%#"3254#"&54632"326654#"&54632?_8A^öp¾z‹oº{‰•ÝûÅð@'I.7B^öo»x‘j¾|‰–ûë'H.7)H/õl¼y‘j¾|‰•úþÿ•``m¦þà’§™© ‹úJ¶üüpÈ]a`d§þÚ“²!•4pÄaanÇa`d§þÛ”±#”ÿÿ�ɦ%¶� ��ÿÿ�ɦº¶�����H�Z˜��@ 0 �/]39/]310H‘¿þé‡öÊ9Ï›þ²þ¢gÍ�����HPö��-@�Oo¯Ïï�/]39/393310'7Pþn¾ˆöËþ1›N^gþ3ÿÿ�ÿ庶�&�����3���ýì��¶�� ³�??10#ûÄï?¶úJ¶���…üXÇ��@ �  ð  �?3ÄÄ]q2910654#"#3632'TA<\=Í”ªd}adXü‘ "J‹|þêºyŠo^-#þR�����º¶��;@RY�� NY  NY �?+�?9/+�9/_^]3+�310!!!#73!!!!ß$þ÷5þÓ7%Ù-5þ�HÞ8þ#¸²þú²þþþ°þ��ÿô��ÙÍ�#�V@0"#"RY ##/#Ÿ# ##RY OYNY�?+�9?+�9/3+�39/_^]3+�31076632&#"!!!!!!7667#737#7s)ó¿Ã®q`Hc+'þ×)%þÕ+™‘7ü1emÀ%ÀÁ'¶ƒÆÎVèDM_q°s²˜Eþüö!n\²s°��9ÿìÁ¶���.�F@$%&@#& )&)QY&& �NY NYQY�?+�?+�?9/+�9/+�33Í1032654&##�!#!!2267#"&547#?33#ü3ž`^D+þ·þßRoþá8=àõ+&@+h{‚’>oœ{¬3ò.ñB.“„KP•þþãýø¶ÔûéÓ5t/VjsáíÑþÍ$(���)ÿìÝÍ�&�J@(RY @ RY �OY NY�?+�?+�9/3+�3Ì_^]2+�310"!!!!327# #7367#736�32&&ƒaž8\'þ˜ +%þèYg…–~ÌþB˜% }'‡WGÓdžV}WXՕа"Q²x{?þþ?õ²C0°ü.9ß1��{ÿì¼Ç���'�3�%@��(%% /�?3?3??9/39/310"&5432&#"3267##"&54632"32654ª€Ê¨mb7U;2M+63!=@`•ûÄï?Ê®}’Ì­þâ+E(*&?Tð“}Ç�)ž#P‚A@#$))&€ �¯Ïï@ H�/+]q333}/33Ì222}/3310"56323267#"&'&&"5632327#"&'&&V3zïþþJ��q��hÁ�� �@� �??933310#3hþ>sþ>Âsºóôôßý!ßâýšþfþg�ÿÿÿ%þt�&�I����Lç��ÿÿÿ%þƒ�&�I����Oç���=Ùé?�� ³ ��/3Í210#"&547!3267é"€Ô˜ÈÖ ªck?…•L™ž‹Xb��þúþD^� �· �GY��?+�?10"'53267!XhF=6=\þ-þ÷MþòPZªû)þ��îÍ…�� ³€��?Í1067!#îS)…@´ç²{9µE��{þ;ÿƒ� � ² €�/Í10667!#{Q!}E´þV3Á9>©M��ÕÙm!� � ² €�/Í10!56673mWþåCµ(Ê;;®J��b5%Ë� ��µ�!�?3?310"3254#"&54632ö?_7B^öiÁ~‚™i»€‰–úþÿ•``d±þé™ŸŽ· ’ž��/J-¼� ��!@  �?3?9/]33310##7!7!3!77w î!þ‹Ûywþœ: <µá——®-ýÍçJLÑ��^95¶��@p�€��� !�?3?39/]3102#"'532654#"'!!6éqÚ³¡]3|9Ycƒ?FLw)þž+$…k›»6¶#NFnC¢¸‹ ���Z9#É��$�!@�p€!�?3?39/]310632#"&54632&#"2654&#"LPtiyÆ¡š—Š÷ª$f+9Wm–I=K3*=H/-X|m›È¬˜­Ž¶wpþ¸[G07MI1B���{Jm¶�� ´� �?3?10!7!{Ïþj'’þ/J´¸•ý)��R5'Ë��"�,�@ ( #� !�?3?29102#"&5467&&54632654&"654&‹˜dYAOÆ«Ž©xm39°=5,4<+##1@^*ËpaXi $bIƒ’i^x&#W>nŠýí,]*2?+,8Œ2(8*)K"&���Z9É��#�!@ !�?3?39/]310#"'53267#"&54632"32654&†ûªY9\ z›&ZshyÆ “ŸþÊ:K2*(A+B ��BA>=21 , 84 95!�/333333333/3339333333333333339/333/39/3/339/393/310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3#"&546323254#"%32##32654&##32654#"'53253T/ÀÎ0mù�oÀÃmýIûáþò·mmmmûÂü0ooÀwú¨ooooþmmûŸ‡‡‡~ˆþs‡‡‡‡á¬mp.,;0m^Ï{B.$*/;J1%Z^4+V}i¾0oÁÁoþÐÁù/ÂmmÂþÑmmmmþooú¨ú;mm¦Jooooü/yýhI‘œœ‘’›š“ÅÅÄaCS1D D8QYb" "ãš+%Jþú fV’þr_c���TþÁª���*�1@ (("""��//99//3333/993310 54676654&#"63232654&#"þ¬üTüVë,AgI»¥OºGR Z?>1HT;GFBIHCHEüVüW©û/2A1R~X‡š8*²P:/5K6DpJ;þí?HI>@IH�ÿÿþúþŠ!&7��Lþè���³&�+5ÿÿ�jÁR¶����uÿì´)� �7�H@'6)06770FY�0 00FY0&FY�?+�?+�9/_^]+�9/39910654&#"#"&547654#"563232.546323ìˆ|H?BA6ºþó±³Î9+-tmfo#|z°;£í|ðÌÌÖ•1 Ye2;y\þþâþ¯Ÿš†E]M9×-e[IvS,f m¶r»Ûþöø%#å��¸��HÃ��@ �� MY�?+�??933107632&#"!!qCÑ„SO8&;@ˆ÷uþÑwì8f{ÃçP¨þNýÛ/‡���^ÿì¨^��+�1@ ##�  FY )�FY��?2+�3?+�339/910"&547!77!##"&'4'!32677!32)¦¶YIþïÄi/ôì‘lˆB¢èýpESdr†Ó¾€i“RåXoÆþ­­aim]I0þçÛ¬o‹‰‰U2s+�ÿÿ�5��u&�0���v¼T�³&�+5ÿÿ�%��Õ!&�P���vÍ���³/&�+5ÿÿÿ…ý¨‹¼&�$���[^��ÿÿ�Zý¨žs&�D���[B��ÿÿþîÿìüÍ�&�2d��\þˆ���ÿšý¨ÿƒ� ��µ �/39/310#"&546324&#"326Œrp‡‡pn7**600*7þ˜k…ml‚k-33--44��fh3É���¶ €�?ÝÄÆ1067!#%467#"&ž=Rx„™þÈ–¢EO869O‹y´«ˆl^tJ)&  &,<��ÿ%þ-�8�<�G�F@'@FIY@�:#GY� *66FY;' 2�GY-��?2+�3?3+�33?3+�3??+10"'53267#?6632&#"!76632&#"3#!"'5327!!!4632#"-hF=6=\Ì£·)ðƒhPE@9F ‹)ðƒhOEA9E Û1Û×Mþ¹hF=6ˆ$Íþt×MÙþÓí-þ`WILX\˜þòPZÅ‘TT¾¯1àPA>T¾¯1àPA>åüþòªÅüþì^WY>:Pc�ÿ%þ;�8�<�?@!;�:#GY� *66FY '2�GY-��?2+�3?33+�33?3+�3??10"'53267#?6632&#"!76632&#"3#!"'5327!!!-hF=6=\Ì£·)ðƒhPE@9F ‹)ðƒhOEA9E Û1Û×Mþ¹hF=6ˆ$Íþt×MÙþÓI-þòPZÅ‘TT¾¯1àPA>T¾¯1àPA>åüþòªÅüþì���{ÿì��#�'@ @  LY LY�?+�?+�99Î10# �$3 67!%"32654&˜Æþ¨àÿ�þáÆ\ä2Šp / /»—ýÐyÊs…yyÇp‚ªþåþAä' ½îÏJÌŸÑ:TÍ·þÀ»“–®>ÆŽ›���ZÿìÙ��"�'@�@ ��GY�GY�?+�?+�99Î102667!#"&54$4#"3266¬ÛsDT' 0¹ªþö·Ãé.KwK–KxCs’Š€´Å<3NÓþº³ëÃÕM·þEÅzì}¹{Û��ÿì`�� @  @ LY �?+�?3Î9/310667!�!"&547!3267š'LT/ <þîÈy9þ¾þþÔóÄ1½{ ¶¸}Ìá ýÂþöþòãÂHB›üiJ3²™˜•���jÿì‰� �5@ @ �     GY�??+�?39/_^]39Î103267!>7!#7##"&5473‰l\–+b-3D+/ :þðÌ®æ‘Ò‰˜^ýsD3{äÌÏp >ajÉâüÏÏ㥜D‘\ÿÿüïÙþ™!��Cúó��ÿÿý¯ÙÿÜ!��vûÌ��ÿÿü€×ÿÆ�RûE���ý¤ÃÿR¤��µ �/ÉÌ2910#76654&#"5632®Å¶NA 3JIez†ì¦AZH{���5ÿìjÍ�$�9@LY  �LY LY�?+�?+�??9/_^]3+�310"!!3267#"�7#!!3�!2&¼‹Û75ýð‹|Jƒ»ÚþþëƆþÏ52y¼R zÌhv¨˲ þ#3–Ÿ -þüM+ Vý‰¶ýÃ;18úa���%ÿìs�%�1@ FY  GY� GY��?+�?+�??9/3+�310"&547#!!36$32&&#"!!3267ÈØËbþÑï0]¿B#µ’\5hBP…'g0þš]Q={[šØÅ& þ1^þRÕîHå#mbá`a#/öO�ÿ…��ß¼� ��%@ MY �??339/+�3910!!#!!!#7&'Ñþç‡/þ·þ¾)u¼þæD15|wý‰¼úDwíó~{ö��ÿƒ��;^� ��%@ JY   �??339/+�3910!#!#!&'¼þê9%Pþña)êþÍ‘¿ “^û¢¦þZ¦þZ^þ¼eþß��5��é¼���5@ MY LY � �??3?9/+�Å+�3910!#!#!!!!!!3&'ÏD1þè‡/þ¶þ¾Zþú…þ×5)x^?u¼ýµÓ*wý‰wý‰wý‰¶ýÃCúDdÚ—c���%��H^���-@   FY  �?3?39/3+�33910#!#!!!!!!!'òVþòhþþÌþøcþòí\\þj½þéZtÍþ3Íþ3Íþ3^þR®û¢°ææ��ÿª���¶���4@ MY�LY�?+�99?339/+�33310#6677!!&&##!#"!VüD¬ß þHyq !þÕ/.}þʃ7W(ÃFþk‘ŒÙ‰þ$¸®þDÇ`Tý…{O_þ3qC��ÿš��Á^���2@KY �JY�?+�99?339/33+�310#6677!#&&'!7!f¸=ŠzÄÜþžPR%þ! \þõg/8 źþÝs}mhhþˆŸþÁ3I@þ=Ç88þ¬¨ç��5�� ¶��%�8@%LY� LY�?+�9?39/33+�3310!!#"!67!!!!7!!&&##7þmþ˃ 9P)ÆþÂÕ2;þö†þÏ52yüÑ þG^]2 !þÕ./qCûL{M]þ/Éo?ý‰¶ýô‰þ&[‘xþDÇ`T��%��¾^��"�8@ FY � " "JY �?+�9?39/33+�3310!67!!!!7!#&&'!7!˜£+2þþcþòí\¶°ÛþžSP%þ#]þög/8 Ä»þÝLX)þ3^þRFhhþ ŒšþÁ3HAþ=Ç88þ¬¨ç�ÿ°þ/°ð�L�N@(# M>8;GB B8@+,,+MY,,882MYJ8MY#�?+�?3+�9/+�9Ý_^]2Ä93310327632&&#"&54667>54&##73 54&#"'667&'53>32&#"°ÛÅ’¤wÿéƒ^)wGXW7zJ8þ‹¥½`ÔÝy’B—¨3_^ª¾mV±n2f¦k/P`b:<*"&%_Q‚›w©Ã ¨ƒ„¼t)J)嘅vY!:TF[SòÈHR{Ñ7Mh”p>YK'• CW!¯�ÿ¤þ/)d�L�P@+C**M /  #KLLKJYLL= JY =0FY=#�?+�?+�39/+�9Ô_^]2Ä933102654&#"'67&'53>32&#"32632&&#"#"&5467>54&##7ÕmDVE©MHu)T¦k/Q_e7>(%#$N>Ë~†¾wêèpUG>EADBL4V¥]¤¦Ïߌs4XX°-°;D.4-"Û4P{p>ZI'– 1>?½hŠ$8ªv—X -++! 囈—¬#;24:Ó�ÿÿ�²��Ó¶u��ÿÿ�wþ•����{ÿì˜Í� ���&@LY LY LY�?+�?+�9/+10# �$32�267!"!74&˜Æþ¨àÿ�þáÆ\äÿýÔ4ý{‡1xÌ=y‚ªþåþAä' ½îþàüCÉ´8“–Û·¥3‹ž���ZÿìTs� ���&@JY GY GY�?+�?+�9/+10#"&54$32267!"!54&Tþö·ÃéµÄäýËM|"þ{PÃK{$L¸Óþº³ëÃÕM·ìýT|r[bª{ndi���¸��ÁÃ��@  LY�?+�??910667>32&#"!!Jæ\v‹_RF4-@Z3ýòþ²'31:¹"á¿•HøZcûü¶ü°bQD>��f��¦f��@  GY�?+�??91067>32&#"!!Ç <‰Bf{TK8*&#=-þjþ½Š'05Y~‰};î0Rý^ý‡u;�ÿÿ�¸��Ás&€��vòR� ´+&�+55ÿÿ�f��¦!&��vL��� ´(&�+55ÿÿ�{þ XÍ�&�2����\Ç��ÿÿ�Zþçs�&�R����\V����{ÿƒÏ1��-�-@, ,LY "$&&LY�?+�3333/33+�3310#"&5&54$766322654&'#"'6Ï£þÚÄ BAwux@þõž§H??>GVª3X3i¤¶ÓÞ´þ»Ö}¨>V¥iàù¨:Òaž4…$DHc®jÍW»[)OmDvºmÂ"("3;œ¡&-&þÇp \1* "-2Qý·)#ÙiþòþÎþ8óNKVC(­ë;,Ù0³þ¹¾þÕpqXW2ÀXÉ���ZÿìTR�,�@�R�g@EDD P8-..4�88¸ÿÀ@& H8844; ;  ! FY& FY��?3+�3?3+�399Ä_^]23/+]9/3Ä9/310"'#"&54632&#"32673254&#"'632#".#"#6632356654&'&&5432'¶i‡ ¾Éö¥ŽfVvvw@þö§ŸI?}HVnná×ÑU©>Í)zÝŒß?NÚl`)Í>ÝÑÖþ¨«íÂ"("4;œ¢&-&þÇw‡\0* "^R��ÿìðB�5�C�U@0;??CC/CCŸC¯C C8LLZ-%3))"00ZZgg  ��/3Ê2/3Ê29/39/333Ê2229/333Ê22233Ê222102#&&#"#62#&&#"#662#&&#"#66!2#&&#"#662#&&#"#66!2#&&#"#662#&&#"#6!2#&&#"#66é]qO�0@ #037;(++;3# �/3/3933333310#6736673#&'5&&'5'766'677'&&&'7BF$a5‹ÑIa4‹¼GÈAÝûZB¿OÝìE±xbC¾ûE±xb›˜C{LbR×C‚&b'Z1B¿OݦGÈAÜ‚þ!Ia5‹ÑF$a5‹ÝDnXb'XúüDnXbYîFÆcbŒûxF2Ã4bEÂ���5þVH‹��$�+@�$!@� LY  U�??3+�?3Þ2Í29910!3!!!#7'!#"&547!3267j“A ürý1þÈþ¦ñ^d#ýþ‰ª"€Ô˜ÈÖ ªdl¶ýLþæFûTýLª¾ØûÙ‹…•L™ž/‹[_���jþoò?��+�/@+#(@ "GY�FY��?+�?+�??39Þ2Í210!7##"&547!3267!3!#"&547!3267͎Ɖ˜-‰l\–+b-¾üþïþÑÙi2ÿÞÇÖ ªck´È¥œD‘\ýsD3{äÌÏüý‘?¤š‹Xb��5��R¶���3@LY  LY LY�?+�?9/+�9/3+�310!!#737!3#3232654##RþÄþÖþIâ–7’ 2!ø8õ!CðúýJd‹’Álìíÿ!þ——þœÒþKujª���sÿì��%�=@ �KYJY� � !JY �?+�?9/_^]+�9/+�9910!!632#"&547#737!"32654m/)þÏeX¦½}ï«ÃÌŒž) /-JFZLCVf5Æþ)¥Šx¶e¦¡FnˆÆßûÇ N=BZH{���5��¦¶���4@  LY  LY �?+�?9/+�999910'##!!2327'7654##¦zt>®DgtVmþÏ5Ròøýd8+)4ª16ÃJ ÙBRýø¶Õþ%qRi?_´��ÿ¼þhs��'�;@ #" �  ��GY� GY �?+�??+�?999999102'"&'#!336"33'76654-’©œ‚7®=4Sx3 HþÓVæ Š:F|NE0 7ª14:s×ÁÎþ‹]R„JYŸˆþ¬Jª¿ô‚ñ}ZVyRhKÖl°�����˜¶� �$@LY�  LY �?+�?9/3+�310!!!#73!!58þË}þÓ—7•.6þ�Rþý¬Tþdþ��ÿô��ö^� �$@ FY  GY �?+�?9/3+�310!!!!#73ö5þI/1þúXþÓZ‹1‹b^øÙëþ^¢ëÑ���5þ�¦¶��'@ �LY LYLY�?+�?+�?9/+10"!!!632#"'3254&VI6uþÓ5.6þ�VPX—ès§þؾ©z£È‘/ ýݶþþgü¨ßþ˜¹13 Ó¥«���%þ ö^��'@GYGY HY�?+�?+�?9/+10%#"'32654&#"!!!632Õ’þö´•R`}œYW/#PþÓíä5þI53P³Ê¸ÑþÀ+6ؼqzþ^ø÷ô�ÿƒþV–¶��-@  � U��MY��?+�33??339310!!!!!!3!;þî˜þÜ—ýÕþªkþé/ ’%’HýºÄÑþÑ\Ïý1Íý3 ªýT¬ýT¬ý-þý`ª��ÿìþoÃs�9�C@$"&&KY $$"5�5GY �FY).GY)�?+�/+�?3+�3???9/3+�31023!36�32&#"327!&&55#!##"'7326654&#"569¯Ê¦d!d9$ÊGA45}¡NERP‰þé\†”¬`þß`˜9þãÊM:/5TƒHHA-LLsäÌÑþ/ã ïþðÏ`a!ýo‡Ö¯-þ=Ãßø ï€Þ`aô�ÿÿ�)þ°Ë&±���¶��ÿÿ�þüs&Ñ���q����5þV¶��$@  U�MY�?+�???393310%!!#!!!�#þÑ]³þð˜þÏ52’Xýºöý`ªÏý1¶ýT¬ý-���%þoã^��$@ �  "GY�?+�???393310!3!#!!‹Xþ!˜õ‰þê`‹ÿ�uþÓï.o^ýÏþÍýu‘!ýß^ýø���5��œ¶��@    �?3?3910!!#!!73!`þ»{VyoþÏ52’’KìXý›NTýö¶ýT¹fý/��Z��^��@ �?3?3910!!!737!!#'‡þÓð-oVD…»Pþ"þ¶}#‡N/^ýødE_³þºm ó ` $Àì-ð®þµ½¡x³µ”·ýL}fÞ¯b��Zÿ¸s�+�7�\@,2" ))2KY�))Ð)à)ð) )¸ÿÀ@ H)) JY @GY"FY�?+�?+�Ì+�9/+_^]+�99910327#"'#"&54$32&#"33&&546326654&#"yZ">8FU‹k`}ÈØ™ ´A/D3{àÎþ/D3{äÌþ1^Ïä�ÿÿ�º��?¶�<����fþ^� �·� �??3/331067!!!É'3)CýžhþÈi'05reRû¢þì^ýˆPa���L��?¶��-@ LY�    ��  �??393/]3+�310!!!!!!7!svVý–'5þ×:þÑ9þä5ì8fPüyþþþò‡��ÿÃþ^��@ FY�  �??3?33+�31067!!!!!7!!Å$:)Cýž1þî7þÈ8þþ1'-1mnRû¢åþùå^ý¶9ª��ÿ‹þVy¶��#@ U� MY��?+�???3910!!!!!!¬þtþª@î@™kXýß²þÑ\ýáüºýúý+þý`ª��ÿœþo¤^��#@ � " GY �?+�???3910!!!!#!{áAsôbþ-… ‰þê^ }þøþ¢=!þ²NýÏþÍýu‘Vþª���¨þVo¶��&@U LY   LY�?+�3?3+�3?10!!!!!!!B”þÑ\ü/þþ 7Ó5þ¼ÆÌ�0þ ýLª´þþüN´ûT���\þo1^�#�2@!"� ! FY !FYGY�?+�?+�?3+�3?9103267!327!&'##"&547#7!Xfa—+b-¢>'3‰þé`a ŽÆ‰˜Pú11yþXD9uæÊÏýAýu‘-‡È¡’Z‡yåå��¸þV‡¶��"@LY� U�MY��?+�??39/+10!#"&547!327!!!w»¡§Â p1l ™ƒ¤‹2þýþÑ\1Vª˜9;%ýü.$ƒJû@ý`ª���¤þoÉ^��)@ GY� "�GY��?+�??39/+�910!7##"&5477!32677!3!…@+€¤‡š3->>9b--¹âŠþê`/´¬–O†ñþÝD2?@ÅÔüœýu‘���¸��m¶��-@LY�??39/+�999910!!##"&547!367!7þÏwfLF‘A§Â p1l }E’DR^‹21.þ¼/ª˜9;%ýü.$v HþÄ,���¤��²^��+@GY �??39/+�9999910736677!!7##7"&5477!>H1‰/E\#-íþÓ?+>I6‰/™¨3^þÝD2^åÙ#ßû¢/´D$úÕ¨šO†ñ��5��é¶��@ LY� �?3?9/+10!632!654#"!j2w»¡§Á pþÎm ™ƒ¤Œþ϶ýÏVª—5@ýÛ.$ƒJýq���%��!^��@ GY  �?3?9/+10!!654&#"!!3632ÑþÓ=>9b-þÓí-?+€¥‡š#F1?@ÅÕ^þÑ´¬•D‘���=ÿì;Í�#�-�4@'� '' MY''$LYLY�?+�?+�9/+�99Ä10�!2!#327# �7&&54733"32654&²X€ÈÙþvþ~j‘¢Å±éþúþ㦬Jü5--×€Ù0\ÛÛEd'BƳòø§¢KþþM&VqubQ='/gÉžvr;D��ÿìs�#�,�4@'''KY''�$JY�FY��?+�?+�9/+�33Ä10"&55$5473336�32!#3267"32654éÒèþå:Õ+)! <=Ù±Æþ²þÊ3g`WŽe¬6Xœ-›­âÎ)ÏdR@G*.磎»Ë[i&0ãV® saS_��=þV;Í� �0�;@ .MY')U�LY*''"LY'�?+�3?+�?9/+�99Ä10"32654&47333�!2!#327!&57&&€Ù0\ÛÛEûzJü5-- ^~ ÈÙþvþ~j‘¢Å†¸VþÑ^§±¦¬ËÉžvr;Dþ¦ubQ='/,=Ƴòø§¢Kþþ= þf°2ÓV���þos��/�;@)% KY " �JY !FY�?+�3?+�?9/+�33Ä10"326546�32!#3267!&&55$54733Xœ-›­ýX<=Ù±Æþ²þÊ3g`WŽe… Pþé\ƒþå:Õ+)!š saS_þï磎»Ë[i&0ãCþ“'Ò¡)ÏdR@G*.ÿÿÿÅ��¶�,��ÿÿÿƒ��–‘&°��6`R�³&�+5ÿÿÿìÿìÃ?&Ð��6y���³>&�+5��5þ�¶��'@ �MY LY�?+�??39/+�310"!!!#"'325sSMmþÏ52’XýÕv¯b¦þÝĨ{z”£È!ýø¶ýT¬ýR‰á‘Ùþœ»13 ÓB���%þ ã^��'@KY� HY �?+�??39/+�310!#"'32654&#"!!‹Xþ-žþõ·‹]b{‰ |oF;NþÓí-n^ýêÛ·ÁþÕ¤+6ȵt„þ–^ýø�ÿÃþV˶��$@LYULY LY �?+�?+�??+10!!!! #"'53266šÿ�1þÈþ¦þô�þðO¤“µ–P?5/ATPiG¶ûTýLª´þÀýþþïuþ?Œ'À��ÿÅþos�!�&@"�FYGYHY�?+�?+�?+�?10%3!#&#"#"'53267>32 þþðþÑÙô¼(CZJH#C[`Y<%/2O&fp…À‰W¾Eßý‘u?‹Ögµ‡Nøio/Òu9��5þ�¤¶��,@ LY    LY�  �??3?+�9/_^]+10"'3267!!!!!�ª{}“‡ 'kþ)†þÏ52y×y1þ×=þÛþ�12²»úý‰¶ýÃ=úƒþßþè��%þ Ë^��"@FY HY�?+�??39/+10!!#"'3267!!D]]Z-î5÷Ô›grobx"Xþ¦bþÑï^þR®û¢ùý:<˜¦þ3^��5þV×¶��,@LY   � LY�U�??2+�?39/_^]+10!!!!!!!!=†þ)†þÏ52y×y1þ1þÉþ¦wý‰¶ýÃ=ûTýLª���%þo ^��%@FY� "� FY��?+�???39/+10!!!!!!3!°`þ¦bþÑï0]]Z-¿þþðþÑÙÍþ3^þR®üý‘���¸þVm¶��"@LY� U�MY��?+�??39/+10!!!#"&547!327![þÑ”D»¡§Â p1l ™ƒ¤‹2þÊþV ;Vª˜9;%ýü.$ƒJúJ���¤þo ^��)@GY " GY �?+�??39/+�91032677!!!37667##"&5477!>>9b--îþæTþé˜ó €¤‡š3^þÝD2?@ÅÔû¢þo‹7-u¬–O†ñ��5þVH¶��#@  LY�U�??33+�?3910!#!!3!!!#7#j= 6þí5”D'§ý1þÈþ¦õ‘:/ýÇmHþÓPýX¶û»EûTýLª´ ­û“���þou^��$@    "�FY��?+�??3?3910!#&'!!67!3!#¬R*þìè?ªþäï–; X1¬¼þþïþÑÙ'>”OýúøËhüÕ^þ#TGËXåüý‘ÿÿÿÅ��¶�,��ÿÿÿ…��^‘&�$��6�uR�³&�+5ÿÿ�Zÿìý?&�D��6��³%&�+5ÿÿÿ…��·V&�$���j�qR� ´#&�+55ÿÿ�Zÿìž&�D���j�� ´4&�+55ÿÿÿ…��o¶�ˆ��ÿÿ�ZÿìÇs�¨��ÿÿ�5��‘&�(��6�'R�³&�+5ÿÿ�Zÿìâ?&�H��6ù��³&&�+5��DÿìÍ��"�&@MY �LY LY �?+�?+�9/+10"63 �#"&54$!3654&267#"¶m´S³æ "Ëþ¢ßÔè–ŠkŽþ½‹Ó@šÁÎNÉ, NþØþêþÿþFèÄ´÷ó)”¦ü%´²wný`ª��%þoô^� �@"GY�GY��?+�?+�?103!!3!%íâ3þIƒá‰þê`^øý”ýu‘ÿÿ�5��áV&Å���jRR� ´*&�+55ÿÿ�jÿìJ&å���j�Ï��� ´4&�+55��þ˜¶��7@ LY  LY LY�MY��?+�?+�?+�9/3+�310"'532677!#73!!!!!ìiH7K4Æ®¥ú„1ûì#9;fqÉ‹}KH��Zÿì�#�1�,@�  � +GY $$GY �?3+�3?+�?99/10%#"&546323466!32677!#"&'%26654&#")‹Ïº»‹åŠª` S-ð @3KN51C-ß½s­,þäIwFCIExKL–ªÛȾh¾¤K[‡û–2$8=cùþÁÐÁVTI‡í|P`‚íXX���ÅÿìqË�,�0@MY**%MY* LY�?+�?+�9/+�9/103267!#"&54654&##732654&#"'6!2D·¨ntzBPT6c,éÒÁÎ mlæ/ž¤²QOŒ£rÑ»ásÎ)n}t`wþ-ÐÁ®¡&M-OUÛws?NiÓÀ��‰ÿìs�+�0@)JY ##FY# GY �?+�?+�9/+�9/103267!#"&554&##732654&#"'632‡A2IM=1C+åϽÏ\}‘-ƒpIQF±JHÞÔÁÑ{ƒ¸s)65ZiþÁÌÅ¢“)ODÓ:E6,.!ÛVއjˆ$"��ƒþVÍË�%�0@MY�$U MY�!MY��?+�?+�?9/+�910!654##73 54&#"'6632!!DOÛæ4~^^ª¾mû—{ÆmÛň þÑ\m?-¨ðÈHR{Ñ[GXœ`©Ã ’wO€ý`ª���²þos�$�0@JY�#"FY� GY��?+�?+�?9/+�910!7654&##732654&#"'6323!L7^^°-}ŒtDVE©MHàØÁÑ€„¸á‰þé`úDH;Ó;D.4-"ÛVއmˆ!3˜%Xýu‘��ÿÃÿìd¶�&�!@ &LY&  LY�?3+�3?+�/103267!#"&547! #"'53266š×<;ETT5b+êÌÅÙ—þðO¤“µ–P?5/ATPiG¶ü<"3?fqþ-Îñ >qÈþÀýþþïuþ?Œ'À�ÿÅÿì¸s�/�"@*GY* GY"HY �?3++�?+�/1032677!#"&547&#"#"'53267>32+@3KN51C-çÎÄÓ b(CZJH#C[`Y<%/2O&fp…À‰W¾E‰ T8=cùþÁϯž6;Ë?‹Ögµ‡Nøio/Òu9ýq2���5ÿìo¶��.@ LY  LY�?+�??39/_^]+�/103267!#"&5467!!!!¤×<;ETT6c+êÌÀÚ!þ)†þÏ52y×y¶ü<"3?fqþ-Îô¤-k›ý‰¶ýÃ=��%ÿì¼^��&@FY HY�?+�??39/+�/10!!32677!#"&5477!!D]]Z-’ C6CQ51C-çÍÂÖ þ¤bþÑï^þR®ýV4&68`wùþÁÏ®Ÿ6;#þ3^��{ÿì¢Í��&@�LY�� LY LY�?+�?+�9/+10!�! �$32&&#"!267!ò°#Dþ°þÏþïþÒØûî²seJ”퉉°!þ“5¶þ¢þË0²êcû6"©þÊ·þ½¥ ���Zÿì¶s��&@�FY�� GY GY�?+�?+�9/+10!�!"&54$32&#"3267!7 8þÛþþáü¥2ÍmÂDm‚ЧÈh[i‡þü–žþõþÿçÕÓG±2&æHÿÔalkj���¨ÿì9¶��@��LY LY�?+�?+�3/10!!3267!#"&547¨7þ7þ‘Ÿ<;ETT5b+êÌÅÙ™´þþý<"3?fqþ-Îñ >qÈ��^ÿìð^��@FY GY �?+�?+�3/1032677!#"&547!7!!b@3KN52D+êÌÁÕTþº/½1þ´` T8=cùþÁÎñ¢Etååþ17���DÿìüË�'�-@# MY ��LY�LY�?+�?+�9/+�9102&&#"33#"3267!"$54675&54$#Ü~A±fjzŽ“‹3{ãˉ{]ÑOºþøîþðØÜò,Ë>Pá/@^N[Sòur\N5+þôVȲ°Ê HãÁÛÿÿ�'ÿì/s‚���ÿÃþͶ�"�+@LY"LY"LY MY�?+�?+�?+�?+10!#"'532677!! #"'53266šø+X'»¥iH732'M'½¤hH7q,-#%ƒ5ª%A6=)óx)ÑËA<>$^,o/#yx1 '-ó þj`)‰<MRz<�ýÙÿÅÁ���· € �/3Ü2Î910#"&54733275673;"ÑŸ’ª«Bþº?eÙ G+¤®’‰˜ªN?’ ‹0�ýÙÿÅÁ���· € �/3Ü2Î910#"&5473327&'53;"ÑŸ’ª«Bí~5Ó8+¤®’‰˜ªNsbji�ýÙÿÅ��!�@  € �/3Ü2Ì299310#"&5473327'#76654#"5632;"ÑŸ’ª«B5CD h 63-* *†uQ5T28%#' 5¨)>5%0™‚v t8I{Lpm.(ß��ÿFþ�ø���� ³� �/?310254&'3#"'5Jm1-´–ƒ\=:þËf5a9vˆr|´��ÿ`þð/� � ³ ��?2210"'53267!hH732!´ü8(mÃ…<5A”^l‘Y¶Å³ÏúËú„ŒJ/7JKÈWN*®œÉn}��ÿ¯þ©ñˆ�'�-@QY %%OY%& OY %�?+�?+�9/+�910#"'32654!#732654&#"'6632ñ½§ƒ‡”þì½ï”TÄ]žªþþŠ.I§ÀVN†™|ÙŠ¾Ø:œÔ¡y…ÒuO 23q¬Ý|nCJdÌQAµ��ÿÉþ¨)s� ��'@PY %�??9/3+�93910%#!!7!3!667#åªMþÙPý²1ö9ºªþ1 &?D=þÊþsëmü—–¯ÐpEþ ��ÿãþ©0s��&@�PY��NY OY%�?+�?+�9/+102#"&'32654&#"'!!67·ÔþîÁuË=®²š®smfohÏó7þXHWθ§ú‡.# cŽ{^^!NÝþúþÛÿÿ�ZÿìƒÍ�����þ¿tu��@ �$NY�?+�9?10!!áý‹8Ï)ýþ¿²Âû �ÿÿ�?ÿìnÍ�����Eþ®3��'�-@!PYOY& OY%�?+�?+�9/+�910#"'32667##"&54632"326654&3Æþ¶î€poty­s*o¸£³’ö£ËÓþAAe7KDT¾¯1àPA>åüþòªÅüþ��žÝÑÁ�"�3�>@1%%+*#�  /#((*5-*�?33/39/3/3993399310#"'532654&''&&54632&&#"##33#7#d‹unXsW-0$%2bF€q_p38<(Q.FgGˆ¦¤øŸªð¨®²dq+6'#'1eC]r4}C .!2^þìoþ^Ñþþý/˜yýïÿÿ�þѶ&�7����z\��ÿÿ�VþoL&�W����z%����þžs� �+�.@&#(#GY#�GYGY�?+�?+�?+�?9910%26654&#""'326767##"&54632373HxFFFExKL;ÿ‚¿e’!HŽS‹¢‡áŠ_Ž<9èôd߈ì|P`‚íXXý5BZcOkm^LÖ¿ÊeÃJZû‡þ/ÿÿ�þž!&‘��K��³2&�+5ÿÿ�þž+&‘��N��³.&�+5ÿÿ�þž&‘��O#���³5&�+5ÿÿ�þž!&‘��:{��³0&�+5��5��ž¶�� ³��??103!581þȶúJ�ÿÿ�5��žs&–���CþõR�³&�+5ÿÿ�5��Ës&–���vÿ»R�³ &�+5ÿÿ�5��©s&–��KÿIR�³ &�+5ÿÿ�5��—V&–���jÿQR� ´&�+55ÿÿ�5��Ð`&–��RÿOR�³ &�+5ÿÿ�5��~þ&–��MÿeR�³&�+5ÿÿ�5��¯}&–��Nÿ4R�³&�+5ÿÿÿ¸þž¶&–���Qd�ÿÿ�5��åf&–��O�XR�³ &�+5ÿÿ�5þRE¶�&–����-“��ÿÿ�"��/õ�'–�‘��Týíÿ—� ·2>�+5�ÿÿ�5��ž¶–��ÿÿ�5��—V&–���jÿQR� ´&�+55ÿÿ�5��ž¶–��ÿÿ�5��—V&–���jÿQR� ´&�+55ÿÿ�5��ž¶–��ÿÿ�5��ž¶–��ÿÿ�5��;ö&–��féR�³&�+5ÿÿÿñþRž¶&–���g�������¶2�I€��6$��$ÿq��7�)��9�)��:�)��<���Dÿ®��Fÿ…��Gÿ…��Hÿ…��JÿÃ��PÿÃ��QÿÃ��Rÿ…��SÿÃ��Tÿ…��UÿÃ��VÿÃ��XÿÃ��‚ÿq��ƒÿq��„ÿq��…ÿq��†ÿq��‡ÿq��Ÿ���¢ÿ…��£ÿ®��¤ÿ®��¥ÿ®��¦ÿ®��§ÿ®��¨ÿ®��©ÿ…��ªÿ…��«ÿ…��¬ÿ…��­ÿ…��´ÿ…��µÿ…��¶ÿ…��·ÿ…��¸ÿ…��ºÿ…��»ÿÃ��¼ÿÃ��½ÿÃ��¾ÿÃ��Âÿq��Ãÿ®��Äÿq��Åÿ®��Æÿq��Çÿ®��Éÿ…��Ëÿ…��Íÿ…��Ïÿ…��Ñÿ…��Óÿ…��Õÿ…��×ÿ…��Ùÿ…��Ûÿ…��Ýÿ…��ßÿÃ��áÿÃ��ãÿÃ��åÿÃ��úÿÃ�ÿÃ�ÿÃ� ÿÃ�ÿ…�ÿ…�ÿ…�ÿ…�ÿÃ�ÿÃ�ÿÃ�!ÿÃ�$�)�&�)�+ÿÃ�-ÿÃ�/ÿÃ�1ÿÃ�3ÿÃ�5ÿÃ�6�)�8��:��Cÿq�Dÿ®�Fÿ®�Hÿ…�JÿÃ�Vÿq�_ÿq�bÿq�iÿq�yÿ®�zÿ×�{ÿ×�~ÿ®�ÿÃ�‚ÿ×�ƒÿ×�„ÿ×�‡ÿ×�‰ÿ×�Œÿ®�ŽÿÃ�ÿ®�ÿ®�“ÿ®�™ÿ®�¤ÿ…�ªÿq�®ÿ…�µÿ…�Êÿ×�Îÿq�Ïÿ…�Õÿq�Øÿ…�Ûÿ…�Þÿ…�êÿ…�íÿ…�îÿÃ�òÿq�ú�)�ü�)�þ�)����WÿÃ�Xÿq�Yÿ®�`ÿ…�bÿÃ�jÿ…�rÿq�sÿq�}ÿì�ÿ…�…ÿ…�‡ÿ…�‰ÿ…�ÿ…�²ÿ…�´ÿ…�Îÿ…�Ïÿq�Ùÿq�Úÿ×�Ûÿq�Üÿ×�Ýÿq�Þÿ×�àÿ…�âÿ×�äÿ×�ðÿ…�òÿ…�ôÿ…� ÿq� ÿ…� ÿq� ÿ…�ÿ…�ÿq�ÿ…�ÿ…�ÿ…�ÿq�ÿq�ÿ®�ÿq� ÿ®�!ÿq�"ÿ®�#ÿq�%ÿq�&ÿ®�'ÿq�(ÿ®�)ÿq�*ÿ®�+ÿq�,ÿ®�-ÿq�.ÿ®�/ÿq�0ÿ®�1ÿq�2ÿ®�3ÿq�4ÿ®�6ÿ…�8ÿ…�:ÿ…�<ÿ…�@ÿ…�Bÿ…�Dÿ…�Jÿ…�Lÿ…�Nÿ…�Rÿ…�Tÿ…�Vÿ…�Xÿ…�Zÿ…�\ÿ…�^ÿ…�`ÿ…�bÿÃ�dÿÃ�fÿÃ�hÿÃ�jÿÃ�lÿÃ�nÿÃ�o��q��s���)� �$ÿq� �7�)� �9�)� �:�)� �<�� �Dÿ®� �Fÿ…� �Gÿ…� �Hÿ…� �JÿÃ� �PÿÃ� �QÿÃ� �Rÿ…� �SÿÃ� �Tÿ…� �UÿÃ� �VÿÃ� �XÿÃ� �‚ÿq� �ƒÿq� �„ÿq� �…ÿq� �†ÿq� �‡ÿq� �Ÿ�� �¢ÿ…� �£ÿ®� �¤ÿ®� �¥ÿ®� �¦ÿ®� �§ÿ®� �¨ÿ®� �©ÿ…� �ªÿ…� �«ÿ…� �¬ÿ…� �­ÿ…� �´ÿ…� �µÿ…� �¶ÿ…� �·ÿ…� �¸ÿ…� �ºÿ…� �»ÿÃ� �¼ÿÃ� �½ÿÃ� �¾ÿÃ� �Âÿq� �Ãÿ®� �Äÿq� �Åÿ®� �Æÿq� �Çÿ®� �Éÿ…� �Ëÿ…� �Íÿ…� �Ïÿ…� �Ñÿ…� �Óÿ…� �Õÿ…� �×ÿ…� �Ùÿ…� �Ûÿ…� �Ýÿ…� �ßÿÃ� �áÿÃ� �ãÿÃ� �åÿÃ� �úÿÃ� ÿÃ� ÿÃ�  ÿÃ� ÿ…� ÿ…� ÿ…� ÿ…� ÿÃ� ÿÃ� ÿÃ� !ÿÃ� $�)� &�)� +ÿÃ� -ÿÃ� /ÿÃ� 1ÿÃ� 3ÿÃ� 5ÿÃ� 6�)� 8�� :�� Cÿq� Dÿ®� Fÿ®� Hÿ…� JÿÃ� Vÿq� _ÿq� bÿq� iÿq� yÿ®� zÿ×� {ÿ×� ~ÿ®� ÿÃ� ‚ÿ×� ƒÿ×� „ÿ×� ‡ÿ×� ‰ÿ×� Œÿ®� ŽÿÃ� ÿ®� ÿ®� “ÿ®� ™ÿ®� ¤ÿ…� ªÿq� ®ÿ…� µÿ…� Êÿ×� Îÿq� Ïÿ…� Õÿq� Øÿ…� Ûÿ…� Þÿ…� êÿ…� íÿ…� îÿÃ� òÿq� ú�)� ü�)� þ�)� ��� WÿÃ� Xÿq� Yÿ®� `ÿ…� bÿÃ� jÿ…� rÿq� sÿq� }ÿì� ÿ…� …ÿ…� ‡ÿ…� ‰ÿ…� ÿ…� ²ÿ…� ´ÿ…� Îÿ…� Ïÿq� Ùÿq� Úÿ×� Ûÿq� Üÿ×� Ýÿq� Þÿ×� àÿ…� âÿ×� äÿ×� ðÿ…� òÿ…� ôÿ…�  ÿq�  ÿ…�  ÿq�  ÿ…� ÿ…� ÿq� ÿ…� ÿ…� ÿ…� ÿq� ÿq� ÿ®� ÿq�  ÿ®� !ÿq� "ÿ®� #ÿq� %ÿq� &ÿ®� 'ÿq� (ÿ®� )ÿq� *ÿ®� +ÿq� ,ÿ®� -ÿq� .ÿ®� /ÿq� 0ÿ®� 1ÿq� 2ÿ®� 3ÿq� 4ÿ®� 6ÿ…� 8ÿ…� :ÿ…� <ÿ…� @ÿ…� Bÿ…� Dÿ…� Jÿ…� Lÿ…� Nÿ…� Rÿ…� Tÿ…� Vÿ…� Xÿ…� Zÿ…� \ÿ…� ^ÿ…� `ÿ…� bÿÃ� dÿÃ� fÿÃ� hÿÃ� jÿÃ� lÿÃ� nÿÃ� o�� q�� s�� �)� �-�¸��&ÿš��*ÿš��2ÿš��4ÿš��7ÿq��8ÿ×��9ÿ…��:ÿ…��<ÿ…��‰ÿš��”ÿš��•ÿš��–ÿš��—ÿš��˜ÿš��šÿš��›ÿ×��œÿ×��ÿ×��žÿ×��Ÿÿ…��Èÿš��Êÿš��Ìÿš��Îÿš��Þÿš��àÿš��âÿš��äÿš�ÿš�ÿš�ÿš�ÿš�$ÿq�&ÿq�*ÿ×�,ÿ×�.ÿ×�0ÿ×�2ÿ×�4ÿ×�6ÿ…�8ÿ…�:ÿ…�Gÿš�fÿ®�mÿ®�qÿq�rÿ…�sÿš�uÿ…�xÿ…�…ÿ×�ÿq�Ÿÿš�¦ÿq�¸ÿš�»ÿš�¼ÿq�¾ÿ®�Áÿ\�Äÿq�Üÿš�áÿ…�äÿš�úÿ…�üÿ…�þÿ…��ÿ…�Tÿ…�_ÿš�aÿ×�lÿš�|ÿ\�~ÿš�€ÿ…�‚ÿ…�„ÿš�†ÿš�ˆÿš�Šÿš�Œÿš�©ÿq�ªÿš�±ÿš�³ÿš�µÿq�¶ÿš�·ÿ…�¹ÿ…�½ÿq�¾ÿš�¿ÿ\�Àÿ…�Áÿ\�Âÿ…�Åÿ…�Çÿ…�Ôÿ\�Õÿ…�ïÿš�ñÿš�óÿš�ýÿ\�þÿ…� ÿ…�ÿš�ÿ…�ÿš�ÿš�ÿq�ÿš�Iÿš�Kÿš�Mÿš�Oÿš�Qÿš�Sÿš�Uÿš�Wÿš�Yÿš�[ÿš�]ÿš�_ÿš�aÿ×�cÿ×�eÿ×�gÿ×�iÿ×�kÿ×�mÿ×�oÿ…�qÿ…�sÿ…�ÿq��7ÿ®�$ÿ®�&ÿ®�qÿ®�ÿ®�¦ÿ®�¼ÿ®�Äÿ®�Üÿ×�äÿ×�©ÿ®�ªÿ×�µÿ®�¶ÿ×�½ÿ®�¾ÿ×�ÿ®�ÿ×�ÿ®��&ÿš��*ÿš��2ÿš��4ÿš��7ÿq��8ÿ×��9ÿ…��:ÿ…��<ÿ…��‰ÿš��”ÿš��•ÿš��–ÿš��—ÿš��˜ÿš��šÿš��›ÿ×��œÿ×��ÿ×��žÿ×��Ÿÿ…��Èÿš��Êÿš��Ìÿš��Îÿš��Þÿš��àÿš��âÿš��äÿš�ÿš�ÿš�ÿš�ÿš�$ÿq�&ÿq�*ÿ×�,ÿ×�.ÿ×�0ÿ×�2ÿ×�4ÿ×�6ÿ…�8ÿ…�:ÿ…�Gÿš�fÿ®�mÿ®�qÿq�rÿ…�sÿš�uÿ…�xÿ…�…ÿ×�ÿq�Ÿÿš�¦ÿq�¸ÿš�»ÿš�¼ÿq�¾ÿ®�Áÿ\�Äÿq�Üÿš�áÿ…�äÿš�úÿ…�üÿ…�þÿ…��ÿ…�Tÿ…�_ÿš�aÿ×�lÿš�|ÿ\�~ÿš�€ÿ…�‚ÿ…�„ÿš�†ÿš�ˆÿš�Šÿš�Œÿš�©ÿq�ªÿš�±ÿš�³ÿš�µÿq�¶ÿš�·ÿ…�¹ÿ…�½ÿq�¾ÿš�¿ÿ\�Àÿ…�Áÿ\�Âÿ…�Åÿ…�Çÿ…�Ôÿ\�Õÿ…�ïÿš�ñÿš�óÿš�ýÿ\�þÿ…� ÿ…�ÿš�ÿ…�ÿš�ÿš�ÿq�ÿš�Iÿš�Kÿš�Mÿš�Oÿš�Qÿš�Sÿš�Uÿš�Wÿš�Yÿš�[ÿš�]ÿš�_ÿš�aÿ×�cÿ×�eÿ×�gÿ×�iÿ×�kÿ×�mÿ×�oÿ…�qÿ…�sÿ…�ÿq�$�ÿq�$� ÿq�$�&ÿ×�$�*ÿ×�$�- �$�2ÿ×�$�4ÿ×�$�7ÿq�$�9ÿ®�$�:ÿ®�$�<ÿ…�$�‰ÿ×�$�”ÿ×�$�•ÿ×�$�–ÿ×�$�—ÿ×�$�˜ÿ×�$�šÿ×�$�Ÿÿ…�$�Èÿ×�$�Êÿ×�$�Ìÿ×�$�Îÿ×�$�Þÿ×�$�àÿ×�$�âÿ×�$�äÿ×�$ÿ×�$ÿ×�$ÿ×�$ÿ×�$$ÿq�$&ÿq�$6ÿ®�$8ÿ…�$:ÿ…�$Gÿ×�$úÿ®�$üÿ®�$þÿ®�$�ÿ…�$ÿq�$ ÿq�$_ÿ×�$Iÿ×�$Kÿ×�$Mÿ×�$Oÿ×�$Qÿ×�$Sÿ×�$Uÿ×�$Wÿ×�$Yÿ×�$[ÿ×�$]ÿ×�$_ÿ×�$oÿ…�$qÿ…�$sÿ…�$ÿq�%�ÿ®�%�ÿ®�%�$ÿ×�%�7ÿÃ�%�9ÿì�%�:ÿì�%�;ÿ×�%�<ÿì�%�=ÿì�%�‚ÿ×�%�ƒÿ×�%�„ÿ×�%�…ÿ×�%�†ÿ×�%�‡ÿ×�%�Ÿÿì�%�Âÿ×�%�Äÿ×�%�Æÿ×�%$ÿÃ�%&ÿÃ�%6ÿì�%8ÿì�%:ÿì�%;ÿì�%=ÿì�%?ÿì�%Cÿ×�% ÿì�%úÿì�%üÿì�%þÿì�%�ÿì�%ÿ®�% ÿ®�%Xÿ×�%ÿ×�%ÿ×�%!ÿ×�%#ÿ×�%%ÿ×�%'ÿ×�%)ÿ×�%+ÿ×�%-ÿ×�%/ÿ×�%1ÿ×�%3ÿ×�%oÿì�%qÿì�%sÿì�%ÿÃ�&�&ÿ×�&�*ÿ×�&�2ÿ×�&�4ÿ×�&�‰ÿ×�&�”ÿ×�&�•ÿ×�&�–ÿ×�&�—ÿ×�&�˜ÿ×�&�šÿ×�&�Èÿ×�&�Êÿ×�&�Ìÿ×�&�Îÿ×�&�Þÿ×�&�àÿ×�&�âÿ×�&�äÿ×�&ÿ×�&ÿ×�&ÿ×�&ÿ×�&Gÿ×�&_ÿ×�&Iÿ×�&Kÿ×�&Mÿ×�&Oÿ×�&Qÿ×�&Sÿ×�&Uÿ×�&Wÿ×�&Yÿ×�&[ÿ×�&]ÿ×�&_ÿ×�'�ÿ®�'�ÿ®�'�$ÿ×�'�7ÿÃ�'�9ÿì�'�:ÿì�'�;ÿ×�'�<ÿì�'�=ÿì�'�‚ÿ×�'�ƒÿ×�'�„ÿ×�'�…ÿ×�'�†ÿ×�'�‡ÿ×�'�Ÿÿì�'�Âÿ×�'�Äÿ×�'�Æÿ×�'$ÿÃ�'&ÿÃ�'6ÿì�'8ÿì�':ÿì�';ÿì�'=ÿì�'?ÿì�'Cÿ×�' ÿì�'úÿì�'üÿì�'þÿì�'�ÿì�'ÿ®�' ÿ®�'Xÿ×�'ÿ×�'ÿ×�'!ÿ×�'#ÿ×�'%ÿ×�''ÿ×�')ÿ×�'+ÿ×�'-ÿ×�'/ÿ×�'1ÿ×�'3ÿ×�'oÿì�'qÿì�'sÿì�'ÿÃ�(�-�{�)�ÿ…�)�ÿ…�)�"�)�)�$ÿ×�)�‚ÿ×�)�ƒÿ×�)�„ÿ×�)�…ÿ×�)�†ÿ×�)�‡ÿ×�)�Âÿ×�)�Äÿ×�)�Æÿ×�)Cÿ×�)ÿ…�) ÿ…�)Xÿ×�)ÿ×�)ÿ×�)!ÿ×�)#ÿ×�)%ÿ×�)'ÿ×�))ÿ×�)+ÿ×�)-ÿ×�)/ÿ×�)1ÿ×�)3ÿ×�.�&ÿ×�.�*ÿ×�.�2ÿ×�.�4ÿ×�.�‰ÿ×�.�”ÿ×�.�•ÿ×�.�–ÿ×�.�—ÿ×�.�˜ÿ×�.�šÿ×�.�Èÿ×�.�Êÿ×�.�Ìÿ×�.�Îÿ×�.�Þÿ×�.�àÿ×�.�âÿ×�.�äÿ×�.ÿ×�.ÿ×�.ÿ×�.ÿ×�.Gÿ×�._ÿ×�.Iÿ×�.Kÿ×�.Mÿ×�.Oÿ×�.Qÿ×�.Sÿ×�.Uÿ×�.Wÿ×�.Yÿ×�.[ÿ×�.]ÿ×�._ÿ×�/�ÿ\�/� ÿ\�/�&ÿ×�/�*ÿ×�/�2ÿ×�/�4ÿ×�/�7ÿ×�/�8ÿì�/�9ÿ×�/�:ÿ×�/�<ÿÃ�/�‰ÿ×�/�”ÿ×�/�•ÿ×�/�–ÿ×�/�—ÿ×�/�˜ÿ×�/�šÿ×�/�›ÿì�/�œÿì�/�ÿì�/�žÿì�/�ŸÿÃ�/�Èÿ×�/�Êÿ×�/�Ìÿ×�/�Îÿ×�/�Þÿ×�/�àÿ×�/�âÿ×�/�äÿ×�/ÿ×�/ÿ×�/ÿ×�/ÿ×�/$ÿ×�/&ÿ×�/*ÿì�/,ÿì�/.ÿì�/0ÿì�/2ÿì�/4ÿì�/6ÿ×�/8ÿÃ�/:ÿÃ�/Gÿ×�/úÿ×�/üÿ×�/þÿ×�/�ÿÃ�/ÿ\�/ ÿ\�/_ÿ×�/aÿì�/Iÿ×�/Kÿ×�/Mÿ×�/Oÿ×�/Qÿ×�/Sÿ×�/Uÿ×�/Wÿ×�/Yÿ×�/[ÿ×�/]ÿ×�/_ÿ×�/aÿì�/cÿì�/eÿì�/gÿì�/iÿì�/kÿì�/mÿì�/oÿÃ�/qÿÃ�/sÿÃ�/ÿ×�2�ÿ®�2�ÿ®�2�$ÿ×�2�7ÿÃ�2�9ÿì�2�:ÿì�2�;ÿ×�2�<ÿì�2�=ÿì�2�‚ÿ×�2�ƒÿ×�2�„ÿ×�2�…ÿ×�2�†ÿ×�2�‡ÿ×�2�Ÿÿì�2�Âÿ×�2�Äÿ×�2�Æÿ×�2$ÿÃ�2&ÿÃ�26ÿì�28ÿì�2:ÿì�2;ÿì�2=ÿì�2?ÿì�2Cÿ×�2 ÿì�2úÿì�2üÿì�2þÿì�2�ÿì�2ÿ®�2 ÿ®�2Xÿ×�2ÿ×�2ÿ×�2!ÿ×�2#ÿ×�2%ÿ×�2'ÿ×�2)ÿ×�2+ÿ×�2-ÿ×�2/ÿ×�21ÿ×�23ÿ×�2oÿì�2qÿì�2sÿì�2ÿÃ�3�þö�3�þö�3�$ÿš�3�;ÿ×�3�=ÿì�3�‚ÿš�3�ƒÿš�3�„ÿš�3�…ÿš�3�†ÿš�3�‡ÿš�3�Âÿš�3�Äÿš�3�Æÿš�3;ÿì�3=ÿì�3?ÿì�3Cÿš�3þö�3 þö�3Xÿš�3ÿš�3ÿš�3!ÿš�3#ÿš�3%ÿš�3'ÿš�3)ÿš�3+ÿš�3-ÿš�3/ÿš�31ÿš�33ÿš�4�ÿ®�4�ÿ®�4�$ÿ×�4�7ÿÃ�4�9ÿì�4�:ÿì�4�;ÿ×�4�<ÿì�4�=ÿì�4�‚ÿ×�4�ƒÿ×�4�„ÿ×�4�…ÿ×�4�†ÿ×�4�‡ÿ×�4�Ÿÿì�4�Âÿ×�4�Äÿ×�4�Æÿ×�4$ÿÃ�4&ÿÃ�46ÿì�48ÿì�4:ÿì�4;ÿì�4=ÿì�4?ÿì�4Cÿ×�4 ÿì�4úÿì�4üÿì�4þÿì�4�ÿì�4ÿ®�4 ÿ®�4Xÿ×�4ÿ×�4ÿ×�4!ÿ×�4#ÿ×�4%ÿ×�4'ÿ×�4)ÿ×�4+ÿ×�4-ÿ×�4/ÿ×�41ÿ×�43ÿ×�4oÿì�4qÿì�4sÿì�4ÿÃ�7�ÿ…�7�ÿ®�7�ÿ…�7�"�)�7�$ÿq�7�&ÿ×�7�*ÿ×�7�2ÿ×�7�4ÿ×�7�7�)�7�Dÿ\�7�Fÿq�7�Gÿq�7�Hÿq�7�Jÿq�7�Pÿš�7�Qÿš�7�Rÿq�7�Sÿš�7�Tÿq�7�Uÿš�7�Vÿ…�7�Xÿš�7�Yÿ×�7�Zÿ×�7�[ÿ×�7�\ÿ×�7�]ÿ®�7�‚ÿq�7�ƒÿq�7�„ÿq�7�…ÿq�7�†ÿq�7�‡ÿq�7�‰ÿ×�7�”ÿ×�7�•ÿ×�7�–ÿ×�7�—ÿ×�7�˜ÿ×�7�šÿ×�7�¢ÿq�7�£ÿ\�7�¤ÿ\�7�¥ÿ\�7�¦ÿ\�7�§ÿ\�7�¨ÿ\�7�©ÿq�7�ªÿq�7�«ÿq�7�¬ÿq�7�­ÿq�7�´ÿq�7�µÿq�7�¶ÿq�7�·ÿq�7�¸ÿq�7�ºÿq�7�»ÿš�7�¼ÿš�7�½ÿš�7�¾ÿš�7�¿ÿ×�7�Âÿq�7�Ãÿ\�7�Äÿq�7�Åÿ\�7�Æÿq�7�Çÿ\�7�Èÿ×�7�Éÿq�7�Êÿ×�7�Ëÿq�7�Ìÿ×�7�Íÿq�7�Îÿ×�7�Ïÿq�7�Ñÿq�7�Óÿq�7�Õÿq�7�×ÿq�7�Ùÿq�7�Ûÿq�7�Ýÿq�7�Þÿ×�7�ßÿq�7�àÿ×�7�áÿq�7�âÿ×�7�ãÿq�7�äÿ×�7�åÿq�7�úÿš�7ÿš�7ÿš�7 ÿš�7ÿ×�7ÿq�7ÿ×�7ÿq�7ÿ×�7ÿq�7ÿ×�7ÿq�7ÿš�7ÿš�7ÿ…�7!ÿ…�7$�)�7&�)�7+ÿš�7-ÿš�7/ÿš�71ÿš�73ÿš�75ÿš�77ÿ×�7<ÿ®�7>ÿ®�7@ÿ®�7Cÿq�7Dÿ\�7Fÿ\�7Gÿ×�7Hÿq�7Jÿ…�7ûÿ×�7ýÿ×�7ÿ®�7ÿ®�7ÿ®�7ÿ…�7 ÿ…�7Wÿš�7Xÿq�7Yÿ\�7_ÿ×�7`ÿq�7bÿš�7ÿq�7ÿ\�7ÿq�7 ÿ\�7!ÿq�7"ÿ\�7#ÿq�7%ÿq�7&ÿ\�7'ÿq�7(ÿ\�7)ÿq�7*ÿ\�7+ÿq�7,ÿ\�7-ÿq�7.ÿ\�7/ÿq�70ÿ\�71ÿq�72ÿ\�73ÿq�74ÿ\�76ÿq�78ÿq�7:ÿq�7<ÿq�7@ÿq�7Bÿq�7Dÿq�7Iÿ×�7Jÿq�7Kÿ×�7Lÿq�7Mÿ×�7Nÿq�7Oÿ×�7Qÿ×�7Rÿq�7Sÿ×�7Tÿq�7Uÿ×�7Vÿq�7Wÿ×�7Xÿq�7Yÿ×�7Zÿq�7[ÿ×�7\ÿq�7]ÿ×�7^ÿq�7_ÿ×�7`ÿq�7bÿš�7dÿš�7fÿš�7hÿš�7jÿš�7lÿš�7nÿš�7pÿ×�7�)�8�ÿ×�8�ÿ×�8�$ÿì�8�‚ÿì�8�ƒÿì�8�„ÿì�8�…ÿì�8�†ÿì�8�‡ÿì�8�Âÿì�8�Äÿì�8�Æÿì�8Cÿì�8ÿ×�8 ÿ×�8Xÿì�8ÿì�8ÿì�8!ÿì�8#ÿì�8%ÿì�8'ÿì�8)ÿì�8+ÿì�8-ÿì�8/ÿì�81ÿì�83ÿì�9�ÿš�9�ÿš�9�"�)�9�$ÿ®�9�&ÿì�9�*ÿì�9�2ÿì�9�4ÿì�9�Dÿ×�9�Fÿ×�9�Gÿ×�9�Hÿ×�9�Jÿì�9�Pÿì�9�Qÿì�9�Rÿ×�9�Sÿì�9�Tÿ×�9�Uÿì�9�Vÿì�9�Xÿì�9�‚ÿ®�9�ƒÿ®�9�„ÿ®�9�…ÿ®�9�†ÿ®�9�‡ÿ®�9�‰ÿì�9�”ÿì�9�•ÿì�9�–ÿì�9�—ÿì�9�˜ÿì�9�šÿì�9�¢ÿ×�9�£ÿ×�9�¤ÿ×�9�¥ÿ×�9�¦ÿ×�9�§ÿ×�9�¨ÿ×�9�©ÿ×�9�ªÿ×�9�«ÿ×�9�¬ÿ×�9�­ÿ×�9�´ÿ×�9�µÿ×�9�¶ÿ×�9�·ÿ×�9�¸ÿ×�9�ºÿ×�9�»ÿì�9�¼ÿì�9�½ÿì�9�¾ÿì�9�Âÿ®�9�Ãÿ×�9�Äÿ®�9�Åÿ×�9�Æÿ®�9�Çÿ×�9�Èÿì�9�Éÿ×�9�Êÿì�9�Ëÿ×�9�Ìÿì�9�Íÿ×�9�Îÿì�9�Ïÿ×�9�Ñÿ×�9�Óÿ×�9�Õÿ×�9�×ÿ×�9�Ùÿ×�9�Ûÿ×�9�Ýÿ×�9�Þÿì�9�ßÿì�9�àÿì�9�áÿì�9�âÿì�9�ãÿì�9�äÿì�9�åÿì�9�úÿì�9ÿì�9ÿì�9 ÿì�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿì�9ÿì�9!ÿì�9+ÿì�9-ÿì�9/ÿì�91ÿì�93ÿì�95ÿì�9Cÿ®�9Dÿ×�9Fÿ×�9Gÿì�9Hÿ×�9Jÿì�9ÿš�9 ÿš�9Wÿì�9Xÿ®�9Yÿ×�9_ÿì�9`ÿ×�9bÿì�9ÿ®�9ÿ×�9ÿ®�9 ÿ×�9!ÿ®�9"ÿ×�9#ÿ®�9%ÿ®�9&ÿ×�9'ÿ®�9(ÿ×�9)ÿ®�9*ÿ×�9+ÿ®�9,ÿ×�9-ÿ®�9.ÿ×�9/ÿ®�90ÿ×�91ÿ®�92ÿ×�93ÿ®�94ÿ×�96ÿ×�98ÿ×�9:ÿ×�9<ÿ×�9@ÿ×�9Bÿ×�9Dÿ×�9Iÿì�9Jÿ×�9Kÿì�9Lÿ×�9Mÿì�9Nÿ×�9Oÿì�9Qÿì�9Rÿ×�9Sÿì�9Tÿ×�9Uÿì�9Vÿ×�9Wÿì�9Xÿ×�9Yÿì�9Zÿ×�9[ÿì�9\ÿ×�9]ÿì�9^ÿ×�9_ÿì�9`ÿ×�9bÿì�9dÿì�9fÿì�9hÿì�9jÿì�9lÿì�9nÿì�:�ÿš�:�ÿš�:�"�)�:�$ÿ®�:�&ÿì�:�*ÿì�:�2ÿì�:�4ÿì�:�Dÿ×�:�Fÿ×�:�Gÿ×�:�Hÿ×�:�Jÿì�:�Pÿì�:�Qÿì�:�Rÿ×�:�Sÿì�:�Tÿ×�:�Uÿì�:�Vÿì�:�Xÿì�:�‚ÿ®�:�ƒÿ®�:�„ÿ®�:�…ÿ®�:�†ÿ®�:�‡ÿ®�:�‰ÿì�:�”ÿì�:�•ÿì�:�–ÿì�:�—ÿì�:�˜ÿì�:�šÿì�:�¢ÿ×�:�£ÿ×�:�¤ÿ×�:�¥ÿ×�:�¦ÿ×�:�§ÿ×�:�¨ÿ×�:�©ÿ×�:�ªÿ×�:�«ÿ×�:�¬ÿ×�:�­ÿ×�:�´ÿ×�:�µÿ×�:�¶ÿ×�:�·ÿ×�:�¸ÿ×�:�ºÿ×�:�»ÿì�:�¼ÿì�:�½ÿì�:�¾ÿì�:�Âÿ®�:�Ãÿ×�:�Äÿ®�:�Åÿ×�:�Æÿ®�:�Çÿ×�:�Èÿì�:�Éÿ×�:�Êÿì�:�Ëÿ×�:�Ìÿì�:�Íÿ×�:�Îÿì�:�Ïÿ×�:�Ñÿ×�:�Óÿ×�:�Õÿ×�:�×ÿ×�:�Ùÿ×�:�Ûÿ×�:�Ýÿ×�:�Þÿì�:�ßÿì�:�àÿì�:�áÿì�:�âÿì�:�ãÿì�:�äÿì�:�åÿì�:�úÿì�:ÿì�:ÿì�: ÿì�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿì�:ÿì�:!ÿì�:+ÿì�:-ÿì�:/ÿì�:1ÿì�:3ÿì�:5ÿì�:Cÿ®�:Dÿ×�:Fÿ×�:Gÿì�:Hÿ×�:Jÿì�:ÿš�: ÿš�:Wÿì�:Xÿ®�:Yÿ×�:_ÿì�:`ÿ×�:bÿì�:ÿ®�:ÿ×�:ÿ®�: ÿ×�:!ÿ®�:"ÿ×�:#ÿ®�:%ÿ®�:&ÿ×�:'ÿ®�:(ÿ×�:)ÿ®�:*ÿ×�:+ÿ®�:,ÿ×�:-ÿ®�:.ÿ×�:/ÿ®�:0ÿ×�:1ÿ®�:2ÿ×�:3ÿ®�:4ÿ×�:6ÿ×�:8ÿ×�::ÿ×�:<ÿ×�:@ÿ×�:Bÿ×�:Dÿ×�:Iÿì�:Jÿ×�:Kÿì�:Lÿ×�:Mÿì�:Nÿ×�:Oÿì�:Qÿì�:Rÿ×�:Sÿì�:Tÿ×�:Uÿì�:Vÿ×�:Wÿì�:Xÿ×�:Yÿì�:Zÿ×�:[ÿì�:\ÿ×�:]ÿì�:^ÿ×�:_ÿì�:`ÿ×�:bÿì�:dÿì�:fÿì�:hÿì�:jÿì�:lÿì�:nÿì�;�&ÿ×�;�*ÿ×�;�2ÿ×�;�4ÿ×�;�‰ÿ×�;�”ÿ×�;�•ÿ×�;�–ÿ×�;�—ÿ×�;�˜ÿ×�;�šÿ×�;�Èÿ×�;�Êÿ×�;�Ìÿ×�;�Îÿ×�;�Þÿ×�;�àÿ×�;�âÿ×�;�äÿ×�;ÿ×�;ÿ×�;ÿ×�;ÿ×�;Gÿ×�;_ÿ×�;Iÿ×�;Kÿ×�;Mÿ×�;Oÿ×�;Qÿ×�;Sÿ×�;Uÿ×�;Wÿ×�;Yÿ×�;[ÿ×�;]ÿ×�;_ÿ×�<�ÿ…�<�ÿ…�<�"�)�<�$ÿ…�<�&ÿ×�<�*ÿ×�<�2ÿ×�<�4ÿ×�<�Dÿš�<�Fÿš�<�Gÿš�<�Hÿš�<�Jÿ×�<�PÿÃ�<�QÿÃ�<�Rÿš�<�SÿÃ�<�Tÿš�<�UÿÃ�<�Vÿ®�<�XÿÃ�<�]ÿ×�<�‚ÿ…�<�ƒÿ…�<�„ÿ…�<�…ÿ…�<�†ÿ…�<�‡ÿ…�<�‰ÿ×�<�”ÿ×�<�•ÿ×�<�–ÿ×�<�—ÿ×�<�˜ÿ×�<�šÿ×�<�¢ÿš�<�£ÿš�<�¤ÿš�<�¥ÿš�<�¦ÿš�<�§ÿš�<�¨ÿš�<�©ÿš�<�ªÿš�<�«ÿš�<�¬ÿš�<�­ÿš�<�´ÿš�<�µÿš�<�¶ÿš�<�·ÿš�<�¸ÿš�<�ºÿš�<�»ÿÃ�<�¼ÿÃ�<�½ÿÃ�<�¾ÿÃ�<�Âÿ…�<�Ãÿš�<�Äÿ…�<�Åÿš�<�Æÿ…�<�Çÿš�<�Èÿ×�<�Éÿš�<�Êÿ×�<�Ëÿš�<�Ìÿ×�<�Íÿš�<�Îÿ×�<�Ïÿš�<�Ñÿš�<�Óÿš�<�Õÿš�<�×ÿš�<�Ùÿš�<�Ûÿš�<�Ýÿš�<�Þÿ×�<�ßÿ×�<�àÿ×�<�áÿ×�<�âÿ×�<�ãÿ×�<�äÿ×�<�åÿ×�<�úÿÃ�<ÿÃ�<ÿÃ�< ÿÃ�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿÃ�<ÿÃ�<ÿ®�<!ÿ®�<+ÿÃ�<-ÿÃ�</ÿÃ�<1ÿÃ�<3ÿÃ�<5ÿÃ�<<ÿ×�<>ÿ×�<@ÿ×�<Cÿ…�<Dÿš�<Fÿš�<Gÿ×�<Hÿš�<Jÿ®�<ÿ…�< ÿ…�<WÿÃ�<Xÿ…�<Yÿš�<_ÿ×�<`ÿš�<bÿÃ�<ÿ…�<ÿš�<ÿ…�< ÿš�<!ÿ…�<"ÿš�<#ÿ…�<%ÿ…�<&ÿš�<'ÿ…�<(ÿš�<)ÿ…�<*ÿš�<+ÿ…�<,ÿš�<-ÿ…�<.ÿš�</ÿ…�<0ÿš�<1ÿ…�<2ÿš�<3ÿ…�<4ÿš�<6ÿš�<8ÿš�<:ÿš�<<ÿš�<@ÿš�<Bÿš�<Dÿš�<Iÿ×�<Jÿš�<Kÿ×�<Lÿš�<Mÿ×�<Nÿš�<Oÿ×�<Qÿ×�<Rÿš�<Sÿ×�<Tÿš�<Uÿ×�<Vÿš�<Wÿ×�<Xÿš�<Yÿ×�<Zÿš�<[ÿ×�<\ÿš�<]ÿ×�<^ÿš�<_ÿ×�<`ÿš�<bÿÃ�<dÿÃ�<fÿÃ�<hÿÃ�<jÿÃ�<lÿÃ�<nÿÃ�=�&ÿì�=�*ÿì�=�2ÿì�=�4ÿì�=�‰ÿì�=�”ÿì�=�•ÿì�=�–ÿì�=�—ÿì�=�˜ÿì�=�šÿì�=�Èÿì�=�Êÿì�=�Ìÿì�=�Îÿì�=�Þÿì�=�àÿì�=�âÿì�=�äÿì�=ÿì�=ÿì�=ÿì�=ÿì�=Gÿì�=_ÿì�=Iÿì�=Kÿì�=Mÿì�=Oÿì�=Qÿì�=Sÿì�=Uÿì�=Wÿì�=Yÿì�=[ÿì�=]ÿì�=_ÿì�>�-�¸�D�ÿì�D� ÿì�Dÿì�D ÿì�E�ÿì�E� ÿì�E�Yÿ×�E�Zÿ×�E�[ÿ×�E�\ÿ×�E�]ÿì�E�¿ÿ×�E7ÿ×�E<ÿì�E>ÿì�E@ÿì�Eûÿ×�Eýÿ×�Eÿì�E ÿì�Epÿ×�F��)�F� �)�F�)�F �)�H�ÿì�H� ÿì�H�Yÿ×�H�Zÿ×�H�[ÿ×�H�\ÿ×�H�]ÿì�H�¿ÿ×�H7ÿ×�H<ÿì�H>ÿì�H@ÿì�Hûÿ×�Hýÿ×�Hÿì�H ÿì�Hpÿ×�I��{�I� �{�I�{�I �{�K�ÿì�K� ÿì�Kÿì�K ÿì�N�Fÿ×�N�Gÿ×�N�Hÿ×�N�Rÿ×�N�Tÿ×�N�¢ÿ×�N�©ÿ×�N�ªÿ×�N�«ÿ×�N�¬ÿ×�N�­ÿ×�N�´ÿ×�N�µÿ×�N�¶ÿ×�N�·ÿ×�N�¸ÿ×�N�ºÿ×�N�Éÿ×�N�Ëÿ×�N�Íÿ×�N�Ïÿ×�N�Ñÿ×�N�Óÿ×�N�Õÿ×�N�×ÿ×�N�Ùÿ×�N�Ûÿ×�N�Ýÿ×�Nÿ×�Nÿ×�Nÿ×�Nÿ×�NHÿ×�N`ÿ×�N6ÿ×�N8ÿ×�N:ÿ×�N<ÿ×�N@ÿ×�NBÿ×�NDÿ×�NJÿ×�NLÿ×�NNÿ×�NRÿ×�NTÿ×�NVÿ×�NXÿ×�NZÿ×�N\ÿ×�N^ÿ×�N`ÿ×�P�ÿì�P� ÿì�Pÿì�P ÿì�Q�ÿì�Q� ÿì�Qÿì�Q ÿì�R�ÿì�R� ÿì�R�Yÿ×�R�Zÿ×�R�[ÿ×�R�\ÿ×�R�]ÿì�R�¿ÿ×�R7ÿ×�R<ÿì�R>ÿì�R@ÿì�Rûÿ×�Rýÿ×�Rÿì�R ÿì�Rpÿ×�S�ÿì�S� ÿì�S�Yÿ×�S�Zÿ×�S�[ÿ×�S�\ÿ×�S�]ÿì�S�¿ÿ×�S7ÿ×�S<ÿì�S>ÿì�S@ÿì�Sûÿ×�Sýÿ×�Sÿì�S ÿì�Spÿ×�U��R�U� �R�U�Dÿ×�U�Fÿ×�U�Gÿ×�U�Hÿ×�U�Jÿì�U�Rÿ×�U�Tÿ×�U�¢ÿ×�U�£ÿ×�U�¤ÿ×�U�¥ÿ×�U�¦ÿ×�U�§ÿ×�U�¨ÿ×�U�©ÿ×�U�ªÿ×�U�«ÿ×�U�¬ÿ×�U�­ÿ×�U�´ÿ×�U�µÿ×�U�¶ÿ×�U�·ÿ×�U�¸ÿ×�U�ºÿ×�U�Ãÿ×�U�Åÿ×�U�Çÿ×�U�Éÿ×�U�Ëÿ×�U�Íÿ×�U�Ïÿ×�U�Ñÿ×�U�Óÿ×�U�Õÿ×�U�×ÿ×�U�Ùÿ×�U�Ûÿ×�U�Ýÿ×�U�ßÿì�U�áÿì�U�ãÿì�U�åÿì�Uÿ×�Uÿ×�Uÿ×�Uÿ×�UDÿ×�UFÿ×�UHÿ×�U�R�U �R�UYÿ×�U`ÿ×�Uÿ×�U ÿ×�U"ÿ×�U&ÿ×�U(ÿ×�U*ÿ×�U,ÿ×�U.ÿ×�U0ÿ×�U2ÿ×�U4ÿ×�U6ÿ×�U8ÿ×�U:ÿ×�U<ÿ×�U@ÿ×�UBÿ×�UDÿ×�UJÿ×�ULÿ×�UNÿ×�URÿ×�UTÿ×�UVÿ×�UXÿ×�UZÿ×�U\ÿ×�U^ÿ×�U`ÿ×�W��)�W� �)�W�)�W �)�Y��R�Y� �R�Y�ÿ®�Y�ÿ®�Y�"�)�Y�R�Yÿ®�Y �R�Y ÿ®�Z��R�Z� �R�Z�ÿ®�Z�ÿ®�Z�"�)�Z�R�Zÿ®�Z �R�Z ÿ®�[�Fÿ×�[�Gÿ×�[�Hÿ×�[�Rÿ×�[�Tÿ×�[�¢ÿ×�[�©ÿ×�[�ªÿ×�[�«ÿ×�[�¬ÿ×�[�­ÿ×�[�´ÿ×�[�µÿ×�[�¶ÿ×�[�·ÿ×�[�¸ÿ×�[�ºÿ×�[�Éÿ×�[�Ëÿ×�[�Íÿ×�[�Ïÿ×�[�Ñÿ×�[�Óÿ×�[�Õÿ×�[�×ÿ×�[�Ùÿ×�[�Ûÿ×�[�Ýÿ×�[ÿ×�[ÿ×�[ÿ×�[ÿ×�[Hÿ×�[`ÿ×�[6ÿ×�[8ÿ×�[:ÿ×�[<ÿ×�[@ÿ×�[Bÿ×�[Dÿ×�[Jÿ×�[Lÿ×�[Nÿ×�[Rÿ×�[Tÿ×�[Vÿ×�[Xÿ×�[Zÿ×�[\ÿ×�[^ÿ×�[`ÿ×�\��R�\� �R�\�ÿ®�\�ÿ®�\�"�)�\�R�\ÿ®�\ �R�\ ÿ®�^�-�¸�‚�ÿq�‚� ÿq�‚�&ÿ×�‚�*ÿ×�‚�- �‚�2ÿ×�‚�4ÿ×�‚�7ÿq�‚�9ÿ®�‚�:ÿ®�‚�<ÿ…�‚�‰ÿ×�‚�”ÿ×�‚�•ÿ×�‚�–ÿ×�‚�—ÿ×�‚�˜ÿ×�‚�šÿ×�‚�Ÿÿ…�‚�Èÿ×�‚�Êÿ×�‚�Ìÿ×�‚�Îÿ×�‚�Þÿ×�‚�àÿ×�‚�âÿ×�‚�äÿ×�‚ÿ×�‚ÿ×�‚ÿ×�‚ÿ×�‚$ÿq�‚&ÿq�‚6ÿ®�‚8ÿ…�‚:ÿ…�‚Gÿ×�‚úÿ®�‚üÿ®�‚þÿ®�‚�ÿ…�‚ÿq�‚ ÿq�‚_ÿ×�‚Iÿ×�‚Kÿ×�‚Mÿ×�‚Oÿ×�‚Qÿ×�‚Sÿ×�‚Uÿ×�‚Wÿ×�‚Yÿ×�‚[ÿ×�‚]ÿ×�‚_ÿ×�‚oÿ…�‚qÿ…�‚sÿ…�‚ÿq�ƒ�ÿq�ƒ� ÿq�ƒ�&ÿ×�ƒ�*ÿ×�ƒ�- �ƒ�2ÿ×�ƒ�4ÿ×�ƒ�7ÿq�ƒ�9ÿ®�ƒ�:ÿ®�ƒ�<ÿ…�ƒ�‰ÿ×�ƒ�”ÿ×�ƒ�•ÿ×�ƒ�–ÿ×�ƒ�—ÿ×�ƒ�˜ÿ×�ƒ�šÿ×�ƒ�Ÿÿ…�ƒ�Èÿ×�ƒ�Êÿ×�ƒ�Ìÿ×�ƒ�Îÿ×�ƒ�Þÿ×�ƒ�àÿ×�ƒ�âÿ×�ƒ�äÿ×�ƒÿ×�ƒÿ×�ƒÿ×�ƒÿ×�ƒ$ÿq�ƒ&ÿq�ƒ6ÿ®�ƒ8ÿ…�ƒ:ÿ…�ƒGÿ×�ƒúÿ®�ƒüÿ®�ƒþÿ®�ƒ�ÿ…�ƒÿq�ƒ ÿq�ƒ_ÿ×�ƒIÿ×�ƒKÿ×�ƒMÿ×�ƒOÿ×�ƒQÿ×�ƒSÿ×�ƒUÿ×�ƒWÿ×�ƒYÿ×�ƒ[ÿ×�ƒ]ÿ×�ƒ_ÿ×�ƒoÿ…�ƒqÿ…�ƒsÿ…�ƒÿq�„�ÿq�„� ÿq�„�&ÿ×�„�*ÿ×�„�- �„�2ÿ×�„�4ÿ×�„�7ÿq�„�9ÿ®�„�:ÿ®�„�<ÿ…�„�‰ÿ×�„�”ÿ×�„�•ÿ×�„�–ÿ×�„�—ÿ×�„�˜ÿ×�„�šÿ×�„�Ÿÿ…�„�Èÿ×�„�Êÿ×�„�Ìÿ×�„�Îÿ×�„�Þÿ×�„�àÿ×�„�âÿ×�„�äÿ×�„ÿ×�„ÿ×�„ÿ×�„ÿ×�„$ÿq�„&ÿq�„6ÿ®�„8ÿ…�„:ÿ…�„Gÿ×�„úÿ®�„üÿ®�„þÿ®�„�ÿ…�„ÿq�„ ÿq�„_ÿ×�„Iÿ×�„Kÿ×�„Mÿ×�„Oÿ×�„Qÿ×�„Sÿ×�„Uÿ×�„Wÿ×�„Yÿ×�„[ÿ×�„]ÿ×�„_ÿ×�„oÿ…�„qÿ…�„sÿ…�„ÿq�…�ÿq�…� ÿq�…�&ÿ×�…�*ÿ×�…�- �…�2ÿ×�…�4ÿ×�…�7ÿq�…�9ÿ®�…�:ÿ®�…�<ÿ…�…�‰ÿ×�…�”ÿ×�…�•ÿ×�…�–ÿ×�…�—ÿ×�…�˜ÿ×�…�šÿ×�…�Ÿÿ…�…�Èÿ×�…�Êÿ×�…�Ìÿ×�…�Îÿ×�…�Þÿ×�…�àÿ×�…�âÿ×�…�äÿ×�…ÿ×�…ÿ×�…ÿ×�…ÿ×�…$ÿq�…&ÿq�…6ÿ®�…8ÿ…�…:ÿ…�…Gÿ×�…úÿ®�…üÿ®�…þÿ®�…�ÿ…�…ÿq�… ÿq�…_ÿ×�…Iÿ×�…Kÿ×�…Mÿ×�…Oÿ×�…Qÿ×�…Sÿ×�…Uÿ×�…Wÿ×�…Yÿ×�…[ÿ×�…]ÿ×�…_ÿ×�…oÿ…�…qÿ…�…sÿ…�…ÿq�†�ÿq�†� ÿq�†�&ÿ×�†�*ÿ×�†�- �†�2ÿ×�†�4ÿ×�†�7ÿq�†�9ÿ®�†�:ÿ®�†�<ÿ…�†�‰ÿ×�†�”ÿ×�†�•ÿ×�†�–ÿ×�†�—ÿ×�†�˜ÿ×�†�šÿ×�†�Ÿÿ…�†�Èÿ×�†�Êÿ×�†�Ìÿ×�†�Îÿ×�†�Þÿ×�†�àÿ×�†�âÿ×�†�äÿ×�†ÿ×�†ÿ×�†ÿ×�†ÿ×�†$ÿq�†&ÿq�†6ÿ®�†8ÿ…�†:ÿ…�†Gÿ×�†úÿ®�†üÿ®�†þÿ®�†�ÿ…�†ÿq�† ÿq�†_ÿ×�†Iÿ×�†Kÿ×�†Mÿ×�†Oÿ×�†Qÿ×�†Sÿ×�†Uÿ×�†Wÿ×�†Yÿ×�†[ÿ×�†]ÿ×�†_ÿ×�†oÿ…�†qÿ…�†sÿ…�†ÿq�‡�ÿq�‡� ÿq�‡�&ÿ×�‡�*ÿ×�‡�- �‡�2ÿ×�‡�4ÿ×�‡�7ÿq�‡�9ÿ®�‡�:ÿ®�‡�<ÿ…�‡�‰ÿ×�‡�”ÿ×�‡�•ÿ×�‡�–ÿ×�‡�—ÿ×�‡�˜ÿ×�‡�šÿ×�‡�Ÿÿ…�‡�Èÿ×�‡�Êÿ×�‡�Ìÿ×�‡�Îÿ×�‡�Þÿ×�‡�àÿ×�‡�âÿ×�‡�äÿ×�‡ÿ×�‡ÿ×�‡ÿ×�‡ÿ×�‡$ÿq�‡&ÿq�‡6ÿ®�‡8ÿ…�‡:ÿ…�‡Gÿ×�‡úÿ®�‡üÿ®�‡þÿ®�‡�ÿ…�‡ÿq�‡ ÿq�‡_ÿ×�‡Iÿ×�‡Kÿ×�‡Mÿ×�‡Oÿ×�‡Qÿ×�‡Sÿ×�‡Uÿ×�‡Wÿ×�‡Yÿ×�‡[ÿ×�‡]ÿ×�‡_ÿ×�‡oÿ…�‡qÿ…�‡sÿ…�‡ÿq�ˆ�-�{�‰�&ÿ×�‰�*ÿ×�‰�2ÿ×�‰�4ÿ×�‰�‰ÿ×�‰�”ÿ×�‰�•ÿ×�‰�–ÿ×�‰�—ÿ×�‰�˜ÿ×�‰�šÿ×�‰�Èÿ×�‰�Êÿ×�‰�Ìÿ×�‰�Îÿ×�‰�Þÿ×�‰�àÿ×�‰�âÿ×�‰�äÿ×�‰ÿ×�‰ÿ×�‰ÿ×�‰ÿ×�‰Gÿ×�‰_ÿ×�‰Iÿ×�‰Kÿ×�‰Mÿ×�‰Oÿ×�‰Qÿ×�‰Sÿ×�‰Uÿ×�‰Wÿ×�‰Yÿ×�‰[ÿ×�‰]ÿ×�‰_ÿ×�Š�-�{�‹�-�{�Œ�-�{��-�{�’�ÿ®�’�ÿ®�’�$ÿ×�’�7ÿÃ�’�9ÿì�’�:ÿì�’�;ÿ×�’�<ÿì�’�=ÿì�’�‚ÿ×�’�ƒÿ×�’�„ÿ×�’�…ÿ×�’�†ÿ×�’�‡ÿ×�’�Ÿÿì�’�Âÿ×�’�Äÿ×�’�Æÿ×�’$ÿÃ�’&ÿÃ�’6ÿì�’8ÿì�’:ÿì�’;ÿì�’=ÿì�’?ÿì�’Cÿ×�’ ÿì�’úÿì�’üÿì�’þÿì�’�ÿì�’ÿ®�’ ÿ®�’Xÿ×�’ÿ×�’ÿ×�’!ÿ×�’#ÿ×�’%ÿ×�’'ÿ×�’)ÿ×�’+ÿ×�’-ÿ×�’/ÿ×�’1ÿ×�’3ÿ×�’oÿì�’qÿì�’sÿì�’ÿÃ�”�ÿ®�”�ÿ®�”�$ÿ×�”�7ÿÃ�”�9ÿì�”�:ÿì�”�;ÿ×�”�<ÿì�”�=ÿì�”�‚ÿ×�”�ƒÿ×�”�„ÿ×�”�…ÿ×�”�†ÿ×�”�‡ÿ×�”�Ÿÿì�”�Âÿ×�”�Äÿ×�”�Æÿ×�”$ÿÃ�”&ÿÃ�”6ÿì�”8ÿì�”:ÿì�”;ÿì�”=ÿì�”?ÿì�”Cÿ×�” ÿì�”úÿì�”üÿì�”þÿì�”�ÿì�”ÿ®�” ÿ®�”Xÿ×�”ÿ×�”ÿ×�”!ÿ×�”#ÿ×�”%ÿ×�”'ÿ×�”)ÿ×�”+ÿ×�”-ÿ×�”/ÿ×�”1ÿ×�”3ÿ×�”oÿì�”qÿì�”sÿì�”ÿÃ�•�ÿ®�•�ÿ®�•�$ÿ×�•�7ÿÃ�•�9ÿì�•�:ÿì�•�;ÿ×�•�<ÿì�•�=ÿì�•�‚ÿ×�•�ƒÿ×�•�„ÿ×�•�…ÿ×�•�†ÿ×�•�‡ÿ×�•�Ÿÿì�•�Âÿ×�•�Äÿ×�•�Æÿ×�•$ÿÃ�•&ÿÃ�•6ÿì�•8ÿì�•:ÿì�•;ÿì�•=ÿì�•?ÿì�•Cÿ×�• ÿì�•úÿì�•üÿì�•þÿì�•�ÿì�•ÿ®�• ÿ®�•Xÿ×�•ÿ×�•ÿ×�•!ÿ×�•#ÿ×�•%ÿ×�•'ÿ×�•)ÿ×�•+ÿ×�•-ÿ×�•/ÿ×�•1ÿ×�•3ÿ×�•oÿì�•qÿì�•sÿì�•ÿÃ�–�ÿ®�–�ÿ®�–�$ÿ×�–�7ÿÃ�–�9ÿì�–�:ÿì�–�;ÿ×�–�<ÿì�–�=ÿì�–�‚ÿ×�–�ƒÿ×�–�„ÿ×�–�…ÿ×�–�†ÿ×�–�‡ÿ×�–�Ÿÿì�–�Âÿ×�–�Äÿ×�–�Æÿ×�–$ÿÃ�–&ÿÃ�–6ÿì�–8ÿì�–:ÿì�–;ÿì�–=ÿì�–?ÿì�–Cÿ×�– ÿì�–úÿì�–üÿì�–þÿì�–�ÿì�–ÿ®�– ÿ®�–Xÿ×�–ÿ×�–ÿ×�–!ÿ×�–#ÿ×�–%ÿ×�–'ÿ×�–)ÿ×�–+ÿ×�–-ÿ×�–/ÿ×�–1ÿ×�–3ÿ×�–oÿì�–qÿì�–sÿì�–ÿÃ�—�ÿ®�—�ÿ®�—�$ÿ×�—�7ÿÃ�—�9ÿì�—�:ÿì�—�;ÿ×�—�<ÿì�—�=ÿì�—�‚ÿ×�—�ƒÿ×�—�„ÿ×�—�…ÿ×�—�†ÿ×�—�‡ÿ×�—�Ÿÿì�—�Âÿ×�—�Äÿ×�—�Æÿ×�—$ÿÃ�—&ÿÃ�—6ÿì�—8ÿì�—:ÿì�—;ÿì�—=ÿì�—?ÿì�—Cÿ×�— ÿì�—úÿì�—üÿì�—þÿì�—�ÿì�—ÿ®�— ÿ®�—Xÿ×�—ÿ×�—ÿ×�—!ÿ×�—#ÿ×�—%ÿ×�—'ÿ×�—)ÿ×�—+ÿ×�—-ÿ×�—/ÿ×�—1ÿ×�—3ÿ×�—oÿì�—qÿì�—sÿì�—ÿÃ�˜�ÿ®�˜�ÿ®�˜�$ÿ×�˜�7ÿÃ�˜�9ÿì�˜�:ÿì�˜�;ÿ×�˜�<ÿì�˜�=ÿì�˜�‚ÿ×�˜�ƒÿ×�˜�„ÿ×�˜�…ÿ×�˜�†ÿ×�˜�‡ÿ×�˜�Ÿÿì�˜�Âÿ×�˜�Äÿ×�˜�Æÿ×�˜$ÿÃ�˜&ÿÃ�˜6ÿì�˜8ÿì�˜:ÿì�˜;ÿì�˜=ÿì�˜?ÿì�˜Cÿ×�˜ ÿì�˜úÿì�˜üÿì�˜þÿì�˜�ÿì�˜ÿ®�˜ ÿ®�˜Xÿ×�˜ÿ×�˜ÿ×�˜!ÿ×�˜#ÿ×�˜%ÿ×�˜'ÿ×�˜)ÿ×�˜+ÿ×�˜-ÿ×�˜/ÿ×�˜1ÿ×�˜3ÿ×�˜oÿì�˜qÿì�˜sÿì�˜ÿÃ�š�ÿ®�š�ÿ®�š�$ÿ×�š�7ÿÃ�š�9ÿì�š�:ÿì�š�;ÿ×�š�<ÿì�š�=ÿì�š�‚ÿ×�š�ƒÿ×�š�„ÿ×�š�…ÿ×�š�†ÿ×�š�‡ÿ×�š�Ÿÿì�š�Âÿ×�š�Äÿ×�š�Æÿ×�š$ÿÃ�š&ÿÃ�š6ÿì�š8ÿì�š:ÿì�š;ÿì�š=ÿì�š?ÿì�šCÿ×�š ÿì�šúÿì�šüÿì�šþÿì�š�ÿì�šÿ®�š ÿ®�šXÿ×�šÿ×�šÿ×�š!ÿ×�š#ÿ×�š%ÿ×�š'ÿ×�š)ÿ×�š+ÿ×�š-ÿ×�š/ÿ×�š1ÿ×�š3ÿ×�šoÿì�šqÿì�šsÿì�šÿÃ�›�ÿ×�›�ÿ×�›�$ÿì�›�‚ÿì�›�ƒÿì�›�„ÿì�›�…ÿì�›�†ÿì�›�‡ÿì�›�Âÿì�›�Äÿì�›�Æÿì�›Cÿì�›ÿ×�› ÿ×�›Xÿì�›ÿì�›ÿì�›!ÿì�›#ÿì�›%ÿì�›'ÿì�›)ÿì�›+ÿì�›-ÿì�›/ÿì�›1ÿì�›3ÿì�œ�ÿ×�œ�ÿ×�œ�$ÿì�œ�‚ÿì�œ�ƒÿì�œ�„ÿì�œ�…ÿì�œ�†ÿì�œ�‡ÿì�œ�Âÿì�œ�Äÿì�œ�Æÿì�œCÿì�œÿ×�œ ÿ×�œXÿì�œÿì�œÿì�œ!ÿì�œ#ÿì�œ%ÿì�œ'ÿì�œ)ÿì�œ+ÿì�œ-ÿì�œ/ÿì�œ1ÿì�œ3ÿì��ÿ×��ÿ×��$ÿì��‚ÿì��ƒÿì��„ÿì��…ÿì��†ÿì��‡ÿì��Âÿì��Äÿì��Æÿì�Cÿì�ÿ×� ÿ×�Xÿì�ÿì�ÿì�!ÿì�#ÿì�%ÿì�'ÿì�)ÿì�+ÿì�-ÿì�/ÿì�1ÿì�3ÿì�ž�ÿ×�ž�ÿ×�ž�$ÿì�ž�‚ÿì�ž�ƒÿì�ž�„ÿì�ž�…ÿì�ž�†ÿì�ž�‡ÿì�ž�Âÿì�ž�Äÿì�ž�Æÿì�žCÿì�žÿ×�ž ÿ×�žXÿì�žÿì�žÿì�ž!ÿì�ž#ÿì�ž%ÿì�ž'ÿì�ž)ÿì�ž+ÿì�ž-ÿì�ž/ÿì�ž1ÿì�ž3ÿì�Ÿ�ÿ…�Ÿ�ÿ…�Ÿ�"�)�Ÿ�$ÿ…�Ÿ�&ÿ×�Ÿ�*ÿ×�Ÿ�2ÿ×�Ÿ�4ÿ×�Ÿ�Dÿš�Ÿ�Fÿš�Ÿ�Gÿš�Ÿ�Hÿš�Ÿ�Jÿ×�Ÿ�PÿÃ�Ÿ�QÿÃ�Ÿ�Rÿš�Ÿ�SÿÃ�Ÿ�Tÿš�Ÿ�UÿÃ�Ÿ�Vÿ®�Ÿ�XÿÃ�Ÿ�]ÿ×�Ÿ�‚ÿ…�Ÿ�ƒÿ…�Ÿ�„ÿ…�Ÿ�…ÿ…�Ÿ�†ÿ…�Ÿ�‡ÿ…�Ÿ�‰ÿ×�Ÿ�”ÿ×�Ÿ�•ÿ×�Ÿ�–ÿ×�Ÿ�—ÿ×�Ÿ�˜ÿ×�Ÿ�šÿ×�Ÿ�¢ÿš�Ÿ�£ÿš�Ÿ�¤ÿš�Ÿ�¥ÿš�Ÿ�¦ÿš�Ÿ�§ÿš�Ÿ�¨ÿš�Ÿ�©ÿš�Ÿ�ªÿš�Ÿ�«ÿš�Ÿ�¬ÿš�Ÿ�­ÿš�Ÿ�´ÿš�Ÿ�µÿš�Ÿ�¶ÿš�Ÿ�·ÿš�Ÿ�¸ÿš�Ÿ�ºÿš�Ÿ�»ÿÃ�Ÿ�¼ÿÃ�Ÿ�½ÿÃ�Ÿ�¾ÿÃ�Ÿ�Âÿ…�Ÿ�Ãÿš�Ÿ�Äÿ…�Ÿ�Åÿš�Ÿ�Æÿ…�Ÿ�Çÿš�Ÿ�Èÿ×�Ÿ�Éÿš�Ÿ�Êÿ×�Ÿ�Ëÿš�Ÿ�Ìÿ×�Ÿ�Íÿš�Ÿ�Îÿ×�Ÿ�Ïÿš�Ÿ�Ñÿš�Ÿ�Óÿš�Ÿ�Õÿš�Ÿ�×ÿš�Ÿ�Ùÿš�Ÿ�Ûÿš�Ÿ�Ýÿš�Ÿ�Þÿ×�Ÿ�ßÿ×�Ÿ�àÿ×�Ÿ�áÿ×�Ÿ�âÿ×�Ÿ�ãÿ×�Ÿ�äÿ×�Ÿ�åÿ×�Ÿ�úÿÃ�ŸÿÃ�ŸÿÃ�Ÿ ÿÃ�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�ŸÿÃ�ŸÿÃ�Ÿÿ®�Ÿ!ÿ®�Ÿ+ÿÃ�Ÿ-ÿÃ�Ÿ/ÿÃ�Ÿ1ÿÃ�Ÿ3ÿÃ�Ÿ5ÿÃ�Ÿ<ÿ×�Ÿ>ÿ×�Ÿ@ÿ×�ŸCÿ…�ŸDÿš�ŸFÿš�ŸGÿ×�ŸHÿš�ŸJÿ®�Ÿÿ…�Ÿ ÿ…�ŸWÿÃ�ŸXÿ…�ŸYÿš�Ÿ_ÿ×�Ÿ`ÿš�ŸbÿÃ�Ÿÿ…�Ÿÿš�Ÿÿ…�Ÿ ÿš�Ÿ!ÿ…�Ÿ"ÿš�Ÿ#ÿ…�Ÿ%ÿ…�Ÿ&ÿš�Ÿ'ÿ…�Ÿ(ÿš�Ÿ)ÿ…�Ÿ*ÿš�Ÿ+ÿ…�Ÿ,ÿš�Ÿ-ÿ…�Ÿ.ÿš�Ÿ/ÿ…�Ÿ0ÿš�Ÿ1ÿ…�Ÿ2ÿš�Ÿ3ÿ…�Ÿ4ÿš�Ÿ6ÿš�Ÿ8ÿš�Ÿ:ÿš�Ÿ<ÿš�Ÿ@ÿš�ŸBÿš�ŸDÿš�ŸIÿ×�ŸJÿš�ŸKÿ×�ŸLÿš�ŸMÿ×�ŸNÿš�ŸOÿ×�ŸQÿ×�ŸRÿš�ŸSÿ×�ŸTÿš�ŸUÿ×�ŸVÿš�ŸWÿ×�ŸXÿš�ŸYÿ×�ŸZÿš�Ÿ[ÿ×�Ÿ\ÿš�Ÿ]ÿ×�Ÿ^ÿš�Ÿ_ÿ×�Ÿ`ÿš�ŸbÿÃ�ŸdÿÃ�ŸfÿÃ�ŸhÿÃ�ŸjÿÃ�ŸlÿÃ�ŸnÿÃ� �þö� �þö� �$ÿš� �;ÿ×� �=ÿì� �‚ÿš� �ƒÿš� �„ÿš� �…ÿš� �†ÿš� �‡ÿš� �Âÿš� �Äÿš� �Æÿš� ;ÿì� =ÿì� ?ÿì� Cÿš� þö�  þö� Xÿš� ÿš� ÿš� !ÿš� #ÿš� %ÿš� 'ÿš� )ÿš� +ÿš� -ÿš� /ÿš� 1ÿš� 3ÿš�¢�ÿì�¢� ÿì�¢ÿì�¢ ÿì�£�ÿì�£� ÿì�£ÿì�£ ÿì�¤�ÿì�¤� ÿì�¤ÿì�¤ ÿì�¥�ÿì�¥� ÿì�¥ÿì�¥ ÿì�¦�ÿì�¦� ÿì�¦ÿì�¦ ÿì�§�ÿì�§� ÿì�§ÿì�§ ÿì�ª�ÿì�ª� ÿì�ª�Yÿ×�ª�Zÿ×�ª�[ÿ×�ª�\ÿ×�ª�]ÿì�ª�¿ÿ×�ª7ÿ×�ª<ÿì�ª>ÿì�ª@ÿì�ªûÿ×�ªýÿ×�ªÿì�ª ÿì�ªpÿ×�«�ÿì�«� ÿì�«�Yÿ×�«�Zÿ×�«�[ÿ×�«�\ÿ×�«�]ÿì�«�¿ÿ×�«7ÿ×�«<ÿì�«>ÿì�«@ÿì�«ûÿ×�«ýÿ×�«ÿì�« ÿì�«pÿ×�¬�ÿì�¬� ÿì�¬�Yÿ×�¬�Zÿ×�¬�[ÿ×�¬�\ÿ×�¬�]ÿì�¬�¿ÿ×�¬7ÿ×�¬<ÿì�¬>ÿì�¬@ÿì�¬ûÿ×�¬ýÿ×�¬ÿì�¬ ÿì�¬pÿ×�­�ÿì�­� ÿì�­�Yÿ×�­�Zÿ×�­�[ÿ×�­�\ÿ×�­�]ÿì�­�¿ÿ×�­7ÿ×�­<ÿì�­>ÿì�­@ÿì�­ûÿ×�­ýÿ×�­ÿì�­ ÿì�­pÿ×�²�ÿì�²� ÿì�²�Yÿ×�²�Zÿ×�²�[ÿ×�²�\ÿ×�²�]ÿì�²�¿ÿ×�²7ÿ×�²<ÿì�²>ÿì�²@ÿì�²ûÿ×�²ýÿ×�²ÿì�² ÿì�²pÿ×�´�ÿì�´� ÿì�´�Yÿ×�´�Zÿ×�´�[ÿ×�´�\ÿ×�´�]ÿì�´�¿ÿ×�´7ÿ×�´<ÿì�´>ÿì�´@ÿì�´ûÿ×�´ýÿ×�´ÿì�´ ÿì�´pÿ×�µ�ÿì�µ� ÿì�µ�Yÿ×�µ�Zÿ×�µ�[ÿ×�µ�\ÿ×�µ�]ÿì�µ�¿ÿ×�µ7ÿ×�µ<ÿì�µ>ÿì�µ@ÿì�µûÿ×�µýÿ×�µÿì�µ ÿì�µpÿ×�¶�ÿì�¶� ÿì�¶�Yÿ×�¶�Zÿ×�¶�[ÿ×�¶�\ÿ×�¶�]ÿì�¶�¿ÿ×�¶7ÿ×�¶<ÿì�¶>ÿì�¶@ÿì�¶ûÿ×�¶ýÿ×�¶ÿì�¶ ÿì�¶pÿ×�¸�ÿ×�¸� ÿ×�¸ÿ×�¸ ÿ×�º�ÿì�º� ÿì�º�Yÿ×�º�Zÿ×�º�[ÿ×�º�\ÿ×�º�]ÿì�º�¿ÿ×�º7ÿ×�º<ÿì�º>ÿì�º@ÿì�ºûÿ×�ºýÿ×�ºÿì�º ÿì�ºpÿ×�¿��R�¿� �R�¿�ÿ®�¿�ÿ®�¿�"�)�¿�R�¿ÿ®�¿ �R�¿ ÿ®�À�ÿì�À� ÿì�À�Yÿ×�À�Zÿ×�À�[ÿ×�À�\ÿ×�À�]ÿì�À�¿ÿ×�À7ÿ×�À<ÿì�À>ÿì�À@ÿì�Àûÿ×�Àýÿ×�Àÿì�À ÿì�Àpÿ×�Á��R�Á� �R�Á�ÿ®�Á�ÿ®�Á�"�)�Á�R�Áÿ®�Á �R�Á ÿ®�Â�ÿq�Â� ÿq�Â�&ÿ×�Â�*ÿ×�Â�- �Â�2ÿ×�Â�4ÿ×�Â�7ÿq�Â�9ÿ®�Â�:ÿ®�Â�<ÿ…�Â�‰ÿ×�Â�”ÿ×�Â�•ÿ×�Â�–ÿ×�Â�—ÿ×�Â�˜ÿ×�Â�šÿ×�Â�Ÿÿ…�Â�Èÿ×�Â�Êÿ×�Â�Ìÿ×�Â�Îÿ×�Â�Þÿ×�Â�àÿ×�Â�âÿ×�Â�äÿ×�Âÿ×�Âÿ×�Âÿ×�Âÿ×�Â$ÿq�Â&ÿq�Â6ÿ®�Â8ÿ…�Â:ÿ…�ÂGÿ×�Âúÿ®�Âüÿ®�Âþÿ®�Â�ÿ…�Âÿq� ÿq�Â_ÿ×�ÂIÿ×�ÂKÿ×�ÂMÿ×�ÂOÿ×�ÂQÿ×�ÂSÿ×�ÂUÿ×�ÂWÿ×�ÂYÿ×�Â[ÿ×�Â]ÿ×�Â_ÿ×�Âoÿ…�Âqÿ…�Âsÿ…�Âÿq�Ã�ÿì�Ã� ÿì�Ãÿì�à ÿì�Ä�ÿq�Ä� ÿq�Ä�&ÿ×�Ä�*ÿ×�Ä�- �Ä�2ÿ×�Ä�4ÿ×�Ä�7ÿq�Ä�9ÿ®�Ä�:ÿ®�Ä�<ÿ…�Ä�‰ÿ×�Ä�”ÿ×�Ä�•ÿ×�Ä�–ÿ×�Ä�—ÿ×�Ä�˜ÿ×�Ä�šÿ×�Ä�Ÿÿ…�Ä�Èÿ×�Ä�Êÿ×�Ä�Ìÿ×�Ä�Îÿ×�Ä�Þÿ×�Ä�àÿ×�Ä�âÿ×�Ä�äÿ×�Äÿ×�Äÿ×�Äÿ×�Äÿ×�Ä$ÿq�Ä&ÿq�Ä6ÿ®�Ä8ÿ…�Ä:ÿ…�ÄGÿ×�Äúÿ®�Äüÿ®�Äþÿ®�Ä�ÿ…�Äÿq�Ä ÿq�Ä_ÿ×�ÄIÿ×�ÄKÿ×�ÄMÿ×�ÄOÿ×�ÄQÿ×�ÄSÿ×�ÄUÿ×�ÄWÿ×�ÄYÿ×�Ä[ÿ×�Ä]ÿ×�Ä_ÿ×�Äoÿ…�Äqÿ…�Äsÿ…�Äÿq�Å�ÿì�Å� ÿì�Åÿì�Å ÿì�Æ�ÿq�Æ� ÿq�Æ�&ÿ×�Æ�*ÿ×�Æ�- �Æ�2ÿ×�Æ�4ÿ×�Æ�7ÿq�Æ�9ÿ®�Æ�:ÿ®�Æ�<ÿ…�Æ�‰ÿ×�Æ�”ÿ×�Æ�•ÿ×�Æ�–ÿ×�Æ�—ÿ×�Æ�˜ÿ×�Æ�šÿ×�Æ�Ÿÿ…�Æ�Èÿ×�Æ�Êÿ×�Æ�Ìÿ×�Æ�Îÿ×�Æ�Þÿ×�Æ�àÿ×�Æ�âÿ×�Æ�äÿ×�Æÿ×�Æÿ×�Æÿ×�Æÿ×�Æ$ÿq�Æ&ÿq�Æ6ÿ®�Æ8ÿ…�Æ:ÿ…�ÆGÿ×�Æúÿ®�Æüÿ®�Æþÿ®�Æ�ÿ…�Æÿq�Æ ÿq�Æ_ÿ×�ÆIÿ×�ÆKÿ×�ÆMÿ×�ÆOÿ×�ÆQÿ×�ÆSÿ×�ÆUÿ×�ÆWÿ×�ÆYÿ×�Æ[ÿ×�Æ]ÿ×�Æ_ÿ×�Æoÿ…�Æqÿ…�Æsÿ…�Æÿq�Ç�ÿì�Ç� ÿì�Çÿì�Ç ÿì�È�&ÿ×�È�*ÿ×�È�2ÿ×�È�4ÿ×�È�‰ÿ×�È�”ÿ×�È�•ÿ×�È�–ÿ×�È�—ÿ×�È�˜ÿ×�È�šÿ×�È�Èÿ×�È�Êÿ×�È�Ìÿ×�È�Îÿ×�È�Þÿ×�È�àÿ×�È�âÿ×�È�äÿ×�Èÿ×�Èÿ×�Èÿ×�Èÿ×�ÈGÿ×�È_ÿ×�ÈIÿ×�ÈKÿ×�ÈMÿ×�ÈOÿ×�ÈQÿ×�ÈSÿ×�ÈUÿ×�ÈWÿ×�ÈYÿ×�È[ÿ×�È]ÿ×�È_ÿ×�Ê�&ÿ×�Ê�*ÿ×�Ê�2ÿ×�Ê�4ÿ×�Ê�‰ÿ×�Ê�”ÿ×�Ê�•ÿ×�Ê�–ÿ×�Ê�—ÿ×�Ê�˜ÿ×�Ê�šÿ×�Ê�Èÿ×�Ê�Êÿ×�Ê�Ìÿ×�Ê�Îÿ×�Ê�Þÿ×�Ê�àÿ×�Ê�âÿ×�Ê�äÿ×�Êÿ×�Êÿ×�Êÿ×�Êÿ×�ÊGÿ×�Ê_ÿ×�ÊIÿ×�ÊKÿ×�ÊMÿ×�ÊOÿ×�ÊQÿ×�ÊSÿ×�ÊUÿ×�ÊWÿ×�ÊYÿ×�Ê[ÿ×�Ê]ÿ×�Ê_ÿ×�Ì�&ÿ×�Ì�*ÿ×�Ì�2ÿ×�Ì�4ÿ×�Ì�‰ÿ×�Ì�”ÿ×�Ì�•ÿ×�Ì�–ÿ×�Ì�—ÿ×�Ì�˜ÿ×�Ì�šÿ×�Ì�Èÿ×�Ì�Êÿ×�Ì�Ìÿ×�Ì�Îÿ×�Ì�Þÿ×�Ì�àÿ×�Ì�âÿ×�Ì�äÿ×�Ìÿ×�Ìÿ×�Ìÿ×�Ìÿ×�ÌGÿ×�Ì_ÿ×�ÌIÿ×�ÌKÿ×�ÌMÿ×�ÌOÿ×�ÌQÿ×�ÌSÿ×�ÌUÿ×�ÌWÿ×�ÌYÿ×�Ì[ÿ×�Ì]ÿ×�Ì_ÿ×�Î�&ÿ×�Î�*ÿ×�Î�2ÿ×�Î�4ÿ×�Î�‰ÿ×�Î�”ÿ×�Î�•ÿ×�Î�–ÿ×�Î�—ÿ×�Î�˜ÿ×�Î�šÿ×�Î�Èÿ×�Î�Êÿ×�Î�Ìÿ×�Î�Îÿ×�Î�Þÿ×�Î�àÿ×�Î�âÿ×�Î�äÿ×�Îÿ×�Îÿ×�Îÿ×�Îÿ×�ÎGÿ×�Î_ÿ×�ÎIÿ×�ÎKÿ×�ÎMÿ×�ÎOÿ×�ÎQÿ×�ÎSÿ×�ÎUÿ×�ÎWÿ×�ÎYÿ×�Î[ÿ×�Î]ÿ×�Î_ÿ×�Ð�ÿ®�Ð�ÿ®�Ð�$ÿ×�Ð�7ÿÃ�Ð�9ÿì�Ð�:ÿì�Ð�;ÿ×�Ð�<ÿì�Ð�=ÿì�Ð�‚ÿ×�Ð�ƒÿ×�Ð�„ÿ×�Ð�…ÿ×�Ð�†ÿ×�Ð�‡ÿ×�Ð�Ÿÿì�Ð�Âÿ×�Ð�Äÿ×�Ð�Æÿ×�Ð$ÿÃ�Ð&ÿÃ�Ð6ÿì�Ð8ÿì�Ð:ÿì�Ð;ÿì�Ð=ÿì�Ð?ÿì�ÐCÿ×�Рÿì�Ðúÿì�Ðüÿì�Ðþÿì�Ð�ÿì�Ðÿ®�Ð ÿ®�ÐXÿ×�Ðÿ×�Ðÿ×�Ð!ÿ×�Ð#ÿ×�Ð%ÿ×�Ð'ÿ×�Ð)ÿ×�Ð+ÿ×�Ð-ÿ×�Ð/ÿ×�Ð1ÿ×�Ð3ÿ×�Ðoÿì�Ðqÿì�Ðsÿì�ÐÿÃ�Ñ��R�Ñ� �R�Ñ� ��Ñ�"�¤�Ñ�@��Ñ�E�=�Ñ�K�=�Ñ�N�=�Ñ�O�=�Ñ�`��Ñ�ç�=�Ñ�é�{�Ñ�R�Ñ �R�Ò�ÿ®�Ò�ÿ®�Ò�$ÿ×�Ò�7ÿÃ�Ò�9ÿì�Ò�:ÿì�Ò�;ÿ×�Ò�<ÿì�Ò�=ÿì�Ò�‚ÿ×�Ò�ƒÿ×�Ò�„ÿ×�Ò�…ÿ×�Ò�†ÿ×�Ò�‡ÿ×�Ò�Ÿÿì�Ò�Âÿ×�Ò�Äÿ×�Ò�Æÿ×�Ò$ÿÃ�Ò&ÿÃ�Ò6ÿì�Ò8ÿì�Ò:ÿì�Ò;ÿì�Ò=ÿì�Ò?ÿì�ÒCÿ×�Ò ÿì�Òúÿì�Òüÿì�Òþÿì�Ò�ÿì�Òÿ®�Ò ÿ®�ÒXÿ×�Òÿ×�Òÿ×�Ò!ÿ×�Ò#ÿ×�Ò%ÿ×�Ò'ÿ×�Ò)ÿ×�Ò+ÿ×�Ò-ÿ×�Ò/ÿ×�Ò1ÿ×�Ò3ÿ×�Òoÿì�Òqÿì�Òsÿì�ÒÿÃ�Ô�-�{�Õ�ÿì�Õ� ÿì�Õ�Yÿ×�Õ�Zÿ×�Õ�[ÿ×�Õ�\ÿ×�Õ�]ÿì�Õ�¿ÿ×�Õ7ÿ×�Õ<ÿì�Õ>ÿì�Õ@ÿì�Õûÿ×�Õýÿ×�Õÿì�Õ ÿì�Õpÿ×�Ö�-�{�×�ÿì�×� ÿì�×�Yÿ×�×�Zÿ×�×�[ÿ×�×�\ÿ×�×�]ÿì�×�¿ÿ×�×7ÿ×�×<ÿì�×>ÿì�×@ÿì�×ûÿ×�×ýÿ×�×ÿì�× ÿì�×pÿ×�Ø�-�{�Ù�ÿì�Ù� ÿì�Ù�Yÿ×�Ù�Zÿ×�Ù�[ÿ×�Ù�\ÿ×�Ù�]ÿì�Ù�¿ÿ×�Ù7ÿ×�Ù<ÿì�Ù>ÿì�Ù@ÿì�Ùûÿ×�Ùýÿ×�Ùÿì�Ù ÿì�Ùpÿ×�Ú�-�{�Û�ÿì�Û� ÿì�Û�Yÿ×�Û�Zÿ×�Û�[ÿ×�Û�\ÿ×�Û�]ÿì�Û�¿ÿ×�Û7ÿ×�Û<ÿì�Û>ÿì�Û@ÿì�Ûûÿ×�Ûýÿ×�Ûÿì�Û ÿì�Ûpÿ×�Ü�-�{�Ý�ÿì�Ý� ÿì�Ý�Yÿ×�Ý�Zÿ×�Ý�[ÿ×�Ý�\ÿ×�Ý�]ÿì�Ý�¿ÿ×�Ý7ÿ×�Ý<ÿì�Ý>ÿì�Ý@ÿì�Ýûÿ×�Ýýÿ×�Ýÿì�Ý ÿì�Ýpÿ×�ç�ÿì�ç� ÿì�çÿì�ç ÿì�ø�&ÿ×�ø�*ÿ×�ø�2ÿ×�ø�4ÿ×�ø�‰ÿ×�ø�”ÿ×�ø�•ÿ×�ø�–ÿ×�ø�—ÿ×�ø�˜ÿ×�ø�šÿ×�ø�Èÿ×�ø�Êÿ×�ø�Ìÿ×�ø�Îÿ×�ø�Þÿ×�ø�àÿ×�ø�âÿ×�ø�äÿ×�øÿ×�øÿ×�øÿ×�øÿ×�øGÿ×�ø_ÿ×�øIÿ×�øKÿ×�øMÿ×�øOÿ×�øQÿ×�øSÿ×�øUÿ×�øWÿ×�øYÿ×�ø[ÿ×�ø]ÿ×�ø_ÿ×�ù�Fÿ×�ù�Gÿ×�ù�Hÿ×�ù�Rÿ×�ù�Tÿ×�ù�¢ÿ×�ù�©ÿ×�ù�ªÿ×�ù�«ÿ×�ù�¬ÿ×�ù�­ÿ×�ù�´ÿ×�ù�µÿ×�ù�¶ÿ×�ù�·ÿ×�ù�¸ÿ×�ù�ºÿ×�ù�Éÿ×�ù�Ëÿ×�ù�Íÿ×�ù�Ïÿ×�ù�Ñÿ×�ù�Óÿ×�ù�Õÿ×�ù�×ÿ×�ù�Ùÿ×�ù�Ûÿ×�ù�Ýÿ×�ùÿ×�ùÿ×�ùÿ×�ùÿ×�ùHÿ×�ù`ÿ×�ù6ÿ×�ù8ÿ×�ù:ÿ×�ù<ÿ×�ù@ÿ×�ùBÿ×�ùDÿ×�ùJÿ×�ùLÿ×�ùNÿ×�ùRÿ×�ùTÿ×�ùVÿ×�ùXÿ×�ùZÿ×�ù\ÿ×�ù^ÿ×�ù`ÿ×�ú�Fÿ×�ú�Gÿ×�ú�Hÿ×�ú�Rÿ×�ú�Tÿ×�ú�¢ÿ×�ú�©ÿ×�ú�ªÿ×�ú�«ÿ×�ú�¬ÿ×�ú�­ÿ×�ú�´ÿ×�ú�µÿ×�ú�¶ÿ×�ú�·ÿ×�ú�¸ÿ×�ú�ºÿ×�ú�Éÿ×�ú�Ëÿ×�ú�Íÿ×�ú�Ïÿ×�ú�Ñÿ×�ú�Óÿ×�ú�Õÿ×�ú�×ÿ×�ú�Ùÿ×�ú�Ûÿ×�ú�Ýÿ×�úÿ×�úÿ×�úÿ×�úÿ×�úHÿ×�ú`ÿ×�ú6ÿ×�ú8ÿ×�ú:ÿ×�ú<ÿ×�ú@ÿ×�úBÿ×�úDÿ×�úJÿ×�úLÿ×�úNÿ×�úRÿ×�úTÿ×�úVÿ×�úXÿ×�úZÿ×�ú\ÿ×�ú^ÿ×�ú`ÿ×�û�ÿ\�û� ÿ\�û�&ÿ×�û�*ÿ×�û�2ÿ×�û�4ÿ×�û�7ÿ×�û�8ÿì�û�9ÿ×�û�:ÿ×�û�<ÿÃ�û�‰ÿ×�û�”ÿ×�û�•ÿ×�û�–ÿ×�û�—ÿ×�û�˜ÿ×�û�šÿ×�û�›ÿì�û�œÿì�û�ÿì�û�žÿì�û�ŸÿÃ�û�Èÿ×�û�Êÿ×�û�Ìÿ×�û�Îÿ×�û�Þÿ×�û�àÿ×�û�âÿ×�û�äÿ×�ûÿ×�ûÿ×�ûÿ×�ûÿ×�û$ÿ×�û&ÿ×�û*ÿì�û,ÿì�û.ÿì�û0ÿì�û2ÿì�û4ÿì�û6ÿ×�û8ÿÃ�û:ÿÃ�ûGÿ×�ûúÿ×�ûüÿ×�ûþÿ×�û�ÿÃ�ûÿ\�û ÿ\�û_ÿ×�ûaÿì�ûIÿ×�ûKÿ×�ûMÿ×�ûOÿ×�ûQÿ×�ûSÿ×�ûUÿ×�ûWÿ×�ûYÿ×�û[ÿ×�û]ÿ×�û_ÿ×�ûaÿì�ûcÿì�ûeÿì�ûgÿì�ûiÿì�ûkÿì�ûmÿì�ûoÿÃ�ûqÿÃ�ûsÿÃ�ûÿ×�ý�ÿ\�ý� ÿ\�ý�&ÿ×�ý�*ÿ×�ý�2ÿ×�ý�4ÿ×�ý�7ÿ×�ý�8ÿì�ý�9ÿ×�ý�:ÿ×�ý�<ÿÃ�ý�‰ÿ×�ý�”ÿ×�ý�•ÿ×�ý�–ÿ×�ý�—ÿ×�ý�˜ÿ×�ý�šÿ×�ý�›ÿì�ý�œÿì�ý�ÿì�ý�žÿì�ý�ŸÿÃ�ý�Èÿ×�ý�Êÿ×�ý�Ìÿ×�ý�Îÿ×�ý�Þÿ×�ý�àÿ×�ý�âÿ×�ý�äÿ×�ýÿ×�ýÿ×�ýÿ×�ýÿ×�ý$ÿ×�ý&ÿ×�ý*ÿì�ý,ÿì�ý.ÿì�ý0ÿì�ý2ÿì�ý4ÿì�ý6ÿ×�ý8ÿÃ�ý:ÿÃ�ýGÿ×�ýúÿ×�ýüÿ×�ýþÿ×�ý�ÿÃ�ýÿ\�ý ÿ\�ý_ÿ×�ýaÿì�ýIÿ×�ýKÿ×�ýMÿ×�ýOÿ×�ýQÿ×�ýSÿ×�ýUÿ×�ýWÿ×�ýYÿ×�ý[ÿ×�ý]ÿ×�ý_ÿ×�ýaÿì�ýcÿì�ýeÿì�ýgÿì�ýiÿì�ýkÿì�ýmÿì�ýoÿÃ�ýqÿÃ�ýsÿÃ�ýÿ×�ÿ�ÿ\�ÿ� ÿ\�ÿ�&ÿ×�ÿ�*ÿ×�ÿ�2ÿ×�ÿ�4ÿ×�ÿ�7ÿ×�ÿ�8ÿì�ÿ�9ÿ×�ÿ�:ÿ×�ÿ�<ÿÃ�ÿ�‰ÿ×�ÿ�”ÿ×�ÿ�•ÿ×�ÿ�–ÿ×�ÿ�—ÿ×�ÿ�˜ÿ×�ÿ�šÿ×�ÿ�›ÿì�ÿ�œÿì�ÿ�ÿì�ÿ�žÿì�ÿ�ŸÿÃ�ÿ�Èÿ×�ÿ�Êÿ×�ÿ�Ìÿ×�ÿ�Îÿ×�ÿ�Þÿ×�ÿ�àÿ×�ÿ�âÿ×�ÿ�äÿ×�ÿÿ×�ÿÿ×�ÿÿ×�ÿÿ×�ÿ$ÿ×�ÿ&ÿ×�ÿ*ÿì�ÿ,ÿì�ÿ.ÿì�ÿ0ÿì�ÿ2ÿì�ÿ4ÿì�ÿ6ÿ×�ÿ8ÿÃ�ÿ:ÿÃ�ÿGÿ×�ÿúÿ×�ÿüÿ×�ÿþÿ×�ÿ�ÿÃ�ÿÿ\�ÿ ÿ\�ÿ_ÿ×�ÿaÿì�ÿIÿ×�ÿKÿ×�ÿMÿ×�ÿOÿ×�ÿQÿ×�ÿSÿ×�ÿUÿ×�ÿWÿ×�ÿYÿ×�ÿ[ÿ×�ÿ]ÿ×�ÿ_ÿ×�ÿaÿì�ÿcÿì�ÿeÿì�ÿgÿì�ÿiÿì�ÿkÿì�ÿmÿì�ÿoÿÃ�ÿqÿÃ�ÿsÿÃ�ÿÿ×���R�� �R�� ���"���@���E�=��K�=��N�=��O�=��`���ç�=��é���R� �R�ÿ\� ÿ\�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7ÿ×�8ÿì�9ÿ×�:ÿ×�<ÿÃ�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�›ÿì�œÿì�ÿì�žÿì�ŸÿÃ�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿ×&ÿ×*ÿì,ÿì.ÿì0ÿì2ÿì4ÿì6ÿ×8ÿÃ:ÿÃGÿ×úÿ×üÿ×þÿ×�ÿÃÿ\ ÿ\_ÿ×aÿìIÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×aÿìcÿìeÿìgÿìiÿìkÿìmÿìoÿÃqÿÃsÿÃÿ×�ÿ\� ÿ\�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7ÿ×�8ÿì�9ÿ×�:ÿ×�<ÿÃ�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�›ÿì�œÿì�ÿì�žÿì�ŸÿÃ�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿ×&ÿ×*ÿì,ÿì.ÿì0ÿì2ÿì4ÿì6ÿ×8ÿÃ:ÿÃGÿ×úÿ×üÿ×þÿ×�ÿÃÿ\ ÿ\_ÿ×aÿìIÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×aÿìcÿìeÿìgÿìiÿìkÿìmÿìoÿÃqÿÃsÿÃÿ×�ÿì� ÿìÿì ÿì�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�-�{��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×$�ÿ…$�ÿ®$�ÿ…$�"�)$�$ÿq$�&ÿ×$�*ÿ×$�2ÿ×$�4ÿ×$�7�)$�Dÿ\$�Fÿq$�Gÿq$�Hÿq$�Jÿq$�Pÿš$�Qÿš$�Rÿq$�Sÿš$�Tÿq$�Uÿš$�Vÿ…$�Xÿš$�Yÿ×$�Zÿ×$�[ÿ×$�\ÿ×$�]ÿ®$�‚ÿq$�ƒÿq$�„ÿq$�…ÿq$�†ÿq$�‡ÿq$�‰ÿ×$�”ÿ×$�•ÿ×$�–ÿ×$�—ÿ×$�˜ÿ×$�šÿ×$�¢ÿq$�£ÿ\$�¤ÿ\$�¥ÿ\$�¦ÿ\$�§ÿ\$�¨ÿ\$�©ÿq$�ªÿq$�«ÿq$�¬ÿq$�­ÿq$�´ÿq$�µÿq$�¶ÿq$�·ÿq$�¸ÿq$�ºÿq$�»ÿš$�¼ÿš$�½ÿš$�¾ÿš$�¿ÿ×$�Âÿq$�Ãÿ\$�Äÿq$�Åÿ\$�Æÿq$�Çÿ\$�Èÿ×$�Éÿq$�Êÿ×$�Ëÿq$�Ìÿ×$�Íÿq$�Îÿ×$�Ïÿq$�Ñÿq$�Óÿq$�Õÿq$�×ÿq$�Ùÿq$�Ûÿq$�Ýÿq$�Þÿ×$�ßÿq$�àÿ×$�áÿq$�âÿ×$�ãÿq$�äÿ×$�åÿq$�úÿš$ÿš$ÿš$ ÿš$ÿ×$ÿq$ÿ×$ÿq$ÿ×$ÿq$ÿ×$ÿq$ÿš$ÿš$ÿ…$!ÿ…$$�)$&�)$+ÿš$-ÿš$/ÿš$1ÿš$3ÿš$5ÿš$7ÿ×$<ÿ®$>ÿ®$@ÿ®$Cÿq$Dÿ\$Fÿ\$Gÿ×$Hÿq$Jÿ…$ûÿ×$ýÿ×$ÿ®$ÿ®$ÿ®$ÿ…$ ÿ…$Wÿš$Xÿq$Yÿ\$_ÿ×$`ÿq$bÿš$ÿq$ÿ\$ÿq$ ÿ\$!ÿq$"ÿ\$#ÿq$%ÿq$&ÿ\$'ÿq$(ÿ\$)ÿq$*ÿ\$+ÿq$,ÿ\$-ÿq$.ÿ\$/ÿq$0ÿ\$1ÿq$2ÿ\$3ÿq$4ÿ\$6ÿq$8ÿq$:ÿq$<ÿq$@ÿq$Bÿq$Dÿq$Iÿ×$Jÿq$Kÿ×$Lÿq$Mÿ×$Nÿq$Oÿ×$Qÿ×$Rÿq$Sÿ×$Tÿq$Uÿ×$Vÿq$Wÿ×$Xÿq$Yÿ×$Zÿq$[ÿ×$\ÿq$]ÿ×$^ÿq$_ÿ×$`ÿq$bÿš$dÿš$fÿš$hÿš$jÿš$lÿš$nÿš$pÿ×$�)%��)%� �)%�)% �)&�ÿ…&�ÿ®&�ÿ…&�"�)&�$ÿq&�&ÿ×&�*ÿ×&�2ÿ×&�4ÿ×&�7�)&�Dÿ\&�Fÿq&�Gÿq&�Hÿq&�Jÿq&�Pÿš&�Qÿš&�Rÿq&�Sÿš&�Tÿq&�Uÿš&�Vÿ…&�Xÿš&�Yÿ×&�Zÿ×&�[ÿ×&�\ÿ×&�]ÿ®&�‚ÿq&�ƒÿq&�„ÿq&�…ÿq&�†ÿq&�‡ÿq&�‰ÿ×&�”ÿ×&�•ÿ×&�–ÿ×&�—ÿ×&�˜ÿ×&�šÿ×&�¢ÿq&�£ÿ\&�¤ÿ\&�¥ÿ\&�¦ÿ\&�§ÿ\&�¨ÿ\&�©ÿq&�ªÿq&�«ÿq&�¬ÿq&�­ÿq&�´ÿq&�µÿq&�¶ÿq&�·ÿq&�¸ÿq&�ºÿq&�»ÿš&�¼ÿš&�½ÿš&�¾ÿš&�¿ÿ×&�Âÿq&�Ãÿ\&�Äÿq&�Åÿ\&�Æÿq&�Çÿ\&�Èÿ×&�Éÿq&�Êÿ×&�Ëÿq&�Ìÿ×&�Íÿq&�Îÿ×&�Ïÿq&�Ñÿq&�Óÿq&�Õÿq&�×ÿq&�Ùÿq&�Ûÿq&�Ýÿq&�Þÿ×&�ßÿq&�àÿ×&�áÿq&�âÿ×&�ãÿq&�äÿ×&�åÿq&�úÿš&ÿš&ÿš& ÿš&ÿ×&ÿq&ÿ×&ÿq&ÿ×&ÿq&ÿ×&ÿq&ÿš&ÿš&ÿ…&!ÿ…&$�)&&�)&+ÿš&-ÿš&/ÿš&1ÿš&3ÿš&5ÿš&7ÿ×&<ÿ®&>ÿ®&@ÿ®&Cÿq&Dÿ\&Fÿ\&Gÿ×&Hÿq&Jÿ…&ûÿ×&ýÿ×&ÿ®&ÿ®&ÿ®&ÿ…& ÿ…&Wÿš&Xÿq&Yÿ\&_ÿ×&`ÿq&bÿš&ÿq&ÿ\&ÿq& ÿ\&!ÿq&"ÿ\&#ÿq&%ÿq&&ÿ\&'ÿq&(ÿ\&)ÿq&*ÿ\&+ÿq&,ÿ\&-ÿq&.ÿ\&/ÿq&0ÿ\&1ÿq&2ÿ\&3ÿq&4ÿ\&6ÿq&8ÿq&:ÿq&<ÿq&@ÿq&Bÿq&Dÿq&Iÿ×&Jÿq&Kÿ×&Lÿq&Mÿ×&Nÿq&Oÿ×&Qÿ×&Rÿq&Sÿ×&Tÿq&Uÿ×&Vÿq&Wÿ×&Xÿq&Yÿ×&Zÿq&[ÿ×&\ÿq&]ÿ×&^ÿq&_ÿ×&`ÿq&bÿš&dÿš&fÿš&hÿš&jÿš&lÿš&nÿš&pÿ×&�)'��)'� �)'�)' �)(�ÿ…(�ÿ®(�ÿ…(�"�)(�$ÿq(�&ÿ×(�*ÿ×(�2ÿ×(�4ÿ×(�7�)(�Dÿ\(�Fÿq(�Gÿq(�Hÿq(�Jÿq(�Pÿš(�Qÿš(�Rÿq(�Sÿš(�Tÿq(�Uÿš(�Vÿ…(�Xÿš(�Yÿ×(�Zÿ×(�[ÿ×(�\ÿ×(�]ÿ®(�‚ÿq(�ƒÿq(�„ÿq(�…ÿq(�†ÿq(�‡ÿq(�‰ÿ×(�”ÿ×(�•ÿ×(�–ÿ×(�—ÿ×(�˜ÿ×(�šÿ×(�¢ÿq(�£ÿ\(�¤ÿ\(�¥ÿ\(�¦ÿ\(�§ÿ\(�¨ÿ\(�©ÿq(�ªÿq(�«ÿq(�¬ÿq(�­ÿq(�´ÿq(�µÿq(�¶ÿq(�·ÿq(�¸ÿq(�ºÿq(�»ÿš(�¼ÿš(�½ÿš(�¾ÿš(�¿ÿ×(�Âÿq(�Ãÿ\(�Äÿq(�Åÿ\(�Æÿq(�Çÿ\(�Èÿ×(�Éÿq(�Êÿ×(�Ëÿq(�Ìÿ×(�Íÿq(�Îÿ×(�Ïÿq(�Ñÿq(�Óÿq(�Õÿq(�×ÿq(�Ùÿq(�Ûÿq(�Ýÿq(�Þÿ×(�ßÿq(�àÿ×(�áÿq(�âÿ×(�ãÿq(�äÿ×(�åÿq(�úÿš(ÿš(ÿš( ÿš(ÿ×(ÿq(ÿ×(ÿq(ÿ×(ÿq(ÿ×(ÿq(ÿš(ÿš(ÿ…(!ÿ…($�)(&�)(+ÿš(-ÿš(/ÿš(1ÿš(3ÿš(5ÿš(7ÿ×(<ÿ®(>ÿ®(@ÿ®(Cÿq(Dÿ\(Fÿ\(Gÿ×(Hÿq(Jÿ…(ûÿ×(ýÿ×(ÿ®(ÿ®(ÿ®(ÿ…( ÿ…(Wÿš(Xÿq(Yÿ\(_ÿ×(`ÿq(bÿš(ÿq(ÿ\(ÿq( ÿ\(!ÿq("ÿ\(#ÿq(%ÿq(&ÿ\('ÿq((ÿ\()ÿq(*ÿ\(+ÿq(,ÿ\(-ÿq(.ÿ\(/ÿq(0ÿ\(1ÿq(2ÿ\(3ÿq(4ÿ\(6ÿq(8ÿq(:ÿq(<ÿq(@ÿq(Bÿq(Dÿq(Iÿ×(Jÿq(Kÿ×(Lÿq(Mÿ×(Nÿq(Oÿ×(Qÿ×(Rÿq(Sÿ×(Tÿq(Uÿ×(Vÿq(Wÿ×(Xÿq(Yÿ×(Zÿq([ÿ×(\ÿq(]ÿ×(^ÿq(_ÿ×(`ÿq(bÿš(dÿš(fÿš(hÿš(jÿš(lÿš(nÿš(pÿ×(�)*�ÿ×*�ÿ×*�$ÿì*�‚ÿì*�ƒÿì*�„ÿì*�…ÿì*�†ÿì*�‡ÿì*�Âÿì*�Äÿì*�Æÿì*Cÿì*ÿ×* ÿ×*Xÿì*ÿì*ÿì*!ÿì*#ÿì*%ÿì*'ÿì*)ÿì*+ÿì*-ÿì*/ÿì*1ÿì*3ÿì,�ÿ×,�ÿ×,�$ÿì,�‚ÿì,�ƒÿì,�„ÿì,�…ÿì,�†ÿì,�‡ÿì,�Âÿì,�Äÿì,�Æÿì,Cÿì,ÿ×, ÿ×,Xÿì,ÿì,ÿì,!ÿì,#ÿì,%ÿì,'ÿì,)ÿì,+ÿì,-ÿì,/ÿì,1ÿì,3ÿì.�ÿ×.�ÿ×.�$ÿì.�‚ÿì.�ƒÿì.�„ÿì.�…ÿì.�†ÿì.�‡ÿì.�Âÿì.�Äÿì.�Æÿì.Cÿì.ÿ×. ÿ×.Xÿì.ÿì.ÿì.!ÿì.#ÿì.%ÿì.'ÿì.)ÿì.+ÿì.-ÿì./ÿì.1ÿì.3ÿì0�ÿ×0�ÿ×0�$ÿì0�‚ÿì0�ƒÿì0�„ÿì0�…ÿì0�†ÿì0�‡ÿì0�Âÿì0�Äÿì0�Æÿì0Cÿì0ÿ×0 ÿ×0Xÿì0ÿì0ÿì0!ÿì0#ÿì0%ÿì0'ÿì0)ÿì0+ÿì0-ÿì0/ÿì01ÿì03ÿì2�ÿ×2�ÿ×2�$ÿì2�‚ÿì2�ƒÿì2�„ÿì2�…ÿì2�†ÿì2�‡ÿì2�Âÿì2�Äÿì2�Æÿì2Cÿì2ÿ×2 ÿ×2Xÿì2ÿì2ÿì2!ÿì2#ÿì2%ÿì2'ÿì2)ÿì2+ÿì2-ÿì2/ÿì21ÿì23ÿì4�ÿ×4�ÿ×4�$ÿì4�‚ÿì4�ƒÿì4�„ÿì4�…ÿì4�†ÿì4�‡ÿì4�Âÿì4�Äÿì4�Æÿì4Cÿì4ÿ×4 ÿ×4Xÿì4ÿì4ÿì4!ÿì4#ÿì4%ÿì4'ÿì4)ÿì4+ÿì4-ÿì4/ÿì41ÿì43ÿì6�ÿš6�ÿš6�"�)6�$ÿ®6�&ÿì6�*ÿì6�2ÿì6�4ÿì6�Dÿ×6�Fÿ×6�Gÿ×6�Hÿ×6�Jÿì6�Pÿì6�Qÿì6�Rÿ×6�Sÿì6�Tÿ×6�Uÿì6�Vÿì6�Xÿì6�‚ÿ®6�ƒÿ®6�„ÿ®6�…ÿ®6�†ÿ®6�‡ÿ®6�‰ÿì6�”ÿì6�•ÿì6�–ÿì6�—ÿì6�˜ÿì6�šÿì6�¢ÿ×6�£ÿ×6�¤ÿ×6�¥ÿ×6�¦ÿ×6�§ÿ×6�¨ÿ×6�©ÿ×6�ªÿ×6�«ÿ×6�¬ÿ×6�­ÿ×6�´ÿ×6�µÿ×6�¶ÿ×6�·ÿ×6�¸ÿ×6�ºÿ×6�»ÿì6�¼ÿì6�½ÿì6�¾ÿì6�Âÿ®6�Ãÿ×6�Äÿ®6�Åÿ×6�Æÿ®6�Çÿ×6�Èÿì6�Éÿ×6�Êÿì6�Ëÿ×6�Ìÿì6�Íÿ×6�Îÿì6�Ïÿ×6�Ñÿ×6�Óÿ×6�Õÿ×6�×ÿ×6�Ùÿ×6�Ûÿ×6�Ýÿ×6�Þÿì6�ßÿì6�àÿì6�áÿì6�âÿì6�ãÿì6�äÿì6�åÿì6�úÿì6ÿì6ÿì6 ÿì6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿì6ÿì6!ÿì6+ÿì6-ÿì6/ÿì61ÿì63ÿì65ÿì6Cÿ®6Dÿ×6Fÿ×6Gÿì6Hÿ×6Jÿì6ÿš6 ÿš6Wÿì6Xÿ®6Yÿ×6_ÿì6`ÿ×6bÿì6ÿ®6ÿ×6ÿ®6 ÿ×6!ÿ®6"ÿ×6#ÿ®6%ÿ®6&ÿ×6'ÿ®6(ÿ×6)ÿ®6*ÿ×6+ÿ®6,ÿ×6-ÿ®6.ÿ×6/ÿ®60ÿ×61ÿ®62ÿ×63ÿ®64ÿ×66ÿ×68ÿ×6:ÿ×6<ÿ×6@ÿ×6Bÿ×6Dÿ×6Iÿì6Jÿ×6Kÿì6Lÿ×6Mÿì6Nÿ×6Oÿì6Qÿì6Rÿ×6Sÿì6Tÿ×6Uÿì6Vÿ×6Wÿì6Xÿ×6Yÿì6Zÿ×6[ÿì6\ÿ×6]ÿì6^ÿ×6_ÿì6`ÿ×6bÿì6dÿì6fÿì6hÿì6jÿì6lÿì6nÿì7��R7� �R7�ÿ®7�ÿ®7�"�)7�R7ÿ®7 �R7 ÿ®8�ÿ…8�ÿ…8�"�)8�$ÿ…8�&ÿ×8�*ÿ×8�2ÿ×8�4ÿ×8�Dÿš8�Fÿš8�Gÿš8�Hÿš8�Jÿ×8�PÿÃ8�QÿÃ8�Rÿš8�SÿÃ8�Tÿš8�UÿÃ8�Vÿ®8�XÿÃ8�]ÿ×8�‚ÿ…8�ƒÿ…8�„ÿ…8�…ÿ…8�†ÿ…8�‡ÿ…8�‰ÿ×8�”ÿ×8�•ÿ×8�–ÿ×8�—ÿ×8�˜ÿ×8�šÿ×8�¢ÿš8�£ÿš8�¤ÿš8�¥ÿš8�¦ÿš8�§ÿš8�¨ÿš8�©ÿš8�ªÿš8�«ÿš8�¬ÿš8�­ÿš8�´ÿš8�µÿš8�¶ÿš8�·ÿš8�¸ÿš8�ºÿš8�»ÿÃ8�¼ÿÃ8�½ÿÃ8�¾ÿÃ8�Âÿ…8�Ãÿš8�Äÿ…8�Åÿš8�Æÿ…8�Çÿš8�Èÿ×8�Éÿš8�Êÿ×8�Ëÿš8�Ìÿ×8�Íÿš8�Îÿ×8�Ïÿš8�Ñÿš8�Óÿš8�Õÿš8�×ÿš8�Ùÿš8�Ûÿš8�Ýÿš8�Þÿ×8�ßÿ×8�àÿ×8�áÿ×8�âÿ×8�ãÿ×8�äÿ×8�åÿ×8�úÿÃ8ÿÃ8ÿÃ8 ÿÃ8ÿ×8ÿš8ÿ×8ÿš8ÿ×8ÿš8ÿ×8ÿš8ÿÃ8ÿÃ8ÿ®8!ÿ®8+ÿÃ8-ÿÃ8/ÿÃ81ÿÃ83ÿÃ85ÿÃ8<ÿ×8>ÿ×8@ÿ×8Cÿ…8Dÿš8Fÿš8Gÿ×8Hÿš8Jÿ®8ÿ…8 ÿ…8WÿÃ8Xÿ…8Yÿš8_ÿ×8`ÿš8bÿÃ8ÿ…8ÿš8ÿ…8 ÿš8!ÿ…8"ÿš8#ÿ…8%ÿ…8&ÿš8'ÿ…8(ÿš8)ÿ…8*ÿš8+ÿ…8,ÿš8-ÿ…8.ÿš8/ÿ…80ÿš81ÿ…82ÿš83ÿ…84ÿš86ÿš88ÿš8:ÿš8<ÿš8@ÿš8Bÿš8Dÿš8Iÿ×8Jÿš8Kÿ×8Lÿš8Mÿ×8Nÿš8Oÿ×8Qÿ×8Rÿš8Sÿ×8Tÿš8Uÿ×8Vÿš8Wÿ×8Xÿš8Yÿ×8Zÿš8[ÿ×8\ÿš8]ÿ×8^ÿš8_ÿ×8`ÿš8bÿÃ8dÿÃ8fÿÃ8hÿÃ8jÿÃ8lÿÃ8nÿÃ9��R9� �R9�ÿ®9�ÿ®9�"�)9�R9ÿ®9 �R9 ÿ®:�ÿ…:�ÿ…:�"�):�$ÿ…:�&ÿ×:�*ÿ×:�2ÿ×:�4ÿ×:�Dÿš:�Fÿš:�Gÿš:�Hÿš:�Jÿ×:�PÿÃ:�QÿÃ:�Rÿš:�SÿÃ:�Tÿš:�UÿÃ:�Vÿ®:�XÿÃ:�]ÿ×:�‚ÿ…:�ƒÿ…:�„ÿ…:�…ÿ…:�†ÿ…:�‡ÿ…:�‰ÿ×:�”ÿ×:�•ÿ×:�–ÿ×:�—ÿ×:�˜ÿ×:�šÿ×:�¢ÿš:�£ÿš:�¤ÿš:�¥ÿš:�¦ÿš:�§ÿš:�¨ÿš:�©ÿš:�ªÿš:�«ÿš:�¬ÿš:�­ÿš:�´ÿš:�µÿš:�¶ÿš:�·ÿš:�¸ÿš:�ºÿš:�»ÿÃ:�¼ÿÃ:�½ÿÃ:�¾ÿÃ:�Âÿ…:�Ãÿš:�Äÿ…:�Åÿš:�Æÿ…:�Çÿš:�Èÿ×:�Éÿš:�Êÿ×:�Ëÿš:�Ìÿ×:�Íÿš:�Îÿ×:�Ïÿš:�Ñÿš:�Óÿš:�Õÿš:�×ÿš:�Ùÿš:�Ûÿš:�Ýÿš:�Þÿ×:�ßÿ×:�àÿ×:�áÿ×:�âÿ×:�ãÿ×:�äÿ×:�åÿ×:�úÿÃ:ÿÃ:ÿÃ: ÿÃ:ÿ×:ÿš:ÿ×:ÿš:ÿ×:ÿš:ÿ×:ÿš:ÿÃ:ÿÃ:ÿ®:!ÿ®:+ÿÃ:-ÿÃ:/ÿÃ:1ÿÃ:3ÿÃ:5ÿÃ:<ÿ×:>ÿ×:@ÿ×:Cÿ…:Dÿš:Fÿš:Gÿ×:Hÿš:Jÿ®:ÿ…: ÿ…:WÿÃ:Xÿ…:Yÿš:_ÿ×:`ÿš:bÿÃ:ÿ…:ÿš:ÿ…: ÿš:!ÿ…:"ÿš:#ÿ…:%ÿ…:&ÿš:'ÿ…:(ÿš:)ÿ…:*ÿš:+ÿ…:,ÿš:-ÿ…:.ÿš:/ÿ…:0ÿš:1ÿ…:2ÿš:3ÿ…:4ÿš:6ÿš:8ÿš::ÿš:<ÿš:@ÿš:Bÿš:Dÿš:Iÿ×:Jÿš:Kÿ×:Lÿš:Mÿ×:Nÿš:Oÿ×:Qÿ×:Rÿš:Sÿ×:Tÿš:Uÿ×:Vÿš:Wÿ×:Xÿš:Yÿ×:Zÿš:[ÿ×:\ÿš:]ÿ×:^ÿš:_ÿ×:`ÿš:bÿÃ:dÿÃ:fÿÃ:hÿÃ:jÿÃ:lÿÃ:nÿÃ;�&ÿì;�*ÿì;�2ÿì;�4ÿì;�‰ÿì;�”ÿì;�•ÿì;�–ÿì;�—ÿì;�˜ÿì;�šÿì;�Èÿì;�Êÿì;�Ìÿì;�Îÿì;�Þÿì;�àÿì;�âÿì;�äÿì;ÿì;ÿì;ÿì;ÿì;Gÿì;_ÿì;Iÿì;Kÿì;Mÿì;Oÿì;Qÿì;Sÿì;Uÿì;Wÿì;Yÿì;[ÿì;]ÿì;_ÿì=�&ÿì=�*ÿì=�2ÿì=�4ÿì=�‰ÿì=�”ÿì=�•ÿì=�–ÿì=�—ÿì=�˜ÿì=�šÿì=�Èÿì=�Êÿì=�Ìÿì=�Îÿì=�Þÿì=�àÿì=�âÿì=�äÿì=ÿì=ÿì=ÿì=ÿì=Gÿì=_ÿì=Iÿì=Kÿì=Mÿì=Oÿì=Qÿì=Sÿì=Uÿì=Wÿì=Yÿì=[ÿì=]ÿì=_ÿì?�&ÿì?�*ÿì?�2ÿì?�4ÿì?�‰ÿì?�”ÿì?�•ÿì?�–ÿì?�—ÿì?�˜ÿì?�šÿì?�Èÿì?�Êÿì?�Ìÿì?�Îÿì?�Þÿì?�àÿì?�âÿì?�äÿì?ÿì?ÿì?ÿì?ÿì?Gÿì?_ÿì?Iÿì?Kÿì?Mÿì?Oÿì?Qÿì?Sÿì?Uÿì?Wÿì?Yÿì?[ÿì?]ÿì?_ÿìC�ÿqC� ÿqC�&ÿ×C�*ÿ×C�- C�2ÿ×C�4ÿ×C�7ÿqC�9ÿ®C�:ÿ®C�<ÿ…C�‰ÿ×C�”ÿ×C�•ÿ×C�–ÿ×C�—ÿ×C�˜ÿ×C�šÿ×C�Ÿÿ…C�Èÿ×C�Êÿ×C�Ìÿ×C�Îÿ×C�Þÿ×C�àÿ×C�âÿ×C�äÿ×Cÿ×Cÿ×Cÿ×Cÿ×C$ÿqC&ÿqC6ÿ®C8ÿ…C:ÿ…CGÿ×Cúÿ®Cüÿ®Cþÿ®C�ÿ…CÿqC ÿqC_ÿ×CIÿ×CKÿ×CMÿ×COÿ×CQÿ×CSÿ×CUÿ×CWÿ×CYÿ×C[ÿ×C]ÿ×C_ÿ×Coÿ…Cqÿ…Csÿ…CÿqD�ÿìD� ÿìDÿìD ÿìE�-�{G�ÿ®G�ÿ®G�$ÿ×G�7ÿÃG�9ÿìG�:ÿìG�;ÿ×G�<ÿìG�=ÿìG�‚ÿ×G�ƒÿ×G�„ÿ×G�…ÿ×G�†ÿ×G�‡ÿ×G�ŸÿìG�Âÿ×G�Äÿ×G�Æÿ×G$ÿÃG&ÿÃG6ÿìG8ÿìG:ÿìG;ÿìG=ÿìG?ÿìGCÿ×G ÿìGúÿìGüÿìGþÿìG�ÿìGÿ®G ÿ®GXÿ×Gÿ×Gÿ×G!ÿ×G#ÿ×G%ÿ×G'ÿ×G)ÿ×G+ÿ×G-ÿ×G/ÿ×G1ÿ×G3ÿ×GoÿìGqÿìGsÿìGÿÃV�ÿqV� ÿqVfÿ×Vmÿ×VqÿqVrÿ…Vsÿ×Vuÿ®Vxÿ…VÿqV ÿqVTÿ…[�ÿ®[�ÿ®[Vÿ×[_ÿ×[bÿ×[dÿì[iÿ×[pÿì[qÿÃ[rÿì[tÿ×[uÿì[xÿì[ˆÿì[ÿ®[ ÿ®[Tÿì\�ÿ…\�ÿ…\Vÿ…\_ÿ…\bÿ…\fÿ×\iÿ…\mÿ×\sÿÃ\vÿì\yÿš\zÿ®\{ÿÃ\|ÿÃ\}ÿÃ\~ÿš\ÿÃ\‚ÿ®\„ÿÃ\†ÿÃ\‡ÿÃ\‰ÿÃ\Œÿš\Žÿš\ÿš\ÿš\’ÿÃ\“ÿš\•ÿÃ\–ÿÃ\˜ÿÃ\™ÿš\šÿÃ\›ÿÃ\ÿ…\ ÿ…\!ÿì]qÿ×]rÿì]xÿì]Tÿì^�ÿ×^� ÿ×^ÿ×^ ÿ×_�ÿq_� ÿq_fÿ×_mÿ×_qÿq_rÿ…_sÿ×_uÿ®_xÿ…_ÿq_ ÿq_Tÿ…`�ÿ®`�ÿ®`Vÿ×`_ÿ×`bÿ×`iÿ×`tÿ×`ÿ®` ÿ®a�ÿ…a�ÿ®a�ÿ…aVÿ\a_ÿ\abÿ\afÿÃaiÿ\amÿÃasÿšavÿÃayÿqazÿša{ÿša|ÿ®a}ÿša~ÿqa€ÿ×aÿÃa‚ÿša„ÿša†ÿ®a‡ÿša‰ÿšaŠÿ×aŒÿqaŽÿšaÿqaÿqa’ÿša“ÿqa”ÿ×a•ÿša–ÿša˜ÿša™ÿqašÿša›ÿšaÿ®aÿ®aÿ®aÿ…a ÿ…a!ÿÃaSÿ×b�ÿqb� ÿqbfÿ×bmÿ×bqÿqbrÿ…bsÿ×buÿ®bxÿ…bÿqb ÿqbTÿ…dfÿìdmÿìdsÿÃf�ÿ®f�ÿ®fVÿ×f_ÿ×fbÿ×fdÿìfiÿ×fpÿìfqÿÃfrÿìftÿ×fuÿìfxÿìfˆÿìfÿ®f ÿ®fTÿìhfÿ×hmÿ×hsÿÃhÿìh‘ÿìi�ÿqi� ÿqifÿ×imÿ×iqÿqirÿ…isÿ×iuÿ®ixÿ…iÿqi ÿqiTÿ…m�ÿ®m�ÿ®mVÿ×m_ÿ×mbÿ×mdÿìmiÿ×mpÿìmqÿÃmrÿìmtÿ×muÿìmxÿìmˆÿìmÿ®m ÿ®mTÿìo�þöo�þöoVÿšo_ÿšobÿšodÿìoiÿšotÿ×oˆÿ×oþöo þöq�ÿ…q�ÿ®q�ÿ…qVÿ\q_ÿ\qbÿ\qfÿÃqiÿ\qmÿÃqsÿšqvÿÃqyÿqqzÿšq{ÿšq|ÿ®q}ÿšq~ÿqq€ÿ×qÿÃq‚ÿšq„ÿšq†ÿ®q‡ÿšq‰ÿšqŠÿ×qŒÿqqŽÿšqÿqqÿqq’ÿšq“ÿqq”ÿ×q•ÿšq–ÿšq˜ÿšq™ÿqqšÿšq›ÿšqÿ®qÿ®qÿ®qÿ…q ÿ…q!ÿÃqSÿ×r�ÿ…r�ÿ…rVÿ…r_ÿ…rbÿ…rfÿ×riÿ…rmÿ×rsÿÃrvÿìryÿšrzÿ®r{ÿÃr|ÿÃr}ÿÃr~ÿšrÿÃr‚ÿ®r„ÿÃr†ÿÃr‡ÿÃr‰ÿÃrŒÿšrŽÿšrÿšrÿšr’ÿÃr“ÿšr•ÿÃr–ÿÃr˜ÿÃr™ÿšršÿÃr›ÿÃrÿ…r ÿ…r!ÿìs�ÿšs�ÿšsVÿ×s_ÿ×sbÿ×sdÿÃsiÿ×spÿìsqÿ®srÿÃstÿìsxÿÃsˆÿìsÿšs ÿšsTÿÃtfÿ×tmÿ×tsÿÃtÿìt‘ÿìu�ÿ…u�ÿ…uVÿ®u_ÿ®ubÿ®ufÿìuiÿ®umÿìuÿ…u ÿ…vqÿ×vrÿìvxÿìvTÿìx�ÿ…x�ÿ…xVÿ…x_ÿ…xbÿ…xfÿ×xiÿ…xmÿ×xsÿÃxvÿìxyÿšxzÿ®x{ÿÃx|ÿÃx}ÿÃx~ÿšxÿÃx‚ÿ®x„ÿÃx†ÿÃx‡ÿÃx‰ÿÃxŒÿšxŽÿšxÿšxÿšx’ÿÃx“ÿšx•ÿÃx–ÿÃx˜ÿÃx™ÿšxšÿÃx›ÿÃxÿ…x ÿ…x!ÿìyˆ�){�ÿì{� ÿì{ÿì{ ÿì|�ÿ®|� ÿ®|ÿì|‘ÿì|ÿ®| ÿ®~ˆ�)€�ÿ®€�ÿ®€ˆÿì€ÿ®€ ÿ®ƒ�ÿšƒyÿ׃~ÿ׃ÿ׃Œÿ׃ÿ׃ÿ׃ÿ׃‘ÿ׃“ÿ׃™ÿ׃ÿšƒÿšƒÿš„�ÿì„� ÿì„ÿì„ ÿì…�ÿ×…�ÿ×…ÿ×… ÿ׆�ÿ®†� ÿ®†ÿ솑ÿì†ÿ®† ÿ®‡yÿׇ~ÿׇŒÿׇÿׇÿׇ“ÿׇ™ÿ׈�ÿ…ˆ� ÿ…ˆyÿìˆ~ÿ숀ÿ׈Šÿ׈Œÿìˆÿ׈ÿìˆÿ숑ÿ׈“ÿ숙ÿìˆÿ…ˆ ÿ…Š�ÿ®Š�ÿ®ŠˆÿìŠÿ®Š ÿ®Œ�ÿìŒ� ÿ쌀ÿ׌Šÿ׌ÿìŒ ÿìŽ�ÿìŽ� ÿ쎀ÿ׎Šÿ׎ÿìŽ ÿì�ÿì�ÿìÿì ÿì“�ÿì“� ÿì“€ÿדŠÿדÿì“ ÿì”�ÿÔ�ÿ×”�ÿÔyÿ×”~ÿ×”ÿ×”Œÿ×”ÿ×”ÿ×”“ÿ×”™ÿ×”ÿ×”ÿ×”ÿ×”ÿÔ ÿ×�ÿ×—� ÿ×—ÿ×— ÿ×™�ÿì™� ÿ와ÿ×™Šÿ×™ÿì™ ÿì�ÿ®� ÿ®ÿ…¦ÿ…¨ÿ×¼ÿš½ÿ×ÁÿšÄÿ…Üÿ×Ýÿ×áÿ×äÿ×öÿ×ÿ® ÿ®nÿ®|ÿš€ÿ®‚ÿ®—ÿ®›ÿ®§ÿ®©ÿ…ªÿ×µÿš¶ÿ×·ÿš¸ÿ×¹ÿšºÿ×½ÿ…¾ÿ׿ÿšÀÿ×ÁÿšÂÿ×ÔÿšÕÿ×÷ÿ×øÿ×ùÿ×úÿ×ûÿ×üÿ×ýÿšþÿ×ÿ® ÿšÿÃÿšÿÃÿ…ÿמ�ÿ…ž�ÿ®ž�ÿ…žŸÿמ¤ÿšžªÿqž®ÿšžµÿšž¸ÿמ»ÿמ¼�)ž¾ÿ®žÌÿšžÍÿšžÎÿ…žÏÿqžÐÿמÑÿמÒÿšžÓÿšžÔÿšžÕÿ…žÖÿšž×ÿšžØÿqžÙÿšžÚÿšžÛÿqžÜÿ®žÝÿ®žÞÿqžßÿמàÿšžáÿšžâÿšžãÿšžäÿ®žåÿšžæÿšžçÿמèÿšžéÿÞêÿqžìÿšžíÿqžîÿ…žòÿ…žóÿšžõÿšžöÿ®ž÷ÿšžùÿšžÿ®žÿ®žÿ®žÿ…ž ÿ…žjÿqžkÿšžlÿמmÿמqÿšžrÿqžsÿ…žuÿšžwÿšžyÿšž}ÿšž~ÿמÿqžÿמƒÿמ„ÿמ…ÿqž†ÿמ‡ÿqžˆÿמ‰ÿqžŠÿמ‹ÿמŒÿמÿqž–ÿšžšÿšžžÿšž ÿמ¢ÿמ¤ÿšž¦ÿšžªÿ®ž¬ÿšž®ÿšž°ÿšž±ÿמ²ÿqž³ÿמ´ÿqžµ�)ž¶ÿ®ž¸ÿ®žºÿ®ž¼ÿמ¾ÿ®žÀÿšžÂÿšžÄÿšžÅÿšžÆÿqžÇÿšžÈÿqžËÿמÍÿšžÎÿšžÏÿ…žÑÿšžÓÿšžÕÿšž×ÿšžÙÿqžÛÿqžÝÿqžàÿqžæÿמèÿמêÿÞìÿšžîÿšžïÿמðÿqžñÿמòÿqžóÿמôÿqžöÿמøÿ®žúÿ®žüÿ®žþÿšž�ÿšžÿšžÿמÿמ ÿqž ÿqž ÿqž ÿqžÿšžÿšžÿšžÿ…žÿšžÿמÿqžÿ®žÿqžÿšžÿ…ŸŸÿן¸ÿן»ÿן¾ÿןáÿןlÿן~ÿן„ÿן†ÿןˆÿןŠÿןŒÿן±ÿן³ÿןÀÿןÂÿןÅÿןÇÿןÕÿןïÿןñÿןóÿןþÿן ÿן ÿןÿןÿןÿ× ÿ× ÿפ�ÿ®¤� ÿ®¤ÿ…¤¦ÿ…¤¨ÿפ¼ÿš¤½ÿפÁÿš¤Äÿ…¤ÜÿפÝÿפáÿפäÿפöÿפÿ®¤ ÿ®¤nÿ®¤|ÿš¤€ÿ®¤‚ÿ®¤—ÿ®¤›ÿ®¤§ÿ®¤©ÿ…¤ªÿפµÿš¤¶ÿפ·ÿš¤¸ÿפ¹ÿš¤ºÿפ½ÿ…¤¾ÿפ¿ÿš¤ÀÿפÁÿš¤ÂÿפÔÿš¤Õÿפ÷ÿפøÿפùÿפúÿפûÿפüÿפýÿš¤þÿפÿ®¤ ÿš¤ÿäÿš¤ÿäÿ…¤ÿ×¥�ÿ®¥� ÿ®¥ÿ…¥¦ÿ…¥¨ÿ×¥¼ÿš¥½ÿ×¥Áÿš¥Äÿ…¥Üÿ×¥Ýÿ×¥áÿ×¥äÿ×¥öÿ×¥ÿ®¥ ÿ®¥nÿ®¥|ÿš¥€ÿ®¥‚ÿ®¥—ÿ®¥›ÿ®¥§ÿ®¥©ÿ…¥ªÿ×¥µÿš¥¶ÿ×¥·ÿš¥¸ÿ×¥¹ÿš¥ºÿ×¥½ÿ…¥¾ÿ×¥¿ÿš¥Àÿ×¥Áÿš¥Âÿ×¥Ôÿš¥Õÿ×¥÷ÿ×¥øÿ×¥ùÿ×¥úÿ×¥ûÿ×¥üÿ×¥ýÿš¥þÿ×¥ÿ®¥ ÿš¥ÿÃ¥ÿš¥ÿÃ¥ÿ…¥ÿצ�ÿ®¦� ÿ®¦ÿ…¦¦ÿ…¦¨ÿצ¼ÿš¦½ÿצÁÿš¦Äÿ…¦ÜÿצÝÿצáÿצäÿצöÿצÿ®¦ ÿ®¦nÿ®¦|ÿš¦€ÿ®¦‚ÿ®¦—ÿ®¦›ÿ®¦§ÿ®¦©ÿ…¦ªÿצµÿš¦¶ÿצ·ÿš¦¸ÿצ¹ÿš¦ºÿצ½ÿ…¦¾ÿצ¿ÿš¦ÀÿצÁÿš¦ÂÿצÔÿš¦Õÿצ÷ÿצøÿצùÿצúÿצûÿצüÿצýÿš¦þÿצÿ®¦ ÿš¦ÿæÿš¦ÿæÿ…¦ÿ×§Ÿÿ×§¸ÿ×§»ÿ×§¾ÿ×§Áÿ×§áÿ×§lÿ×§|ÿ×§~ÿ×§„ÿ×§†ÿ×§ˆÿ×§Šÿ×§Œÿ×§±ÿ×§³ÿ×§¿ÿ×§Àÿ×§Áÿ×§Âÿ×§Åÿš§Çÿš§Ôÿ×§Õÿ×§ïÿ×§ñÿ×§óÿ×§ýÿ×§þÿ×§ ÿ×§ ÿ×§ÿ×§ÿ×§ÿ×§ÿì¨�ÿ…¨�ÿ…¨Ÿÿ쨤ÿš¨ªÿq¨®ÿš¨µÿš¨¸ÿ쨻ÿ쨾ÿèÉÿì¨Îÿ®¨ÏÿרÕÿ®¨ØÿרÛÿרÞÿרáÿרêÿרë�f¨íÿרîÿì¨òÿ®¨ô�f¨ÿ…¨ ÿ…¨jÿרlÿì¨rÿq¨sÿ®¨~ÿì¨ÿר„ÿ쨅ÿר†ÿ쨇ÿרˆÿ쨉ÿרŠÿ쨌ÿì¨ÿר˜�f¨¨�f¨±ÿ쨲ÿר³ÿ쨴ÿרÀÿרÂÿרÅÿרÆÿèÇÿרÈÿèÎÿš¨Ïÿ®¨ÕÿרÙÿq¨Ûÿq¨Ýÿq¨àÿרïÿì¨ðÿרñÿì¨òÿרóÿì¨ôÿרþÿר ÿq¨ ÿר ÿq¨ ÿרÿš¨ÿ®¨ÿì¨ÿרÿרÿš¨ÿ®ª�ÿqª� ÿqªÿšª¦ÿšª¼ÿqª¾ÿתÁÿšªÄÿšªÜÿתáÿתäÿתÿqª ÿqªnÿת|ÿšª€ÿ®ª‚ÿ®ª—ÿת›ÿת§ÿת©ÿšªªÿתµÿqª¶ÿת·ÿ…ª¹ÿ…ª½ÿšª¾ÿת¿ÿšªÀÿתÁÿšªÂÿתÅÿšªÇÿšªÔÿšªÕÿתáÿתãÿתýÿšªþÿתÿת ÿqªÿתÿqªÿתÿšªÿ׫�ÿ׫� ÿ׫ªÿì«Áÿ׫ÿ׫ ÿ׫rÿì«|ÿ׫¿ÿ׫Áÿ׫Åÿ׫Çÿ׫Ôÿ׫Ùÿì«Ûÿì«Ýÿì«ýÿ׬�ÿ®¬�ÿ®¬ÿ®¬ ÿ®¬€ÿ쬂ÿ쬷ÿ쬹ÿì¬ ÿ׬ÿ×­�ÿ…­�ÿ®­�ÿ…­Ÿÿ×­¤ÿš­ªÿq­®ÿš­µÿš­¸ÿ×­»ÿ×­¼�)­¾ÿ®­Ìÿš­Íÿš­Îÿ…­Ïÿq­Ðÿ×­Ñÿ×­Òÿš­Óÿš­Ôÿš­Õÿ…­Öÿš­×ÿš­Øÿq­Ùÿš­Úÿš­Ûÿq­Üÿ®­Ýÿ®­Þÿq­ßÿ×­àÿš­áÿš­âÿš­ãÿš­äÿ®­åÿš­æÿš­çÿ×­èÿš­éÿíêÿq­ìÿš­íÿq­îÿ…­òÿ…­óÿš­õÿš­öÿ®­÷ÿš­ùÿš­ÿ®­ÿ®­ÿ®­ÿ…­ ÿ…­jÿq­kÿš­lÿ×­mÿ×­qÿš­rÿq­sÿ…­uÿš­wÿš­yÿš­}ÿš­~ÿ×­ÿq­ÿ×­ƒÿ×­„ÿ×­…ÿq­†ÿ×­‡ÿq­ˆÿ×­‰ÿq­Šÿ×­‹ÿ×­Œÿ×­ÿq­–ÿš­šÿš­žÿš­ ÿ×­¢ÿ×­¤ÿš­¦ÿš­ªÿ®­¬ÿš­®ÿš­°ÿš­±ÿ×­²ÿq­³ÿ×­´ÿq­µ�)­¶ÿ®­¸ÿ®­ºÿ®­¼ÿ×­¾ÿ®­Àÿš­Âÿš­Äÿš­Åÿš­Æÿq­Çÿš­Èÿq­Ëÿ×­Íÿš­Îÿš­Ïÿ…­Ñÿš­Óÿš­Õÿš­×ÿš­Ùÿq­Ûÿq­Ýÿq­àÿq­æÿ×­èÿ×­êÿíìÿš­îÿš­ïÿ×­ðÿq­ñÿ×­òÿq­óÿ×­ôÿq­öÿ×­øÿ®­úÿ®­üÿ®­þÿš­�ÿš­ÿš­ÿ×­ÿ×­ ÿq­ ÿq­ ÿq­ ÿq­ÿš­ÿš­ÿš­ÿ…­ÿš­ÿ×­ÿq­ÿ®­ÿq­ÿš­ÿ…®£�á®ê�)®ÿ×®ÿ×°Ÿÿ×°¸ÿ×°»ÿ×°¾ÿ×°Áÿ×°áÿ×°lÿ×°|ÿ×°~ÿ×°„ÿ×°†ÿ×°ˆÿ×°Šÿ×°Œÿ×°±ÿ×°³ÿ×°¿ÿ×°Àÿ×°Áÿ×°Âÿ×°Åÿš°Çÿš°Ôÿ×°Õÿ×°ïÿ×°ñÿ×°óÿ×°ýÿ×°þÿ×° ÿ×° ÿ×°ÿ×°ÿ×°ÿ×°ÿì±�ÿ®±�ÿ®±ÿ®± ÿ®±€ÿ챂ÿì±·ÿì±¹ÿì± ÿ×±ÿ×´Ÿÿ×´¸ÿ×´»ÿ×´¾ÿ×´Áÿ×´áÿ×´lÿ×´|ÿ×´~ÿ×´„ÿ×´†ÿ×´ˆÿ×´Šÿ×´Œÿ×´±ÿ×´³ÿ×´¿ÿ×´Àÿ×´Áÿ×´Âÿ×´Åÿš´Çÿš´Ôÿ×´Õÿ×´ïÿ×´ñÿ×´óÿ×´ýÿ×´þÿ×´ ÿ×´ ÿ×´ÿ×´ÿ×´ÿ×´ÿì¸�ÿ®¸�ÿ®¸ÿ츤ÿ׸¦ÿ츨ÿ׸ªÿ׸®ÿ׸°ÿ׸±ÿ층ÿ׸¼ÿø½ÿ׸¿ÿ׸Áÿ׸Äÿì¸Çÿì¸Îÿì¸Õÿì¸òÿì¸ÿ®¸ ÿ®¸rÿ׸sÿì¸zÿì¸|ÿ׸€ÿ츂ÿ츟ÿ׸¡ÿ츩ÿ층ÿø·ÿ츹ÿ츻ÿ׸½ÿ츿ÿ׸Áÿ׸Êÿ׸Îÿ׸Ïÿì¸Ôÿ׸Ùÿ׸Ûÿ׸Ýÿ׸åÿ׸çÿì¸õÿì¸÷ÿ׸ùÿ׸ûÿ׸ýÿ׸ÿ׸ÿ׸ ÿ׸ÿ׸ÿ׸ÿì¸ÿì¸ÿ׸ÿìº�þöº�þöº¤ÿ…ºªÿšº®ÿ…º°ÿ׺µÿ…º¿ÿ׺ÎÿšºÕÿšºòÿšºþöº þöºrÿšºsÿšºvÿ캟ÿ׺»ÿ׺Êÿ׺Îÿ…ºÏÿšºÙÿšºÛÿšºÝÿšºåÿ׺ÿ׺ÿ׺ ÿ®º ÿ®ºÿ…ºÿšºÿ…ºÿš»Ÿÿ×»¸ÿ×»»ÿ×»¾ÿ×»áÿ×»lÿ×»~ÿ×»„ÿ×»†ÿ×»ˆÿ×»Šÿ×»Œÿ×»±ÿ×»³ÿ×»Àÿ×»Âÿ×»Åÿ×»Çÿ×»Õÿ×»ïÿ×»ñÿ×»óÿ×»þÿ×» ÿ×» ÿ×»ÿ×»ÿ×»ÿ×¼�ÿ…¼�ÿ®¼�ÿ…¼Ÿÿ×¼¤ÿš¼ªÿq¼®ÿš¼µÿš¼¸ÿ×¼»ÿ×¼¼�)¼¾ÿ®¼Ìÿš¼Íÿš¼Îÿ…¼Ïÿq¼Ðÿ×¼Ñÿ×¼Òÿš¼Óÿš¼Ôÿš¼Õÿ…¼Öÿš¼×ÿš¼Øÿq¼Ùÿš¼Úÿš¼Ûÿq¼Üÿ®¼Ýÿ®¼Þÿq¼ßÿ×¼àÿš¼áÿš¼âÿš¼ãÿš¼äÿ®¼åÿš¼æÿš¼çÿ×¼èÿš¼éÿüêÿq¼ìÿš¼íÿq¼îÿ…¼òÿ…¼óÿš¼õÿš¼öÿ®¼÷ÿš¼ùÿš¼ÿ®¼ÿ®¼ÿ®¼ÿ…¼ ÿ…¼jÿq¼kÿš¼lÿ×¼mÿ×¼qÿš¼rÿq¼sÿ…¼uÿš¼wÿš¼yÿš¼}ÿš¼~ÿ×¼ÿq¼ÿ×¼ƒÿ×¼„ÿ×¼…ÿq¼†ÿ×¼‡ÿq¼ˆÿ×¼‰ÿq¼Šÿ×¼‹ÿ×¼Œÿ×¼ÿq¼–ÿš¼šÿš¼žÿš¼ ÿ×¼¢ÿ×¼¤ÿš¼¦ÿš¼ªÿ®¼¬ÿš¼®ÿš¼°ÿš¼±ÿ×¼²ÿq¼³ÿ×¼´ÿq¼µ�)¼¶ÿ®¼¸ÿ®¼ºÿ®¼¼ÿ×¼¾ÿ®¼Àÿš¼Âÿš¼Äÿš¼Åÿš¼Æÿq¼Çÿš¼Èÿq¼Ëÿ×¼Íÿš¼Îÿš¼Ïÿ…¼Ñÿš¼Óÿš¼Õÿš¼×ÿš¼Ùÿq¼Ûÿq¼Ýÿq¼àÿq¼æÿ×¼èÿ×¼êÿüìÿš¼îÿš¼ïÿ×¼ðÿq¼ñÿ×¼òÿq¼óÿ×¼ôÿq¼öÿ×¼øÿ®¼úÿ®¼üÿ®¼þÿš¼�ÿš¼ÿš¼ÿ×¼ÿ×¼ ÿq¼ ÿq¼ ÿq¼ ÿq¼ÿš¼ÿš¼ÿš¼ÿ…¼ÿš¼ÿ×¼ÿq¼ÿ®¼ÿq¼ÿš¼ÿ…½�ÿ…½�ÿ…½Ÿÿ콤ÿš½ªÿq½®ÿš½µÿš½¸ÿì½»ÿì½¾ÿýÉÿì½Îÿ®½Ïÿ×½Õÿ®½Øÿ×½Ûÿ×½Þÿ×½áÿ×½êÿ×½ë�f½íÿ×½îÿì½òÿ®½ô�f½ÿ…½ ÿ…½jÿ×½lÿì½rÿq½sÿ®½~ÿì½ÿ×½„ÿì½…ÿ×½†ÿ콇ÿ×½ˆÿ콉ÿ×½Šÿ콌ÿì½ÿ×½˜�f½¨�f½±ÿì½²ÿ×½³ÿì½´ÿ×½Àÿ×½Âÿ×½Åÿ×½ÆÿýÇÿ×½ÈÿýÎÿš½Ïÿ®½Õÿ×½Ùÿq½Ûÿq½Ýÿq½àÿ×½ïÿì½ðÿ×½ñÿì½òÿ×½óÿì½ôÿ×½þÿ×½ ÿq½ ÿ×½ ÿq½ ÿ×½ÿš½ÿ®½ÿì½ÿ×½ÿ×½ÿš½ÿ®¾�ÿ®¾�ÿ®¾ÿ×¾¤ÿ×¾¦ÿ×¾¨ÿþªÿ×¾®ÿ×¾°ÿ×¾±ÿ×¾µÿ×¾¼ÿþ½ÿþ¿ÿ×¾Äÿ×¾Çÿ×¾Îÿì¾Õÿì¾òÿì¾ÿ®¾ ÿ®¾rÿ×¾sÿì¾zÿ×¾€ÿ쾂ÿ쾟ÿ×¾¡ÿ×¾©ÿ×¾µÿþ·ÿþ¹ÿþ»ÿ×¾½ÿ×¾Êÿ×¾Îÿ×¾Ïÿì¾Ùÿ×¾Ûÿ×¾Ýÿ×¾åÿ×¾çÿ×¾õÿ×¾÷ÿþùÿþûÿþÿ×¾ÿ×¾ ÿ×¾ÿ×¾ÿ×¾ÿì¾ÿ×¾ÿ×¾ÿ쿟ÿ׿¸ÿ׿»ÿ׿¾ÿ׿Áÿ׿áÿ׿lÿ׿|ÿ׿~ÿ׿„ÿ׿†ÿ׿ˆÿ׿Šÿ׿Œÿ׿±ÿ׿³ÿ׿¿ÿ׿Àÿ׿Áÿ׿Âÿ׿Åÿš¿Çÿš¿Ôÿ׿Õÿ׿ïÿ׿ñÿ׿óÿ׿ýÿ׿þÿ׿ ÿ׿ ÿ׿ÿ׿ÿ׿ÿ׿ÿìÀ£�áÀê�)Àÿ×Àÿ×ã�áÃê�)Ãÿ×Ãÿ×Ä�ÿ®Ä� ÿ®Äÿ…Ħÿ…Ĩÿ×ļÿšÄ½ÿ×ÄÁÿšÄÄÿ…ÄÜÿ×ÄÝÿ×Äáÿ×Ääÿ×Äöÿ×Äÿ®Ä ÿ®Änÿ®Ä|ÿšÄ€ÿ®Ä‚ÿ®Ä—ÿ®Ä›ÿ®Ä§ÿ®Ä©ÿ…Īÿ×ĵÿšÄ¶ÿ×Ä·ÿšÄ¸ÿ×ĹÿšÄºÿ׼ÿ…ľÿ×Ä¿ÿšÄÀÿ×ÄÁÿšÄÂÿ×ÄÔÿšÄÕÿ×Ä÷ÿ×Äøÿ×Äùÿ×Äúÿ×Äûÿ×Äüÿ×ÄýÿšÄþÿ×Äÿ®Ä ÿšÄÿÃÄÿšÄÿÃÄÿ…Äÿׯ�ÿ®Æ� ÿ®Æÿ…Ʀÿ…ƨÿׯ¼ÿšÆ½ÿׯÁÿšÆÄÿ…ÆÜÿׯÝÿׯáÿׯäÿׯöÿׯÿ®Æ ÿ®Ænÿ®Æ|ÿšÆ€ÿ®Æ‚ÿ®Æ—ÿ®Æ›ÿ®Æ§ÿ®Æ©ÿ…ƪÿׯµÿšÆ¶ÿׯ·ÿšÆ¸ÿׯ¹ÿšÆºÿׯ½ÿ…ƾÿׯ¿ÿšÆÀÿׯÁÿšÆÂÿׯÔÿšÆÕÿׯ÷ÿׯøÿׯùÿׯúÿׯûÿׯüÿׯýÿšÆþÿׯÿ®Æ ÿšÆÿÃÆÿšÆÿÃÆÿ…Æÿ×Ç�ÿ®Ç�ÿ®ÇÿìǤÿ×ǦÿìǨÿ×Ǫÿ×Ç®ÿ×ǰÿ×DZÿìǵÿ×ǼÿÃǽÿ×Ç¿ÿ×ÇÁÿ×ÇÄÿìÇÇÿìÇÎÿìÇÕÿìÇòÿìÇÿ®Ç ÿ®Çrÿ×ÇsÿìÇzÿìÇ|ÿ×Ç€ÿìÇ‚ÿìÇŸÿ×Ç¡ÿìÇ©ÿìǵÿÃÇ·ÿìǹÿìÇ»ÿ×ǽÿìÇ¿ÿ×ÇÁÿ×ÇÊÿ×ÇÎÿ×ÇÏÿìÇÔÿ×ÇÙÿ×ÇÛÿ×ÇÝÿ×Çåÿ×ÇçÿìÇõÿìÇ÷ÿ×Çùÿ×Çûÿ×Çýÿ×Çÿ×Çÿ×Ç ÿ×Çÿ×Çÿ×ÇÿìÇÿìÇÿ×ÇÿìÈ�ÿ®È�ÿ®ÈÿìȤÿ×ȦÿìȨÿ×Ȫÿ×È®ÿ×Ȱÿ×ȱÿìȵÿ×ȼÿÃȽÿ×È¿ÿ×ÈÁÿ×ÈÄÿìÈÇÿìÈÎÿìÈÕÿìÈòÿìÈÿ®È ÿ®Èrÿ×ÈsÿìÈzÿìÈ|ÿ×È€ÿìÈ‚ÿìÈŸÿ×È¡ÿìÈ©ÿìȵÿÃÈ·ÿìȹÿìÈ»ÿ×ȽÿìÈ¿ÿ×ÈÁÿ×ÈÊÿ×ÈÎÿ×ÈÏÿìÈÔÿ×ÈÙÿ×ÈÛÿ×ÈÝÿ×Èåÿ×ÈçÿìÈõÿìÈ÷ÿ×Èùÿ×Èûÿ×Èýÿ×Èÿ×Èÿ×È ÿ×Èÿ×Èÿ×ÈÿìÈÿìÈÿ×ÈÿìÊ�ÿìÊ� ÿìÊÿìÊ ÿìÌé�)Í�ÿšÍ�ÿ×Í�ÿšÍÎÿÃÍÏÿìÍÕÿÃÍØÿìÍÛÿìÍÞÿìÍêÿìÍíÿìÍòÿÃÍÿ×Íÿ×Íÿ×ÍÿšÍ ÿšÍjÿìÍsÿÃÍÿìÍ…ÿì͇ÿì͉ÿìÍÿìͲÿìÍ´ÿìÍÏÿÃÍàÿìÍðÿìÍòÿìÍôÿìÍ ÿìÍ ÿìÍÿÃÍÿìÍÿìÍÿÃÎ�ÿìÎ� ÿìÎÿìÎ ÿìÏ�ÿìÏ� ÿìÏÿìÏ ÿìÐÏÿ×ÐØÿ×ÐÛÿ×ÐÞÿ×Ðáÿ×Ðêÿ×Ðíÿ×Ðjÿ×Ðÿ×Ð…ÿ×Їÿ×Љÿ×Ðÿ×вÿ×дÿ×ÐÀÿ×ÐÂÿ×ÐÆÿ×ÐÈÿ×ÐÕÿ×Ðàÿ×Ððÿ×Ðòÿ×Ðôÿ×Ðþÿ×Ð ÿ×Ð ÿ×Ðÿ×Ðÿ×Ñé�)ÔÏÿ×ÔØÿ×ÔÛÿ×ÔÞÿ×Ôáÿ×Ôêÿ×Ôíÿ×Ôjÿ×Ôÿ×Ô…ÿ×Ô‡ÿ×Ô‰ÿ×Ôÿ×Ô²ÿ×Ô´ÿ×ÔÀÿ×ÔÂÿ×ÔÆÿ×ÔÈÿ×ÔÕÿ×Ôàÿ×Ôðÿ×Ôòÿ×Ôôÿ×Ôþÿ×Ô ÿ×Ô ÿ×Ôÿ×Ôÿר�ÿìØ� ÿìØÐÿרÜÿìØÝÿìØßÿרáÿìØäÿìØöÿìØÿìØ ÿìØ ÿרªÿìØ¶ÿìØ¼ÿר¾ÿìØÀÿìØÂÿìØËÿרÕÿìØæÿרøÿìØúÿìØüÿìØþÿìØÿרÿרÿìØÿìØÿìÚ�ÿìÚ� ÿìÚÐÿ×ÚÜÿìÚÝÿìÚßÿ×ÚáÿìÚäÿìÚöÿìÚÿìÚ ÿìÚ ÿ×ÚªÿìÚ¶ÿìÚ¼ÿ×Ú¾ÿìÚÀÿìÚÂÿìÚËÿ×ÚÕÿìÚæÿ×ÚøÿìÚúÿìÚüÿìÚþÿìÚÿ×Úÿ×ÚÿìÚÿìÚÿìÜ�ÿšÜ�ÿ×Ü�ÿšÜÎÿÃÜÏÿìÜÕÿÃÜØÿìÜÛÿìÜÞÿìÜêÿìÜíÿìÜòÿÃÜÿ×Üÿ×Üÿ×ÜÿšÜ ÿšÜjÿìÜsÿÃÜÿìÜ…ÿì܇ÿì܉ÿìÜÿìܲÿìÜ´ÿìÜÏÿÃÜàÿìÜðÿìÜòÿìÜôÿìÜ ÿìÜ ÿìÜÿÃÜÿìÜÿìÜÿÃÝ�ÿ®Ý�ÿ®ÝÎÿ×ÝÕÿ×Ýòÿ×Ýÿ®Ý ÿ®Ýsÿ×ÝÏÿ×Ýÿ×Ýÿ×Þ�ÿìÞ� ÿìÞÐÿ×ÞÜÿìÞÝÿìÞßÿ×ÞáÿìÞäÿìÞöÿìÞÿìÞ ÿìÞ ÿ×ÞªÿìÞ¶ÿìÞ¼ÿ×Þ¾ÿìÞÀÿìÞÂÿìÞËÿ×ÞÕÿìÞæÿ×ÞøÿìÞúÿìÞüÿìÞþÿìÞÿ×Þÿ×ÞÿìÞÿìÞÿìßÏÿ×ߨÿ×ßÛÿ×ßÞÿ×ßáÿ×ßêÿ×ßíÿ×ßjÿ×ßÿ×ß…ÿ×߇ÿ×߉ÿ×ßÿ×ß²ÿ×ß´ÿ×ßÀÿ×ßÂÿ×߯ÿ×ßÈÿ×ßÕÿ×ßàÿ×ßðÿ×ßòÿ×ßôÿ×ßþÿ×ß ÿ×ß ÿ×ßÿ×ßÿ×à�ÿìà� ÿìàÿìà ÿìã�ÿìã� ÿìãÿìã ÿìä�ÿ…ä� ÿ…äÐÿ×äÜÿšäÝÿÃäßÿ×äáÿ®ääÿšäöÿÃäÿ…ä ÿ…ämÿ×äÿ×äƒÿ×ä‹ÿ×ä ÿ×äªÿšä¶ÿšä¸ÿÃäºÿÃä¼ÿ×ä¾ÿšäÀÿ®äÂÿ®äÆÿ×äÈÿ×äËÿ×äÕÿ®äæÿ×äêÿ×äøÿÃäúÿÃäüÿÃäþÿ®äÿ×äÿ×äÿšäÿšäÿšæ�ÿ…æ� ÿ…æÐÿ׿ÜÿšæÝÿÃæßÿ׿áÿ®æäÿšæöÿÃæÿ…æ ÿ…æmÿ׿ÿ׿ƒÿ׿‹ÿ׿ ÿ׿ªÿšæ¶ÿšæ¸ÿÃæºÿÃæ¼ÿ׿¾ÿšæÀÿ®æÂÿ®æÆÿ׿Èÿ׿Ëÿ׿Õÿ®ææÿ׿êÿ׿øÿÃæúÿÃæüÿÃæþÿ®æÿ׿ÿ׿ÿšæÿšæÿšç�ÿìç� ÿìçÐÿ×çÜÿìçÝÿìçßÿ×çáÿìçäÿìçöÿìçÿìç ÿìç ÿ×çªÿìç¶ÿìç¼ÿ×ç¾ÿìçÀÿìçÂÿìçËÿ×çÕÿìçæÿ×çøÿìçúÿìçüÿìçþÿìçÿ×çÿ×çÿìçÿìçÿìè�ÿìè� ÿìèÐÿ×èÜÿìèÝÿìèßÿ×èáÿìèäÿìèöÿìèÿìè ÿìè ÿ×èªÿìè¶ÿìè¼ÿ×è¾ÿìèÀÿìèÂÿìèËÿ×èÕÿìèæÿ×èøÿìèúÿìèüÿìèþÿìèÿ×èÿ×èÿìèÿìèÿìê�ÿìê� ÿìêÿìê ÿìë�ÿìë� ÿìëÿìë ÿìëÿ×ëÿ×ì�ÿšì�ÿ×ì�ÿšìÎÿÃìÏÿììÕÿÃìØÿììÛÿììÞÿììêÿììíÿììòÿÃìÿ×ìÿ×ìÿ×ìÿšì ÿšìjÿììsÿÃìÿìì…ÿìì‡ÿìì‰ÿììÿìì²ÿìì´ÿììÏÿÃìàÿììðÿììòÿììôÿìì ÿìì ÿììÿÃìÿììÿììÿÃò�ÿ…ò� ÿ…òÐÿ×òÜÿšòÝÿÃòßÿ×òáÿ®òäÿšòöÿÃòÿ…ò ÿ…òmÿ×òÿ×òƒÿ×ò‹ÿ×ò ÿ×òªÿšò¶ÿšò¸ÿÃòºÿÃò¼ÿ×ò¾ÿšòÀÿ®òÂÿ®òÆÿ×òÈÿ×òËÿ×òÕÿ®òæÿ×òêÿ×òøÿÃòúÿÃòüÿÃòþÿ®òÿ×òÿ×òÿšòÿšòÿšó�ÿ…ó� ÿ…óÐÿ×óÜÿšóÝÿÃóßÿ×óáÿ®óäÿšóöÿÃóÿ…ó ÿ…ómÿ×óÿ×óƒÿ×ó‹ÿ×ó ÿ×óªÿšó¶ÿšó¸ÿÃóºÿÃó¼ÿ×ó¾ÿšóÀÿ®óÂÿ®óÆÿ×óÈÿ×óËÿ×óÕÿ®óæÿ×óêÿ×óøÿÃóúÿÃóüÿÃóþÿ®óÿ×óÿ×óÿšóÿšóÿšô�ÿìô� ÿìôÿìô ÿìôÿ×ôÿ×õÏÿ×õØÿ×õÛÿ×õÞÿ×õáÿ×õêÿ×õíÿ×õjÿ×õÿ×õ…ÿ×õ‡ÿ×õ‰ÿ×õÿ×õ²ÿ×õ´ÿ×õÀÿ×õÂÿ×õÆÿ×õÈÿ×õÕÿ×õàÿ×õðÿ×õòÿ×õôÿ×õþÿ×õ ÿ×õ ÿ×õÿ×õÿ×ö�ÿ®ö�ÿ®öÎÿ×öÕÿ×öòÿ×öÿ®ö ÿ®ösÿ×öÏÿ×öÿ×öÿ×ø�ÿ…ø�ÿ®ø�ÿ…øŸÿ×ø¤ÿšøªÿqø®ÿšøµÿšø¸ÿ×ø»ÿ×ø¼�)ø¾ÿ®øÌÿšøÍÿšøÎÿ…øÏÿqøÐÿ×øÑÿ×øÒÿšøÓÿšøÔÿšøÕÿ…øÖÿšø×ÿšøØÿqøÙÿšøÚÿšøÛÿqøÜÿ®øÝÿ®øÞÿqøßÿ×øàÿšøáÿšøâÿšøãÿšøäÿ®øåÿšøæÿšøçÿ×øèÿšøéÿÃøêÿqøìÿšøíÿqøîÿ…øòÿ…øóÿšøõÿšøöÿ®ø÷ÿšøùÿšøÿ®øÿ®øÿ®øÿ…ø ÿ…øjÿqøkÿšølÿ×ømÿ×øqÿšørÿqøsÿ…øuÿšøwÿšøyÿšø}ÿšø~ÿ×øÿqøÿ×øƒÿ×ø„ÿ×ø…ÿqø†ÿ×ø‡ÿqøˆÿ×ø‰ÿqøŠÿ×ø‹ÿ×øŒÿ×øÿqø–ÿšøšÿšøžÿšø ÿ×ø¢ÿ×ø¤ÿšø¦ÿšøªÿ®ø¬ÿšø®ÿšø°ÿšø±ÿ×ø²ÿqø³ÿ×ø´ÿqøµ�)ø¶ÿ®ø¸ÿ®øºÿ®ø¼ÿ×ø¾ÿ®øÀÿšøÂÿšøÄÿšøÅÿšøÆÿqøÇÿšøÈÿqøËÿ×øÍÿšøÎÿšøÏÿ…øÑÿšøÓÿšøÕÿšø×ÿšøÙÿqøÛÿqøÝÿqøàÿqøæÿ×øèÿ×øêÿÃøìÿšøîÿšøïÿ×øðÿqøñÿ×øòÿqøóÿ×øôÿqøöÿ×øøÿ®øúÿ®øüÿ®øþÿšø�ÿšøÿšøÿ×øÿ×ø ÿqø ÿqø ÿqø ÿqøÿšøÿšøÿšøÿ…øÿšøÿ×øÿqøÿ®øÿqøÿšøÿ…ù�ÿšù�ÿ×ù�ÿšùÎÿÃùÏÿìùÕÿÃùØÿìùÛÿìùÞÿìùêÿìùíÿìùòÿÃùÿ×ùÿ×ùÿ×ùÿšù ÿšùjÿìùsÿÃùÿìù…ÿìù‡ÿìù‰ÿìùÿìù²ÿìù´ÿìùÏÿÃùàÿìùðÿìùòÿìùôÿìù ÿìù ÿìùÿÃùÿìùÿìùÿÃú�ÿšú�ÿšú�"�)ú�$ÿ®ú�&ÿìú�*ÿìú�2ÿìú�4ÿìú�Dÿ×ú�Fÿ×ú�Gÿ×ú�Hÿ×ú�Jÿìú�Pÿìú�Qÿìú�Rÿ×ú�Sÿìú�Tÿ×ú�Uÿìú�Vÿìú�Xÿìú�‚ÿ®ú�ƒÿ®ú�„ÿ®ú�…ÿ®ú�†ÿ®ú�‡ÿ®ú�‰ÿìú�”ÿìú�•ÿìú�–ÿìú�—ÿìú�˜ÿìú�šÿìú�¢ÿ×ú�£ÿ×ú�¤ÿ×ú�¥ÿ×ú�¦ÿ×ú�§ÿ×ú�¨ÿ×ú�©ÿ×ú�ªÿ×ú�«ÿ×ú�¬ÿ×ú�­ÿ×ú�´ÿ×ú�µÿ×ú�¶ÿ×ú�·ÿ×ú�¸ÿ×ú�ºÿ×ú�»ÿìú�¼ÿìú�½ÿìú�¾ÿìú�Âÿ®ú�Ãÿ×ú�Äÿ®ú�Åÿ×ú�Æÿ®ú�Çÿ×ú�Èÿìú�Éÿ×ú�Êÿìú�Ëÿ×ú�Ìÿìú�Íÿ×ú�Îÿìú�Ïÿ×ú�Ñÿ×ú�Óÿ×ú�Õÿ×ú�×ÿ×ú�Ùÿ×ú�Ûÿ×ú�Ýÿ×ú�Þÿìú�ßÿìú�àÿìú�áÿìú�âÿìú�ãÿìú�äÿìú�åÿìú�úÿìúÿìúÿìú ÿìúÿìúÿ×úÿìúÿ×úÿìúÿ×úÿìúÿ×úÿìúÿìúÿìú!ÿìú+ÿìú-ÿìú/ÿìú1ÿìú3ÿìú5ÿìúCÿ®úDÿ×úFÿ×úGÿìúHÿ×úJÿìúÿšú ÿšúWÿìúXÿ®úYÿ×ú_ÿìú`ÿ×úbÿìúÿ®úÿ×úÿ®ú ÿ×ú!ÿ®ú"ÿ×ú#ÿ®ú%ÿ®ú&ÿ×ú'ÿ®ú(ÿ×ú)ÿ®ú*ÿ×ú+ÿ®ú,ÿ×ú-ÿ®ú.ÿ×ú/ÿ®ú0ÿ×ú1ÿ®ú2ÿ×ú3ÿ®ú4ÿ×ú6ÿ×ú8ÿ×ú:ÿ×ú<ÿ×ú@ÿ×úBÿ×úDÿ×úIÿìúJÿ×úKÿìúLÿ×úMÿìúNÿ×úOÿìúQÿìúRÿ×úSÿìúTÿ×úUÿìúVÿ×úWÿìúXÿ×úYÿìúZÿ×ú[ÿìú\ÿ×ú]ÿìú^ÿ×ú_ÿìú`ÿ×úbÿìúdÿìúfÿìúhÿìújÿìúlÿìúnÿìû��Rû� �Rû�ÿ®û�ÿ®û�"�)û�Rûÿ®û �Rû ÿ®ü�ÿšü�ÿšü�"�)ü�$ÿ®ü�&ÿìü�*ÿìü�2ÿìü�4ÿìü�Dÿ×ü�Fÿ×ü�Gÿ×ü�Hÿ×ü�Jÿìü�Pÿìü�Qÿìü�Rÿ×ü�Sÿìü�Tÿ×ü�Uÿìü�Vÿìü�Xÿìü�‚ÿ®ü�ƒÿ®ü�„ÿ®ü�…ÿ®ü�†ÿ®ü�‡ÿ®ü�‰ÿìü�”ÿìü�•ÿìü�–ÿìü�—ÿìü�˜ÿìü�šÿìü�¢ÿ×ü�£ÿ×ü�¤ÿ×ü�¥ÿ×ü�¦ÿ×ü�§ÿ×ü�¨ÿ×ü�©ÿ×ü�ªÿ×ü�«ÿ×ü�¬ÿ×ü�­ÿ×ü�´ÿ×ü�µÿ×ü�¶ÿ×ü�·ÿ×ü�¸ÿ×ü�ºÿ×ü�»ÿìü�¼ÿìü�½ÿìü�¾ÿìü�Âÿ®ü�Ãÿ×ü�Äÿ®ü�Åÿ×ü�Æÿ®ü�Çÿ×ü�Èÿìü�Éÿ×ü�Êÿìü�Ëÿ×ü�Ìÿìü�Íÿ×ü�Îÿìü�Ïÿ×ü�Ñÿ×ü�Óÿ×ü�Õÿ×ü�×ÿ×ü�Ùÿ×ü�Ûÿ×ü�Ýÿ×ü�Þÿìü�ßÿìü�àÿìü�áÿìü�âÿìü�ãÿìü�äÿìü�åÿìü�úÿìüÿìüÿìü ÿìüÿìüÿ×üÿìüÿ×üÿìüÿ×üÿìüÿ×üÿìüÿìüÿìü!ÿìü+ÿìü-ÿìü/ÿìü1ÿìü3ÿìü5ÿìüCÿ®üDÿ×üFÿ×üGÿìüHÿ×üJÿìüÿšü ÿšüWÿìüXÿ®üYÿ×ü_ÿìü`ÿ×übÿìüÿ®üÿ×üÿ®ü ÿ×ü!ÿ®ü"ÿ×ü#ÿ®ü%ÿ®ü&ÿ×ü'ÿ®ü(ÿ×ü)ÿ®ü*ÿ×ü+ÿ®ü,ÿ×ü-ÿ®ü.ÿ×ü/ÿ®ü0ÿ×ü1ÿ®ü2ÿ×ü3ÿ®ü4ÿ×ü6ÿ×ü8ÿ×ü:ÿ×ü<ÿ×ü@ÿ×üBÿ×üDÿ×üIÿìüJÿ×üKÿìüLÿ×üMÿìüNÿ×üOÿìüQÿìüRÿ×üSÿìüTÿ×üUÿìüVÿ×üWÿìüXÿ×üYÿìüZÿ×ü[ÿìü\ÿ×ü]ÿìü^ÿ×ü_ÿìü`ÿ×übÿìüdÿìüfÿìühÿìüjÿìülÿìünÿìý��Rý� �Rý�ÿ®ý�ÿ®ý�"�)ý�Rýÿ®ý �Rý ÿ®þ�ÿšþ�ÿšþ�"�)þ�$ÿ®þ�&ÿìþ�*ÿìþ�2ÿìþ�4ÿìþ�Dÿ×þ�Fÿ×þ�Gÿ×þ�Hÿ×þ�Jÿìþ�Pÿìþ�Qÿìþ�Rÿ×þ�Sÿìþ�Tÿ×þ�Uÿìþ�Vÿìþ�Xÿìþ�‚ÿ®þ�ƒÿ®þ�„ÿ®þ�…ÿ®þ�†ÿ®þ�‡ÿ®þ�‰ÿìþ�”ÿìþ�•ÿìþ�–ÿìþ�—ÿìþ�˜ÿìþ�šÿìþ�¢ÿ×þ�£ÿ×þ�¤ÿ×þ�¥ÿ×þ�¦ÿ×þ�§ÿ×þ�¨ÿ×þ�©ÿ×þ�ªÿ×þ�«ÿ×þ�¬ÿ×þ�­ÿ×þ�´ÿ×þ�µÿ×þ�¶ÿ×þ�·ÿ×þ�¸ÿ×þ�ºÿ×þ�»ÿìþ�¼ÿìþ�½ÿìþ�¾ÿìþ�Âÿ®þ�Ãÿ×þ�Äÿ®þ�Åÿ×þ�Æÿ®þ�Çÿ×þ�Èÿìþ�Éÿ×þ�Êÿìþ�Ëÿ×þ�Ìÿìþ�Íÿ×þ�Îÿìþ�Ïÿ×þ�Ñÿ×þ�Óÿ×þ�Õÿ×þ�×ÿ×þ�Ùÿ×þ�Ûÿ×þ�Ýÿ×þ�Þÿìþ�ßÿìþ�àÿìþ�áÿìþ�âÿìþ�ãÿìþ�äÿìþ�åÿìþ�úÿìþÿìþÿìþ ÿìþÿìþÿ×þÿìþÿ×þÿìþÿ×þÿìþÿ×þÿìþÿìþÿìþ!ÿìþ+ÿìþ-ÿìþ/ÿìþ1ÿìþ3ÿìþ5ÿìþCÿ®þDÿ×þFÿ×þGÿìþHÿ×þJÿìþÿšþ ÿšþWÿìþXÿ®þYÿ×þ_ÿìþ`ÿ×þbÿìþÿ®þÿ×þÿ®þ ÿ×þ!ÿ®þ"ÿ×þ#ÿ®þ%ÿ®þ&ÿ×þ'ÿ®þ(ÿ×þ)ÿ®þ*ÿ×þ+ÿ®þ,ÿ×þ-ÿ®þ.ÿ×þ/ÿ®þ0ÿ×þ1ÿ®þ2ÿ×þ3ÿ®þ4ÿ×þ6ÿ×þ8ÿ×þ:ÿ×þ<ÿ×þ@ÿ×þBÿ×þDÿ×þIÿìþJÿ×þKÿìþLÿ×þMÿìþNÿ×þOÿìþQÿìþRÿ×þSÿìþTÿ×þUÿìþVÿ×þWÿìþXÿ×þYÿìþZÿ×þ[ÿìþ\ÿ×þ]ÿìþ^ÿ×þ_ÿìþ`ÿ×þbÿìþdÿìþfÿìþhÿìþjÿìþlÿìþnÿìÿ��Rÿ� �Rÿ�ÿ®ÿ�ÿ®ÿ�"�)ÿ�Rÿÿ®ÿ �Rÿ ÿ®��ÿ…��ÿ…��"�)��$ÿ…��&ÿ×��*ÿ×��2ÿ×��4ÿ×��Dÿš��Fÿš��Gÿš��Hÿš��Jÿ×��PÿÃ��QÿÃ��Rÿš��SÿÃ��Tÿš��UÿÃ��Vÿ®��XÿÃ��]ÿ×��‚ÿ…��ƒÿ…��„ÿ…��…ÿ…��†ÿ…��‡ÿ…��‰ÿ×��”ÿ×��•ÿ×��–ÿ×��—ÿ×��˜ÿ×��šÿ×��¢ÿš��£ÿš��¤ÿš��¥ÿš��¦ÿš��§ÿš��¨ÿš��©ÿš��ªÿš��«ÿš��¬ÿš��­ÿš��´ÿš��µÿš��¶ÿš��·ÿš��¸ÿš��ºÿš��»ÿÃ��¼ÿÃ��½ÿÃ��¾ÿÃ��Âÿ…��Ãÿš��Äÿ…��Åÿš��Æÿ…��Çÿš��Èÿ×��Éÿš��Êÿ×��Ëÿš��Ìÿ×��Íÿš��Îÿ×��Ïÿš��Ñÿš��Óÿš��Õÿš��×ÿš��Ùÿš��Ûÿš��Ýÿš��Þÿ×��ßÿ×��àÿ×��áÿ×��âÿ×��ãÿ×��äÿ×��åÿ×��úÿÃ�ÿÃ�ÿÃ� ÿÃ�ÿ×�ÿš�ÿ×�ÿš�ÿ×�ÿš�ÿ×�ÿš�ÿÃ�ÿÃ�ÿ®�!ÿ®�+ÿÃ�-ÿÃ�/ÿÃ�1ÿÃ�3ÿÃ�5ÿÃ�<ÿ×�>ÿ×�@ÿ×�Cÿ…�Dÿš�Fÿš�Gÿ×�Hÿš�Jÿ®�ÿ…� ÿ…�WÿÃ�Xÿ…�Yÿš�_ÿ×�`ÿš�bÿÃ�ÿ…�ÿš�ÿ…� ÿš�!ÿ…�"ÿš�#ÿ…�%ÿ…�&ÿš�'ÿ…�(ÿš�)ÿ…�*ÿš�+ÿ…�,ÿš�-ÿ…�.ÿš�/ÿ…�0ÿš�1ÿ…�2ÿš�3ÿ…�4ÿš�6ÿš�8ÿš�:ÿš�<ÿš�@ÿš�Bÿš�Dÿš�Iÿ×�Jÿš�Kÿ×�Lÿš�Mÿ×�Nÿš�Oÿ×�Qÿ×�Rÿš�Sÿ×�Tÿš�Uÿ×�Vÿš�Wÿ×�Xÿš�Yÿ×�Zÿš�[ÿ×�\ÿš�]ÿ×�^ÿš�_ÿ×�`ÿš�bÿÃ�dÿÃ�fÿÃ�hÿÃ�jÿÃ�lÿÃ�nÿÃ��R� �R�ÿ®�ÿ®�"�)�Rÿ® �R ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�$ÿq�7�)�9�)�:�)�<��Dÿ®�Fÿ…�Gÿ…�Hÿ…�JÿÃ�PÿÃ�QÿÃ�Rÿ…�SÿÃ�Tÿ…�UÿÃ�VÿÃ�XÿÃ�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�Ÿ��¢ÿ…�£ÿ®�¤ÿ®�¥ÿ®�¦ÿ®�§ÿ®�¨ÿ®�©ÿ…�ªÿ…�«ÿ…�¬ÿ…�­ÿ…�´ÿ…�µÿ…�¶ÿ…�·ÿ…�¸ÿ…�ºÿ…�»ÿÃ�¼ÿÃ�½ÿÃ�¾ÿÃ�Âÿq�Ãÿ®�Äÿq�Åÿ®�Æÿq�Çÿ®�Éÿ…�Ëÿ…�Íÿ…�Ïÿ…�Ñÿ…�Óÿ…�Õÿ…�×ÿ…�Ùÿ…�Ûÿ…�Ýÿ…�ßÿÃ�áÿÃ�ãÿÃ�åÿÃ�úÿÃÿÃÿà ÿÃÿ…ÿ…ÿ…ÿ…ÿÃÿÃÿÃ!ÿÃ$�)&�)+ÿÃ-ÿÃ/ÿÃ1ÿÃ3ÿÃ5ÿÃ6�)8�:�CÿqDÿ®Fÿ®Hÿ…JÿÃVÿq_ÿqbÿqiÿqyÿ®zÿ×{ÿ×~ÿ®ÿÂÿ׃ÿׄÿׇÿ׉ÿ׌ÿ®ŽÿÃÿ®ÿ®“ÿ®™ÿ®¤ÿ…ªÿq®ÿ…µÿ…Êÿ×ÎÿqÏÿ…ÕÿqØÿ…Ûÿ…Þÿ…êÿ…íÿ…îÿÃòÿqú�)ü�)þ�)��WÿÃXÿqYÿ®`ÿ…bÿÃjÿ…rÿqsÿq}ÿìÿ……ÿ…‡ÿ…‰ÿ…ÿ…²ÿ…´ÿ…Îÿ…ÏÿqÙÿqÚÿ×ÛÿqÜÿ×ÝÿqÞÿ×àÿ…âÿ×äÿ×ðÿ…òÿ…ôÿ… ÿq ÿ… ÿq ÿ…ÿ…ÿqÿ…ÿ…ÿ…ÿqÿqÿ®ÿq ÿ®!ÿq"ÿ®#ÿq%ÿq&ÿ®'ÿq(ÿ®)ÿq*ÿ®+ÿq,ÿ®-ÿq.ÿ®/ÿq0ÿ®1ÿq2ÿ®3ÿq4ÿ®6ÿ…8ÿ…:ÿ…<ÿ…@ÿ…Bÿ…Dÿ…Jÿ…Lÿ…Nÿ…Rÿ…Tÿ…Vÿ…Xÿ…Zÿ…\ÿ…^ÿ…`ÿ…bÿÃdÿÃfÿÃhÿÃjÿÃlÿÃnÿÃo�q�s��)�$ÿq�7�)�9�)�:�)�<��Dÿ®�Fÿ…�Gÿ…�Hÿ…�JÿÃ�PÿÃ�QÿÃ�Rÿ…�SÿÃ�Tÿ…�UÿÃ�VÿÃ�XÿÃ�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�Ÿ��¢ÿ…�£ÿ®�¤ÿ®�¥ÿ®�¦ÿ®�§ÿ®�¨ÿ®�©ÿ…�ªÿ…�«ÿ…�¬ÿ…�­ÿ…�´ÿ…�µÿ…�¶ÿ…�·ÿ…�¸ÿ…�ºÿ…�»ÿÃ�¼ÿÃ�½ÿÃ�¾ÿÃ�Âÿq�Ãÿ®�Äÿq�Åÿ®�Æÿq�Çÿ®�Éÿ…�Ëÿ…�Íÿ…�Ïÿ…�Ñÿ…�Óÿ…�Õÿ…�×ÿ…�Ùÿ…�Ûÿ…�Ýÿ…�ßÿÃ�áÿÃ�ãÿÃ�åÿÃ�úÿÃÿÃÿà ÿÃÿ…ÿ…ÿ…ÿ…ÿÃÿÃÿÃ!ÿÃ$�)&�)+ÿÃ-ÿÃ/ÿÃ1ÿÃ3ÿÃ5ÿÃ6�)8�:�CÿqDÿ®Fÿ®Hÿ…JÿÃVÿq_ÿqbÿqiÿqyÿ®zÿ×{ÿ×~ÿ®ÿÂÿ׃ÿׄÿׇÿ׉ÿ׌ÿ®ŽÿÃÿ®ÿ®“ÿ®™ÿ®¤ÿ…ªÿq®ÿ…µÿ…Êÿ×ÎÿqÏÿ…ÕÿqØÿ…Ûÿ…Þÿ…êÿ…íÿ…îÿÃòÿqú�)ü�)þ�)��WÿÃXÿqYÿ®`ÿ…bÿÃjÿ…rÿqsÿq}ÿìÿ……ÿ…‡ÿ…‰ÿ…ÿ…²ÿ…´ÿ…Îÿ…ÏÿqÙÿqÚÿ×ÛÿqÜÿ×ÝÿqÞÿ×àÿ…âÿ×äÿ×ðÿ…òÿ…ôÿ… ÿq ÿ… ÿq ÿ…ÿ…ÿqÿ…ÿ…ÿ…ÿqÿqÿ®ÿq ÿ®!ÿq"ÿ®#ÿq%ÿq&ÿ®'ÿq(ÿ®)ÿq*ÿ®+ÿq,ÿ®-ÿq.ÿ®/ÿq0ÿ®1ÿq2ÿ®3ÿq4ÿ®6ÿ…8ÿ…:ÿ…<ÿ…@ÿ…Bÿ…Dÿ…Jÿ…Lÿ…Nÿ…Rÿ…Tÿ…Vÿ…Xÿ…Zÿ…\ÿ…^ÿ…`ÿ…bÿÃdÿÃfÿÃhÿÃjÿÃlÿÃnÿÃo�q�s��)�&ÿš�*ÿš�2ÿš�4ÿš�7ÿq�8ÿ×�9ÿ…�:ÿ…�<ÿ…�‰ÿš�”ÿš�•ÿš�–ÿš�—ÿš�˜ÿš�šÿš�›ÿ×�œÿ×�ÿ×�žÿ×�Ÿÿ…�Èÿš�Êÿš�Ìÿš�Îÿš�Þÿš�àÿš�âÿš�äÿšÿšÿšÿšÿš$ÿq&ÿq*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ…8ÿ…:ÿ…Gÿšfÿ®mÿ®qÿqrÿ…sÿšuÿ…xÿ……ÿ×ÿqŸÿš¦ÿq¸ÿš»ÿš¼ÿq¾ÿ®Áÿ\ÄÿqÜÿšáÿ…äÿšúÿ…üÿ…þÿ…�ÿ…Tÿ…_ÿšaÿ×lÿš|ÿ\~ÿš€ÿ…‚ÿ…„ÿš†ÿšˆÿšŠÿšŒÿš©ÿqªÿš±ÿš³ÿšµÿq¶ÿš·ÿ…¹ÿ…½ÿq¾ÿš¿ÿ\Àÿ…Áÿ\Âÿ…Åÿ…Çÿ…Ôÿ\Õÿ…ïÿšñÿšóÿšýÿ\þÿ… ÿ…ÿšÿ…ÿšÿšÿqÿšIÿšKÿšMÿšOÿšQÿšSÿšUÿšWÿšYÿš[ÿš]ÿš_ÿšaÿ×cÿ×eÿ×gÿ×iÿ×kÿ×mÿ×oÿ…qÿ…sÿ…ÿq �$ÿq �7�) �9�) �:�) �<� �Dÿ® �Fÿ… �Gÿ… �Hÿ… �Jÿà �Pÿà �Qÿà �Rÿ… �Sÿà �Tÿ… �Uÿà �Vÿà �Xÿà �‚ÿq �ƒÿq �„ÿq �…ÿq �†ÿq �‡ÿq �Ÿ� �¢ÿ… �£ÿ® �¤ÿ® �¥ÿ® �¦ÿ® �§ÿ® �¨ÿ® �©ÿ… �ªÿ… �«ÿ… �¬ÿ… �­ÿ… �´ÿ… �µÿ… �¶ÿ… �·ÿ… �¸ÿ… �ºÿ… �»ÿà �¼ÿà �½ÿà �¾ÿà �Âÿq �Ãÿ® �Äÿq �Åÿ® �Æÿq �Çÿ® �Éÿ… �Ëÿ… �Íÿ… �Ïÿ… �Ñÿ… �Óÿ… �Õÿ… �×ÿ… �Ùÿ… �Ûÿ… �Ýÿ… �ßÿà �áÿà �ãÿà �åÿà �úÿà ÿà ÿà  ÿà ÿ… ÿ… ÿ… ÿ… ÿà ÿà ÿà !ÿà $�) &�) +ÿà -ÿà /ÿà 1ÿà 3ÿà 5ÿà 6�) 8� :� Cÿq Dÿ® Fÿ® Hÿ… Jÿà Vÿq _ÿq bÿq iÿq yÿ® zÿ× {ÿ× ~ÿ® ÿà ‚ÿ× ƒÿ× „ÿ× ‡ÿ× ‰ÿ× Œÿ® Žÿà ÿ® ÿ® “ÿ® ™ÿ® ¤ÿ… ªÿq ®ÿ… µÿ… Êÿ× Îÿq Ïÿ… Õÿq Øÿ… Ûÿ… Þÿ… êÿ… íÿ… îÿà òÿq ú�) ü�) þ�) �� Wÿà Xÿq Yÿ® `ÿ… bÿà jÿ… rÿq sÿq }ÿì ÿ… …ÿ… ‡ÿ… ‰ÿ… ÿ… ²ÿ… ´ÿ… Îÿ… Ïÿq Ùÿq Úÿ× Ûÿq Üÿ× Ýÿq Þÿ× àÿ… âÿ× äÿ× ðÿ… òÿ… ôÿ…  ÿq  ÿ…  ÿq  ÿ… ÿ… ÿq ÿ… ÿ… ÿ… ÿq ÿq ÿ® ÿq  ÿ® !ÿq "ÿ® #ÿq %ÿq &ÿ® 'ÿq (ÿ® )ÿq *ÿ® +ÿq ,ÿ® -ÿq .ÿ® /ÿq 0ÿ® 1ÿq 2ÿ® 3ÿq 4ÿ® 6ÿ… 8ÿ… :ÿ… <ÿ… @ÿ… Bÿ… Dÿ… Jÿ… Lÿ… Nÿ… Rÿ… Tÿ… Vÿ… Xÿ… Zÿ… \ÿ… ^ÿ… `ÿ… bÿà dÿà fÿà hÿà jÿà lÿà nÿà o� q� s� �) �&ÿš �*ÿš �2ÿš �4ÿš �7ÿq �8ÿ× �9ÿ… �:ÿ… �<ÿ… �‰ÿš �”ÿš �•ÿš �–ÿš �—ÿš �˜ÿš �šÿš �›ÿ× �œÿ× �ÿ× �žÿ× �Ÿÿ… �Èÿš �Êÿš �Ìÿš �Îÿš �Þÿš �àÿš �âÿš �äÿš ÿš ÿš ÿš ÿš $ÿq &ÿq *ÿ× ,ÿ× .ÿ× 0ÿ× 2ÿ× 4ÿ× 6ÿ… 8ÿ… :ÿ… Gÿš fÿ® mÿ® qÿq rÿ… sÿš uÿ… xÿ… …ÿ× ÿq Ÿÿš ¦ÿq ¸ÿš »ÿš ¼ÿq ¾ÿ® Áÿ\ Äÿq Üÿš áÿ… äÿš úÿ… üÿ… þÿ… �ÿ… Tÿ… _ÿš aÿ× lÿš |ÿ\ ~ÿš €ÿ… ‚ÿ… „ÿš †ÿš ˆÿš Šÿš Œÿš ©ÿq ªÿš ±ÿš ³ÿš µÿq ¶ÿš ·ÿ… ¹ÿ… ½ÿq ¾ÿš ¿ÿ\ Àÿ… Áÿ\ Âÿ… Åÿ… Çÿ… Ôÿ\ Õÿ… ïÿš ñÿš óÿš ýÿ\ þÿ…  ÿ… ÿš ÿ… ÿš ÿš ÿq ÿš Iÿš Kÿš Mÿš Oÿš Qÿš Sÿš Uÿš Wÿš Yÿš [ÿš ]ÿš _ÿš aÿ× cÿ× eÿ× gÿ× iÿ× kÿ× mÿ× oÿ… qÿ… sÿ… ÿq!qÿ×!rÿì!xÿì!TÿìS�ÿÃS�ÿÃSÿÃS ÿÃT�ÿ…T�ÿ…TVÿ…T_ÿ…Tbÿ…Tfÿ×Tiÿ…Tmÿ×TsÿÃTvÿìTyÿšTzÿ®T{ÿÃT|ÿÃT}ÿÃT~ÿšTÿÃT‚ÿ®T„ÿÃT†ÿÃT‡ÿÃT‰ÿÃTŒÿšTŽÿšTÿšTÿšT’ÿÃT“ÿšT•ÿÃT–ÿÃT˜ÿÃT™ÿšTšÿÃT›ÿÃTÿ…T ÿ…T!ÿìX�ÿqX� ÿqX�&ÿ×X�*ÿ×X�- X�2ÿ×X�4ÿ×X�7ÿqX�9ÿ®X�:ÿ®X�<ÿ…X�‰ÿ×X�”ÿ×X�•ÿ×X�–ÿ×X�—ÿ×X�˜ÿ×X�šÿ×X�Ÿÿ…X�Èÿ×X�Êÿ×X�Ìÿ×X�Îÿ×X�Þÿ×X�àÿ×X�âÿ×X�äÿ×Xÿ×Xÿ×Xÿ×Xÿ×X$ÿqX&ÿqX6ÿ®X8ÿ…X:ÿ…XGÿ×Xúÿ®Xüÿ®Xþÿ®X�ÿ…XÿqX ÿqX_ÿ×XIÿ×XKÿ×XMÿ×XOÿ×XQÿ×XSÿ×XUÿ×XWÿ×XYÿ×X[ÿ×X]ÿ×X_ÿ×Xoÿ…Xqÿ…Xsÿ…XÿqY�ÿìY� ÿìYÿìY ÿìZ�ÿ®Z�ÿ®ZVÿ×Z_ÿ×Zbÿ×ZdÿìZiÿ×ZpÿìZqÿÃZrÿìZtÿ×ZuÿìZxÿìZˆÿìZÿ®Z ÿ®ZTÿì`�I�R`�W�R`�Y�f`�Z�f`�[�f`�\�f`�¿�f`%�R`'�R`7�f`û�f`ý�f`4�R`5�R`]�R`^�R`p�f`�R`�Rb�I�fb�W�fb�Y�fb�Z�fb�[�fb�\�fb�¿�fb%�fb'�fb7�fbû�fbý�fb4�fb5�fb]�fb^�fbp�fb�fb�fj�ÿìj� ÿìjÿìj ÿìl�ÿ®l�ÿ®lÿìl¤ÿ×l¦ÿìl¨ÿ×lªÿ×l®ÿ×l°ÿ×l±ÿìlµÿ×l¼ÿÃl½ÿ×l¿ÿ×lÁÿ×lÄÿìlÇÿìlÎÿìlÕÿìlòÿìlÿ®l ÿ®lrÿ×lsÿìlzÿìl|ÿ×l€ÿìl‚ÿìlŸÿ×l¡ÿìl©ÿìlµÿÃl·ÿìl¹ÿìl»ÿ×l½ÿìl¿ÿ×lÁÿ×lÊÿ×lÎÿ×lÏÿìlÔÿ×lÙÿ×lÛÿ×lÝÿ×låÿ×lçÿìlõÿìl÷ÿ×lùÿ×lûÿ×lýÿ×lÿ×lÿ×l ÿ×lÿ×lÿ×lÿìlÿìlÿ×lÿìm�ÿ®m�ÿ®mÎÿ×mÕÿ×mòÿ×mÿ®m ÿ®msÿ×mÏÿ×mÿ×mÿ×n�ÿ®n� ÿ®nÿ×n¦ÿ×n¼ÿ®nÁÿ®nÄÿ×nÜÿ×näÿ×nÿ®n ÿ®n|ÿ®n€ÿÃn‚ÿÃn©ÿ×nªÿ×nµÿ®n¶ÿ×n·ÿÃn¹ÿÃn½ÿ×n¾ÿ×n¿ÿ®nÁÿ®nÔÿ®nýÿ®n ÿšnÿšnÿ×nÿ×o�ÿ…o� ÿ…oÐÿ×oÜÿšoÝÿÃoßÿ×oáÿ®oäÿšoöÿÃoÿ…o ÿ…omÿ×oÿ×oƒÿ×o‹ÿ×o ÿ×oªÿšo¶ÿšo¸ÿÃoºÿÃo¼ÿ×o¾ÿšoÀÿ®oÂÿ®oÆÿ×oÈÿ×oËÿ×oÕÿ®oæÿ×oêÿ×oøÿÃoúÿÃoüÿÃoþÿ®oÿ×oÿ×oÿšoÿšoÿšpŸÿ×p¸ÿ×p»ÿ×p¾ÿ×páÿ×plÿ×p~ÿ×p„ÿ×p†ÿ×pˆÿ×pŠÿ×pŒÿ×p±ÿ×p³ÿ×pÀÿ×pÂÿ×pÅÿ×pÇÿ×pÕÿ×pïÿ×pñÿ×póÿ×pþÿ×p ÿ×p ÿ×pÿ×pÿ×pÿ×r�ÿqr� ÿqrÿšr¦ÿšr¼ÿqr¾ÿ×rÁÿšrÄÿšrÜÿ×ráÿ×räÿ×rÿqr ÿqrnÿ×r|ÿšr€ÿ®r‚ÿ®r—ÿ×r›ÿ×r§ÿ×r©ÿšrªÿ×rµÿqr¶ÿ×r·ÿ…r¹ÿ…r½ÿšr¾ÿ×r¿ÿšrÀÿ×rÁÿšrÂÿ×rÅÿšrÇÿšrÔÿšrÕÿ×ráÿ×rãÿ×rýÿšrþÿ×rÿ×r ÿqrÿ×rÿqrÿ×rÿšrÿ×s�ÿqs� ÿqsÏÿ×sØÿ×sÛÿ×sÜÿšsÝÿÃsÞÿ×sáÿÃsäÿšsêÿ×síÿ×söÿÃsÿqs ÿqsjÿ×smÿ×s}ÿìsÿ×sÿ×sƒÿ×s…ÿ×s‡ÿ×s‰ÿ×s‹ÿ×sÿ×sªÿšs²ÿ×s´ÿ×s¶ÿšs¸ÿ×sºÿ×s¾ÿšsÀÿÃsÂÿÃsÆÿ×sÈÿ×sÕÿÃsàÿ×sðÿ×sòÿ×sôÿ×søÿÃsúÿÃsüÿÃsþÿÃs ÿ×s ÿ×sÿ…sÿ…sÿ×sÿšsÿ×t�ÿqt� ÿqtÿšt¦ÿšt¼ÿqt¾ÿ×tÁÿštÄÿštÜÿ×táÿ×täÿ×tÿqt ÿqtnÿ×t|ÿšt€ÿ®t‚ÿ®t—ÿ×t›ÿ×t§ÿ×t©ÿštªÿ×tµÿqt¶ÿ×t·ÿ…t¹ÿ…t½ÿšt¾ÿ×t¿ÿštÀÿ×tÁÿštÂÿ×tÅÿštÇÿštÔÿštÕÿ×táÿ×tãÿ×týÿštþÿ×tÿ×t ÿqtÿ×tÿqtÿ×tÿštÿ×u�ÿqu� ÿquÏÿ×uØÿ×uÛÿ×uÜÿšuÝÿÃuÞÿ×uáÿÃuäÿšuêÿ×uíÿ×uöÿÃuÿqu ÿqujÿ×umÿ×u}ÿìuÿ×uÿ×uƒÿ×u…ÿ×u‡ÿ×u‰ÿ×u‹ÿ×uÿ×uªÿšu²ÿ×u´ÿ×u¶ÿšu¸ÿ×uºÿ×u¾ÿšuÀÿÃuÂÿÃuÆÿ×uÈÿ×uÕÿÃuàÿ×uðÿ×uòÿ×uôÿ×uøÿÃuúÿÃuüÿÃuþÿÃu ÿ×u ÿ×uÿ…uÿ…uÿ×uÿšuÿ×v ÿìvÿìx ÿìxÿìz�ÿ®z�ÿ®zÿ®z ÿ®z€ÿìz‚ÿìz·ÿìz¹ÿìz ÿ×zÿ×|�ÿq|�ÿq|¤ÿÃ|ªÿ®|®ÿÃ|µÿÃ|Îÿ×|Õÿ×|òÿ×|ÿq| ÿq|rÿ®|sÿ×|ÎÿÃ|Ïÿ×|Ùÿ®|Ûÿ®|Ýÿ®| ÿ®| ÿ®|ÿÃ|ÿ×|ÿÃ|ÿ×}�ÿì}� ÿì}Ðÿ×}Üÿì}Ýÿì}ßÿ×}áÿì}äÿì}öÿì}ÿì} ÿì} ÿ×}ªÿì}¶ÿì}¼ÿ×}¾ÿì}Àÿì}Âÿì}Ëÿ×}Õÿì}æÿ×}øÿì}úÿì}üÿì}þÿì}ÿ×}ÿ×}ÿì}ÿì}ÿì~�ÿ®~�ÿ®~ÿì~¤ÿ×~¦ÿì~¨ÿ×~ªÿ×~®ÿ×~°ÿ×~±ÿì~µÿ×~¼ÿÃ~½ÿ×~¿ÿ×~Áÿ×~Äÿì~Çÿì~Îÿì~Õÿì~òÿì~ÿ®~ ÿ®~rÿ×~sÿì~zÿì~|ÿ×~€ÿì~‚ÿì~Ÿÿ×~¡ÿì~©ÿì~µÿÃ~·ÿì~¹ÿì~»ÿ×~½ÿì~¿ÿ×~Áÿ×~Êÿ×~Îÿ×~Ïÿì~Ôÿ×~Ùÿ×~Ûÿ×~Ýÿ×~åÿ×~çÿì~õÿì~÷ÿ×~ùÿ×~ûÿ×~ýÿ×~ÿ×~ÿ×~ ÿ×~ÿ×~ÿ×~ÿì~ÿì~ÿ×~ÿì�ÿì� ÿìÐÿ×ÜÿìÝÿìßÿ×áÿìäÿìöÿìÿì ÿì ÿתÿì¶ÿì¼ÿ×¾ÿìÀÿìÂÿìËÿ×Õÿìæÿ×øÿìúÿìüÿìþÿìÿ×ÿ×ÿìÿìÿì€�ÿ…€�ÿ…€Ÿÿ쀤ÿš€ªÿq€®ÿš€µÿš€¸ÿ쀻ÿ쀾ÿÀÉÿì€Îÿ®€Ïÿ×€Õÿ®€Øÿ×€Ûÿ×€Þÿ×€áÿ×€êÿ×€ë�f€íÿ×€îÿì€òÿ®€ô�f€ÿ…€ ÿ…€jÿ×€lÿì€rÿq€sÿ®€~ÿì€ÿ×€„ÿ쀅ÿ×€†ÿ쀇ÿ×€ˆÿ쀉ÿ×€Šÿ쀌ÿì€ÿ×€˜�f€¨�f€±ÿ쀲ÿ×€³ÿ쀴ÿ×€Àÿ×€Âÿ×€Åÿ×€ÆÿÀÇÿ×€ÈÿÀÎÿš€Ïÿ®€Õÿ×€Ùÿq€Ûÿq€Ýÿq€àÿ×€ïÿì€ðÿ×€ñÿì€òÿ×€óÿì€ôÿ×€þÿ×€ ÿq€ ÿ×€ ÿq€ ÿ×€ÿš€ÿ®€ÿì€ÿ×€ÿ×€ÿš€ÿ®�ÿ®�ÿ®Îÿ×Õÿ×òÿ×ÿ® ÿ®sÿ×Ïÿ×ÿ×ÿׂ�ÿ…‚�ÿ…‚Ÿÿ삤ÿš‚ªÿq‚®ÿš‚µÿš‚¸ÿì‚»ÿ삾ÿÂÉÿì‚Îÿ®‚ÏÿׂÕÿ®‚ØÿׂÛÿׂÞÿׂáÿׂêÿׂë�f‚íÿׂîÿì‚òÿ®‚ô�f‚ÿ…‚ ÿ…‚jÿׂlÿì‚rÿq‚sÿ®‚~ÿì‚ÿׂ„ÿì‚…ÿׂ†ÿ삇ÿׂˆÿ삉ÿׂŠÿ삌ÿì‚ÿׂ˜�f‚¨�f‚±ÿ삲ÿׂ³ÿì‚´ÿׂÀÿׂÂÿׂÅÿׂÆÿÂÇÿׂÈÿÂÎÿš‚Ïÿ®‚ÕÿׂÙÿq‚Ûÿq‚Ýÿq‚àÿׂïÿì‚ðÿׂñÿì‚òÿׂóÿì‚ôÿׂþÿׂ ÿq‚ ÿׂ ÿq‚ ÿׂÿš‚ÿ®‚ÿì‚ÿׂÿׂÿš‚ÿ®ƒ�ÿ®ƒ�ÿ®ƒÎÿ׃Õÿ׃òÿ׃ÿ®ƒ ÿ®ƒsÿ׃Ïÿ׃ÿ׃ÿׄ�ÿ®„�ÿ®„ÎÿׄÕÿׄòÿׄÿ®„ ÿ®„sÿׄÏÿׄÿׄÿ×…�ÿ®…�ÿ®…Îÿ×…Õÿ×…òÿ×…ÿ®… ÿ®…sÿ×…Ïÿ×…ÿ×…ÿ׆�ÿ®†�ÿ®†ÿ솤ÿ׆¦ÿ솨ÿ׆ªÿ׆®ÿ׆°ÿ׆±ÿ솵ÿ׆¼ÿƽÿ׆¿ÿ׆Áÿ׆Äÿì†Çÿì†Îÿì†Õÿì†òÿì†ÿ®† ÿ®†rÿ׆sÿì†zÿì†|ÿ׆€ÿ솂ÿ솟ÿ׆¡ÿ솩ÿ솵ÿÆ·ÿ솹ÿ솻ÿ׆½ÿ솿ÿ׆Áÿ׆Êÿ׆Îÿ׆Ïÿì†Ôÿ׆Ùÿ׆Ûÿ׆Ýÿ׆åÿ׆çÿì†õÿì†÷ÿ׆ùÿ׆ûÿ׆ýÿ׆ÿ׆ÿ׆ ÿ׆ÿ׆ÿ׆ÿì†ÿì†ÿ׆ÿì‡�ÿì‡� ÿì‡ÐÿׇÜÿì‡Ýÿì‡ßÿׇáÿì‡äÿì‡öÿì‡ÿì‡ ÿ쇠ÿׇªÿ쇶ÿ쇼ÿׇ¾ÿì‡Àÿì‡Âÿì‡ËÿׇÕÿì‡æÿׇøÿì‡úÿì‡üÿì‡þÿì‡ÿׇÿׇÿì‡ÿì‡ÿìˆ�ÿ®ˆ�ÿ®ˆÿ숤ÿ׈¦ÿ숨ÿ׈ªÿ׈®ÿ׈°ÿ׈±ÿ숵ÿ׈¼ÿȽÿ׈¿ÿ׈Áÿ׈ÄÿìˆÇÿìˆÎÿìˆÕÿìˆòÿìˆÿ®ˆ ÿ®ˆrÿ׈sÿìˆzÿìˆ|ÿ׈€ÿ숂ÿ숟ÿ׈¡ÿ숩ÿ숵ÿÈ·ÿ숹ÿ숻ÿ׈½ÿ숿ÿ׈Áÿ׈Êÿ׈Îÿ׈ÏÿìˆÔÿ׈Ùÿ׈Ûÿ׈Ýÿ׈åÿ׈çÿìˆõÿìˆ÷ÿ׈ùÿ׈ûÿ׈ýÿ׈ÿ׈ÿ׈ ÿ׈ÿ׈ÿ׈ÿìˆÿìˆÿ׈ÿì‰�ÿì‰� ÿì‰Ðÿ׉Üÿì‰Ýÿì‰ßÿ׉áÿì‰äÿì‰öÿì‰ÿì‰ ÿ쉠ÿ׉ªÿ쉶ÿ쉼ÿ׉¾ÿì‰Àÿì‰Âÿì‰Ëÿ׉Õÿì‰æÿ׉øÿì‰úÿì‰üÿì‰þÿì‰ÿ׉ÿ׉ÿì‰ÿì‰ÿìŠ�ÿ®Š�ÿ®Šÿ스ÿ׊¦ÿ슨ÿ׊ªÿ׊®ÿ׊°ÿ׊±ÿ습ÿ׊¼ÿʽÿ׊¿ÿ׊Áÿ׊ÄÿìŠÇÿìŠÎÿìŠÕÿìŠòÿìŠÿ®Š ÿ®Šrÿ׊sÿìŠzÿìŠ|ÿ׊€ÿ슂ÿ슟ÿ׊¡ÿ슩ÿ습ÿÊ·ÿ승ÿ슻ÿ׊½ÿ슿ÿ׊Áÿ׊Êÿ׊Îÿ׊ÏÿìŠÔÿ׊Ùÿ׊Ûÿ׊Ýÿ׊åÿ׊çÿìŠõÿìŠ÷ÿ׊ùÿ׊ûÿ׊ýÿ׊ÿ׊ÿ׊ ÿ׊ÿ׊ÿ׊ÿìŠÿìŠÿ׊ÿì‹�ÿ®‹�ÿ®‹Îÿ׋Õÿ׋òÿ׋ÿ®‹ ÿ®‹sÿ׋Ïÿ׋ÿ׋ÿ׌Ÿÿ׌¸ÿ׌»ÿ׌¾ÿ׌áÿ׌lÿ׌~ÿ׌„ÿ׌†ÿ׌ˆÿ׌Šÿ׌Œÿ׌±ÿ׌³ÿ׌Àÿ׌Âÿ׌Åÿ׌Çÿ׌Õÿ׌ïÿ׌ñÿ׌óÿ׌þÿ׌ ÿ׌ ÿ׌ÿ׌ÿ׌ÿו£�á•ê�)•ÿוÿ×–�ÿì–� ÿì–ÿì– ÿì—�ÿ®—� ÿ®—ÿ×—¦ÿ×—¼ÿ®—Áÿ®—Äÿ×—Üÿ×—äÿ×—ÿ®— ÿ®—|ÿ®—€ÿׂÿשÿ×—ªÿ×—µÿ®—¶ÿ×—·ÿ×¹ÿ×½ÿ×—¾ÿ×—¿ÿ®—Áÿ®—Ôÿ®—ýÿ®— ÿš—ÿš—ÿ×—ÿט�ÿ…˜� ÿ…˜ÐÿטÜÿš˜ÝÿØßÿטáÿ®˜äÿš˜öÿØÿ…˜ ÿ…˜mÿטÿטƒÿט‹ÿט ÿטªÿš˜¶ÿš˜¸ÿغÿؼÿט¾ÿš˜Àÿ®˜Âÿ®˜ÆÿטÈÿטËÿטÕÿ®˜æÿטêÿטøÿØúÿØüÿØþÿ®˜ÿטÿטÿš˜ÿš˜ÿš™�þö™�þö™¤ÿ…™ªÿš™®ÿ…™°ÿ×™µÿ…™¿ÿ×™Îÿš™Õÿš™òÿš™þö™ þö™rÿš™sÿš™vÿ왟ÿ×™»ÿ×™Êÿ×™Îÿ…™Ïÿš™Ùÿš™Ûÿš™Ýÿš™åÿ×™ÿ×™ÿ×™ ÿ®™ ÿ®™ÿ…™ÿš™ÿ…™ÿšš�ÿìš� ÿìšÐÿךÜÿìšÝÿìšßÿךáÿìšäÿìšöÿìšÿìš ÿìš ÿךªÿìš¶ÿìš¼ÿך¾ÿìšÀÿìšÂÿìšËÿךÕÿìšæÿךøÿìšúÿìšüÿìšþÿìšÿךÿךÿìšÿìšÿì›�ÿš›�ÿ×›�ÿš›�)›Ÿÿ×›¤ÿ®›¦�)›ªÿ…›®ÿ®›µÿ®›¸ÿ×›»ÿ×›¼�)›¾ÿÛÄ�)›ÌÿÛÍÿÛÎÿš›Ïÿ®›Ðÿ×›Ñÿ×›ÒÿÛÓÿÛÔÿÛÕÿš›ÖÿÛ×ÿÛØÿ®›ÙÿÛÚÿÛÛÿ®›Þÿ®›ßÿ×›àÿÛáÿš›âÿÛãÿÛåÿÛæÿÛçÿ×›èÿÛêÿ®›ë�)›ìÿÛíÿ®›îÿÛòÿš›óÿÛô�)›õÿÛ÷ÿÛùÿÛÿ×›ÿ×›ÿ×›ÿš› ÿš›jÿ®›kÿÛlÿ×›qÿÛrÿ…›sÿš›uÿÛwÿ×›yÿÛ}ÿÛ~ÿ×›ÿ®›„ÿ×›…ÿ®›†ÿ×›‡ÿ®›ˆÿ×›‰ÿ®›Šÿ×›Œÿ×›ÿ®›–ÿÛ˜�)›šÿÛžÿÛ ÿ×›¢ÿ×›¤ÿÛ¦ÿÛ¨�)›©�)›¬ÿÛ®ÿÛ°ÿÛ±ÿ×›²ÿ®›³ÿ×›´ÿ®›µ�)›¼ÿ×›½�)›Àÿš›Âÿš›ÄÿÛÅÿ×›ÆÿÛÇÿ×›ÈÿÛËÿ×›ÍÿÛÎÿ®›Ïÿš›ÑÿÛÓÿÛÕÿš›×ÿÛÙÿ…›Ûÿ…›Ýÿ…›àÿ®›æÿ×›èÿ×›ìÿÛîÿÛïÿ×›ðÿ®›ñÿ×›òÿ®›óÿ×›ôÿ®›öÿ×›þÿš›�ÿÛÿÛÿ×›ÿ×› ÿš› ÿ®› ÿš› ÿ®›ÿ×›ÿ×›ÿ®›ÿš›ÿÛÿ×›ÿ®›�)›ÿ®›ÿ®›ÿšœ�ÿÜ�ÿÜÎÿÜÏÿלÕÿÜØÿלÛÿלÞÿלêÿלíÿלòÿÜÿÜ ÿÜjÿלsÿÜÿל…ÿל‡ÿל‰ÿלÿל²ÿל´ÿלÏÿÜàÿלðÿלòÿלôÿל ÿל ÿלÿÜÿלÿלÿÃ�ÿÃ� ÿÃÿã�f¦ÿüÿÃÁÿ®ÄÿÃÜÿ×áÿ×äÿ×ÿà ÿÃ|ÿ®€ÿÂÿéÿêÿ×µÿöÿ×·ÿ×¹ÿ×½ÿþÿ׿ÿ®Àÿ×Áÿ®Âÿ×Ôÿ®Õÿ×ýÿ®þÿ× ÿ×ÿÃÿ×ÿÃÿÃÿמ�ÿÞ� ÿÞÿÞ ÿÞÿמÿןŸÿן£�៸ÿן»ÿן¾ÿßÜÿןáÿ®Ÿäÿןlÿן{�=Ÿ}ÿìŸ~ÿן„ÿן†ÿןˆÿןŠÿןŒÿןªÿן±ÿן³ÿן¶ÿן¾ÿןÀÿ®ŸÂÿ®ŸÅÿ߯ÿןÇÿßÈÿןÕÿ®Ÿïÿןñÿןóÿןþÿ®Ÿÿןÿןÿןÿ× Ïÿì Øÿì Ûÿì Þÿì áÿì êÿì íÿì jÿì ÿì …ÿì ‡ÿì ‰ÿì ÿì ²ÿì ´ÿì Àÿì Âÿì Õÿì àÿì ðÿì òÿì ôÿì þÿì  ÿì  ÿì ÿ× ÿ× ÿì ÿì¡�ÿ®¡�ÿ®¡ÿ®¡ ÿ®¡€ÿì¡‚ÿì¡·ÿ졹ÿì¡ ÿסÿ×¢é�)£Ÿÿ×££�᣸ÿ×£»ÿ×£¾ÿãÜÿ×£áÿ®£äÿ×£lÿ×£{�=£}ÿì£~ÿ×£„ÿ×£†ÿ×£ˆÿ×£Šÿ×£Œÿ×£ªÿ×£±ÿ×£³ÿ×£¶ÿ×£¾ÿ×£Àÿ®£Âÿ®£ÅÿãÆÿ×£ÇÿãÈÿ×£Õÿ®£ïÿ×£ñÿ×£óÿ×£þÿ®£ÿ×£ÿ×£ÿ×£ÿפÏÿì¤Øÿì¤Ûÿì¤Þÿì¤áÿì¤êÿì¤íÿì¤jÿì¤ÿ줅ÿ줇ÿ줉ÿì¤ÿ줲ÿ줴ÿì¤Àÿì¤Âÿì¤Õÿì¤àÿì¤ðÿì¤òÿì¤ôÿì¤þÿì¤ ÿì¤ ÿì¤ÿפÿפÿì¤ÿ쥟ÿ×¥¸ÿ×¥»ÿ×¥¾ÿ×¥Áÿ×¥áÿ×¥lÿ×¥|ÿ×¥~ÿ×¥„ÿ×¥†ÿ×¥ˆÿ×¥Šÿ×¥Œÿ×¥±ÿ×¥³ÿ×¥¿ÿ×¥Àÿ×¥Áÿ×¥Âÿ×¥Åÿš¥Çÿš¥Ôÿ×¥Õÿ×¥ïÿ×¥ñÿ×¥óÿ×¥ýÿ×¥þÿ×¥ ÿ×¥ ÿ×¥ÿ×¥ÿ×¥ÿ×¥ÿì¦ÏÿצØÿצÛÿצÞÿצáÿצêÿצíÿצjÿצÿצ…ÿצ‡ÿצ‰ÿצÿצ²ÿצ´ÿצÀÿצÂÿצÆÿצÈÿצÕÿצàÿצðÿצòÿצôÿצþÿצ ÿצ ÿצÿצÿ×§Ÿÿ×§¸ÿ×§»ÿ×§¾ÿ×§Áÿ×§áÿ×§lÿ×§|ÿ×§~ÿ×§„ÿ×§†ÿ×§ˆÿ×§Šÿ×§Œÿ×§±ÿ×§³ÿ×§¿ÿ×§Àÿ×§Áÿ×§Âÿ×§Åÿš§Çÿš§Ôÿ×§Õÿ×§ïÿ×§ñÿ×§óÿ×§ýÿ×§þÿ×§ ÿ×§ ÿ×§ÿ×§ÿ×§ÿ×§ÿì¨ÏÿרØÿרÛÿרÞÿרáÿרêÿרíÿרjÿרÿר…ÿר‡ÿר‰ÿרÿר²ÿר´ÿרÀÿרÂÿרÆÿרÈÿרÕÿרàÿרðÿרòÿרôÿרþÿר ÿר ÿרÿרÿשŸÿש¸ÿש»ÿש¾ÿשÁÿשáÿשlÿש|ÿש~ÿש„ÿש†ÿשˆÿשŠÿשŒÿש±ÿש³ÿש¿ÿשÀÿשÁÿשÂÿשÅÿš©Çÿš©ÔÿשÕÿשïÿשñÿשóÿשýÿשþÿש ÿש ÿשÿשÿשÿשÿìªÏÿתØÿתÛÿתÞÿתáÿתêÿתíÿתjÿתÿת…ÿת‡ÿת‰ÿתÿת²ÿת´ÿתÀÿתÂÿתÆÿתÈÿתÕÿתàÿתðÿתòÿתôÿתþÿת ÿת ÿתÿתÿ׫£�á«ê�)«ÿ׫ÿ׬�ÿì¬� ÿì¬ÿì¬ ÿì­�ÿš­�ÿ×­�ÿš­�)­Ÿÿ×­¤ÿ®­¦�)­ªÿ…­®ÿ®­µÿ®­¸ÿ×­»ÿ×­¼�)­¾ÿíÄ�)­ÌÿíÍÿíÎÿš­Ïÿ®­Ðÿ×­Ñÿ×­ÒÿíÓÿíÔÿíÕÿš­Öÿí×ÿíØÿ®­ÙÿíÚÿíÛÿ®­Þÿ®­ßÿ×­àÿíáÿš­âÿíãÿíåÿíæÿíçÿ×­èÿíêÿ®­ë�)­ìÿííÿ®­îÿíòÿš­óÿíô�)­õÿí÷ÿíùÿíÿ×­ÿ×­ÿ×­ÿš­ ÿš­jÿ®­kÿílÿ×­qÿírÿ…­sÿš­uÿíwÿ×­yÿí}ÿí~ÿ×­ÿ®­„ÿ×­…ÿ®­†ÿ×­‡ÿ®­ˆÿ×­‰ÿ®­Šÿ×­Œÿ×­ÿ®­–ÿí˜�)­šÿížÿí ÿ×­¢ÿ×­¤ÿí¦ÿí¨�)­©�)­¬ÿí®ÿí°ÿí±ÿ×­²ÿ®­³ÿ×­´ÿ®­µ�)­¼ÿ×­½�)­Àÿš­Âÿš­ÄÿíÅÿ×­ÆÿíÇÿ×­ÈÿíËÿ×­ÍÿíÎÿ®­Ïÿš­ÑÿíÓÿíÕÿš­×ÿíÙÿ…­Ûÿ…­Ýÿ…­àÿ®­æÿ×­èÿ×­ìÿíîÿíïÿ×­ðÿ®­ñÿ×­òÿ®­óÿ×­ôÿ®­öÿ×­þÿš­�ÿíÿíÿ×­ÿ×­ ÿš­ ÿ®­ ÿš­ ÿ®­ÿ×­ÿ×­ÿ®­ÿš­ÿíÿ×­ÿ®­�)­ÿ®­ÿ®­ÿš®�ÿš®�ÿ×®�ÿš®ÎÿîÏÿì®ÕÿîØÿì®Ûÿì®Þÿì®êÿì®íÿì®òÿîÿ×®ÿ×®ÿ×®ÿš® ÿš®jÿì®sÿîÿì®…ÿ쮇ÿ쮉ÿì®ÿ쮲ÿì®´ÿì®Ïÿîàÿì®ðÿì®òÿì®ôÿì® ÿì® ÿì®ÿîÿì®ÿì®ÿï�ÿ\¯� ÿ\¯ÿš¯£�f¯¦ÿš¯¼ÿH¯Áÿ…¯Äÿš¯Üÿ®¯áÿׯäÿ®¯ÿ\¯ ÿ\¯|ÿ…¯€ÿq¯‚ÿq¯©ÿš¯ªÿ®¯µÿH¯¶ÿ®¯·ÿš¯¹ÿš¯½ÿš¯¾ÿ®¯¿ÿ…¯ÀÿׯÁÿ…¯ÂÿׯÅÿïÆÿׯÇÿïÈÿׯÔÿ…¯Õÿׯýÿ…¯þÿׯ ÿH¯ÿ®¯ÿH¯ÿ®¯ÿš¯ÿ®°�ÿq°� ÿq°Üÿš°áÿ×°äÿš°ÿq° ÿq°mÿ×°ÿ×°ƒÿ×°‹ÿ×°ªÿš°¶ÿš°¸ÿ×°ºÿ×°¾ÿš°Àÿ×°Âÿ×°Æÿ×°Èÿ×°Õÿ×°þÿ×°ÿq°ÿq°ÿš±ÿ×±¦ÿ×±¼ÿñÄÿ×±€ÿ챂ÿ챩ÿ×±µÿñ·ÿì±¹ÿì±½ÿ×± ÿ×±ÿ×±ÿײ�ÿì²� ÿì²ÐÿײÜÿì²Ýÿì²ßÿײáÿì²äÿì²öÿì²ÿì² ÿì² ÿײªÿì²¶ÿì²¼ÿײ¾ÿì²Àÿì²Âÿì²ËÿײÕÿì²æÿײøÿì²úÿì²üÿì²þÿì²ÿײÿײÿì²ÿì²ÿ쳟ÿ׳¸ÿ׳»ÿ׳¾ÿ׳áÿ׳lÿ׳~ÿ׳„ÿ׳†ÿ׳ˆÿ׳Šÿ׳Œÿ׳±ÿ׳³ÿ׳Àÿ׳Âÿ׳Åÿ׳Çÿ׳Õÿ׳ïÿ׳ñÿ׳óÿ׳þÿ׳ ÿ׳ ÿ׳ÿ׳ÿ׳ÿ×µ�ÿ…µ�ÿ®µ�ÿ…µŸÿ×µ¤ÿšµªÿqµ®ÿšµµÿšµ¸ÿ×µ»ÿ×µ¼�)µ¾ÿ®µÌÿšµÍÿšµÎÿ…µÏÿqµÐÿ×µÑÿ×µÒÿšµÓÿšµÔÿšµÕÿ…µÖÿšµ×ÿšµØÿqµÙÿšµÚÿšµÛÿqµÜÿ®µÝÿ®µÞÿqµßÿ×µàÿšµáÿšµâÿšµãÿšµäÿ®µåÿšµæÿšµçÿ×µèÿšµéÿõêÿqµìÿšµíÿqµîÿ…µòÿ…µóÿšµõÿšµöÿ®µ÷ÿšµùÿšµÿ®µÿ®µÿ®µÿ…µ ÿ…µjÿqµkÿšµlÿ×µmÿ×µqÿšµrÿqµsÿ…µuÿšµwÿšµyÿšµ}ÿšµ~ÿ×µÿqµÿ×µƒÿ×µ„ÿ×µ…ÿqµ†ÿ×µ‡ÿqµˆÿ×µ‰ÿqµŠÿ×µ‹ÿ×µŒÿ×µÿqµ–ÿšµšÿšµžÿšµ ÿ×µ¢ÿ×µ¤ÿšµ¦ÿšµªÿ®µ¬ÿšµ®ÿšµ°ÿšµ±ÿ×µ²ÿqµ³ÿ×µ´ÿqµµ�)µ¶ÿ®µ¸ÿ®µºÿ®µ¼ÿ×µ¾ÿ®µÀÿšµÂÿšµÄÿšµÅÿšµÆÿqµÇÿšµÈÿqµËÿ×µÍÿšµÎÿšµÏÿ…µÑÿšµÓÿšµÕÿšµ×ÿšµÙÿqµÛÿqµÝÿqµàÿqµæÿ×µèÿ×µêÿõìÿšµîÿšµïÿ×µðÿqµñÿ×µòÿqµóÿ×µôÿqµöÿ×µøÿ®µúÿ®µüÿ®µþÿšµ�ÿšµÿšµÿ×µÿ×µ ÿqµ ÿqµ ÿqµ ÿqµÿšµÿšµÿšµÿ…µÿšµÿ×µÿqµÿ®µÿqµÿšµÿ…¶�ÿš¶�ÿ×¶�ÿš¶ÎÿöÏÿì¶ÕÿöØÿì¶Ûÿì¶Þÿì¶êÿì¶íÿì¶òÿöÿ×¶ÿ×¶ÿ×¶ÿš¶ ÿš¶jÿì¶sÿöÿì¶…ÿ춇ÿ춉ÿì¶ÿì¶²ÿì¶´ÿì¶Ïÿöàÿì¶ðÿì¶òÿì¶ôÿì¶ ÿì¶ ÿì¶ÿöÿì¶ÿì¶ÿ÷�ÿ…·�ÿ…·Ÿÿ×·¤ÿ®·ªÿ…·®ÿ®·µÿ®·¸ÿ×·»ÿ×·¾ÿ÷Êÿ®·Ìÿ÷Íÿ÷Îÿš·Ïÿš·Òÿ÷Óÿ÷Ôÿ÷Õÿš·Öÿ÷×ÿ÷Øÿš·Ùÿ÷Úÿ÷Ûÿš·Þÿš·àÿ÷áÿ®·âÿ÷ãÿ÷åÿ÷æÿ÷èÿ÷éÿ×·êÿš·ë�)·ìÿ÷íÿš·îÿ®·òÿš·óÿ÷ô�)·õÿ÷÷ÿ÷ùÿ÷ÿ…· ÿ…·jÿš·kÿ÷lÿ×·qÿ÷rÿ…·sÿš·uÿ÷wÿ×·yÿ÷}ÿ×·~ÿ×·ÿš·„ÿ×·…ÿš·†ÿ×·‡ÿš·ˆÿ×·‰ÿš·Šÿ×·Œÿ×·ÿš·–ÿ÷˜�)·šÿ÷žÿ÷¤ÿ÷¦ÿ÷¨�)·¬ÿ÷®ÿ÷°ÿ÷±ÿ×·²ÿš·³ÿ×·´ÿš·Àÿ®·Âÿ®·Äÿ÷Æÿ®·Èÿ®·Íÿ÷Îÿ®·Ïÿš·Ñÿ÷Óÿ÷Õÿ®·×ÿ÷Ùÿ…·Úÿ®·Ûÿ…·Üÿ®·Ýÿ…·Þÿ®·àÿš·áÿì·âÿ®·ãÿì·äÿ®·ìÿ÷îÿ÷ïÿ×·ðÿš·ñÿ×·òÿš·óÿ×·ôÿš·þÿ®·�ÿ÷ÿ÷ ÿ®· ÿš· ÿ®· ÿš·ÿ×·ÿ×·ÿ®·ÿš·ÿ÷ÿ×·ÿš·ÿì·ÿš·ÿ®·ÿš¸�ÿ®¸�ÿ®¸Îÿì¸Õÿì¸òÿì¸ÿ®¸ ÿ®¸sÿì¸Ïÿì¸ÿì¸ÿì¹�ÿ…¹�ÿ…¹Ÿÿ×¹¤ÿ®¹ªÿ…¹®ÿ®¹µÿ®¹¸ÿ×¹»ÿ×¹¾ÿùÊÿ®¹ÌÿùÍÿùÎÿš¹Ïÿš¹ÒÿùÓÿùÔÿùÕÿš¹Öÿù×ÿùØÿš¹ÙÿùÚÿùÛÿš¹Þÿš¹àÿùáÿ®¹âÿùãÿùåÿùæÿùèÿùéÿ×¹êÿš¹ë�)¹ìÿùíÿš¹îÿ®¹òÿš¹óÿùô�)¹õÿù÷ÿùùÿùÿ…¹ ÿ…¹jÿš¹kÿùlÿ×¹qÿùrÿ…¹sÿš¹uÿùwÿ×¹yÿù}ÿ×¹~ÿ×¹ÿš¹„ÿ×¹…ÿš¹†ÿ×¹‡ÿš¹ˆÿ×¹‰ÿš¹Šÿ×¹Œÿ×¹ÿš¹–ÿù˜�)¹šÿùžÿù¤ÿù¦ÿù¨�)¹¬ÿù®ÿù°ÿù±ÿ×¹²ÿš¹³ÿ×¹´ÿš¹Àÿ®¹Âÿ®¹ÄÿùÆÿ®¹Èÿ®¹ÍÿùÎÿ®¹Ïÿš¹ÑÿùÓÿùÕÿ®¹×ÿùÙÿ…¹Úÿ®¹Ûÿ…¹Üÿ®¹Ýÿ…¹Þÿ®¹àÿš¹áÿì¹âÿ®¹ãÿì¹äÿ®¹ìÿùîÿùïÿ×¹ðÿš¹ñÿ×¹òÿš¹óÿ×¹ôÿš¹þÿ®¹�ÿùÿù ÿ®¹ ÿš¹ ÿ®¹ ÿš¹ÿ×¹ÿ×¹ÿ®¹ÿš¹ÿùÿ×¹ÿš¹ÿì¹ÿš¹ÿ®¹ÿšº�ÿ®º�ÿ®ºÎÿìºÕÿìºòÿìºÿ®º ÿ®ºsÿìºÏÿìºÿìºÿ컟ÿ×»£�Ỹÿ×»»ÿ×»¾ÿûÜÿ×»áÿ®»äÿ×»lÿ×»{�=»}ÿì»~ÿ×»„ÿ×»†ÿ×»ˆÿ×»Šÿ×»Œÿ×»ªÿ×»±ÿ×»³ÿ×»¶ÿ×»¾ÿ×»Àÿ®»Âÿ®»ÅÿûÆÿ×»ÇÿûÈÿ×»Õÿ®»ïÿ×»ñÿ×»óÿ×»þÿ®»ÿ×»ÿ×»ÿ×»ÿ×¼Ïÿì¼Øÿì¼Ûÿì¼Þÿì¼áÿì¼êÿì¼íÿì¼jÿì¼ÿì¼…ÿ켇ÿ켉ÿì¼ÿì¼²ÿì¼´ÿì¼Àÿì¼Âÿì¼Õÿì¼àÿì¼ðÿì¼òÿì¼ôÿì¼þÿì¼ ÿì¼ ÿì¼ÿ×¼ÿ×¼ÿì¼ÿì½£�á½ê�)½ÿ×½ÿ×¾�ÿì¾� ÿì¾ÿì¾ ÿì¿£�á¿ê�)¿ÿ׿ÿ×À�ÿìÀ� ÿìÀÿìÀ ÿìÃ�ÿÃÃ� ÿÃÃÿ׿ÿ×üÿ…ÃÁÿ®ÃÄÿ×ÃÜÿ×ÃÝÿìÃáÿìÃäÿ×ÃöÿìÃÿÃà ÿÃÃ|ÿ®Ã€ÿÃÂÿÃéÿ×êÿ×õÿ…öÿ×÷ÿšÃ¹ÿšÃ½ÿ×þÿ×ÿÿ®ÃÀÿìÃÁÿ®ÃÂÿìÃÔÿ®ÃÕÿìÃøÿìÃúÿìÃüÿìÃýÿ®Ãþÿìà ÿ®Ãÿ×Ãÿ®Ãÿ×Ãÿ×Ãÿ×Ä�ÿšÄ� ÿšÄÜÿ×ÄÝÿ×Ääÿ×Äöÿ×ÄÿšÄ ÿšÄªÿ×Ķÿ×ĸÿ×ĺÿ׾ÿ×Äøÿ×Äúÿ×Äüÿ×Äÿ®Äÿ®Äÿ׿ÿ×Å€ÿìÅ‚ÿìŵÿ×Å·ÿìŹÿìÅ ÿìÅÿìÆ�ÿìÆ� ÿìÆÿìÆ ÿìǼÿ×Ç€ÿìÇ‚ÿìǵÿ×Ç·ÿìǹÿìÇ ÿìÇÿìÈ�ÿìÈ� ÿìÈÿìÈ ÿìÊŸÿ×ʸÿ×Ê»ÿ×ʾÿ×ÊÁÿ×Êáÿ×Êlÿ×Ê|ÿ×Ê~ÿ×Ê„ÿ×ʆÿ×ʈÿ×ÊŠÿ×ÊŒÿ×ʱÿ×ʳÿ×Ê¿ÿ×ÊÀÿ×ÊÁÿ×ÊÂÿ×ÊÅÿšÊÇÿšÊÔÿ×ÊÕÿ×Êïÿ×Êñÿ×Êóÿ×Êýÿ×Êþÿ×Ê ÿ×Ê ÿ×Êÿ×Êÿ×Êÿ×ÊÿìËÏÿ×ËØÿ×ËÛÿ×ËÞÿ×Ëáÿ×Ëêÿ×Ëíÿ×Ëjÿ×Ëÿ×Ë…ÿסÿ×ˉÿ×Ëÿ×˲ÿ×Ë´ÿ×ËÀÿ×ËÂÿ×ËÆÿ×ËÈÿ×ËÕÿ×Ëàÿ×Ëðÿ×Ëòÿ×Ëôÿ×Ëþÿ×Ë ÿ×Ë ÿ×Ëÿ×Ëÿ×Ì�ÿÃÌ� ÿÃÌ£�f̼ÿ×̾ÿ×ÌÁÿ®ÌÜÿÃÌáÿ×ÌäÿÃÌÿÃÌ ÿÃÌmÿìÌ|ÿ®Ì€ÿ×ÌÿìÌ‚ÿ×̃ÿìÌ‹ÿì̪ÿÃ̵ÿ×̶ÿÃÌ·ÿ×̸ÿì̹ÿ×̺ÿì̾ÿÃÌ¿ÿ®ÌÀÿ×ÌÁÿ®ÌÂÿ×ÌÅÿÃÌÆÿ×ÌÇÿÃÌÈÿ×ÌÔÿ®ÌÕÿ×Ìýÿ®Ìþÿ×Ì ÿ×ÌÿÃÌÿ×ÌÿÃÌÿÃÍáÿ×ÍÀÿ×ÍÂÿ×ÍÕÿ×Íþÿ×Σ�áÎê�)Îÿ×Îÿ×Ï�ÿìÏ� ÿìÏÿìÏ ÿìÒ£�áÒê�)Òÿ×Òÿ×Ó�ÿìÓ� ÿìÓÿìÓ ÿìÖ£�áÖê�)Öÿ×Öÿ××�ÿì×� ÿì×ÿì× ÿìÙ�ÿqÙ� ÿqÙÿšÙ¦ÿšÙ¼ÿqÙ¾ÿ×ÙÁÿšÙÄÿšÙÜÿ×Ùáÿ×Ùäÿ×ÙÿqÙ ÿqÙnÿ×Ù|ÿšÙ€ÿ®Ù‚ÿ®Ù—ÿ×Ù›ÿ×Ù§ÿ×Ù©ÿšÙªÿ×ÙµÿqÙ¶ÿ×Ù·ÿ…Ù¹ÿ…Ù½ÿšÙ¾ÿ×Ù¿ÿšÙÀÿ×ÙÁÿšÙÂÿ×ÙÅÿšÙÇÿšÙÔÿšÙÕÿ×Ùáÿ×Ùãÿ×ÙýÿšÙþÿ×Ùÿ×Ù ÿqÙÿ×ÙÿqÙÿ×ÙÿšÙÿ×Ú�ÿìÚ� ÿìÚÿìÚ ÿìÛ�ÿqÛ� ÿqÛÿšÛ¦ÿšÛ¼ÿqÛ¾ÿ×ÛÁÿšÛÄÿšÛÜÿ×Ûáÿ×Ûäÿ×ÛÿqÛ ÿqÛnÿ×Û|ÿšÛ€ÿ®Û‚ÿ®Û—ÿ×Û›ÿ×Û§ÿ×Û©ÿšÛªÿ×ÛµÿqÛ¶ÿ×Û·ÿ…Û¹ÿ…Û½ÿšÛ¾ÿ×Û¿ÿšÛÀÿ×ÛÁÿšÛÂÿ×ÛÅÿšÛÇÿšÛÔÿšÛÕÿ×Ûáÿ×Ûãÿ×ÛýÿšÛþÿ×Ûÿ×Û ÿqÛÿ×ÛÿqÛÿ×ÛÿšÛÿ×Ü�ÿìÜ� ÿìÜÿìÜ ÿìÞ�ÿìÞ� ÿìÞÿìÞ ÿìà�ÿìà� ÿìàÿìà ÿìá�ÿ®á�ÿ®áÿìá¤ÿ×á¦ÿìá¨ÿ×áªÿ×á®ÿ×á°ÿ×á±ÿìáµÿ×á¼ÿÃá½ÿ×á¿ÿ×áÁÿ×áÄÿìáÇÿìáÎÿìáÕÿìáòÿìáÿ®á ÿ®árÿ×ásÿìázÿìá|ÿ×á€ÿìá‚ÿìáŸÿ×á¡ÿìá©ÿìáµÿÃá·ÿìá¹ÿìá»ÿ×á½ÿìá¿ÿ×áÁÿ×áÊÿ×áÎÿ×áÏÿìáÔÿ×áÙÿ×áÛÿ×áÝÿ×áåÿ×áçÿìáõÿìá÷ÿ×áùÿ×áûÿ×áýÿ×áÿ×áÿ×á ÿ×áÿ×áÿ×áÿìáÿìáÿ×áÿìâ�ÿìâ� ÿìâÐÿ×âÜÿìâÝÿìâßÿ×âáÿìâäÿìâöÿìâÿìâ ÿìâ ÿ×âªÿìâ¶ÿìâ¼ÿ×â¾ÿìâÀÿìâÂÿìâËÿ×âÕÿìâæÿ×âøÿìâúÿìâüÿìâþÿìâÿ×âÿ×âÿìâÿìâÿìã�ÿ®ã�ÿ®ãÿìã¤ÿ×ã¦ÿìã¨ÿ×ãªÿ×ã®ÿ×ã°ÿ×ã±ÿìãµÿ×ã¼ÿÃã½ÿ×ã¿ÿ×ãÁÿ×ãÄÿìãÇÿìãÎÿìãÕÿìãòÿìãÿ®ã ÿ®ãrÿ×ãsÿìãzÿìã|ÿ×ã€ÿìã‚ÿìãŸÿ×ã¡ÿìã©ÿìãµÿÃã·ÿìã¹ÿìã»ÿ×ã½ÿìã¿ÿ×ãÁÿ×ãÊÿ×ãÎÿ×ãÏÿìãÔÿ×ãÙÿ×ãÛÿ×ãÝÿ×ãåÿ×ãçÿìãõÿìã÷ÿ×ãùÿ×ãûÿ×ãýÿ×ãÿ×ãÿ×ã ÿ×ãÿ×ãÿ×ãÿìãÿìãÿ×ãÿìä�ÿìä� ÿìäÐÿ×äÜÿìäÝÿìäßÿ×äáÿìääÿìäöÿìäÿìä ÿìä ÿ×äªÿìä¶ÿìä¼ÿ×ä¾ÿìäÀÿìäÂÿìäËÿ×äÕÿìäæÿ×äøÿìäúÿìäüÿìäþÿìäÿ×äÿ×äÿìäÿìäÿìåŸÿ×å¸ÿ×å»ÿ×å¾ÿ×åÁÿ×åáÿ×ålÿ×å|ÿ×å~ÿ×å„ÿ×å†ÿ×åˆÿ×åŠÿ×åŒÿ×å±ÿ×å³ÿ×å¿ÿ×åÀÿ×åÁÿ×åÂÿ×åÅÿšåÇÿšåÔÿ×åÕÿ×åïÿ×åñÿ×åóÿ×åýÿ×åþÿ×å ÿ×å ÿ×åÿ×åÿ×åÿ×åÿìæÏÿ׿Øÿ׿Ûÿ׿Þÿ׿áÿ׿êÿ׿íÿ׿jÿ׿ÿ׿…ÿ׿‡ÿ׿‰ÿ׿ÿ׿²ÿ׿´ÿ׿Àÿ׿Âÿ׿Æÿ׿Èÿ׿Õÿ׿àÿ׿ðÿ׿òÿ׿ôÿ׿þÿ׿ ÿ׿ ÿ׿ÿ׿ÿ×ç�ÿ®ç�ÿ®çÿ®ç ÿ®ç€ÿìç‚ÿìç·ÿìç¹ÿìç ÿ×çÿ×èé�)é�ÿìé� ÿìéÿìé ÿìéÿ×éÿ×ï�ÿ®ï�ÿ®ïÿìï¤ÿ×ï¦ÿìï¨ÿ×ïªÿ×ï®ÿ×ï°ÿ×ï±ÿìïµÿ×ï¼ÿÃï½ÿ×ï¿ÿ×ïÁÿ×ïÄÿìïÇÿìïÎÿìïÕÿìïòÿìïÿ®ï ÿ®ïrÿ×ïsÿìïzÿìï|ÿ×ï€ÿìï‚ÿìïŸÿ×ï¡ÿìï©ÿìïµÿÃï·ÿìï¹ÿìï»ÿ×ï½ÿìï¿ÿ×ïÁÿ×ïÊÿ×ïÎÿ×ïÏÿìïÔÿ×ïÙÿ×ïÛÿ×ïÝÿ×ïåÿ×ïçÿìïõÿìï÷ÿ×ïùÿ×ïûÿ×ïýÿ×ïÿ×ïÿ×ï ÿ×ïÿ×ïÿ×ïÿìïÿìïÿ×ïÿìð�ÿìð� ÿìðÐÿ×ðÜÿìðÝÿìðßÿ×ðáÿìðäÿìðöÿìðÿìð ÿìð ÿ×ðªÿìð¶ÿìð¼ÿ×ð¾ÿìðÀÿìðÂÿìðËÿ×ðÕÿìðæÿ×ðøÿìðúÿìðüÿìðþÿìðÿ×ðÿ×ðÿìðÿìðÿìñ�ÿ®ñ�ÿ®ñÿìñ¤ÿ×ñ¦ÿìñ¨ÿ×ñªÿ×ñ®ÿ×ñ°ÿ×ñ±ÿìñµÿ×ñ¼ÿÃñ½ÿ×ñ¿ÿ×ñÁÿ×ñÄÿìñÇÿìñÎÿìñÕÿìñòÿìñÿ®ñ ÿ®ñrÿ×ñsÿìñzÿìñ|ÿ×ñ€ÿìñ‚ÿìñŸÿ×ñ¡ÿìñ©ÿìñµÿÃñ·ÿìñ¹ÿìñ»ÿ×ñ½ÿìñ¿ÿ×ñÁÿ×ñÊÿ×ñÎÿ×ñÏÿìñÔÿ×ñÙÿ×ñÛÿ×ñÝÿ×ñåÿ×ñçÿìñõÿìñ÷ÿ×ñùÿ×ñûÿ×ñýÿ×ñÿ×ñÿ×ñ ÿ×ñÿ×ñÿ×ñÿìñÿìñÿ×ñÿìò�ÿìò� ÿìòÐÿ×òÜÿìòÝÿìòßÿ×òáÿìòäÿìòöÿìòÿìò ÿìò ÿ×òªÿìò¶ÿìò¼ÿ×ò¾ÿìòÀÿìòÂÿìòËÿ×òÕÿìòæÿ×òøÿìòúÿìòüÿìòþÿìòÿ×òÿ×òÿìòÿìòÿìó�ÿ®ó�ÿ®óÿìó¤ÿ×ó¦ÿìó¨ÿ×óªÿ×ó®ÿ×ó°ÿ×ó±ÿìóµÿ×ó¼ÿÃó½ÿ×ó¿ÿ×óÁÿ×óÄÿìóÇÿìóÎÿìóÕÿìóòÿìóÿ®ó ÿ®órÿ×ósÿìózÿìó|ÿ×ó€ÿìó‚ÿìóŸÿ×ó¡ÿìó©ÿìóµÿÃó·ÿìó¹ÿìó»ÿ×ó½ÿìó¿ÿ×óÁÿ×óÊÿ×óÎÿ×óÏÿìóÔÿ×óÙÿ×óÛÿ×óÝÿ×óåÿ×óçÿìóõÿìó÷ÿ×óùÿ×óûÿ×óýÿ×óÿ×óÿ×ó ÿ×óÿ×óÿ×óÿìóÿìóÿ×óÿìô�ÿìô� ÿìôÐÿ×ôÜÿìôÝÿìôßÿ×ôáÿìôäÿìôöÿìôÿìô ÿìô ÿ×ôªÿìô¶ÿìô¼ÿ×ô¾ÿìôÀÿìôÂÿìôËÿ×ôÕÿìôæÿ×ôøÿìôúÿìôüÿìôþÿìôÿ×ôÿ×ôÿìôÿìôÿìõ�ÿ®õ�ÿ®õÿìõ¤ÿ×õ¦ÿìõ¨ÿ×õªÿ×õ®ÿ×õ°ÿ×õ±ÿìõµÿ×õ¼ÿÃõ½ÿ×õ¿ÿ×õÁÿ×õÄÿìõÇÿìõÎÿìõÕÿìõòÿìõÿ®õ ÿ®õrÿ×õsÿìõzÿìõ|ÿ×õ€ÿìõ‚ÿìõŸÿ×õ¡ÿìõ©ÿìõµÿÃõ·ÿìõ¹ÿìõ»ÿ×õ½ÿìõ¿ÿ×õÁÿ×õÊÿ×õÎÿ×õÏÿìõÔÿ×õÙÿ×õÛÿ×õÝÿ×õåÿ×õçÿìõõÿìõ÷ÿ×õùÿ×õûÿ×õýÿ×õÿ×õÿ×õ ÿ×õÿ×õÿ×õÿìõÿìõÿ×õÿìö�ÿìö� ÿìöÐÿ×öÜÿìöÝÿìößÿ×öáÿìöäÿìööÿìöÿìö ÿìö ÿ×öªÿìö¶ÿìö¼ÿ×ö¾ÿìöÀÿìöÂÿìöËÿ×öÕÿìöæÿ×öøÿìöúÿìöüÿìöþÿìöÿ×öÿ×öÿìöÿìöÿì÷�ÿ…÷�ÿ…÷Ÿÿì÷¤ÿš÷ªÿq÷®ÿš÷µÿš÷¸ÿì÷»ÿì÷¾ÿÃ÷Éÿì÷Îÿ®÷Ïÿ×÷Õÿ®÷Øÿ×÷Ûÿ×÷Þÿ×÷áÿ×÷êÿ×÷ë�f÷íÿ×÷îÿì÷òÿ®÷ô�f÷ÿ…÷ ÿ…÷jÿ×÷lÿì÷rÿq÷sÿ®÷~ÿì÷ÿ×÷„ÿì÷…ÿ×÷†ÿì÷‡ÿ×÷ˆÿì÷‰ÿ×÷Šÿì÷Œÿì÷ÿ×÷˜�f÷¨�f÷±ÿì÷²ÿ×÷³ÿì÷´ÿ×÷Àÿ×÷Âÿ×÷Åÿ×÷ÆÿÃ÷Çÿ×÷ÈÿÃ÷Îÿš÷Ïÿ®÷Õÿ×÷Ùÿq÷Ûÿq÷Ýÿq÷àÿ×÷ïÿì÷ðÿ×÷ñÿì÷òÿ×÷óÿì÷ôÿ×÷þÿ×÷ ÿq÷ ÿ×÷ ÿq÷ ÿ×÷ÿš÷ÿ®÷ÿì÷ÿ×÷ÿ×÷ÿš÷ÿ®ø�ÿ®ø�ÿ®øÎÿ×øÕÿ×øòÿ×øÿ®ø ÿ®øsÿ×øÏÿ×øÿ×øÿ×ù�ÿ…ù�ÿ…ùŸÿìù¤ÿšùªÿqù®ÿšùµÿšù¸ÿìù»ÿìù¾ÿÃùÉÿìùÎÿ®ùÏÿ×ùÕÿ®ùØÿ×ùÛÿ×ùÞÿ×ùáÿ×ùêÿ×ùë�fùíÿ×ùîÿìùòÿ®ùô�fùÿ…ù ÿ…ùjÿ×ùlÿìùrÿqùsÿ®ù~ÿìùÿ×ù„ÿìù…ÿ×ù†ÿìù‡ÿ×ùˆÿìù‰ÿ×ùŠÿìùŒÿìùÿ×ù˜�fù¨�fù±ÿìù²ÿ×ù³ÿìù´ÿ×ùÀÿ×ùÂÿ×ùÅÿ×ùÆÿÃùÇÿ×ùÈÿÃùÎÿšùÏÿ®ùÕÿ×ùÙÿqùÛÿqùÝÿqùàÿ×ùïÿìùðÿ×ùñÿìùòÿ×ùóÿìùôÿ×ùþÿ×ù ÿqù ÿ×ù ÿqù ÿ×ùÿšùÿ®ùÿìùÿ×ùÿ×ùÿšùÿ®ú�ÿ®ú�ÿ®úÎÿ×úÕÿ×úòÿ×úÿ®ú ÿ®úsÿ×úÏÿ×úÿ×úÿ×û�ÿ…û�ÿ…ûŸÿìû¤ÿšûªÿqû®ÿšûµÿšû¸ÿìû»ÿìû¾ÿÃûÉÿìûÎÿ®ûÏÿ×ûÕÿ®ûØÿ×ûÛÿ×ûÞÿ×ûáÿ×ûêÿ×ûë�fûíÿ×ûîÿìûòÿ®ûô�fûÿ…û ÿ…ûjÿ×ûlÿìûrÿqûsÿ®û~ÿìûÿ×û„ÿìû…ÿ×û†ÿìû‡ÿ×ûˆÿìû‰ÿ×ûŠÿìûŒÿìûÿ×û˜�fû¨�fû±ÿìû²ÿ×û³ÿìû´ÿ×ûÀÿ×ûÂÿ×ûÅÿ×ûÆÿÃûÇÿ×ûÈÿÃûÎÿšûÏÿ®ûÕÿ×ûÙÿqûÛÿqûÝÿqûàÿ×ûïÿìûðÿ×ûñÿìûòÿ×ûóÿìûôÿ×ûþÿ×û ÿqû ÿ×û ÿqû ÿ×ûÿšûÿ®ûÿìûÿ×ûÿ×ûÿšûÿ®ü�ÿ®ü�ÿ®üÎÿ×üÕÿ×üòÿ×üÿ®ü ÿ®üsÿ×üÏÿ×üÿ×üÿ×ÿ�ÿ…ÿ�ÿ®ÿ�ÿ…ÿŸÿ×ÿ¤ÿšÿªÿqÿ®ÿšÿµÿšÿ¸ÿ×ÿ»ÿ×ÿ¼�)ÿ¾ÿ®ÿÌÿšÿÍÿšÿÎÿ…ÿÏÿqÿÐÿ×ÿÑÿ×ÿÒÿšÿÓÿšÿÔÿšÿÕÿ…ÿÖÿšÿ×ÿšÿØÿqÿÙÿšÿÚÿšÿÛÿqÿÜÿ®ÿÝÿ®ÿÞÿqÿßÿ×ÿàÿšÿáÿšÿâÿšÿãÿšÿäÿ®ÿåÿšÿæÿšÿçÿ×ÿèÿšÿéÿÃÿêÿqÿìÿšÿíÿqÿîÿ…ÿòÿ…ÿóÿšÿõÿšÿöÿ®ÿ÷ÿšÿùÿšÿÿ®ÿÿ®ÿÿ®ÿÿ…ÿ ÿ…ÿjÿqÿkÿšÿlÿ×ÿmÿ×ÿqÿšÿrÿqÿsÿ…ÿuÿšÿwÿšÿyÿšÿ}ÿšÿ~ÿ×ÿÿqÿÿ×ÿƒÿ×ÿ„ÿ×ÿ…ÿqÿ†ÿ×ÿ‡ÿqÿˆÿ×ÿ‰ÿqÿŠÿ×ÿ‹ÿ×ÿŒÿ×ÿÿqÿ–ÿšÿšÿšÿžÿšÿ ÿ×ÿ¢ÿ×ÿ¤ÿšÿ¦ÿšÿªÿ®ÿ¬ÿšÿ®ÿšÿ°ÿšÿ±ÿ×ÿ²ÿqÿ³ÿ×ÿ´ÿqÿµ�)ÿ¶ÿ®ÿ¸ÿ®ÿºÿ®ÿ¼ÿ×ÿ¾ÿ®ÿÀÿšÿÂÿšÿÄÿšÿÅÿšÿÆÿqÿÇÿšÿÈÿqÿËÿ×ÿÍÿšÿÎÿšÿÏÿ…ÿÑÿšÿÓÿšÿÕÿšÿ×ÿšÿÙÿqÿÛÿqÿÝÿqÿàÿqÿæÿ×ÿèÿ×ÿêÿÃÿìÿšÿîÿšÿïÿ×ÿðÿqÿñÿ×ÿòÿqÿóÿ×ÿôÿqÿöÿ×ÿøÿ®ÿúÿ®ÿüÿ®ÿþÿšÿ�ÿšÿÿšÿÿ×ÿÿ×ÿ ÿqÿ ÿqÿ ÿqÿ ÿqÿÿšÿÿšÿÿšÿÿ…ÿÿšÿÿ×ÿÿqÿÿ®ÿÿqÿÿšÿÿ…��ÿš��ÿ×��ÿš�ÎÿÃ�Ïÿì�ÕÿÃ�Øÿì�Ûÿì�Þÿì�êÿì�íÿì�òÿÃ�ÿ×�ÿ×�ÿ×�ÿš� ÿš�jÿì�sÿÃ�ÿì�…ÿì�‡ÿì�‰ÿì�ÿì�²ÿì�´ÿì�ÏÿÃ�àÿì�ðÿì�òÿì�ôÿì� ÿì� ÿì�ÿÃ�ÿì�ÿì�ÿÃ�ÿš�ÿ×�ÿš�)Ÿÿפÿ®¦�)ªÿ…®ÿ®µÿ®¸ÿ×»ÿ×¼�)¾ÿÃÄ�)ÌÿÃÍÿÃÎÿšÏÿ®Ðÿ×Ñÿ×ÒÿÃÓÿÃÔÿÃÕÿšÖÿÃ×ÿÃØÿ®ÙÿÃÚÿÃÛÿ®Þÿ®ßÿ×àÿÃáÿšâÿÃãÿÃåÿÃæÿÃçÿ×èÿÃêÿ®ë�)ìÿÃíÿ®îÿÃòÿšóÿÃô�)õÿÃ÷ÿÃùÿÃÿ×ÿ×ÿ×ÿš ÿšjÿ®kÿÃlÿ×qÿÃrÿ…sÿšuÿÃwÿ×yÿÃ}ÿÃ~ÿ×ÿ®„ÿ×…ÿ®†ÿׇÿ®ˆÿ׉ÿ®Šÿ׌ÿ×ÿ®–ÿØ�)šÿÞÿàÿ×¢ÿפÿæÿè�)©�)¬ÿîÿðÿñÿײÿ®³ÿ×´ÿ®µ�)¼ÿ×½�)ÀÿšÂÿšÄÿÃÅÿׯÿÃÇÿ×ÈÿÃËÿ×ÍÿÃÎÿ®ÏÿšÑÿÃÓÿÃÕÿš×ÿÃÙÿ…Ûÿ…Ýÿ…àÿ®æÿ×èÿ×ìÿÃîÿÃïÿ×ðÿ®ñÿ×òÿ®óÿ×ôÿ®öÿ×þÿš�ÿÃÿÃÿ×ÿ× ÿš ÿ® ÿš ÿ®ÿ×ÿ×ÿ®ÿšÿÃÿ×ÿ®�)ÿ®ÿ®ÿš�ÿÃ�ÿÃÎÿÃÏÿ×ÕÿÃØÿ×Ûÿ×Þÿ×êÿ×íÿ×òÿÃÿà ÿÃjÿ×sÿÃÿ×…ÿׇÿ׉ÿ×ÿײÿ×´ÿ×ÏÿÃàÿ×ðÿ×òÿ×ôÿ× ÿ× ÿ×ÿÃÿ×ÿ×ÿßÿ×£�á¸ÿ×»ÿ×¾ÿÃÜÿ×áÿ®äÿ×lÿ×{�=}ÿì~ÿׄÿ׆ÿ׈ÿ׊ÿ׌ÿתÿ×±ÿ׳ÿ×¶ÿ×¾ÿ×Àÿ®Âÿ®ÅÿÃÆÿ×ÇÿÃÈÿ×Õÿ®ïÿ×ñÿ×óÿ×þÿ®ÿ×ÿ×ÿ×ÿ×ÏÿìØÿìÛÿìÞÿìáÿìêÿìíÿìjÿìÿì…ÿì‡ÿì‰ÿìÿì²ÿì´ÿìÀÿìÂÿìÕÿìàÿìðÿìòÿìôÿìþÿì ÿì ÿìÿ×ÿ×ÿìÿìŸÿ׸ÿ×»ÿ×¾ÿ×Áÿ×áÿ×lÿ×|ÿ×~ÿׄÿ׆ÿ׈ÿ׊ÿ׌ÿ×±ÿ׳ÿ׿ÿ×Àÿ×Áÿ×Âÿ×ÅÿšÇÿšÔÿ×Õÿ×ïÿ×ñÿ×óÿ×ýÿ×þÿ× ÿ× ÿ×ÿ×ÿ×ÿ×ÿìÏÿìØÿìÛÿìÞÿìáÿìêÿìíÿìjÿìÿì…ÿì‡ÿì‰ÿìÿì²ÿì´ÿìÀÿìÂÿìÕÿìàÿìðÿìòÿìôÿìþÿì ÿì ÿìÿ×ÿ×ÿìÿì �ÿš � ÿš ÿ® ¦ÿ® ¨ÿà ªÿà °ÿà ¼ÿq ½ÿà ¿ÿà Áÿà Äÿ® Ðÿ× Üÿà ßÿ× áÿ× äÿà ÿš  ÿš rÿà vÿ× |ÿà €ÿà ‚ÿà Ÿÿà  ÿ× ©ÿ® ªÿà µÿq ¶ÿà ·ÿà ¹ÿà »ÿà ¼ÿ× ½ÿ® ¾ÿà ¿ÿà Àÿ× Áÿà Âÿ× Êÿà Ëÿ× Ôÿà Õÿ× Ùÿà Ûÿà Ýÿà åÿà æÿ× ÷ÿà ùÿà ûÿà ýÿà þÿ× ÿà ÿ× ÿà ÿ×  ÿ× ÿ× ÿ× ÿ× ÿ® ÿà �ÿš � ÿš Ðÿ× Üÿà Ýÿ× ßÿ× áÿ× äÿà öÿ× ÿš  ÿš  ÿ× ªÿà ¶ÿà ¼ÿ× ¾ÿà Àÿ× Âÿ× Ëÿ× Õÿ× æÿ× øÿ× úÿ× üÿ× þÿ× ÿ× ÿ× ÿš ÿš ÿà �ÿš � ÿš ÿ® ¦ÿ® ¨ÿà ªÿà °ÿà ¼ÿq ½ÿà ¿ÿà Áÿà Äÿ® Ðÿ× Üÿà ßÿ× áÿ× äÿà ÿš  ÿš rÿà vÿ× |ÿà €ÿà ‚ÿà Ÿÿà  ÿ× ©ÿ® ªÿà µÿq ¶ÿà ·ÿà ¹ÿà »ÿà ¼ÿ× ½ÿ® ¾ÿà ¿ÿà Àÿ× Áÿà Âÿ× Êÿà Ëÿ× Ôÿà Õÿ× Ùÿà Ûÿà Ýÿà åÿà æÿ× ÷ÿà ùÿà ûÿà ýÿà þÿ× ÿà ÿ× ÿà ÿ×  ÿ× ÿ× ÿ× ÿ× ÿ® ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿã�áê�)ÿ×ÿ×�ÿì� ÿìÿì ÿì�ÿš� ÿšÿ®¦ÿ®¨ÿêÿðÿüÿq½ÿÿÿÃÁÿÃÄÿ®Ðÿ×ÜÿÃßÿ×áÿ×äÿÃÿš ÿšrÿÃvÿ×|ÿÀÿÂÿßÿàÿשÿ®ªÿõÿq¶ÿ÷ÿùÿûÿüÿ×½ÿ®¾ÿÿÿÃÀÿ×ÁÿÃÂÿ×ÊÿÃËÿ×ÔÿÃÕÿ×ÙÿÃÛÿÃÝÿÃåÿÃæÿ×÷ÿÃùÿÃûÿÃýÿÃþÿ×ÿÃÿ×ÿÃÿ× ÿ×ÿ×ÿ×ÿ×ÿ®ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃ�ÿš� ÿšÿ®¦ÿ®¨ÿêÿðÿüÿq½ÿÿÿÃÁÿÃÄÿ®Ðÿ×ÜÿÃßÿ×áÿ×äÿÃÿš ÿšrÿÃvÿ×|ÿÀÿÂÿßÿàÿשÿ®ªÿõÿq¶ÿ÷ÿùÿûÿüÿ×½ÿ®¾ÿÿÿÃÀÿ×ÁÿÃÂÿ×ÊÿÃËÿ×ÔÿÃÕÿ×ÙÿÃÛÿÃÝÿÃåÿÃæÿ×÷ÿÃùÿÃûÿÃýÿÃþÿ×ÿÃÿ×ÿÃÿ× ÿ×ÿ×ÿ×ÿ×ÿ®ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃ�ÿ®�ÿ®ªÿì°ÿ×¼ÿ׿ÿ×ÿ® ÿ®rÿì€ÿì‚ÿìŸÿ×µÿ×·ÿì¹ÿì»ÿ×Êÿ×ÙÿìÛÿìÝÿìåÿ×ÿ×ÿ×�ÿ×� ÿ×ÐÿìÝÿìßÿìöÿìÿ× ÿ× ÿì¼ÿìËÿìæÿìøÿìúÿìüÿìÿìÿìÿ×ÿ×�ÿ®� ÿ®ÿæÿêÿ×°ÿ×¼ÿÿÿ×Áÿ×ÄÿÃÜÿ×äÿ×ÿ® ÿ®rÿ×|ÿ×€ÿׂÿןÿשÿêÿ×µÿöÿ×·ÿ×¹ÿ×»ÿ×½ÿþÿ׿ÿ×Áÿ×Êÿ×Ôÿ×Ùÿ×Ûÿ×Ýÿ×åÿ×ýÿ×ÿ×ÿ× ÿ×ÿ×ÿÃÿ×�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃáÿ×Àÿ×Âÿ×Õÿ×þÿ×£�áê�)ÿ×ÿ×�ÿì� ÿìÿì ÿì�ÿq� ÿq�&ÿ×�*ÿ×�- �2ÿ×�4ÿ×�7ÿq�9ÿ®�:ÿ®�<ÿ…�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�Ÿÿ…�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿq&ÿq6ÿ®8ÿ…:ÿ…Gÿ×úÿ®üÿ®þÿ®�ÿ…ÿq ÿq_ÿ×Iÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×oÿ…qÿ…sÿ…ÿq�ÿì� ÿìÿì ÿì�ÿq� ÿq�&ÿ×�*ÿ×�- �2ÿ×�4ÿ×�7ÿq�9ÿ®�:ÿ®�<ÿ…�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�Ÿÿ…�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿq&ÿq6ÿ®8ÿ…:ÿ…Gÿ×úÿ®üÿ®þÿ®�ÿ…ÿq ÿq_ÿ×Iÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×oÿ…qÿ…sÿ…ÿq �ÿì � ÿì ÿì  ÿì!�ÿq!� ÿq!�&ÿ×!�*ÿ×!�- !�2ÿ×!�4ÿ×!�7ÿq!�9ÿ®!�:ÿ®!�<ÿ…!�‰ÿ×!�”ÿ×!�•ÿ×!�–ÿ×!�—ÿ×!�˜ÿ×!�šÿ×!�Ÿÿ…!�Èÿ×!�Êÿ×!�Ìÿ×!�Îÿ×!�Þÿ×!�àÿ×!�âÿ×!�äÿ×!ÿ×!ÿ×!ÿ×!ÿ×!$ÿq!&ÿq!6ÿ®!8ÿ…!:ÿ…!Gÿ×!úÿ®!üÿ®!þÿ®!�ÿ…!ÿq! ÿq!_ÿ×!Iÿ×!Kÿ×!Mÿ×!Oÿ×!Qÿ×!Sÿ×!Uÿ×!Wÿ×!Yÿ×![ÿ×!]ÿ×!_ÿ×!oÿ…!qÿ…!sÿ…!ÿq"�ÿì"� ÿì"ÿì" ÿì#�ÿq#� ÿq#�&ÿ×#�*ÿ×#�- #�2ÿ×#�4ÿ×#�7ÿq#�9ÿ®#�:ÿ®#�<ÿ…#�‰ÿ×#�”ÿ×#�•ÿ×#�–ÿ×#�—ÿ×#�˜ÿ×#�šÿ×#�Ÿÿ…#�Èÿ×#�Êÿ×#�Ìÿ×#�Îÿ×#�Þÿ×#�àÿ×#�âÿ×#�äÿ×#ÿ×#ÿ×#ÿ×#ÿ×#$ÿq#&ÿq#6ÿ®#8ÿ…#:ÿ…#Gÿ×#úÿ®#üÿ®#þÿ®#�ÿ…#ÿq# ÿq#_ÿ×#Iÿ×#Kÿ×#Mÿ×#Oÿ×#Qÿ×#Sÿ×#Uÿ×#Wÿ×#Yÿ×#[ÿ×#]ÿ×#_ÿ×#oÿ…#qÿ…#sÿ…#ÿq$�ÿì$� ÿì$ÿì$ ÿì%�ÿq%� ÿq%�&ÿ×%�*ÿ×%�- %�2ÿ×%�4ÿ×%�7ÿq%�9ÿ®%�:ÿ®%�<ÿ…%�‰ÿ×%�”ÿ×%�•ÿ×%�–ÿ×%�—ÿ×%�˜ÿ×%�šÿ×%�Ÿÿ…%�Èÿ×%�Êÿ×%�Ìÿ×%�Îÿ×%�Þÿ×%�àÿ×%�âÿ×%�äÿ×%ÿ×%ÿ×%ÿ×%ÿ×%$ÿq%&ÿq%6ÿ®%8ÿ…%:ÿ…%Gÿ×%úÿ®%üÿ®%þÿ®%�ÿ…%ÿq% ÿq%_ÿ×%Iÿ×%Kÿ×%Mÿ×%Oÿ×%Qÿ×%Sÿ×%Uÿ×%Wÿ×%Yÿ×%[ÿ×%]ÿ×%_ÿ×%oÿ…%qÿ…%sÿ…%ÿq&�ÿì&� ÿì&ÿì& ÿì'�ÿq'� ÿq'�&ÿ×'�*ÿ×'�- '�2ÿ×'�4ÿ×'�7ÿq'�9ÿ®'�:ÿ®'�<ÿ…'�‰ÿ×'�”ÿ×'�•ÿ×'�–ÿ×'�—ÿ×'�˜ÿ×'�šÿ×'�Ÿÿ…'�Èÿ×'�Êÿ×'�Ìÿ×'�Îÿ×'�Þÿ×'�àÿ×'�âÿ×'�äÿ×'ÿ×'ÿ×'ÿ×'ÿ×'$ÿq'&ÿq'6ÿ®'8ÿ…':ÿ…'Gÿ×'úÿ®'üÿ®'þÿ®'�ÿ…'ÿq' ÿq'_ÿ×'Iÿ×'Kÿ×'Mÿ×'Oÿ×'Qÿ×'Sÿ×'Uÿ×'Wÿ×'Yÿ×'[ÿ×']ÿ×'_ÿ×'oÿ…'qÿ…'sÿ…'ÿq(�ÿì(� ÿì(ÿì( ÿì)�ÿq)� ÿq)�&ÿ×)�*ÿ×)�- )�2ÿ×)�4ÿ×)�7ÿq)�9ÿ®)�:ÿ®)�<ÿ…)�‰ÿ×)�”ÿ×)�•ÿ×)�–ÿ×)�—ÿ×)�˜ÿ×)�šÿ×)�Ÿÿ…)�Èÿ×)�Êÿ×)�Ìÿ×)�Îÿ×)�Þÿ×)�àÿ×)�âÿ×)�äÿ×)ÿ×)ÿ×)ÿ×)ÿ×)$ÿq)&ÿq)6ÿ®)8ÿ…):ÿ…)Gÿ×)úÿ®)üÿ®)þÿ®)�ÿ…)ÿq) ÿq)_ÿ×)Iÿ×)Kÿ×)Mÿ×)Oÿ×)Qÿ×)Sÿ×)Uÿ×)Wÿ×)Yÿ×)[ÿ×)]ÿ×)_ÿ×)oÿ…)qÿ…)sÿ…)ÿq*�ÿì*� ÿì*ÿì* ÿì+�ÿq+� ÿq+�&ÿ×+�*ÿ×+�- +�2ÿ×+�4ÿ×+�7ÿq+�9ÿ®+�:ÿ®+�<ÿ…+�‰ÿ×+�”ÿ×+�•ÿ×+�–ÿ×+�—ÿ×+�˜ÿ×+�šÿ×+�Ÿÿ…+�Èÿ×+�Êÿ×+�Ìÿ×+�Îÿ×+�Þÿ×+�àÿ×+�âÿ×+�äÿ×+ÿ×+ÿ×+ÿ×+ÿ×+$ÿq+&ÿq+6ÿ®+8ÿ…+:ÿ…+Gÿ×+úÿ®+üÿ®+þÿ®+�ÿ…+ÿq+ ÿq+_ÿ×+Iÿ×+Kÿ×+Mÿ×+Oÿ×+Qÿ×+Sÿ×+Uÿ×+Wÿ×+Yÿ×+[ÿ×+]ÿ×+_ÿ×+oÿ…+qÿ…+sÿ…+ÿq,�ÿì,� ÿì,ÿì, ÿì-�ÿq-� ÿq-�&ÿ×-�*ÿ×-�- -�2ÿ×-�4ÿ×-�7ÿq-�9ÿ®-�:ÿ®-�<ÿ…-�‰ÿ×-�”ÿ×-�•ÿ×-�–ÿ×-�—ÿ×-�˜ÿ×-�šÿ×-�Ÿÿ…-�Èÿ×-�Êÿ×-�Ìÿ×-�Îÿ×-�Þÿ×-�àÿ×-�âÿ×-�äÿ×-ÿ×-ÿ×-ÿ×-ÿ×-$ÿq-&ÿq-6ÿ®-8ÿ…-:ÿ…-Gÿ×-úÿ®-üÿ®-þÿ®-�ÿ…-ÿq- ÿq-_ÿ×-Iÿ×-Kÿ×-Mÿ×-Oÿ×-Qÿ×-Sÿ×-Uÿ×-Wÿ×-Yÿ×-[ÿ×-]ÿ×-_ÿ×-oÿ…-qÿ…-sÿ…-ÿq.�ÿì.� ÿì.ÿì. ÿì/�ÿq/� ÿq/�&ÿ×/�*ÿ×/�- /�2ÿ×/�4ÿ×/�7ÿq/�9ÿ®/�:ÿ®/�<ÿ…/�‰ÿ×/�”ÿ×/�•ÿ×/�–ÿ×/�—ÿ×/�˜ÿ×/�šÿ×/�Ÿÿ…/�Èÿ×/�Êÿ×/�Ìÿ×/�Îÿ×/�Þÿ×/�àÿ×/�âÿ×/�äÿ×/ÿ×/ÿ×/ÿ×/ÿ×/$ÿq/&ÿq/6ÿ®/8ÿ…/:ÿ…/Gÿ×/úÿ®/üÿ®/þÿ®/�ÿ…/ÿq/ ÿq/_ÿ×/Iÿ×/Kÿ×/Mÿ×/Oÿ×/Qÿ×/Sÿ×/Uÿ×/Wÿ×/Yÿ×/[ÿ×/]ÿ×/_ÿ×/oÿ…/qÿ…/sÿ…/ÿq0�ÿì0� ÿì0ÿì0 ÿì1�ÿq1� ÿq1�&ÿ×1�*ÿ×1�- 1�2ÿ×1�4ÿ×1�7ÿq1�9ÿ®1�:ÿ®1�<ÿ…1�‰ÿ×1�”ÿ×1�•ÿ×1�–ÿ×1�—ÿ×1�˜ÿ×1�šÿ×1�Ÿÿ…1�Èÿ×1�Êÿ×1�Ìÿ×1�Îÿ×1�Þÿ×1�àÿ×1�âÿ×1�äÿ×1ÿ×1ÿ×1ÿ×1ÿ×1$ÿq1&ÿq16ÿ®18ÿ…1:ÿ…1Gÿ×1úÿ®1üÿ®1þÿ®1�ÿ…1ÿq1 ÿq1_ÿ×1Iÿ×1Kÿ×1Mÿ×1Oÿ×1Qÿ×1Sÿ×1Uÿ×1Wÿ×1Yÿ×1[ÿ×1]ÿ×1_ÿ×1oÿ…1qÿ…1sÿ…1ÿq2�ÿì2� ÿì2ÿì2 ÿì3�ÿq3� ÿq3�&ÿ×3�*ÿ×3�- 3�2ÿ×3�4ÿ×3�7ÿq3�9ÿ®3�:ÿ®3�<ÿ…3�‰ÿ×3�”ÿ×3�•ÿ×3�–ÿ×3�—ÿ×3�˜ÿ×3�šÿ×3�Ÿÿ…3�Èÿ×3�Êÿ×3�Ìÿ×3�Îÿ×3�Þÿ×3�àÿ×3�âÿ×3�äÿ×3ÿ×3ÿ×3ÿ×3ÿ×3$ÿq3&ÿq36ÿ®38ÿ…3:ÿ…3Gÿ×3úÿ®3üÿ®3þÿ®3�ÿ…3ÿq3 ÿq3_ÿ×3Iÿ×3Kÿ×3Mÿ×3Oÿ×3Qÿ×3Sÿ×3Uÿ×3Wÿ×3Yÿ×3[ÿ×3]ÿ×3_ÿ×3oÿ…3qÿ…3sÿ…3ÿq4�ÿì4� ÿì4ÿì4 ÿì5�-�{6�ÿì6� ÿì6�Yÿ×6�Zÿ×6�[ÿ×6�\ÿ×6�]ÿì6�¿ÿ×67ÿ×6<ÿì6>ÿì6@ÿì6ûÿ×6ýÿ×6ÿì6 ÿì6pÿ×7�-�{8�ÿì8� ÿì8�Yÿ×8�Zÿ×8�[ÿ×8�\ÿ×8�]ÿì8�¿ÿ×87ÿ×8<ÿì8>ÿì8@ÿì8ûÿ×8ýÿ×8ÿì8 ÿì8pÿ×9�-�{:�ÿì:� ÿì:�Yÿ×:�Zÿ×:�[ÿ×:�\ÿ×:�]ÿì:�¿ÿ×:7ÿ×:<ÿì:>ÿì:@ÿì:ûÿ×:ýÿ×:ÿì: ÿì:pÿ×;�-�{<�ÿì<� ÿì<�Yÿ×<�Zÿ×<�[ÿ×<�\ÿ×<�]ÿì<�¿ÿ×<7ÿ×<<ÿì<>ÿì<@ÿì<ûÿ×<ýÿ×<ÿì< ÿì<pÿ×=�-�{>�ÿì>� ÿì>�Yÿ×>�Zÿ×>�[ÿ×>�\ÿ×>�]ÿì>�¿ÿ×>7ÿ×><ÿì>>ÿì>@ÿì>ûÿ×>ýÿ×>ÿì> ÿì>pÿ×?�-�{@�ÿì@� ÿì@�Yÿ×@�Zÿ×@�[ÿ×@�\ÿ×@�]ÿì@�¿ÿ×@7ÿ×@<ÿì@>ÿì@@ÿì@ûÿ×@ýÿ×@ÿì@ ÿì@pÿ×A�-�{B�ÿìB� ÿìB�Yÿ×B�Zÿ×B�[ÿ×B�\ÿ×B�]ÿìB�¿ÿ×B7ÿ×B<ÿìB>ÿìB@ÿìBûÿ×Býÿ×BÿìB ÿìBpÿ×C�-�{D�ÿìD� ÿìD�Yÿ×D�Zÿ×D�[ÿ×D�\ÿ×D�]ÿìD�¿ÿ×D7ÿ×D<ÿìD>ÿìD@ÿìDûÿ×Dýÿ×DÿìD ÿìDpÿ×I�ÿ®I�ÿ®I�$ÿ×I�7ÿÃI�9ÿìI�:ÿìI�;ÿ×I�<ÿìI�=ÿìI�‚ÿ×I�ƒÿ×I�„ÿ×I�…ÿ×I�†ÿ×I�‡ÿ×I�ŸÿìI�Âÿ×I�Äÿ×I�Æÿ×I$ÿÃI&ÿÃI6ÿìI8ÿìI:ÿìI;ÿìI=ÿìI?ÿìICÿ×I ÿìIúÿìIüÿìIþÿìI�ÿìIÿ®I ÿ®IXÿ×Iÿ×Iÿ×I!ÿ×I#ÿ×I%ÿ×I'ÿ×I)ÿ×I+ÿ×I-ÿ×I/ÿ×I1ÿ×I3ÿ×IoÿìIqÿìIsÿìIÿÃJ�ÿìJ� ÿìJ�Yÿ×J�Zÿ×J�[ÿ×J�\ÿ×J�]ÿìJ�¿ÿ×J7ÿ×J<ÿìJ>ÿìJ@ÿìJûÿ×Jýÿ×JÿìJ ÿìJpÿ×K�ÿ®K�ÿ®K�$ÿ×K�7ÿÃK�9ÿìK�:ÿìK�;ÿ×K�<ÿìK�=ÿìK�‚ÿ×K�ƒÿ×K�„ÿ×K�…ÿ×K�†ÿ×K�‡ÿ×K�ŸÿìK�Âÿ×K�Äÿ×K�Æÿ×K$ÿÃK&ÿÃK6ÿìK8ÿìK:ÿìK;ÿìK=ÿìK?ÿìKCÿ×K ÿìKúÿìKüÿìKþÿìK�ÿìKÿ®K ÿ®KXÿ×Kÿ×Kÿ×K!ÿ×K#ÿ×K%ÿ×K'ÿ×K)ÿ×K+ÿ×K-ÿ×K/ÿ×K1ÿ×K3ÿ×KoÿìKqÿìKsÿìKÿÃL�ÿìL� ÿìL�Yÿ×L�Zÿ×L�[ÿ×L�\ÿ×L�]ÿìL�¿ÿ×L7ÿ×L<ÿìL>ÿìL@ÿìLûÿ×Lýÿ×LÿìL ÿìLpÿ×M�ÿ®M�ÿ®M�$ÿ×M�7ÿÃM�9ÿìM�:ÿìM�;ÿ×M�<ÿìM�=ÿìM�‚ÿ×M�ƒÿ×M�„ÿ×M�…ÿ×M�†ÿ×M�‡ÿ×M�ŸÿìM�Âÿ×M�Äÿ×M�Æÿ×M$ÿÃM&ÿÃM6ÿìM8ÿìM:ÿìM;ÿìM=ÿìM?ÿìMCÿ×M ÿìMúÿìMüÿìMþÿìM�ÿìMÿ®M ÿ®MXÿ×Mÿ×Mÿ×M!ÿ×M#ÿ×M%ÿ×M'ÿ×M)ÿ×M+ÿ×M-ÿ×M/ÿ×M1ÿ×M3ÿ×MoÿìMqÿìMsÿìMÿÃO�ÿ®O�ÿ®O�$ÿ×O�7ÿÃO�9ÿìO�:ÿìO�;ÿ×O�<ÿìO�=ÿìO�‚ÿ×O�ƒÿ×O�„ÿ×O�…ÿ×O�†ÿ×O�‡ÿ×O�ŸÿìO�Âÿ×O�Äÿ×O�Æÿ×O$ÿÃO&ÿÃO6ÿìO8ÿìO:ÿìO;ÿìO=ÿìO?ÿìOCÿ×O ÿìOúÿìOüÿìOþÿìO�ÿìOÿ®O ÿ®OXÿ×Oÿ×Oÿ×O!ÿ×O#ÿ×O%ÿ×O'ÿ×O)ÿ×O+ÿ×O-ÿ×O/ÿ×O1ÿ×O3ÿ×OoÿìOqÿìOsÿìOÿÃQ�ÿ®Q�ÿ®Q�$ÿ×Q�7ÿÃQ�9ÿìQ�:ÿìQ�;ÿ×Q�<ÿìQ�=ÿìQ�‚ÿ×Q�ƒÿ×Q�„ÿ×Q�…ÿ×Q�†ÿ×Q�‡ÿ×Q�ŸÿìQ�Âÿ×Q�Äÿ×Q�Æÿ×Q$ÿÃQ&ÿÃQ6ÿìQ8ÿìQ:ÿìQ;ÿìQ=ÿìQ?ÿìQCÿ×Q ÿìQúÿìQüÿìQþÿìQ�ÿìQÿ®Q ÿ®QXÿ×Qÿ×Qÿ×Q!ÿ×Q#ÿ×Q%ÿ×Q'ÿ×Q)ÿ×Q+ÿ×Q-ÿ×Q/ÿ×Q1ÿ×Q3ÿ×QoÿìQqÿìQsÿìQÿÃS�ÿ®S�ÿ®S�$ÿ×S�7ÿÃS�9ÿìS�:ÿìS�;ÿ×S�<ÿìS�=ÿìS�‚ÿ×S�ƒÿ×S�„ÿ×S�…ÿ×S�†ÿ×S�‡ÿ×S�ŸÿìS�Âÿ×S�Äÿ×S�Æÿ×S$ÿÃS&ÿÃS6ÿìS8ÿìS:ÿìS;ÿìS=ÿìS?ÿìSCÿ×S ÿìSúÿìSüÿìSþÿìS�ÿìSÿ®S ÿ®SXÿ×Sÿ×Sÿ×S!ÿ×S#ÿ×S%ÿ×S'ÿ×S)ÿ×S+ÿ×S-ÿ×S/ÿ×S1ÿ×S3ÿ×SoÿìSqÿìSsÿìSÿÃU�ÿ®U�ÿ®U�$ÿ×U�7ÿÃU�9ÿìU�:ÿìU�;ÿ×U�<ÿìU�=ÿìU�‚ÿ×U�ƒÿ×U�„ÿ×U�…ÿ×U�†ÿ×U�‡ÿ×U�ŸÿìU�Âÿ×U�Äÿ×U�Æÿ×U$ÿÃU&ÿÃU6ÿìU8ÿìU:ÿìU;ÿìU=ÿìU?ÿìUCÿ×U ÿìUúÿìUüÿìUþÿìU�ÿìUÿ®U ÿ®UXÿ×Uÿ×Uÿ×U!ÿ×U#ÿ×U%ÿ×U'ÿ×U)ÿ×U+ÿ×U-ÿ×U/ÿ×U1ÿ×U3ÿ×UoÿìUqÿìUsÿìUÿÃX�I�RX�W�RX�Y�fX�Z�fX�[�fX�\�fX�¿�fX%�RX'�RX7�fXû�fXý�fX4�RX5�RX]�RX^�RXp�fX�RX�RZ�I�RZ�W�RZ�Y�fZ�Z�fZ�[�fZ�\�fZ�¿�fZ%�RZ'�RZ7�fZû�fZý�fZ4�RZ5�RZ]�RZ^�RZp�fZ�RZ�R\�I�R\�W�R\�Y�f\�Z�f\�[�f\�\�f\�¿�f\%�R\'�R\7�f\û�f\ý�f\4�R\5�R\]�R\^�R\p�f\�R\�R^�I�R^�W�R^�Y�f^�Z�f^�[�f^�\�f^�¿�f^%�R^'�R^7�f^û�f^ý�f^4�R^5�R^]�R^^�R^p�f^�R^�R`�I�R`�W�R`�Y�f`�Z�f`�[�f`�\�f`�¿�f`%�R`'�R`7�f`û�f`ý�f`4�R`5�R`]�R`^�R`p�f`�R`�Ra�ÿ×a�ÿ×a�$ÿìa�‚ÿìa�ƒÿìa�„ÿìa�…ÿìa�†ÿìa�‡ÿìa�Âÿìa�Äÿìa�ÆÿìaCÿìaÿ×a ÿ×aXÿìaÿìaÿìa!ÿìa#ÿìa%ÿìa'ÿìa)ÿìa+ÿìa-ÿìa/ÿìa1ÿìa3ÿìf�I�ff�W�ff�Y�ff�Z�ff�[�ff�\�ff�¿�ff%�ff'�ff7�ffû�ffý�ff4�ff5�ff]�ff^�ffp�ff�ff�fh�I�fh�W�fh�Y�fh�Z�fh�[�fh�\�fh�¿�fh%�fh'�fh7�fhû�fhý�fh4�fh5�fh]�fh^�fhp�fh�fh�fj�I�fj�W�fj�Y�fj�Z�fj�[�fj�\�fj�¿�fj%�fj'�fj7�fjû�fjý�fj4�fj5�fj]�fj^�fjp�fj�fj�fl�I�fl�W�fl�Y�fl�Z�fl�[�fl�\�fl�¿�fl%�fl'�fl7�flû�flý�fl4�fl5�fl]�fl^�flp�fl�fl�fn�I�fn�W�fn�Y�fn�Z�fn�[�fn�\�fn�¿�fn%�fn'�fn7�fnû�fný�fn4�fn5�fn]�fn^�fnp�fn�fn�fo�ÿ…o�ÿ…o�"�)o�$ÿ…o�&ÿ×o�*ÿ×o�2ÿ×o�4ÿ×o�Dÿšo�Fÿšo�Gÿšo�Hÿšo�Jÿ×o�PÿÃo�QÿÃo�Rÿšo�SÿÃo�Tÿšo�UÿÃo�Vÿ®o�XÿÃo�]ÿ×o�‚ÿ…o�ƒÿ…o�„ÿ…o�…ÿ…o�†ÿ…o�‡ÿ…o�‰ÿ×o�”ÿ×o�•ÿ×o�–ÿ×o�—ÿ×o�˜ÿ×o�šÿ×o�¢ÿšo�£ÿšo�¤ÿšo�¥ÿšo�¦ÿšo�§ÿšo�¨ÿšo�©ÿšo�ªÿšo�«ÿšo�¬ÿšo�­ÿšo�´ÿšo�µÿšo�¶ÿšo�·ÿšo�¸ÿšo�ºÿšo�»ÿÃo�¼ÿÃo�½ÿÃo�¾ÿÃo�Âÿ…o�Ãÿšo�Äÿ…o�Åÿšo�Æÿ…o�Çÿšo�Èÿ×o�Éÿšo�Êÿ×o�Ëÿšo�Ìÿ×o�Íÿšo�Îÿ×o�Ïÿšo�Ñÿšo�Óÿšo�Õÿšo�×ÿšo�Ùÿšo�Ûÿšo�Ýÿšo�Þÿ×o�ßÿ×o�àÿ×o�áÿ×o�âÿ×o�ãÿ×o�äÿ×o�åÿ×o�úÿÃoÿÃoÿÃo ÿÃoÿ×oÿšoÿ×oÿšoÿ×oÿšoÿ×oÿšoÿÃoÿÃoÿ®o!ÿ®o+ÿÃo-ÿÃo/ÿÃo1ÿÃo3ÿÃo5ÿÃo<ÿ×o>ÿ×o@ÿ×oCÿ…oDÿšoFÿšoGÿ×oHÿšoJÿ®oÿ…o ÿ…oWÿÃoXÿ…oYÿšo_ÿ×o`ÿšobÿÃoÿ…oÿšoÿ…o ÿšo!ÿ…o"ÿšo#ÿ…o%ÿ…o&ÿšo'ÿ…o(ÿšo)ÿ…o*ÿšo+ÿ…o,ÿšo-ÿ…o.ÿšo/ÿ…o0ÿšo1ÿ…o2ÿšo3ÿ…o4ÿšo6ÿšo8ÿšo:ÿšo<ÿšo@ÿšoBÿšoDÿšoIÿ×oJÿšoKÿ×oLÿšoMÿ×oNÿšoOÿ×oQÿ×oRÿšoSÿ×oTÿšoUÿ×oVÿšoWÿ×oXÿšoYÿ×oZÿšo[ÿ×o\ÿšo]ÿ×o^ÿšo_ÿ×o`ÿšobÿÃodÿÃofÿÃohÿÃojÿÃolÿÃonÿÃp��Rp� �Rp�ÿ®p�ÿ®p�"�)p�Rpÿ®p �Rp ÿ®q�ÿ…q�ÿ…q�"�)q�$ÿ…q�&ÿ×q�*ÿ×q�2ÿ×q�4ÿ×q�Dÿšq�Fÿšq�Gÿšq�Hÿšq�Jÿ×q�PÿÃq�QÿÃq�Rÿšq�SÿÃq�Tÿšq�UÿÃq�Vÿ®q�XÿÃq�]ÿ×q�‚ÿ…q�ƒÿ…q�„ÿ…q�…ÿ…q�†ÿ…q�‡ÿ…q�‰ÿ×q�”ÿ×q�•ÿ×q�–ÿ×q�—ÿ×q�˜ÿ×q�šÿ×q�¢ÿšq�£ÿšq�¤ÿšq�¥ÿšq�¦ÿšq�§ÿšq�¨ÿšq�©ÿšq�ªÿšq�«ÿšq�¬ÿšq�­ÿšq�´ÿšq�µÿšq�¶ÿšq�·ÿšq�¸ÿšq�ºÿšq�»ÿÃq�¼ÿÃq�½ÿÃq�¾ÿÃq�Âÿ…q�Ãÿšq�Äÿ…q�Åÿšq�Æÿ…q�Çÿšq�Èÿ×q�Éÿšq�Êÿ×q�Ëÿšq�Ìÿ×q�Íÿšq�Îÿ×q�Ïÿšq�Ñÿšq�Óÿšq�Õÿšq�×ÿšq�Ùÿšq�Ûÿšq�Ýÿšq�Þÿ×q�ßÿ×q�àÿ×q�áÿ×q�âÿ×q�ãÿ×q�äÿ×q�åÿ×q�úÿÃqÿÃqÿÃq ÿÃqÿ×qÿšqÿ×qÿšqÿ×qÿšqÿ×qÿšqÿÃqÿÃqÿ®q!ÿ®q+ÿÃq-ÿÃq/ÿÃq1ÿÃq3ÿÃq5ÿÃq<ÿ×q>ÿ×q@ÿ×qCÿ…qDÿšqFÿšqGÿ×qHÿšqJÿ®qÿ…q ÿ…qWÿÃqXÿ…qYÿšq_ÿ×q`ÿšqbÿÃqÿ…qÿšqÿ…q ÿšq!ÿ…q"ÿšq#ÿ…q%ÿ…q&ÿšq'ÿ…q(ÿšq)ÿ…q*ÿšq+ÿ…q,ÿšq-ÿ…q.ÿšq/ÿ…q0ÿšq1ÿ…q2ÿšq3ÿ…q4ÿšq6ÿšq8ÿšq:ÿšq<ÿšq@ÿšqBÿšqDÿšqIÿ×qJÿšqKÿ×qLÿšqMÿ×qNÿšqOÿ×qQÿ×qRÿšqSÿ×qTÿšqUÿ×qVÿšqWÿ×qXÿšqYÿ×qZÿšq[ÿ×q\ÿšq]ÿ×q^ÿšq_ÿ×q`ÿšqbÿÃqdÿÃqfÿÃqhÿÃqjÿÃqlÿÃqnÿÃr��Rr� �Rr�ÿ®r�ÿ®r�"�)r�Rrÿ®r �Rr ÿ®s�ÿ…s�ÿ…s�"�)s�$ÿ…s�&ÿ×s�*ÿ×s�2ÿ×s�4ÿ×s�Dÿšs�Fÿšs�Gÿšs�Hÿšs�Jÿ×s�PÿÃs�QÿÃs�Rÿšs�SÿÃs�Tÿšs�UÿÃs�Vÿ®s�XÿÃs�]ÿ×s�‚ÿ…s�ƒÿ…s�„ÿ…s�…ÿ…s�†ÿ…s�‡ÿ…s�‰ÿ×s�”ÿ×s�•ÿ×s�–ÿ×s�—ÿ×s�˜ÿ×s�šÿ×s�¢ÿšs�£ÿšs�¤ÿšs�¥ÿšs�¦ÿšs�§ÿšs�¨ÿšs�©ÿšs�ªÿšs�«ÿšs�¬ÿšs�­ÿšs�´ÿšs�µÿšs�¶ÿšs�·ÿšs�¸ÿšs�ºÿšs�»ÿÃs�¼ÿÃs�½ÿÃs�¾ÿÃs�Âÿ…s�Ãÿšs�Äÿ…s�Åÿšs�Æÿ…s�Çÿšs�Èÿ×s�Éÿšs�Êÿ×s�Ëÿšs�Ìÿ×s�Íÿšs�Îÿ×s�Ïÿšs�Ñÿšs�Óÿšs�Õÿšs�×ÿšs�Ùÿšs�Ûÿšs�Ýÿšs�Þÿ×s�ßÿ×s�àÿ×s�áÿ×s�âÿ×s�ãÿ×s�äÿ×s�åÿ×s�úÿÃsÿÃsÿÃs ÿÃsÿ×sÿšsÿ×sÿšsÿ×sÿšsÿ×sÿšsÿÃsÿÃsÿ®s!ÿ®s+ÿÃs-ÿÃs/ÿÃs1ÿÃs3ÿÃs5ÿÃs<ÿ×s>ÿ×s@ÿ×sCÿ…sDÿšsFÿšsGÿ×sHÿšsJÿ®sÿ…s ÿ…sWÿÃsXÿ…sYÿšs_ÿ×s`ÿšsbÿÃsÿ…sÿšsÿ…s ÿšs!ÿ…s"ÿšs#ÿ…s%ÿ…s&ÿšs'ÿ…s(ÿšs)ÿ…s*ÿšs+ÿ…s,ÿšs-ÿ…s.ÿšs/ÿ…s0ÿšs1ÿ…s2ÿšs3ÿ…s4ÿšs6ÿšs8ÿšs:ÿšs<ÿšs@ÿšsBÿšsDÿšsIÿ×sJÿšsKÿ×sLÿšsMÿ×sNÿšsOÿ×sQÿ×sRÿšsSÿ×sTÿšsUÿ×sVÿšsWÿ×sXÿšsYÿ×sZÿšs[ÿ×s\ÿšs]ÿ×s^ÿšs_ÿ×s`ÿšsbÿÃsdÿÃsfÿÃshÿÃsjÿÃslÿÃsnÿÃt��Rt� �Rt�ÿ®t�ÿ®t�"�)t�Rtÿ®t �Rt ÿ®��{� �{�{ �{�ÿ…�ÿ®�ÿ…�"�)�$ÿq�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7�)�Dÿ\�Fÿq�Gÿq�Hÿq�Jÿq�Pÿš�Qÿš�Rÿq�Sÿš�Tÿq�Uÿš�Vÿ…�Xÿš�Yÿ×�Zÿ×�[ÿ×�\ÿ×�]ÿ®�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�¢ÿq�£ÿ\�¤ÿ\�¥ÿ\�¦ÿ\�§ÿ\�¨ÿ\�©ÿq�ªÿq�«ÿq�¬ÿq�­ÿq�´ÿq�µÿq�¶ÿq�·ÿq�¸ÿq�ºÿq�»ÿš�¼ÿš�½ÿš�¾ÿš�¿ÿ×�Âÿq�Ãÿ\�Äÿq�Åÿ\�Æÿq�Çÿ\�Èÿ×�Éÿq�Êÿ×�Ëÿq�Ìÿ×�Íÿq�Îÿ×�Ïÿq�Ñÿq�Óÿq�Õÿq�×ÿq�Ùÿq�Ûÿq�Ýÿq�Þÿ×�ßÿq�àÿ×�áÿq�âÿ×�ãÿq�äÿ×�åÿq�úÿšÿšÿš ÿšÿ×ÿqÿ×ÿqÿ×ÿqÿ×ÿqÿšÿšÿ…!ÿ…$�)&�)+ÿš-ÿš/ÿš1ÿš3ÿš5ÿš7ÿ×<ÿ®>ÿ®@ÿ®CÿqDÿ\Fÿ\Gÿ×HÿqJÿ…ûÿ×ýÿ×ÿ®ÿ®ÿ®ÿ… ÿ…WÿšXÿqYÿ\_ÿ×`ÿqbÿšÿqÿ\ÿq ÿ\!ÿq"ÿ\#ÿq%ÿq&ÿ\'ÿq(ÿ\)ÿq*ÿ\+ÿq,ÿ\-ÿq.ÿ\/ÿq0ÿ\1ÿq2ÿ\3ÿq4ÿ\6ÿq8ÿq:ÿq<ÿq@ÿqBÿqDÿqIÿ×JÿqKÿ×LÿqMÿ×NÿqOÿ×Qÿ×RÿqSÿ×TÿqUÿ×VÿqWÿ×XÿqYÿ×Zÿq[ÿ×\ÿq]ÿ×^ÿq_ÿ×`ÿqbÿšdÿšfÿšhÿšjÿšlÿšnÿšpÿ×�)��)� �)�) �)�����>��������9��������� �9������� �B�������*�M��������X������� �w��������ƒ�������R�–��������è������ ��ü������ �.������ �.F�������*t�� ���rž�� ���� ��"�� ��T8�� ��*N�� ��Œ�� ��&¤�� ��¤Ê�� ��(n�� � �8–�� � �\Î�� � �\*�� ��T†Digitized data copyright © 2010-2011, Google Corporation.Open SansBold ItalicAscender - Open Sans Bold Italic Build 100Version 1.10OpenSans-BoldItalicOpen Sans is a trademark of Google and may be registered in certain jurisdictions.Ascender Corporationhttp://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0�D�i�g�i�t�i�z�e�d� �d�a�t�a� �c�o�p�y�r�i�g�h�t� �©� �2�0�1�0�-�2�0�1�1�,� �G�o�o�g�l�e� �C�o�r�p�o�r�a�t�i�o�n�.�O�p�e�n� �S�a�n�s�B�o�l�d� �I�t�a�l�i�c�A�s�c�e�n�d�e�r� �-� �O�p�e�n� �S�a�n�s� �B�o�l�d� �I�t�a�l�i�c� �B�u�i�l�d� �1�0�0�V�e�r�s�i�o�n� �1�.�1�0�O�p�e�n�S�a�n�s�-�B�o�l�d�I�t�a�l�i�c�O�p�e�n� �S�a�n�s� �i�s� �a� �t�r�a�d�e�m�a�r�k� �o�f� �G�o�o�g�l�e� �a�n�d� �m�a�y� �b�e� �r�e�g�i�s�t�e�r�e�d� �i�n� �c�e�r�t�a�i�n� �j�u�r�i�s�d�i�c�t�i�o�n�s�.�A�s�c�e�n�d�e�r� �C�o�r�p�o�r�a�t�i�o�n�h�t�t�p�:�/�/�w�w�w�.�a�s�c�e�n�d�e�r�c�o�r�p�.�c�o�m�/�h�t�t�p�:�/�/�w�w�w�.�a�s�c�e�n�d�e�r�c�o�r�p�.�c�o�m�/�t�y�p�e�d�e�s�i�g�n�e�r�s�.�h�t�m�l�L�i�c�e�n�s�e�d� �u�n�d�e�r� �t�h�e� �A�p�a�c�h�e� �L�i�c�e�n�s�e�,� �V�e�r�s�i�o�n� �2�.�0�h�t�t�p�:�/�/�w�w�w�.�a�p�a�c�h�e�.�o�r�g�/�l�i�c�e�n�s�e�s�/�L�I�C�E�N�S�E�-�2�.�0���ÿô��ÿf�f��������������������ª      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«.notdefnullnonmarkingreturnspaceexclamquotedbl numbersigndollarpercent ampersand quotesingle parenleft parenrightasteriskpluscommahyphenperiodslashzeroonetwothreefourfivesixseveneightninecolon semicolonlessequalgreaterquestionatABCDEFGHI.altJKLMNOPQRSTUVWXYZ bracketleft backslash bracketright asciicircum underscoregraveabcdefghijklmnopqrstuvwxyz braceleftbar braceright asciitildenonbreakingspace exclamdowncentsterlingcurrencyyen brokenbarsectiondieresis copyright ordfeminine guillemotleft logicalnotuni00AD registered overscoredegree plusminus twosuperior threesuperioracutemu paragraphperiodcenteredcedilla onesuperior ordmasculineguillemotright onequarteronehalf threequarters questiondownAgraveAacute AcircumflexAtilde AdieresisAringAECcedillaEgraveEacute Ecircumflex Edieresis Igrave.alt Iacute.altIcircumflex.alt Idieresis.altEthNtildeOgraveOacute OcircumflexOtilde OdieresismultiplyOslashUgraveUacute Ucircumflex UdieresisYacuteThorn germandblsagraveaacute acircumflexatilde adieresisaringaeccedillaegraveeacute ecircumflex edieresisigraveiacute icircumflex idieresisethntildeograveoacute ocircumflexotilde odieresisdivideoslashugraveuacute ucircumflex udieresisyacutethorn ydieresisAmacronamacronAbreveabreveAogonekaogonekCacutecacute Ccircumflex ccircumflexCdotcdotCcaronccaronDcarondcaronDcroatdcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGbrevegbreveGdotgdot Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbar Itilde.altitilde Imacron.altimacron Ibreve.altibreve Iogonek.altiogonekIdotaccent.altdotlessiIJ.altij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotLslashlslashNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautOEoeRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflexScedillascedillaScaronscaron Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflex YdieresisZacutezacute Zdotaccent zdotaccentZcaronzcaronlongsflorin Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute Scommaaccent scommaaccent circumflexcaronmacronbreve dotaccentringogonektilde hungarumlauttonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos.alt Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIota.altKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9Iotadieresis.altUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronpirhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055.alt afii10056.alt afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygraveendashemdash afii00208 underscoredbl quoteleft quoterightquotesinglbase quotereversed quotedblleft quotedblright quotedblbasedagger daggerdblbulletellipsis perthousandminutesecond guilsinglleftguilsinglright exclamdblfraction nsuperiorfranc afii08941pesetaEuro afii61248 afii61289 afii61352 trademarkOmega estimated oneeighth threeeighths fiveeighths seveneighths partialdiffDeltaproduct summationminusradicalinfinityintegral approxequalnotequal lessequal greaterequallozengeuniFB01uniFB02 cyrillicbrevedotlessjcaroncommaaccent commaaccentcommaaccentrotate zerosuperior foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperioruni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni1F4Duni02F3 dasiaoxiauniFB03uniFB04OhornohornUhornuhornuni0300uni0301uni0303hookdotbelowuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BF uni04C0.altuni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CE uni04CF.altuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7 uni1EC8.altuni1EC9 uni1ECA.altuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABuni030Fcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCcyrillicbighookLCone.pnumzero.osone.ostwo.osthree.osfour.osfive.ossix.osseven.oseight.osnine.osffuni2120Tcedillatcedillag.altgcircumflex.alt gbreve.altgdot.altgcommaaccent.altIIgraveIacute Icircumflex IdieresisItildeImacronIbreveIogonek IdotaccentIJ IotatonosIota Iotadieresis afii10055 afii10056uni04C0uni04CFuni1EC8uni1ECA����� ÿÿ����� ���������©������������� �4�6�latn���MOL �ROM ���ÿÿ����ÿÿ����ÿÿ���������� �nä�latn���MOL �(ROM �B��ÿÿ� ��� ���������ÿÿ� ��� � ��������ÿÿ� ��� � �������liga�°liga�¶liga�¼lnum�Âlnum�Èlnum�Îlocl�Ôlocl�Úonum�àonum�èonum�ðpnum�øpnum�þpnumsalt saltsaltss01"ss01*ss012ss02:ss02@ss02Fss03Lss03Rss03Xtnum^tnumftnumn���� ���� ���� ������������������������������������������������������������������������������������������������������������������������������ ���&�.�6�>�F�N�V�^�����P�����z�����ª�����Æ�����î�����ô������������2����H���‘’“”•���J�J���ß�ß��á�á��ã�ã��å�å���.��,�Ž���‘�ê�ì�î�ð�ò�ôZgw¡¢ÉØEG��–©����� ƒ„…†‡ˆ‰Š‹Œ��������� ƒ…†‡ˆ‰Š‹Œ„���������‚‚� ���‚������ ������������ƒŒ��������‚��� �‚����������ƒŒ����� !��$%��IJ���6���� ���"�(^��I�O]��I�L��I5��O4��L���I������������^���������V0‚R *†H†÷  ‚C0‚?1 0 +�0a +‚7 S0Q0, +‚7¢€�<�<�<�O�b�s�o�l�e�t�e�>�>�>0!0 +�V DÀÊXT_°¦)ù2¯’ ‚]0‚z0‚b 8%×úøa¯žôç&µÖZÕ0  *†H†÷ �0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0 070615000000Z 120614235959Z0\1 0 UUS10U VeriSign, Inc.1402U+VeriSign Time Stamping Services Signer - G20Ÿ0  *†H†÷ ��0‰�ĵòR¼ˆ†`)J[/K‘k‡‘ó5TX5êÑ6^bMRQ4qÂ{f‰ÈÝ*Äj ö7Ù˜t‘ö’®°µv–ñ©JcEG.k ’NK+ŒîXJ‹Ôä,ø‚ªXÙÍBó-ÀuÞ«ÇŽšlL•ÞÛïgárÂIž`<áâ¾£cxi{­-�£Ä0Á04+(0&0$+0†http://ocsp.verisign.com0 Uÿ0�03U,0*0( & $†"http://crl.verisign.com/tss-ca.crl0U%ÿ 0 +0UÿÀ0U0¤010 UTSA1-20  *†H†÷ �‚�PÅKÈ$€ßä $ÂÞ±¡¡¦‚- ƒ7 ‚,°ZaµØþˆÛñ‘‘³V@¦ë’¾89°u6t:˜Oä7º™‰Ê•B°¹Ç WàúÕdB5NÑ3¢ÈMª'Çòá†L8MƒxÆüSàëà‡Ý¤–ž^ ˜â¥¾¿‚…Ã`áß­(ØÇ¥KdÚÇ[½¬9Õ8"¡3‹/Ššë¼!?DA µe$¼HÓD€ë¡ÏÉ´ÏTÇ£€\ùy>]r}ˆž,C¢ÊSÎ}=ö*:¸O”�¥m ƒ]ù^Sô³WpÃûõ­• ÞÄ€`É+n†ñëôx'ÑÅî4[^¹I2ò30‚Ä0‚- G¿•ßRFC÷ÛmH 1¤0  *†H†÷ �0‹1 0 UZA10U Western Cape10U Durbanville10 U Thawte10U Thawte Certification10UThawte Timestamping CA0 031204000000Z 131203235959Z0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0‚"0  *†H†÷ �‚�0‚ ‚�©Ê²¤ÌÍ ¯ }‰¬‡uð´NñßÁ¿ga½£dÚ»ùÊ3«„0‰X~ŒÛkÝ6ž¿Ñìxòw¦~o<¿“¯ ºhôl”ʽR-«H=õ¶Õ]_Ÿú/k¤÷£š¦ÈáLRã`ì@~¹ Þ?Ǵ߇½_zj1.™¨G Î1s W-Íx43•™¹Þh/ªæãŠŒ*Ë!‡f½ƒXWou¿<ª&‡]Ê<Ÿ„êTÁ nÄþÅJݹ—"|Û>'ÑxìŸ1Éñæ"ÛijGCš_ ä^õî|ñ}«bõM ÞÐ"V¨•Í®ˆv®îº óäMÙ ûh ®;³‡Á»�£Û0Ø04+(0&0$+0†http://ocsp.verisign.com0Uÿ0ÿ�0AU:0806 4 2†0http://crl.verisign.com/ThawteTimestampingCA.crl0U% 0 +0Uÿ0$U0¤010U TSA2048-1-530  *†H†÷ ��JkùêXÂD1‰y™+–¿‚¬ÖLͰŠXnß)£^ÈÊ“çR ïG'/�8°äÉ“NšÔ"b÷?7!Op1€ñ‹8‡³èè—�þÏU–N$Ò©'Nz®·aAó*ÎçÉÙ^Ý»+…>µµÙáWÿ¾´Å~õÏ žð—þ+Ó;R8'÷?J0‚ü0‚e eR&á².áY)…¬"ç\0  *†H†÷ �0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority0 090521000000Z 190520235959Z0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0‚"0  *†H†÷ �‚�0‚ ‚�¾g´`ªIoV|fÉ^† Õñ¬§qƒŽ‹‰øˆ‰º-„!•äÑœPLûÒ"½Úò²5;à ûü.Z¿‰|=;%öóX{œôµÆ ¸€Î¾'tag'MjåìaXy£à'°áM4+G D¹Þf$fŠÍOºÅ8ÈTáröfuj¹IhÏ8y ª0¨Û,`Hž×ª©ƒ×8‘09–:|@T¶­à/ƒÜ¨R>³×+ý!¶§\£ ©¦P4.M§ÎÉ^%ÔŒ¼ón|)¼]ü1‡ZÕŒ…gXˆ ¿5ðê+£!çöƒå¨í`x^{`ƒýW ]A cT`ÖC!ï�£‚Û0‚×0Uÿ0ÿ�0pU i0g0e `†H†øE0V0(+https://www.verisign.com/cps0*+0https://www.verisign.com/rpa0Uÿ0m+ a0_¡] [0Y0W0U image/gif0!00+åÓ†¬ŽkÃÏ€jÔH,{.0%#http://logo.verisign.com/vslogo.gif0U%0++04+(0&0$+0†http://ocsp.verisign.com01U*0(0& $ "† http://crl.verisign.com/pca3.crl0)U"0 ¤010UClass3CA2048-1-550U—Ðk¨&pÈ¡?”-Ä5›¤¡ò0  *†H†÷ ��‹ÀÝ”ØA¢ai°¨xÇ0Æ<~B÷$¶äƒsœ¡âú/ëÀÊDçràP¶U ƒn–’äšQj´71Ü¥-ëŒ�ÇOçM2º…øN¾úgUeðj¾zÊd8xEv1ó†z`³]ö‹fv‚Yáƒå½I¥8VåÞAwX0‚0‚û fãðgyÊmPSoˆƒ0  *†H†÷ �0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0 100729000000Z 120808235959Z0Ð1 0 UUS10U Massachusetts10 UWoburn10U Monotype Imaging Inc.1>0<U 5Digital ID Class 3 - Microsoft Software Validation v210U Type Operations10UMonotype Imaging Inc.0Ÿ0  *†H†÷ ��0‰�”D •i|U ÐÛ25ŠL3«^ ¡L×*‡8ט¥@ðI "SOÂC¦Ê‹©VïnH¨9c;$¹˜ÏÊ5}rãGWýyËŠJç@p-5c®€ÏįØû÷Éü‰Ø×¤ Û ò¢ò{ïÍuÁ÷ePd"½}¼­¸KÌXEMÑYLM�£‚ƒ0‚0 U0�0Uÿ€0DU=0;09 7 5†3http://csc3-2009-2-crl.verisign.com/CSC3-2009-2.crl0DU =0;09 `†H†øE0*0(+https://www.verisign.com/rpa0U% 0 +0u+i0g0$+0†http://ocsp.verisign.com0?+0†3http://csc3-2009-2-aia.verisign.com/CSC3-2009-2.cer0U#0€—Ðk¨&pÈ¡?”-Ä5›¤¡ò0 `†H†øB0 +‚70�ÿ0  *†H†÷ �‚�Næ"‡ßgAâÒî~ΙÖc½ðµ“åjrbáõÒ<8î¨=_ºG‚_[KIô ú“ ÐVD¢ˆóû®÷ 5Þ< ¬D”`E*›þ›oL;±4gp†ÿZ9\Zãl‚«5|eKý˜mµ”Iœˆp¾=±b•´Û´ÔÚèA~þ}¹¤’ënò"ŠÆw6MŠZ S1Ó+(¯RázkµwD½ ­ô]%,ãÍŠ0>KœyʦN® ÂÌ$ Á”‚öñº¶›šØ\<ñê'M<‰o3ŠÓ†ÞéX3u=ë“iâDoN�lÏÕ…ÚV¦š¦?ËL!hò`ºáè]9!2í1‚g0‚c0Ë0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CAfãðgyÊmPSoˆƒ0 +� p0 +‚7 10�0 *†H†÷  1  +‚70 +‚7 10  +‚70# *†H†÷  1™v¾: 4™Ulj±&×|x‰0  *†H†÷ �€m>¤?B½Ä*P¼`¯z£>¥Gš¾’^^þ†ÌýÂýwEÅÆÅ•õu êuÃc~ŠšÝE%ÉîI ,½]4­@†·XœˆdV^P þ½ž#Ôaez\U>Ÿ… 5`Kí-êsÌ6úÞàÚê ßM—¾ÑóÇu’•uÁ߸ƒÿ¯¡‚0‚{ *†H†÷  1‚l0‚h0g0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA8%×úøa¯žôç&µÖZÕ0 +� ]0 *†H†÷  1  *†H†÷ 0 *†H†÷  1 110505165509Z0# *†H†÷  1ÙsÁÀÍz–úél¦«SƪR0  *†H†÷ �€ˆÄ<–‘amêñ¸5‚¾¸0æK/ÂŒ~m®9Ï‚2ûZÜî®Cf¹Ìuè ŽÄ<¤—;?KF ÍFfZ¢ª�d¼ßœX–ŒÆtµè''ï$œÚÿ¥ÎoHÚÒvçØZØ8nåË©»©©d?x5øÔj4%‹˜nWæHZ^•0{„ÿ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fonts/data/OpenSans-Bold.ttf������������������������������������������������0000644�0001750�0001751�00000666520�14507104216�021631� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0DSIG’t:�WÜ��tGDEF�&¯�S¼���GPOS 77�SÜ���8GSUB+=·�T��ÆOS/2¢u¡��¸���`cmap)«/h��´��cvt -”��¨���ªfpgm»s¤u��Ð��àgasp���S°��� glyfÊô��%¬�KÄhead÷áÇ��<���6hhea) R��t���$hmtx$Ò�����škernT+ ~�qp�¶6loca²Û`+��T��Vmaxp5��˜��� name¼f{"�'¨��ÜpostCïl�-„��&+prepÈ ük��°���ø����š:©9³_<õ� �����ÉB ����ÉèJ¡û ý¨ �� ����������ý¨�� û þy ���������������£���ª�Š��X����/�\��� ���¼��š3��š3��Ñ�fü à�ï@� [���(����1ASC� � ÿýþ�„X �Ÿ����^¶��� �Í�Á��������J�uÇ�…+�-‘�X5�?��R!�…¶�R¶�=\�?‘�XR�?“�=H�uN�‘�J‘�y‘�N‘�N‘�#‘�d‘�H‘�7‘�H‘�BH�uR�?‘�X‘�X‘�XÑ�-�f…��`�¸�wì�¸{�¸d�¸Ë�w�¸�B¦ÿhP�¸…�¸‹�¸�¸^�w�¸^�wH�¸h�^¢�) �®3��¼��V��þ��¢�1¦�N� ¦�3B�JÿüÛLÕ�V� �\�\º�\�)…�B� q�“qÿ}ö� q� Û� B� ô�\� �\¢� ú�\y�/B�š��Ù� � ��ç�7'�hÇ'�R‘�X��J�u‘�‘�R‘�q‘�hÇã�jÛ¨�d�/ì�R‘�X“�=¨�d�ÿúm�\‘�X�/�;ÛLH� =�qH�u¤ÿÛ�\�9ì�R �. �. �ZÑ�=…��…��…��…��…��…��ž���w{�¸{�¸{�¯{�¸�*�BÿÜ�9ì�/�¸^�w^�w^�w^�w^�w‘�^�w �® �® �® �®þ���¸°� Õ�VÕ�VÕ�VÕ�VÕ�VÕ�VV�V�\º�\º�\º�\º�\qÿ›q�‘qÿ†qÿãô�\B� ô�\ô�\ô�\ô�\ô�\‘�Xô�\B�šB�šB�šB�š��� ��…��Õ�V…��Õ�V…��Õ�V�w�\�w�\�w�\�w�\ì�¸�\ì�/1�\{�¸º�\{�¸º�\{�¸º�\{�¸º�\{�¯º�\Ë�w…�Ë�w…�Ë�w…�Ë�w…��¸B� ��B�ÿñqÿ›�?qÿé�qÿ¯�Bq�+�Bq� Ã�Bº�“¦ÿhqÿ}P�¸ö� ö� …�¸q� …�¸q�c…�¸q� …�¸m� …�¤���¸B� �¸B� �¸B� ;��¸B� ^�wô�\^�wô�\^�wô�\É�wÓ�\H�¸¢� H�¸¢�cH�¸¢�Sh�^ú�\h�^ú�\h�^ú�\h�^ú�\¢�)y�/¢�)y�/¢�)y�/ �®B�š �®B�š �®B�š �®B�š �®B�š �®B�š¼��Ù�þ����þ��¢�1ç�7¢�1ç�7¢�1ç�7� ‘�Å…��Õ�Vž��V�V^�wô�\h�^ú�\Û�ºÛ�ºÛÛ�ãq�“žT¦� Û�Ï´�œž×ž�º…ÿÈH�u ÿ®ÿÿ°ÿÆÿˆJÿÆBÿÉ…��`�¸}�¸D�9{�¸¢�1�¸^�w�BP�¸3��‹�¸�¸‘�R^�wö�¸�¸¾�N¢�)þ��á�\V���mJ�7�9þ��-�\q�NB� B� )�-�\H� ‹�ô�\q�Nü�\B� ò�\B� ö� ì�H� Ã�ü�\ô�\é�ò�yü�\9�\N�))�V�\¼ÿϲ�ç�mB� )�ô�\)�ç�m{�¸q�)}�¸j�wh�^�B�9¦ÿhþ��¸q�)`�¸9��ö�¸…���¸`�¸}�¸� {�¸‹��/�^–�¸–�¸`�¸ö�‹�¸�¸^�wö�¸�¸�w¢�)9��á�\V��?�¸Ó�m �¸é�¸Ñ��?�¸�¸N�H�¸RÿöÕ�Vú�\� Ó� P�º�\ü��q�NÃ� Ã� ô� )��Á� L� ô�\7� � �\m�/��ƒ�\ � � ?�{Á� á� ®��Í� é� �J� ¾��º�\B�Ó� 1�\ú�\q�“qÿåqÿ}��� B�ô� ��`� ¦�¸� ¼��Ù�¼��Ù�¼��Ù�þ������R��R��RJÿü¼�¼�T�?º���%�?!�{5�{�b×�u ?�?!�…Ç�…ò�Rò�R�u þwb�f‘�#‘�R#�¸‘�B\�?)�)9�‡þ�J�7ô�f �: �; �Z �C¦�;D�9î�¦ �)‘�Xd�%¨�qL��‘�X‘�X‘�V‘�Xª�X‰�)‰�)ž�hqÿ}�^�^�N�)� �T�-�;�-�+������������ª�����V��y��H��š���Í������������T��Tqÿ}¼�Û�) ��þ�3‹�¸Û� …��Õ�V°þrª�X��y �) �)š�wo�\�®�š��ü��üÐ��ûà��üÙ��üÙ{�¸–�¸º�\Ã� ´�w�b��L��š�¸f� ×���� �¸7� o�)ü�–�¸ � �)q��m²�^�wô�\¼��×��¼��×�� �w )�\°�wo�\´�w�\´�w�j�w1�\ß�hu�´ž�ôžÍžËé�)¦�)T�¸j� î�/é��¸� y�/î�ß�¸Ñ� ;��‰��/�^q�N �¸R� P�¸Ë� %�ö�Ý����º�¸ò� ¬�¸�  ��¸� 7�w?�\�w�\¢�)f�/þ��˜��þ��˜��ò��� q�)T�/o�mÏ�{Ó�m?�{Ó�¸T� –��¸��–��¸���B‹��ü���¸J� ´�Ñ���¸L� Ý�¸ô� Ó�m?�{J�¸h� �B…��Õ�V…��Õ�Vž��V�V{�vº�\‰�¤Ï�\‰�¤Ï�\‹��ü��/�^q�Nº�9¦�9–�¸Ã� –�¸Ã� ^�wô�\^�wô�\^�wô�\N�H�J9����9����9����Ó�m?�{}�¸Ó� ?�¸Í� y�/î�Û��)� V�� � �\�\h�\b�\N�ö�9œ�J�ND�{��X�¸ž� f�wN�\�)ß�//�Xs�N‹�Ë��…��Õ�V…��Õ�V…��Õ�V…��ÕÿÓ…��Õ�V…��Õ�V…��Õ�V…��Õ�V…��Õ�V…��Õ�V…��Õ�V…��Õ�V{�¸º�\{�¸º�\{�¸º�\{�¸º�\{ÿͺÿß{�¸º�\{�¸º�\{�«º�\�Bq�u�Bq�‘^�wô�\^�wô�\^�wô�\^�wôÿß^�wô�\^�wô�\^�wô�\š�wo�\š�wo�\š�wo�\š�wo�\š�wo�\ �®B�š �®B�š�®�š�®�š�®�š�®�š�®�šþ����þ����þ����1�\��û��ü-��û ��ü-��ü1��ü1��ü1��ü1��ü1¦� V�V�Ù�-ß�fð�=‘�B‘�N‘�‘�d‘�H‘�7‘�H‘�B1�)á�R¢�)y�/�\�\�\�\�\¦�¸¦ÿƦ�§¦ÿ ¦ÿý¦ÿµ¦�¦ÿ˦�n¦�¬Ã�¸ÿ¦�¸ÿý�¸ÿý�¸�¸�‹�¬���������� ����°�€��0�H�I�~�Ë�Ï'2a’¡°ðÿ7¼ÇÉÝó #ŠŒ¡ªÎÒÖ O_†‘¿Ï?…ÇÊñùM   " & 0 3 : < D p y  ¤ § ¬!!!! !"!&!.!^"""""""+"H"`"e%Êûþÿÿýÿÿ��� �I�J� �Ì�Ð(3b’ ¯ðú7¼ÆÉØó� #„ŒŽ£«ÑÖ�P`ˆ’ÀÐ�>€ ÈËòM �   & 0 2 9 < D p t  £ § «!!!! !"!&!.!["""""""+"H"`"d%Êû�þÿÿüÿÿÿã��ÿãÿÂ��ÿÂ��ÿÂ��ÿ°�¿�²�aÿI����ÿ–þ…þ„þvÿhÿcÿbÿ]�gÿD��ýÏ����ýÍþ‚þ��ýš��þ ��þ ��þ äXäãzä}��ä}��ã âBáïáîáíáêáááàáÛáÚáÓáËáÈá™ávát��áá á ânàþàûàôàÈà%à"àààààßçßÐßÍÜi��OS����®�����ª���®���À�����������ð�����������������������à���ê��������0��L��\����������p��r��������������������������������`��������������������������������������<������–—˜™š›�ëœ�í�ïž�ñŸ�ó &'()*+,-./0123456789:;<=>?@AIJ$%TUVWXY¡\]^_`abcdef¢hijklmnopqrstuv£hœžŸ ¤¥£¤¥¦§ijêëìíîïðñòóôõkö÷“”•–—˜™šøù¦ÊËÌÍÎÏÐÑÒÓÔÕÖ×§¨F©�opqrstu45]^��@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! �, °`E°% Fa#E#aH-, EhD-,E#F`° a °F`°&#HH-,E#F#a° ` °&a° a°&#HH-,E#F`°@a °f`°&#HH-,E#F#a°@` °&a°@a°&#HH-, <�<-, E# °ÍD# ¸ZQX# °D#Y °íQX# °MD#Y °&QX# ° D#Y!!-, EhD °` E°FvhŠE`D-,± C#Ce -,�± C#C -,�°(#p±(>°(#p±(E:±� -, E°%Ead°PQXED!!Y-,I°#D-, E°�C`D-,°C°Ce -, i°@a°�‹ ±,ÀŠŒ¸�b`+ d#da\X°aY-,ŠEŠŠ‡°+°)#D°)zä-,Ee°,#DE°+#D-,KRXED!!Y-,KQXED!!Y-,°%# Šõ�°`#íì-,°%# Šõ�°a#íì-,°%õ�íì-,°C°RX!!!!!F#F`ŠŠF# FŠ`Ša¸ÿ€b# #б ŠpE` °�PX°a¸ÿº‹°FŒY°`h:Y-, E°%FRK°Q[X°%F ha°%°%?#!8!Y-, E°%FPX°%F ha°%°%?#!8!Y-,�°C°C -,!! d#d‹¸@�b-,!°€QX d#d‹¸ �b²�@/+Y°`-,!°ÀQX d#d‹¸Ub²�€/+Y°`-, d#d‹¸@�b`#!-,KSXа%Id#Ei°@‹a°€b° aj°#D#°ö!#Š 9/Y-,KSX °%Idi °&°%Id#a°€b° aj°#D°&°öа#D°ö°#D°íа& 9# 9//Y-,E#E`#E`#E`#vh°€b -,°H+-, E°�TX°@D E°@aD!!Y-,E±0/E#Ea`°`iD-,KQX°/#p°#B!!Y-,KQX °%EiSXD!!Y!!Y-,E°C°�`c°`iD-,°/ED-,E# EŠ`D-,F#F`ŠŠF# FŠ`Ša¸ÿ€b# #б ŠpE` °�PX°a¸ÿ€‹°ŒYh:-,K#QX¹�3ÿà±4 ³3�4�YDD-,°CX°&EŠXdf°`d° `f X!°@Y°aY#XeY°)#D#°)à!!!!!Y-,°CTXKS#KQZX8!!Y!!!!Y-,°CX°%Ed° `f X!°@Y°a#XeY°)#D°%°% XY°%°% F°%#B<°%°%°%°% F°%°`#B< X�Y°%°%°)à°) EeD°%°%°)à°%°% XY°%°%CH°%°%°%°%°`CH!Y!!!!!!!-,°% F°%#B°%°%EH!!!!-,°% °%°%CH!!!-,E# E °�P X#e#Y#h °@PX!°@Y#XeYŠ`D-,KS#KQZX EŠ`D!!Y-,KTX EŠ`D!!Y-,KS#KQZX8!!Y-,°�!KTX8!!Y-,°CTX°F+!!!!Y-,°CTX°G+!!!Y-,°CTX°H+!!!!Y-,°CTX°I+!!!Y-, Š#KSŠKQZX#8!!Y-,�°%I°�SX °@8!Y-,F#F`#Fa#  FŠa¸ÿ€bб@@ŠpE`h:-, Š#IdŠ#SX<!Y-,KRX}zY-,°�KKTB-,±�B±#ˆQ±@ˆSZX¹�� ˆTX²C`BY±$ˆQX¹ ��@ˆTX²C`B±$ˆTX² C`B�KKRX²C`BY¹@��€ˆTX²C`BY¹@��€c¸�ˆTX²C`BY¹@��c¸�ˆTX²C`BY±&ˆQX¹@��c¸�ˆTX²@C`BY¹@��c¸�ˆTX²€C`BYYYYYY±�CTX@ @@ @  ±CTX²@º�� �³  ±€CRX²@¸€± @²@º€� @Y¹@��€ˆU¹@��c¸�ˆUZX³ � ³ � YYYBBBBB-,Eh#KQX# E d°@PX|YhŠ`YD-,°�°%°%°#>�°#>± ° #eB° #B°#?�°#?± °#eB°#B°-,°€°CP°°CT[X!#° ÉŠíY-,°Y+-,Šå-@‹ !H U UHUÿPLOMdNLd&4U%3$UÿÿÿMLdLLF 3UU3U?¯KFËFÛF#3"U3U3UU3U¯Ï03�Uo��¯�ï��€¸±TS++K¸ÿRK° P[°ˆ°%S°ˆ°@QZ°ˆ°�UZ[X±ŽY…�BK°2SX° YK°dSX°±�BYss+ss+++++s^st++++t+++++++++++++^���¶��u¶Í��������������^��{��ÿì����ÿì����ÿì��þÿì��¶�ü”ÿëþÿàþ¼ÿì�þV������������������������������������������������������������å�ö�+�Ó�²�ö�ö�í�ß�²��T�����$�$�$�$�V�z�ïzèpˆµâ Kh}ž¼þ+yá*…à }ÙHq—®ò R ” Ë  5 Š ¿ ð  Z y ¿ ú < { Ñ $ „ ª ßm¯ã:Xz¦½à:ŽÊsµQ»ø?V²í0…Û e§ãq¶:…œë++\«`¾ã`—fº×ßm„ºô.ƒ¦ï#D~«á5K`vÖçø  , = œ ¨ ¹ Ê Û í þ!! !2!‚!“!¤!µ!Æ!×!é""v"‡"˜"©"»"Ì##†#–#¦#¶#Æ#×#è$$$$­$½$Î$ß$ð%%%‘%¡%±%Â%Ò%â%ó&4&’&¢&³&Ã&Ô&ä'>'O'`'p''‘''©'º'Ê'Û'ë'ü( ((.(?(K(S(À(Ñ(á(ò)))$)0)<)M)])n)~))Ÿ)°)Á)Í)Ý)î**R*§*¸*É*Ú*ë*ü+ ++#+4+K+W+c+t+…+‘+œ+á+ò,,,,-,9,E,Q,,Ð,á,ò,þ- --*-6-‰-Ö-ç-÷...*.;.ª/2/C/S/_/k/|/Œ//­/¾/Î/Ú/æ/÷0000.0?0~0Ù0ê0ú1 11,1<1N1_1q1ƒ11›1¬1½1Î1Þ1ð222"232D2T2~2Í3X3ü4 44/4?4J4U4†4¸4Ï4ü55M5u5¶5í66Z6n6w6Œ6¡6¶6Ê6ß6ó777767>7F7N7V7¯7·7¿7ö7þ88D8L8q8y8Ã8Ë8Ó9A9I9™9ý::!:1:A:Q:b:t:Ú;E;Œ<>7>¬>´>ü?H?’?Þ@@W@¾A$AvAÕAçAøBBB)B;B•B¦B÷BÿCCC!C†CàD&D7DHDzD‚DÇDÏD×E(E0E„EäFF,FaFŸF§F¯F·F¿FÇFÏF×G!G)G1GdGGÒHH]H«HèI;IIðIøJZJ·JÖK"K*K}KÞLL$L[L—LâMMMDMLMTM{MƒMêMòN%N]N‘NÒOObOOéPCP‘P¢QQQmQuQ}QQ—QóRIRŸR°RÀRôSS>SOS`SqS‚S”S¦S·SÈSÝSòTT+TLTkTŠT«TàUUAU†UòVV^VùWW W7WfWrWWÄXX„XÿYƒYüZWZÌ[([0[†[[´[Ë[â\>\w\œ\Þ\ó]$]Œ]Á]×^(^`^–^Ó^ß^ë__?_a_ƒ_¦_Ü``p`½`äaLa™a™a™a™a™a™a™a™a™a™a™a™a™a™bìc\cmcuddKd¾dÏdàdìdøee:eeeŸeþf`f°gggg"gNgdgug†g–g¦h)h„hØi(iŽiîj6jƒjïk\kÖlOlèm€n0nînönþoSo©oôp$~PüÀe~eþ´:CgH9CM_V\q…࿉ÁTV]˜º­‘wÅYþëu¸‡þÿcƒVf=J,`†5=@;Xj0]��…¦œ¶��¶��?Í3310#œ)Å)¶ýð���Rþ¼y¶� � @ � $�??3333331073#&R›’ú“ˆø“š1 ήÁþ2ôõþ7¹ªÆ���=þ¼d¶� � @ � $�??33333310#654'3d›’ø‡”ú“š1þùþ:¨¸ÉöôÎÁ¯þ1��?V��5@    ��?Ä293393933393910%'%7°)u!þ¬ß㜉ìÝþ®'m)þhüþ×y9þÉw)úhp��X�ã9Å� �&@ �� ��/3332293333310!5!3!!#Ûþ}ƒÛƒþ}ÛdÛ†þzÛþ��?þøË�î��·��/Æ33Â210%#7!Ë4|ÜA$×Êþë ì��=¨V¢��µ���/333105!=¨úú��uÿåÓ9� �@ �� TY �?+331074632#"&uZVS[\RT\TVXRO[Y�����D¶��@ ���??993310!Dýßþë!¶úJ¶��JÿìHÍ� ��(@ �� OY OY�?+�?+993310!"�!2�326&#"Hûþûýþÿúýý5]nl`akm^Ûþþ|sƒoþ€þŽþóéì  ëë��y��N¶� �*@ ��  �??3939339910!!77'3NþËM¨•×þN‹˜M‡ºw��N��PË��=@�  NYNY�?+�9?+�9399333310!!5>54&#"'>32!Püo£d,aQU W¨lލh‰ÒtG•¼¼}×s§n;XVNHÇ\L)d´te±º¬±���NÿìBË�&�[@/" �� '(!$OYPY   $  OY �?+�3?9/_^]+�9+�39933339910!"'32654&##532654#"'6!2¦–±¶þÎþäî¸UÌd™’¨¸oqªÐH•[Èão‰À$«‘ÓëO+6hsgVíYl¦0;Õ¸���#��q¶� ��F@"  ��PY �??39/3+�3933393339910#!!5!3!5467#q°þÒý°þ" %4þô/þÑ/×°üiø>ìRNþk��dÿì5¶��V@+  �OY��NY OY�?+�?+�9/3+�99399393993102�!"'3 54!"'!!76fÔûþÒþçô–OÒ^þÛ5€({7ýö#=¦îÏõþøO *5èÝ Béþúþá��HÿìPÇ��$�A@! ""��%&PYOYOY�?+�?+�9/+99339910�!2&#"3632�#"&2654&#"Hon}GYWŸÉd cÚÄÞþøê¢ñƒcjcd^…}m²¨÷`¼­ªöÙêþï– ¿…{k{zQw¤���7��P´��.@��NY��??+�999393103!!ã%ý/ý×°Âû���HÿìJÉ��"�.�S@), &  /0!!)) � QY �#QY��?+�?+�93939939329399102#"$5467&&54$32654&'"6654&JÒ|Фþææðþî…“}nxhsrqÕâOaMebNdÉ¿¢p¯EX¿r´ÛÌ»}ÂJO´kÂû¼V`cQCuBbÌQD<_2.`?EP���BÿìJÇ��%�?@  ##��&' PYOYPY�?+�?+�9/+99399210�!"'532667##"&54�32%"32654&Jþ”þ‚CT\›Èj :˜r¿Ü æ¢ó‚ýï`lbd^†}FþPþVø[ë^LõÚë˜þßÁ„|j|{Pw¤���uÿåÓs� ��&@ ��TY TY �?+�?+33331074632#"&4632#"&uZVS[\RT\ZVS[]QT\TVXRO[Y‹TVXRQYX��?þøÓs���(@  TY �/Æ?+33Â23310%#74632#"&¼4|ÜA$/ZVS[]QT\îÊþë ìÛTVXRQYX���X�Ë9���'@����=/333233993310%59üáýT¬Ë¶ððþÃþç���X¢9����$@ �P`��/33/]33233105!5!Xáüá%ÛÛþ}ÛÛ���X�Ë9���+@����=/3333339933105X¬ýTáüº=ðþþJ���ÿå Ë��%�D@" ��  &'� ��##TY# OY�?+�?+�3/_^]99393331054676654&#"'6324632#"&RmiC`V–ÀmßúÎódŒ`3þ×ZVS[\RT\åJ`ŽPK^:ADbÛ}Æ¥n dGJ<<þªTVXRO[Y���fÿfÇÉ�4�?�Y@+9955 ;( !!-�-�@A77 =  *1$*�/3?399//33333993393393910#"&'##"&54�3232654&&#"�!267! �4$!232677&#"Ç\¨oJrl§±Ì ØVÏCL@L†óœÊþÖŸ'jþ{Öþûþ„þWÙ‘ÜZ¼ü�¬Z^ 3@}‹ðïˆG:Õ¹Ó!þ‹»—¡÷‚¦þÇÍþìþÙ/-À[d÷“å´þ´þªÓÝ œ������…¼�� �C@! � �� LY  ��?3?39/+3333933999910!!!!&'7jýëjþ²{ýþ“%!œ\þ¤¼úD`Ù|$€þ���¸��ô¶��� �V@, "!MY  �LY�LY��?+�?+�9/_^]+�93333393910! #!32654&##32654!¸Ç7{f‹{þßøýÝ6´~q{…£Ê€zþü¶±Áƒ¨ ªÈàsNZTIýÅþƒbe¶��wÿìÑË��1@ ��LY LY �?+�3?+�3333310"!27# �4$32&&%¯ÀošÛ´ÞþÁþ®¦7ÑÕ×dR¦ÉþùëþMþüKƒjäW·gü':��¸��u¶���(@ ��  LY LY�?+�?+333310�!!! �!#3 uþeþ|þbËfŒþ¾þ`¥…Àéþ—þ€¶þ†þ¥×üH��¸��¶� �E@$��    LY  LY LY�?+�?+�9/_^]+33333310!!!!!!!ü¶Jýìïþ¶þþ¿þþ‡���¸��þ¶� �4@ ��  LYLY�??+�9/+3333310!!!!!!éþÏFýëðþ¶þþ‡ý��wÿì'Ë��L@'  LY�LY�� LY�?3+�3?9/++�3339333910!# ��!2&#"327!ãDù‚þµþ£•gáÑg ­Éòúadþë5ý .%…lbŒZøPþòäîû1��¸��f¶� �7@ ��  LYL �?3?39/]+33333310!!!!!!!fþËý½þÊ6C5wý‰¶ýÃ=���B��Û¶� �2@��   �?33?3393333310!!57'5!Ûýg²²™²²°R²R°°RüNR��ÿhþRî¶� �@ �LY� �?Ä+�333310"'3265!iNPBfX6êþR‡Zú¨ÿ�þô���¸��P¶� �B@  ��  �?3?9339333233339310!!!!7!Pþ þƒþÊ6zŒXþh^ýö¶ýc¬ñýy���¸��?¶��@���LY��?+�?333103!!¸6Q¶ûJÿ���¸��Ó¶��:@�    ��?33?3993323393310!#!!3!!46##þ  þë¦Zo¦þß þ‡{þ¢uýX¶û¢^úJ´1€û‡��¸��ɶ��6@ �  �� �?3?39932393399910!!#!!35!Éþvý„ þë‡{RþÛ}ýP¶û¹v´��wÿìçÍ� ��(@��  LY LY�?+�?+333310�! ��! �3 !"çþ˜þ°þ°þ˜iQQeûÕº¹sþ¹¼Ýþ•þz†mmþ|þ”õøíîù���¸��ª¶���4@ � �LY LY�??+�9/+333331032654&##!#!! îfŽw¼þÙþð…þÊÓ qlmhÊìúýø¶å���wþ¤çÍ���B@" ��  LY LY�?Æ+�?+�9333393310!# ��! �3 !"ç·±`þsþôþ°þ˜iQQeûÕº¹sþ¹¼Ýþþþ£QþwH†mmþ|þ”õøíîù���¸��H¶���N@&  �  � MY�� LY  �?3?+�9/+�9333Ä293931032654&##!! �!îd“Œ–^þʪ*Ž‚Jdþ¨þ£-bihXýyý϶ÙÝÉ9þ1���^ÿìË�'�E@"!�! ��()!� !  LY LY�?+�?+�9933993339310#"'32654&&'.54$32&&#"þãþê´”ÍUfm0]††PèrÏqdu™JX^&S›Í˜–ÆäX B6NM+C>D?tšgÂÞ61ñ0&RB)=9JbÅ���)��y¶��&@�� LY�??+�3933310!!!!!ìþÊþsPþs´þþ��®ÿì^¶��%@  LY�?+�?3333310# �5!3265^‘þî»þæþÈ5ˆ˜‰¶üN¢ô‚!û®ü©žŸª}�����3¶� �*@ �  � �?3?3333393310!!!67ú9þþ®þ91 @¶úJ¶üšMÍ(\æ�����¼¶��F@" �    �?333?3333333933333310!!&'!!667!667!HþŸÆ 50 Åþ þ‹1»1+Õ%Õ* ,º1�),6ï3ý¶üâÝ¢9ïB3üÍ7âQNéH������V¶� �D@    ��   �?3?3933332339933933310!!!!!Vþžþ¬þ¬þ´åþ:V;5Nþ5)ý×òÄýòý+�����þ¶��6@� ���??3933939233310!!!1NþþÌþP\ZüƒýÇ/‡���1��q¶� �;@�� LYLY�?+�9?+�999333310!!5!!!qûÀ½ýVýDÏÉí�Èü���þ¼s¶��@ � $�?3?3329310!!#3sþäààþ¼úÓú¬��� ��B¶��@ ��??333310!!!þëýß¶úJ¶��3þ¼¶�� @ �$�?3?33393103#5!!3ßßäþqTÓù���=¾��-@����?39/32333933103#¶ïïþ¾þè¶üJƒý}��ÿüþ¼NÿH��¶�$�?33310!5!Nü®Rþ¼Œ��LÙ!��@ �@ €��/Ý93Ê210&&'5!Ã?ôDV?¬Ù,ÅBeÈ���Vÿì;u��"�J@&  ""$# KY FYFY��??+�3?+�9/+333392910!'##"&5467754#"'6323265f;M£ƒ¡¹ùû®†µeÁëáðþÑv…‚”j˜aK¸ª²© 1ªQÎeÄÈýXZze�� ÿì´���B@!!   �  �GY�GY��?+�?+�99??3339933102#"'##!36"32654&ÆàçÇÅp3é1 kpqhkt^opsþËþóþëþÐ{þ–E˜¦ô‹ !´œ­¥¥¥��\ÿìÝs��*@   GY�GY��?+�?+333910 �!2&&#"3267fýö šZH|>îîX–KJ—=-Lì%þ®þ¸/2þû/$��\ÿìq���@@!  !  � �GY�GY��?+�?+�99??3333910"323&5!#'#'26754&#"ÅáåÉÓo 2ê; hjumo}fqr23¤}bfùì‘¥óˆ£!´œ­¥¥¥���\ÿìbs���F@# JY �JY FY�?+�?+�9/+�933333910"!&& ��32�!3267oan¬r6þòþÐøíý/‚e´bP¶š{qq{üR*3þò’*.ì('���)��u��<@ ��FY GY ��??+�?3+�3339333310!!#5754632&#"! þøþϨ¨¼Ïž{N\NA:yü‡y“RR¿°/àMþÏ1••GOû^��ÿ}þß� ��/@ IY� �GY��?+�3??+3333310"'53265!432#"FuTFIMG1Îp¦¦SS¦þðVTªû)²Ák••GO��� ��ö��I@#   � �??39393?3339333393107!!!!Å…9XþD×þ þ¾ƒþÏ1`ªTþý‡Åiþ¤ýJþ�� ��Ñ��·���??3310!!!ÑþÏ1��� ��Bs�#�M@&�� % $  GY  �?33??3+�39333933939310!!4&#"!33663236632!4&#"‰þÏQWujþÏé)-ªnûY-¯n¾ÃþÎQWpoyy¬Åýò^MW¤NVÃ×ý'yy ®��� ��¨s��1@��  GY  �?3??+�93393310!!4&#"!336632¨þÏV^€rþÏé)3³rÃÊyy«Æýò^QSÓÇ���\ÿì˜s� ��(@ � GYGY�?+�?+33331032654&#"�!"&5�!2“m{zkl{zlþàþÿ¡ö„¡ö„1¦ª©§¦¦¥§þïþ̰0Œþú��� þ´s���C@"  ! � GY �GY��?+�???+�99993333310"'#!33632"324&ÅpþÏø+kÒÆàiÂÝqhktÍeŒþ;J‘¦þÎþð³þøŠ“‹ !´œR¥¥���\þqs� � �B@!" ! GY �GY �?+�?+�99??333399310%26754&#""3237!!47#otlo{×kÆàåÇjž<þÎ 1¢Û…¦%´œþ®¨¦ï14PTù¶Õ=kQT�� ��ws��%@    ��?29??3393102&#"!3366>)%5’£þÏç-4±s þâ –‡ýÇ^¼^s���\ÿì¬s�%�?@ ��'&�   FY FY�?+�?+�993333339910#"&'53254&&'.54632&&#"¬ïîz¬KUÕQ¦,lZy7çÔÊ¿\T’L‡W“ƒz:L¬´! ü(6`$-9&6\wW•£XÜ$.I)<;5\x���/ÿì7L��=@   FY �GY�?+�?3Á+�3993333910%27#"&5#5773!!wPpr¦·§’¨XÃ9þÇIß#ã3¹¹fìîåýåA>���šÿì¢^��2@  GY��??+�9?33333910!'##"&5!3265!¸)1´sÅÈ1V^€r1NUÓÆÙýsyy«Æû¢������^� �,@ �  ��?2?3333393310!!367!ªþV?Ø$ (×?þV^ýƒyl`…}û¢�����Å^��J@$ �  ��?3?339933333933333310!#!!3677!3667!7VtÌþ¸þÂ0 ŠPƒ  . †+þ¾‡îü‹^þ…êL¥UýèVa]Hû,ïû¢�� ��–^� �L@$�   �� �?3?3933333339933939910!!!!…þ˜ZÙÛZþ”}þ¥ëìþ¦;#þœdýÝýÅþ���þ^��L@%  ��  GY ��?2?+�3?39333333939310!367!#"'532677NÓ  ÏGþ'Añ¡OL7AQy"^ý‹Rpg[uû¯®ò cd7���7��ª^� �=@��  FYFY�?+�9?+�933399310!!5!5!!ªüþBþ ´ÁéÆýQ��þ¼Õ¶��6@ ! $ �?3?393333933339104춯463"&5ƒ}~‚ÂôcKäêJdôÂW\ïXR>™}áFDþÕ¼" #± þÕDFâ}š��Çþ/¢��·���?/93103#ÇÛÛø!��Rþ¼¶�"�6@ ��$#$�?3?39333393333910#566554675&'4&'523"R½§cKvsãJd§¾Q{…}ƒ-pr5âDG+ Vk "¼+FDá5snþ TTïRa���X'9}��7@� �  ��/32}/3333333993310"56323267#"&'&B7}6g™IKb5~6e›BxZƒ C6çm 7@9çm%8��uþÓ^���&@ � TY "�??+�39333103!#"&54632¨ô3þ¦^ZVS[]QT\^ü1%TVXRQYX��ÿìË��D@#  OYOY��??99//99++9939333210%$753&&#"3267#3þ\ÑÓ²¦…ZH|>ytíR„dв°;ú¦ž Aë$§«þ¹-þ= ¼��R��jË��X@,   QY �NY�OY��?+�?+�99/3+�39939933399102&#"!!!!56655#53546¼ÃÃ]sNTwþ‰—ÎûègM²²åËRæ@YSÁÛªNþüø,rd‘ÛÃÉÙ��q�þ!ª��'�<@"  �"�() %�/3Æ2993Â291047'76327'#"''7&732654&#"¼6“[ji[–55}’_esT}‘6ÏmPQoqONoÓf_“57Ynk\}‘}33{‘}]hMonNPnp�����‰¶��q@8 �  ��RY� RY  �??39/3+�3Æ29+�333339339933399310!3#3#!5#535#53!H9þÃöööþá÷÷÷¾þ‡<\Zý²Š²ÝÝ²Š²ë�Çþ/¢���#@ ���?/99//9333103#3#ÇÛÛÛÛüÑþüÑ���jÿì)�-�8�U@* ."�'3�9:6611**%%KYKY�?+�?+�993333993333333910467&54632&&#"#"'53254&&'&&7654&'yH=…ß¶ªÁRDNQJcr£š}>?ïÉË’QÆFÂ%ZP·Šß‚tNe…%5%Oƒ(T•ƒžT¾ 3.01J-@©m±S(iJ”¯OÏ)9u'03"J‹Ch.9YD^1O�øÅ� �� @  ��� �/33393Ì2104632#"&%4632#"&K@BKLA@K“Q?^\��X��9� ��6@   �/33333/39333333310!5!3!!#5!Ûþ}ƒÛƒþ}Ûþ}á¢Û…þ{ÛþþßÛÛ��/J¾Ë��(@ �  �?39?399333310!576654&#"'632!¾ýyàf90(Qc{“½‰ž^i`J¨ÛdY2&(X˜…uU–u_��;9¶É�%�>@ ��   &' # !�?3?39/399933933910#"'53254&##532654&#"'6632šª^h°º‚”{XNp\SQ23/T9e>—g¢á7 nOy‹F¾Zk55 49&2&(/>€�LÙ!��@ @�� €��/Í93É210567!L¬?V4ûGÙÈe4Í2��� þ¨^��?@  � GY �??+�99??33339339103265!#'##"&'!!ÑX^~r1ç+*xX>h þÏ1Ñyy­Äû¢–UU.,UþÀJ��qþü��/@ �MY�/3/+�9/93Ä2910####"&563!¡¦¢>TØËÚè\þüPù°3úûþ��u)Ó}� �@ ��  TY�/+33104632#"&uZVS[]QT\ÓTVXRQYX��ÿÛþ¢����4@ �     �?33/99|/9339310#"'53254'73¢—žND[H¦NÁJXú€r¨>Sš=e���\JH¶� �*@ ��   �??3939339910#77'%3Hî0Nm-¿J¾p_$*=ë��9ðáÇ� ��@ �� �?3Ä2993310#"&5463232654&#"á·Ÿ™¹³£˜ºþ#AHH??HHA\«Áŧ©ÂŦdeeddcc��R�^š�� �Z@* �  ��    �/3339/33393333333Ö2Á233Á23310'7'7šþÛþêÛsþþÛþêÛs#þ;w\\wþ9þ;w\\wþ9�ÿÿ�.��’¶�&�{Ò��'É��<œý·� ³�?55�ÿÿ�.��´¶�&�{Ò��'É���töý·�²�?5�ÿÿ�Z��°É�&�u��'��<ºý·� ³-�?55���=þy×^��'�F@$"��()� %%TY% OY#�?+�?+�3/_^]3939333103267#"&54676655#"&54632ÉYlm9WYO´`fb÷jÜûa_5(ZVS[]QT\^JbŽMNX?9J:*Ý8EÁ©lžiFJ=;VTVXRQYX�ÿÿ����…s&�$���C�R�³&�+5ÿÿ����…s&�$���v�ÑR�³&�+5ÿÿ����…s&�$��K�VR�³&�+5ÿÿ����…`&�$��R�VR�³&�+5ÿÿ����…V&�$���j�VR� ´#&�+55ÿÿ����… &�$��PuX� ³#�?55������%¶���o@<  ��LY LY      LYLY�?+�??+�399//_^]++3333393339310!!!!!!!!!!#%ü—þ–þÅ–ýÍýò3ûz\þ¤¶þþ¿þþ‡`N�ÿÿ�wþÑË&�&����z��ÿÿ�¸��s&�(���Cÿ·R�³&�+5ÿÿ�¸��s&�(���v�\R�³&�+5ÿÿ�¯��s&�(��KÿõR�³&�+5ÿÿ�¸��V&�(���jÿùR� ´!&�+55ÿÿ�*��Ûs&�,���CþÞR�³&�+5ÿÿ�B��.s&�,���vÿ¡R�³&�+5ÿÿÿÜ��As&�,��Kÿ"R�³&�+5ÿÿ�9��çV&�,���jÿ"R� ´!&�+55��/��u¶� ��H@$    � LY��  LY LY�?+�?+�9/3+�333333333103! ��!!#%4&##3#3 /‰ËfŒþeþ|þb‰ÐÒ£ííƒÂRdþ†þ­þ—þ€Tèïþšþþ¬ÿÿ�¸��É`&�1��R�ÓR�³&�+5ÿÿ�wÿìçs&�2���C�uR�³&�+5ÿÿ�wÿìçs&�2���vFR�³&�+5ÿÿ�wÿìçs&�2��K�ÃR�³#&�+5ÿÿ�wÿìç`&�2��R�ÃR�³&�+5ÿÿ�wÿìçV&�2���j�ÃR� ´+&�+55�� š� �@ � ��/2229333107'¬þÕ˜-1™þÏ-•þÏþÓ–Ó-šþÕ+–þÏþј-þÕ˜���wÿ¦ç���"�9@ � �#$! LY !LY�?+�?+�9999339910�!"''7&�!27&#"4'3 çþ˜þ°Å‹Z¢ZÆiQÆ’T XÂûÕ8úTi¹¼æ3þ LhsÝþ•þzA‡lˆÂƒmF}hƒÂþ†¿tô-ùõ´uý'ÿÿ�®ÿì^s&�8���C�+R�³&�+5ÿÿ�®ÿì^s&�8���vR�³&�+5ÿÿ�®ÿì^s&�8��K�šR�³ &�+5ÿÿ�®ÿì^V&�8���j�šR� ´(&�+55ÿÿ����þs&�<���v�R�³&�+5��¸��ª¶� ��6@��  MY MY  �??99//++33333310!#!!3232654&##ªþãþú™þÊ6²þ ýDd‘Žˆ|åøþÛ¶åîþdt‚þÏ%ô&Ù@aL:0*4([bzN¬®"ò$2{)3<*HwQ@j17P.^þ;�þyxHN‹ !´œR¥¥�ÿÿ��þ&�\���jÜ�� ´,&�+55ÿÿ����…þ&�$��M�XR�³&�+5ÿÿ�Vÿì;¬&�D��M ��³&&�+5ÿÿ����…}&�$��N�VR�³&�+5ÿÿ�Vÿì;+&�D��N ��³&&�+5ÿÿ��þ…¼&�$���Q{��ÿÿ�VþLu&�D���Q¬��ÿÿ�wÿìÑs&�&���v R�³&�+5ÿÿ�\ÿìü!&�F���vo��³&�+5ÿÿ�wÿìÑs&�&��K�ªR�³$&�+5ÿÿ�\ÿì!&�F��Kï��³#&�+5ÿÿ�wÿìÑf&�&��OÙR�³&�+5ÿÿ�\ÿìÝ&�F��O;���³&�+5ÿÿ�wÿìÑs&�&��L�¬R�³&�+5ÿÿ�\ÿì!&�F��L���³&�+5ÿÿ�¸��us&�'��L�hR�³&�+5ÿÿ�\ÿì%&�G���8o��ÿÿ�/��u¶�’����\ÿì ��(�`@1  *&) �#GYJY��GY��?+�??99//3+�3+�993333333399910"323&55!5!5!3##'#'26754&#"øÀÜÆàl©5 þÅ;2››ø@ hLnie…oSd  TPƒe3Ç¡¡ÇûT‘¥ó{–°Š©•–�ÿÿ�¸��þ&�(��Mÿ÷R�³&�+5ÿÿ�\ÿìb¬&�H��Mû��³&�+5ÿÿ�¸��}&�(��NÿêR�³&�+5ÿÿ�\ÿìb+&�H��N���³&�+5ÿÿ�¸��I&�(��O/5�³&�+5ÿÿ�\ÿìb&�H��O?���³#&�+5ÿÿ�¸þ¶&�(���Q5��ÿÿ�\þ(bs&�H���Q?�ÿÿ�¯��s&�(��LÿõR�³&�+5ÿÿ�\ÿìb!&�H��L��³!&�+5ÿÿ�wÿì's&�*��K�¸R�³(&�+5ÿÿ�þm!&�J��KÜ��³N&�+5ÿÿ�wÿì'}&�*��N�¾R�³&�+5ÿÿ�þm+&�J��Nâ��³D&�+5ÿÿ�wÿì'f&�*��OüR�³"&�+5ÿÿ�þm&�J��O���³H&�+5ÿÿ�wþ;'Ë&�*���9��ÿÿ�þm!&�J��:Z��³E&�+5ÿÿ�¸��fs&�+��K� R�³&�+5ÿÿ� ��¨ª&�K��K�5‰� ´## ��?Þ5������¶���Q@(� LY �    �?3?399//33333+333333333333103#!!!#535!!5!5!f¹¹þËý½þʸ¸6C5þËý½ôÇûÓwý‰-ÇÂÂÂýô´����¨��T@*��  GY JY   � �?3?99//3+�3+�9333333933910!!4#"!#535!!!3632¨þÏ´sþÏœœ1;þÅfÞÅÊPò¯Âþ/¬Ç¡¡ÇS¶¤ÒÇ�ÿÿÿñ��.`&�,��Rÿ"R�³&�+5ÿÿÿ›��Ø&�ó��RþÌ���³&�+5ÿÿ�?��åþ&�,��Mÿ$R�³&�+5ÿÿÿé��¬&�ó��MþÎ���³&�+5ÿÿ���}&�,��Nÿ$R�³&�+5ÿÿÿ¯��Â+&�ó��NþÌ���³&�+5ÿÿ�BþÛ¶&�,���Qw�ÿÿ�+þß&�L���Q!�ÿÿ�B��Ûf&�,��O�TR�³&�+5�� ��Ñ^��·��??3310!!!ÑþÏ1^�ÿÿ�BþR ¶�&�,����-��ÿÿ�“þ)�&�L����MJ��ÿÿÿhþR s&�-��KþêR�³&�+5ÿÿÿ}þç!&7��KþÈ���³&�+5ÿÿ�¸þ;P¶&�.���9�œ��ÿÿ� þ;ö&�N���9u��� ��ö^��A@    �??39393?333333293107!!!!Ï:EþHÓþ¤þÆþÏ1Fªnþ�ý¢ªZþ°^þÛ¡R�ÿÿ�¸��?s&�/���vÿ¿R�³&�+5ÿÿ� ��ô¬&�O���vÿg‹� ´ ��?Þ5�ÿÿ�¸þ;?¶&�/���9J�ÿÿ�cþ;Ñ&�O���9ÿ��ÿÿ�¸��?·&�/��8uÿ£�² �?5�ÿÿ� ��…&�O���8�Ï��ÿÿ�¸��?¶&�/���O/ýpÿÿ� ��—�&�O���O¸ý8����?¶� �E@!   ��   �� LY��?+�?993993993333333103'7!7!¸Eq¶6uþüQì)ÄoÀýüXÄžþXÿ�������¤� �K@#� �   ��??99//3393333399�9999107!'7!éFu»þÏGq¸1¢+ÅpýhÝ+Åp-�ÿÿ�¸��És&�1���vDR�³&�+5ÿÿ� ��¨!&�Q���v�ª���³&�+5ÿÿ�¸þ;ɶ&�1���9�ø��ÿÿ� þ;¨s&�Q���9u�ÿÿ�¸��És&�1��L�îR�³&�+5ÿÿ� ��¨!&�Q��LN��³&�+5ÿÿ���¢¶�'�Q�ú���í���¸þRɶ��H@#   �LY�'�?+�3?33?39932933999910"'3267#!!3'&5!ørS]Imv üþ þë‡{ñþR[SNþÛ}ýP¶ü…—mçúJÏß�� þ¨s��A@!  GY�GY��?+�3???+�933933910"'53254#"!336632=kM;<{´€rþÏé)2°tÃʼþðªðÛ«Æýò^OUÓÇü®³À�ÿÿ�wÿìçþ&�2��M�ÃR�³&�+5ÿÿ�\ÿ올&�R��M ��³&�+5ÿÿ�wÿìç}&�2��N�ÅR�³&�+5ÿÿ�\ÿì˜+&�R��N ��³&�+5ÿÿ�wÿìçs&�2��SBR� ´(&�+55ÿÿ�\ÿì˜!&�R��S{�� ´,&�+55��wÿìPÍ��#�e@6  ��%$LY   LY  LY LYLY�?+�?+�?+�?+�9/_^]+333339333310!!# ��!2!!!!!"3267&&Pü—&Ž-þÁþ°S>=„#dýÍýò3û¸¦¬¬¤Az&#… Šikƒ þþ¿þþ‡Ëûóôù‹��\ÿì{s��+�2�p@:/0&&004 3/JY//�,JY )GY �FY�#GY�?+�3+�?+�99?+�9/+�9333339399910 '#"&5�!2632�!326732654&#"%"!&&¦þñ•ú¢ø…pÈGïôý•…kºdQ½ûfm{zkl{zlê^| Âu››Œ±-OMœþòî”*.ì'(E¦ª©§¦¦¥Âsyo}ÿÿ�¸��Hs&�5���v�‘R�³ &�+5ÿÿ� ��“!&�U���v��³&�+5ÿÿ�¸þ;H¶&�5���9�´��ÿÿ�cþ;ws&�U���9ÿ��ÿÿ�¸��Hs&�5��L�3R�³&�+5ÿÿ�S��¸!&�U��L™��³&�+5ÿÿ�^ÿìs&�6���v�NR�³0&�+5ÿÿ�\ÿì¬!&�V���v ��³.&�+5ÿÿ�^ÿìs&�6��KÿêR�³5&�+5ÿÿ�\ÿìÂ!&�V��K£��³3&�+5ÿÿ�^þË&�6����zb��ÿÿ�\þ¬s&�V����z-��ÿÿ�^ÿìs&�6��LÿêR�³-&�+5ÿÿ�\ÿìÌ!&�V��L­��³+&�+5ÿÿ�)þ;y¶&�7���9)�ÿÿ�/þ;7L&�W���9Î�ÿÿ�)��ys&�7��LÿæR�³ &�+5ÿÿ�/ÿìÄ(&�W��8��²�?5���)��y¶��F@#  �� LYLY �??9/3+�3+�39333339910!!#53!!!3#ìþÊøøþsPþs÷÷Tþbþþþžþ��/ÿì7L��]@/     FY JY �GY�?+�?3Á9/3+�3+�39933399333910%27#"&55#535#5773!!!!wPp4•Iºª’¨XÃ9þÇþêIß#ã¶¼”ÆÁfìîåÁÆ”A>ÿÿ�®ÿì^`&�8��R�œR�³&�+5ÿÿ�šÿì¢&�X��R1��³&�+5ÿÿ�®ÿì^þ&�8��M�šR�³&�+5ÿÿ�šÿ좬&�X��M/��³&�+5ÿÿ�®ÿì^}&�8��N�šR�³&�+5ÿÿ�šÿì¢+&�X��N1��³&�+5ÿÿ�®ÿì^&�8��P�¸R� ´&�+55ÿÿ�šÿ좲&�X��PN�� ´&�+55ÿÿ�®ÿì^s&�8��SR� ´%&�+55ÿÿ�šÿìÕ!&�X��S�¼��� ´'&�+55ÿÿ�®þ^¶&�8���QH��ÿÿ�šþ¢^&�X���Qø��ÿÿ����¼s&�:��KqR�³+&�+5ÿÿ���Å!&�Z��K����³+&�+5ÿÿ����þs&�<��K�R�³&�+5ÿÿ��þ!&�\��KÜ��³$&�+5ÿÿ����þV&�<���j�R� ´&�+55ÿÿ�1��qs&�=���v�NR�³&�+5ÿÿ�7��ª!&�]���v��³&�+5ÿÿ�1��qf&�=��OR�³&�+5ÿÿ�7��ª&�]��O�¶���³&�+5ÿÿ�1��qs&�=��LÿñR�³&�+5ÿÿ�7��¸!&�]��L™��³&�+5�� ��?� �"@  ��GY��??+�333310"!4632&Pþϼ͞xG\-‰û\°¿°/à��Åþ/Ë��I@$��   FYGYGY�?+�Ä+�9/3+�39333333910#"'5325#5754632&#"3#é¼°kM;;}¨¨¯Â–pHR?määy±Âðªq“RR½²/à‰Få�����…ª���!�-�w@;! ��"(@ ((  /.LY +!%��?3?3Ì9Î2239/+333393393Ê293É2999910!!!&54632&'5667!4&#"366¾/öþ´jýélþ´ô+ˆpm1fVDpL.jV·l6**7V&2¶U>úÝJþ¶#:Wn€ü-!æEGÄþ¿‹*x t7þÛ-33-\3���Vÿì;ª��"�,�8�D�~@?'(#,##3--9?3??  ""FE,66'<0B@ KY FYFY��??+�3?+�9/+�Ü2ÞÌ39333392993Ê293É210!'##"&5467754#"'63232655667!#"&546324&#"326f;M£ƒ¡¹ùû®†µeÁëáðþÑv…‚”jþË.jV¤€ÏŽppˆ‡qnž6**710*6˜aK¸ª²© 1ªQÎeÄÈýXZze/*x iDþÅl„€nl„i-33--44�ÿÿ����%s&�ˆ���v R�³&�+5ÿÿ�Vÿìþ!&�¨���vÑ���³A&�+5ÿÿ�wÿ¦çs&�š���v1R�³+&�+5ÿÿ�\ÿ´˜!&�º���v{��³,&�+5ÿÿ�^þ;Ë&�6���9û�ÿÿ�\þ;¬s&�V���9È���ºÙ!� �*@ ��  €��/2Í2933333310&'#567!TMN—˽Ce™HÙ]SQ_½p4³F���ºÙ!��*@�   �€ �/3Í2933333310!&&'5367Tþ›lwË“RT–!T§2/ƒ{]SWY�ÙÁ¬��¶����/39É10!!¦ýZ¬Ó��ãÙö+� �&@� @o��€ �/3Í]293Ê210#"&'3327ö ܦ®Ïª/UUÎ+š¸¶œ/6}��“éß��¶�� �/39É10432#"“¦¦SS¦••GO�T×J²� �� @   �/33/]39310#"&546324&#"326JŽppˆ‡qnž6**600*6Çl„€nl„i-33--44�� þ ����· �?3/9910327#"&54673Ý-#7o- äJì‹8-ÉlÙU¯)5È6Àm+·Q��×ÙD^��@ ��� �/Í93É210667!#×5 Nm²ø6ÜTº³���ºøã´� ���2@�  ! �/333Ì999393É23104632#"&%432#"&'667!#ºG:9JJ9:G#ƒ9JJ9�+5�ÿÿ�u'Ó{����Bÿÿÿ��‘õ�'�(���TýÆÿ—� ·2>�+5�ÿÿÿ��õõ�'�+���TýÆÿ—� ·2>�+5�ÿÿÿ��×õ�'�,�ü��TýÆÿ—� ·2>�+5�ÿÿÿÆÿì9õ�&�2R�Týïÿ—� ·2 >�+5�ÿÿÿˆ��õ�'�<��Tý±ÿ—� · 2 >�+5�ÿÿÿÆ��lõ�&vZ�Týïÿ—� ·$2$$>�+5�ÿÿÿÉÿì´&†��Uÿ��� µ"&�+555ÿÿ����…¼�$��ÿÿ�¸��ô¶�%����¸��T¶��@�LY�??+33310!!Týšþʶÿ�ûJ¶�ÿÿ�9�� ¼(��ÿÿ�¸��¶�(��ÿÿ�1��q¶�=��ÿÿ�¸��f¶�+����wÿìçÍ����G@% LY   LY LY�?+�?+�9/_^]+33333310!5�! ��! �32654&#"3ýø¼þ˜þ°þ°þ˜iQQeûÏ¿º½¼¼»»Àfþþ‰þ•þz†mmþ|þ”òûûòòüû�ÿÿ�B��Û¶�,��ÿÿ�¸��P¶�.�������3¶� �/@��  �LY �?3?+333393310!!!&&š 3 þëþÇðRñþÇþï <²>Þ)ü“¶úJoðÿÿ�¸��Ó¶�0��ÿÿ�¸��ɶ�1����R��?¶��� �C@#  �LY � ��  LY LY�?+�?+�9/_^]+33333310!!!!!ÍøýRœüdÄüwþ=þüHÿ���ÿÿ�wÿìçÍ�2����¸��=¶��#@�� LY�?3?+333310!!!!!=þËýæþÊ…´ûL¶�ÿÿ�¸��ª¶�3����N��y¶��S@)  � ��   LY � � LY��?+�99?+�93993339939310355!!"'63!N×þ5ãþJ3°Æþ#ð-Ïô Ëíþ þ=ýô ÿ��ÿÿ�)��y¶�7��ÿÿ����þ¶�<����\ÿì…Ë��"�+�V@+�""+ -',�+ +MY! $$MY    �??99//3+�33+�333339333331032654&##5#"$546$335!32###"33ü¡·ª“)þéºþè¡·55¶¡þèºþé)“ª´¤¼©“ˆ¥ûÇრ›ù´´ù› þúƒá9¤‰¬ÿÿ����V¶�;����m��–¶��@@ �� MY �??339/3+�33333933310�!#!# �!33!3265!–þÖþÓ3þê3þÏþÛ"–²®š#×þçþöþL´ ãþ!Œüø‘”ã��7��Í� �W@+  " !  ��LY  LY�?3+�3?+�993333333333993310"!!&54$3 �!!654&%µÄ„†ýs˜¥«<Ñ?y¦›vý}‹„ÅËÜÈËýHþé]AƸ–þ²þçÆþÄ`þüHÈÇÛÿÿ�9��çV&�,���jÿ"R� ´!&�+55ÿÿ����þV&�<���j�R� ´&�+55ÿÿ�\ÿì�^&~��T1��³3&�+5ÿÿ�Nÿì%^&‚��T%��³/&�+5ÿÿ� þ¨^&„��Tw��³&�+5ÿÿ� ÿì^&†��Tÿ ���³&�+5ÿÿ�ÿî¼´&’��UD�� µ*&�+555��\ÿì�q� �*�K@&)", +GY%HY%)� �GY �?+�93+�?+�9?3333339910%26754&#""323673327#"&'#ovko{×kÍéóÚv™2+ü 'T [pv"nß³ ´œþ¬¥¥ó00TT^7aûhþÈv ð MZ§�� þ���)�X@," '' +*"##"GY## � GY �GY���?+�?+�9/+�9?333393339102#"'!4$"32654&##532654&¶ë˜¼¹þûäÈ~þÏöÝ0Œ<€|…H5cnnй•­Á®Òò?ýé4à÷îþúüú'|pnsòmf\d��þ‹^��<@�  �??33333333939310!47!3667!‹þ½8,þV=¤B9¤=þc-6þV„Rþ>òI,åYüû´tþç��\ÿì˜��)�k@5�!!$+$*$$!''GY�!!   FY��?+�3?99323+�9933333993393910&54632&&#"�!"$5464&'326åíñÕoÑy\¬XIJŠŠ¸­þæþ÷ôþÛÁC_i{‡xioz–—¿£-B×-76.6iF^ö þýþïøÒ¶ðþ]–:#µ~e}ˆ���Nÿì%s�&�b@2�## ('&&JY& &&   FY FY �?+�?+�999/_^]+�933333933910#"!267! $54675&54632&&#"3H¨’“ gÜY¬þúþöþç€ÕþêséX^wŠMqn…°ÓAH}-)ôM¥¤k† 1ј.&Ý026B7���\þ…ò� �P@' ���" ! !FY�"�??+�99339333333399310!6654&'$4�%#!5!�òŠþÍEOOfþH43þÞVÉþø…'IgA¦ž%•Á]›/ )M~Ѧü ß¶§þÖþç“JZ5 !}��� þ¨s��2@��  GY  �????+�93393310!4&#"!336632¨þÏV^€rþÏé)3³rÃÊþyyy«Æýò^QSÓÇ��\ÿì–+� ���?@ ��FY FY  FY�?+�?+�9/+33333310�! ��! �267!"!&&–þóþîþ÷þî  ýáukþ7iyln Æi þkþu›…—ˆþiü5éîëì…Õöæå�� ÿì^� �!@   GY �?+�?33910327#"&5ÑI#ã3¹¹�ÿÿ� ��ö^�ú����ÿìá!�"�b@2 ""�$"��# HY HY ��??+�?+�393333339939393103'&&#"5632327#"&'&'#Ù#$\_24OWs£s3%L7!$r'nŒ)r+ .Î!\ZJ üF“Žüühb ì lwC…4šLþ�ÿÿ� þ¨^�w������s^� �*@ � ��?2?9323393310!36!!9ÚEsf4X¿ þî^ý”䙌+ÿ�þþµ ��\þ…ò�.�e@2%&&))#  �##0��/!++/JY &" JY ��?+�33?9/+�9333339933339333104675&5467##5!#"33#"!6654&'&&\’‰Û‡™ÍC$K¹ë“¦¨­š/aˆ¦žŠþÍEOOfÙß¶~¿6 4Êk% ßÒ‰u_RÒ{{GU5!}f•Á]›/ )&Úÿÿ�\ÿì˜s�R����ÿì¢^��B@!  FY �GY�?+�??+�333339933910%27#"&5!!#57!#C?)6–þ®þÏé²×ì6Û#Û¤£Bü‹uƒféýÊ31���yþ–s���/@��  GY GY�?+�??+3333310#"'#!�32%"32654&–õÚšþÍÿ›ì‚ýñqj+t#ã3¹¹få���ÿî¼^��-@ � GY��?+�?333333310 !32654&'!�‘þýÿ2ir}r+3(þï `ý–’ºËkÖ·ívþÆþÊ���\þúw� �#�P@)## ��%$FY FY"  �??3+�3?393?+33339333104&#"66$�5474632�Õ^Z9@‰¨ý´þíþænxÝZJÖºá�•þñ²Nœ§Oaýú Ìüaà#ö›’zÏyþê4¹×þÜû«þû›þ ��ÿÏþÉm� �X@,"!  HY�HY��?+�3??+�3?93333333339333102!327#"&'!&&#"56ðZrP)J3þ9ÃF=14Un}Ÿ4hþÆþ»ö†F88;rm3q{Ýíýþ%@5 Fý•u`F>ô��þF��A@   �FY �??3+�3?3?33339333106654!�!$�!ð°PNþÓþ×þåþÞþÜ#€£úÇ¥³à<þçþúþîþÒþ&Ú !3ýÅ­“ ;��mÿì{^�'�?@ %%) (! FY��?3+�33?39/333393910"&'##"47!3265!3265!¸zœ) .›wÖì0@%}c`SLLT^d}%@1îiind.š¡þúþѤ¬t†'þÙ‡s©£3¢þþ›þùþÔÿÿ� ÿì&†���jþõ��� ´#&�+55ÿÿ�ÿî¼&’���j'�� ´+&�+55ÿÿ�\ÿì˜^&�R��TB��³"&�+5ÿÿ�ÿî¼^&’��TN��³&�+5ÿÿ�mÿì{^&–��T5���³0&�+5ÿÿ�¸��V&�(���jÿñR� ´!&�+55��)ÿî¶��R@*   LYLY�MY��?+�??+�39/+3339392910"'3266554&#!!!!!!2mtWcI62S_þ°þËþ‘ZþJ\ä÷Í&�+D7YGý^´þþþðνÑÙ�ÿÿ�¸��Ts&a���v�…R�³&�+5��wÿì#Ë��?@  LY�LY LY�?+�?+�9/+33339910"!!327# �4$32'&JªÖ yý… ɼ«ójÍzþ¨þ”²Mâ‚ÝloWŽÉ¿ªþ²ÂMþü(#ƒjãW¸70ü%<ÿÿ�^ÿìË�6��ÿÿ�B��Û¶�,��ÿÿ�9��çV&�,���jÿ"R� ´!&�+55ÿÿÿhþRî¶�-����ÿꢶ��#�Q@* �%$�#LY�� LYLYLY�?+�?+�?+�9/+33399333103 !!!'"'5326!32654&##ús'þÚþàþiþÝ>_¶›T@:35>7[ X^ƒƒ£H…èÔäå´†þþc¨þaW ûHefc[���¸��¨¶���R@* ��  LY LY LY�?+�??39/+�Ä+33333933310!!!!!!!3 32654&##¨þÚþàþiþ#þÊ6Ý5s'ýX^„‡ HÉäåwý‰¶ýÃ=ýÏèþaefeY��)��¶��F@#  �� LY LY ��?3?+�39/+333933910!4&#!!!!!!2ÏFPþ”þËþ‘ZþJÐæYGý^´þþþðѺýçÿÿ�¸��`s&´���v�øR�³&�+5ÿÿ��ÿì9‘&½��6�^R�³&�+5��¸þV=¶� �2@ ��  LY'�??3+�?333339310!!!!!!!=þTþÕþR65þVª¶ûL´ÿÿ����…¼�$����¸��¾¶� ��?@ �� �LY�� LY LY�?+�?+�9/+333339103 !!!!32654&##îz8ý¤þVœýšh’”´O…èÔþ7¶ÿ�üHefeY�ÿÿ�¸��ô¶�%��ÿÿ�¸��T¶a���� þVô¶� ��Q@(     '� �LY LY�?+�?+�33?33333939393103!3!!!! q‘¨)TÃþÕülþÕÉ •];"COûLýTªþV^åþ�Ͳÿÿ�¸��¶�(�������‹¶��T@(  �    �� �?33?339333333332333393333310!!!!!!þ?Ù!Ù@þþ´þþßþþ´ø¾ý<Äý<ÄýBýåýåý��^ÿì×Ë�&�J@%� �!(!'MY $$MY$ MY �?+�?+�9/+�93339933910! '3 54&##532654&#"'6$32ªÈ«É×þ¹þßþ¾Ã^ýnqíè‰{èÔ……ÎÀ‡}«ï`¸¶’ÊêO-3×ahòXfKYwÏSMÈ��¸��ݶ��,@  ��?2?39933339910!3!!4#!¸ £sþìýZþ‹¶ý>½×VúJ¾û �ÿÿ�¸��Ý‘&²��6�üR�³&�+5��¸��`¶� �4@ ��   �?3?39333233333310!!!!!`þ ýîþÊ6 Jýëåý¶ý<ÄýB���ÿê=¶��1@ �� LY LY�??+�?+3339310!!!'"'5326!=þËþš>_¶›T@:35>7[ ›´†þþc¨þaW ÿÿ�¸��Ó¶�0��ÿÿ�¸��f¶�+��ÿÿ�wÿìçÍ�2��ÿÿ�¸��=¶n��ÿÿ�¸��ª¶�3��ÿÿ�wÿìÑË�&��ÿÿ�)��y¶�7�����ÿì9¶��E@!  ��  LY�?+�?39333393939310#"'3267!379þ@�   � LY '�??+�33?33/3333339310!!3!!!!!ìÆ5ÚþÕù"6Æ8´´û@ý`ª¶ûL´úJ�����u¶� ��A@! �� LY LY LY�?+�?+�9/+33393310!!!!3 32654&##uþÏþÕþVþ‘¤{8ý/h’”³PÉäå´ýÏèþaefeY���¸��‡¶� ���A@ ��  LY LY�?+�?39/+�?333339310!!!3 32654&##!! þÑþ×þp6d5ýNQ™Ž‰¬C™þË5Éäå¶ýÏéþbeffXýy¶���¸��¾¶� ��2@�� LY LY�?+�?9/+3333310!!!3 32654&##¾ý¤þV6z8ý0h’”´OÉþ7¶ýÏèþaefeY���Hÿì×Ë��I@&  LY    LY �LY�?+�?+�9/_^]+33333910"'63 ��!"'3267!5!&&)c¿]bèÿEcþ“þ¨íÃó«¿É ý†xÀÉ8'úgþqþþ–þ}KMººþª¿���¸ÿìÍ���Q@+  ��  LY LY    LY�?+�??9/_^]+�?+3333393310�! �!!!!�! �3 4&#"þ¯þÅþßþ´þèþÊ6"I<Nü+¨¡L¥£¤©Ýþ˜þwM>ý‰¶ýÃ!3þxþ˜ôùíôúú�ÿö��š¶� ��Q@(��   ��MY LY  �?3?+�9/+�933339339310!&&54$!!!#"33¤þªþ¨ |„ ÜþÊ™x„€„‘1ýσ2ÑŽÉÙúJ1‡Vdap�ÿÿ�Vÿì;u�D����\ÿìž%��#�D@"% !!��$ FYGYHY�?+�?+�9/+�933333310�%6%36632�! �2#"\%7—,#‚þµ~|=5®dÏæþÝÿ�ÿ�þá1ÙÄ6kY‚ž5(þö1P”{RXýìþðþÓox+#2Q)Ë×��� ��Ë^��� �N@'��" !JY JY  JY �?+�?+�9/+�93333393910#!!24&##3264#!326¨qnw‹ÿ�îýÃ=æåþçffòøae¢ÿ�Ýad9Z‡c£«^•ý•B;þøIfÝ8��� ��¤^��@�FY�??+33310!!¤þ-þÏ^åü‡^��þo1^���I@% �  FY # FY �?+�33?3?+333399339310%#!!!36!3\åWMwþîýþî^`‚¤ßšºþ²’ý‘þop•Æ$üÿÿ�\ÿìbs�H�������ü^��R@' �    � �?33?339333333223333393333310!!!!!!ðŽ;þdÃþºþVþäþVþºÃþd;?ýáýèýº7ýÉ7ýÉF��Nÿì#s�(�L@&'  #*)('('JY((  FY FY �?+�?+�9/+�933399339102654&#"'6632# '532654&##5¶­‘jzMÃPZwàŠÑü߉u„ú©þè–VÍ`•”œ¢v°8=66&!Õ-' ‰½9 "}efžVEü(.C>DAÓ�� ��#^� �,@     �?3?39932339910!!47!ÇoþÙýþþ’^þFFþðû¢¾wÙüò^�ÿÿ� ��#?&Ò��6�–���³&�+5�� ��ô^� �6@ �  ��?3?39333233333310!!!!}PþEâþ¦þ7þÏ1^ýèýº7ýÉ^ýá����ÿì‰^��5@ �� FY  HY�??+�3?+3339310!!!#"'5326!‰þÏþç \™|jD119M=Nyþ‰þ¥ ô¤O�� ��!^��:@ ��    �?33?3993323393310!!#&''!!>!!þã6+ÆÙÉ+1þä¤À3 !%,± q>Ólþ ønÇDü^þ#MÈG–ƒn²��� ��¬^� �3@  FY   �?3?39/+33333310!!!!!Ѫ1þÏþVþÏ^þR®û¢Íþ3^�ÿÿ�\ÿì˜s�R���� ��˜^��#@ FY�?3?+333310!!!˜þÎþkþÏ^û¢yü‡^�ÿÿ� þ´s�S��ÿÿ�\ÿìÝs�F����/��=^��(@�� FY�??+�3339310!!!5=þ’þÏþ‘^åü‡yåÿÿ��þ^�\����\þ'����P@(� ! �FY� FY �??3+�3?3+�3?333393333310��!$�54�%!4&'66Ñ>þÆþäþåþåþÁ4&ýÅš†ŸX–‡…˜dþÔò÷þ×þä/îý%°ü± ¸‡„µýd²�ÿÿ� ��–^�[���� þod^� �4@ ��  �FY#�??+�3?333339310%3!!!!!Á£þîüN1¾2ßý‘^ü‡y��{�� ^��-@ FY  �??39/+33333103267!!#"&5¬‡X—M1þÏj¶U·È^þg’( ãû¢¼8.»­ �� ��!^� �5@ ��  FY�?+�3?3333339310!!!!!!!!ù1w1w1^ü‡yü‡y��� þoÅ^���>@  ��  �FY#�??+�33?33/3333339310%3!!!!!!!!¤þíùî1w1w1 ßý‘^ü‡yü‡yû¢�����f^� ��A@!  JY FY JY�?+�?+�9/+333933103 !!!54&##32–×÷þýëþœ5ghÐÔË^þP¤¦þœyåýA:þø��� ��-^� ���C@! ��JY�� JY�?+�?39/+�?3333393103 #!!4&##32!!Ñ“�ööõþ11Xhg‰ËþÏ1®¤¦±³^ýA:þøÓ^�� ��¢^� ��2@ ��JY��JY�?+�?9/+33333103 !!!4&##32Ñ×øþýì1 hgÑÕË®¤¦þœ^ýA:þø���Jÿì¼s��?@   JY �FY�FY��?+�?+�9/+33333910"'53267!5!&&#"'663 ��¢Ò†®™nx þZ¦kdwVK½^�þñEîP€€Ë{|?Ñ#-þäþäþÜþÕ�� ÿì¨s���I@& ��  GY FY  GY�?+�??9/+�?+3333393310�#"$'#!!36$32�32654&#"¨þêöÝþ÷ÉþÏ1ÍÖíý%bqobcpob1þíþÎøéþ3^þRÖíþÉþõ§©©§§¥¦������^� ��M@&   �  JYJY �?3?+�9/+�933339329310!!&&5463!!#33#"Jþ¶-loóÒþϨÉnYªÑKUº-ªs¢¸û¢ bFOIÿÿ�\ÿìb&�H���j�� ´1&�+55��þ¨�&�j@6$$$('' GYJY  ��GY��?+�3??99//3+�3+�93333399339910"'53254&#"!#535!!!3632=kM;<{^VsþÏœœ1;þÅfÞÅʼþðª²nn¯Âþ/¬Ç¡¡ÇS¶¤ÒÇüë³À�ÿÿ� ��ª!&Í���v��³&�+5��\ÿìðs��A@! JY� FY�FY��?+�?+�9/+333339910 ��!2&#"!!3267þêþå!¸­Xªkis¥þ[ngOŸfŽ#* JÙAz}˃}$,êI�ÿÿ�\ÿì¬s�V��ÿÿ�“��ß�L��ÿÿÿå��“&�ó���jþÎ��� ´&�+55ÿÿÿ}þß�M�����ÿìÓ^���Q@*�� ! JY�FY HY�JY��?+�?+�?+�9/+3339933310!##"'5326!32!4&##32-î \™|jD119M=#Žøïþ°ba…¿yþ‰þ¥ ô¤OþP¤¦þœ`A:þø�� ��Ó^���Q@)�   FY��JY�� JY �?3+�?39/+�Å+3333393331032!!!!!!!4&##32^Žøïþþ;þ¤þÏ1\1Dba…¿®¤¦þœÍþ3^þR®ýA:þø�����¨��V@+��   GY JY   � �?3?99//3+�3+�9333339933910!!4#"!#535!!!3632¨þÏ´sþÏœœ1;þÅfÞÅÊPò¯Âþ/¬Ç¡¡ÇS¶¤ÒÇ�ÿÿ� ��ô!&Ô���v�®���³&�+5ÿÿ��þ?&�\��6û��³&�+5�� þoÁ^� �6@ � �� #��FY��?+�3?3?333393103!!!!! 1¾2þxþî^ü‡yû¢þo‘���¸��}ì��#@ �LY�??Æ+333310!!!!!îþʹ ýq¶6ýÊ��� ��Ï��#@ �FY�??Æ+333310!!!!!ÑþÏþ^1ýê�ÿÿ����¼s&�:���C�üR�³&&�+5ÿÿ���Å!&�Z���C�‡���³&&�+5ÿÿ����¼s&�:���vºR�³&&�+5ÿÿ���Å!&�Z���vd���³&&�+5ÿÿ����¼V&�:���joR� ´3&�+55ÿÿ���Å&�Z���j�þ��� ´3&�+55ÿÿ����þs&�<���Cÿ|R�³&�+5ÿÿ��þ!&�\���CÿY���³&�+5��R´®š��µ���/333105!R\´ææ��R´®š��µ���/333105!R\´ææ��R´®š��µ���/333105!R\´ææ�ÿüþ1NÿÓ��� @ �� �/39/3233310!5!5!5!Nü®Rü®Rþ1‹Œ‹���Á¤¶��@  ��?Í33Â210'673'e5ÛB#Á[qþõê���Á¤¶��@ �?Æ33Â210#7–2~ÛE¶Åþæ(Í���?þøË�î��@ ��/Í33Â210%#7!Ë4|ÜA$×Êþë ì���Á¤¶��@ ��� �?Í33Â210#&'7?%@Û;a¶õÿ� U��Áw¶���%@ � �?3Í233Â2Ô2Â210673!%673!ìe5ÛB#þèþe5ÛB#þè×[qþõê[qþõê��Áw¶�� �#@  �  �?3Æ233Â2Ô2Â210#7!#7!¤2~ÛEá2~ÛE Åþæ(ÍÅþæ(Í��?þøž�î�� �"@  �  �/3Æ233Â2Ô2Â210%#7!#7!Ë4|ÜA$â4|ÜA$×Êþë ìÊþë ì��{��¦� �N@%  ��  � ��??9/3339933339333993310%!5!%¦þ´7þê7þÉ777L üB¾ñ¡þ_���{��º��}@> �  �        ��??99//3339933333993333339333933333310%%!5'75!%%oKþµ7þé8þ´L//þ´L87Kþµ/-òþ‡yòåÕñxþˆñÕ���b® )� �¶�� �/Í93104632#"&b”‹‰–—ˆŠ•욣¤™˜¦¦���uÿåb9� ��#�,@ ���$ TY! �?33+�3333Ô2Ä21074632#"&%4632#"&%4632#"&uZVS[\RT\GZWS[\RU\HZVS[\RT\TVXRO[YQTVXRO[YQTVXRO[Y���?ÿî �Ë� ���"�-�7�B�C�d@1.>8388E �D)###C5 @+1;& �?3Ä2???333Ä223/33À29333À29333À2103254#"#"&5!2%#3254#"#"&5!23254#"#"&5!2;-2``2-»²¬¥´Y©µ°üÕð+…-2``2-»²¬¥´Y©µP,2``2,º°®¤´X©µý5�}üú{}æçíàÉíØúJ¶ü}üú{}åçíßÉíÞ}üú{}äèíßÉíýj�ÿÿ�…¦œ¶� ��ÿÿ�…¦B¶�����R�^ ��0@��/39/39333333Á210RsÛþéÛþ=Çwþ¤þ¤wÅ��R�^ ��0@���/39/393333Á23310'7 þÛþêÛs#þ;w\\wþ9�ÿÿ�uÿå¶�'�H�������þw��‘¶��@ ��??33Á210#‘üÕï+¶úJ¶���fü Ç��*@  � �  �??39Ä299339104&#"#3363 D<9ZHÇ¢IŽü‘L@`qþ´ºTeúþ/��#��'¶��V@+ � RY�NY NY �??+�9/+�9/3+�333333333910!!!#53!!!!é<þÄþÏ••oýÂý縲þú²þþþ°þ��R��jË�%�y@=  " &' ! RY !RY�NY�OY��?+�?+�99/3+�3Æ2+�3993399333333939102&&#"!!!!!!5667#535#53546Á¾Ã]NƒEPLgþ™gþ—FJÎûèdK²²²²äËRæ#VVq°s²Jl'þüø*jU²s°sÎÔ���¸ÿìé¶���)�q@< #''% + +�*QY �NY&#&QY#!PY # # #NY�??+�99//3++�3+�?+3339339933391032654&##!#!! 27#"&5#5773!!ÙB‹~ˆTþÏþç5þßuðNSaŠ£–’¨XšþðHhumhÊìúýø¶åûú#Ï3¦­>lgëíÑþÍGD==DG>ýZ§¾¶­ud7f@IIŒtZO¶úJ¶û¢¬ÀĨªÁǤdeeddcc8¸ª²¹2›)f_¾+¤-���)ÿîßÉ��$�F@ "  %&""   ��/3/9/93399399333339310%2673#"&5556746324#"66�+5��X�9��Z@- �      ��/33333Ä22323333399392910'7#5!7!5!3!!1ÉYêPPþ`ƒÉ\íþ®O¡¢þíT¿ÛªÙVÃÙªÛ��V��9=�� �:@ ��    ��/2/9/339333333331035!5VáüáýT¬ÛÛ¶ïïþÂþè���X��9=�� �6@  ��  ��/2/39/39333333331035! 5Xáü¬ýTáüÛÛø>ïþþJ���X��PÁ�� �B@  �� � �??9333993393333310#3Pþ=rþ=Ãr»ôôôßý!ßâýšþfþgÿÿ�)��ø�&�I����L��ÿÿ�)��ê�&�I����O����hÙ3?� �@ � � �/3Í293Ê210#"&'!32673ôæíãYsec ?»«¤ÂgS[_�ÿ}þÑ^� �!@  �GY��?+�3?33310"'53265!FuTFIMG1ÎþðVTªû)²Á�^Ͷ��@ ��� ��?É93É210667!#^'PV²ç1¼@°ƒ�^þ;¶ÿƒ��@ ��� �/Í93É210667!#^'K[²þV1¼@¨Œ��NÙ¦!��@ ��� €�/Í93Á210!5673¦'þæNX²1¼@ª‰���)5ßË� �� @�   !�?3?399331032654&#"!"&5!2%-12..21-ºþ¢¤´X©µ�}|€{{}þ3íàÉí��� Jö¼� ��B@  �   �??9/3392999333399310##5!533!547ö}îþì}þ• 5á——šAýͤVbl¿��T9˶��L@$ �� !�?3?39/3393993939393102#"'53254&#"'!!6¯¾·žd2…7¬WQ?8m%þœ8•€‘ 4À *ƒ?@+¸¸‡���-5Ù×���8@ ���  !�?3?39/39993399310#"&5%366322654&#"Ù¯›žÄ#C¯¶ YJt„þ¨;><9Dm¨¿¢¤¢5„W+/ŒþôG?6DjBT���;J×¶��(@��� �??399939310!5!šTþMœþ¿J´¸•ý)��-5ÛË��!�-�F@!%+%+  ./ (( !"��?2?393939933993399102#"&5467&54632654'"6654&…¨CLKB#¿—¡·GW®:9;<…eu+-4&&2*Ëyi?d+*=I,u•ŒxAj.Y~hzýn-99-Q,,£/)22+/��+9ÕÉ��"�2@ ��#$ !�?3?39/3999339210#"'53267##"&54632%"32654&ÕöçI613Œ‹G~z攤¼þ¬5B8;7FD3þü¼pƒb”ƒ‰ªÕ#GA7A?+CS��TþÁî�� �����#�'�+�/�3�7�;�?�C�G�S�[�k�t�|�‰ @‡@<0A=1� NTcpp``ll€zggv„vkkH„HX„‡‡XT E)% D($  Š‹‚}}kduullvvkVKKkk\ ZQQ…t\\ -  $1'2D=G>(A+B ��BA>=21 , 84 95!�/333333333/3339333333333333339/333/39/3/339/393/399333333333933939332933333333333310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3#"&546323254#"%32##32654&##32654#"'53253T/ÀÎ0mù�oÀÃmýIûáþò·mmmmûÂü0ooÀwú¨ooooþmmûŸ‡‡‡~ˆþs‡‡‡‡á¬mp.,;0m^Ï{B.$*/;J1%Z^4+V}i¾0oÁÁoþÐÁù/ÂmmÂþÑmmmmþooú¨ú;mm¦Jooooü/yýhI‘œœ‘’›š“ÅÅÄaCS1D D8QYb" "ãš+%Jþú fV’þr_c��TþÁª���*�X@)%  +, (("""��//99//3333/9933999939333210 54676654&#"63232654&#"þ¬üTüVë,AgI»¥OºGR Z?>1HT;GFBIHCHEüVüW©û/2A1R~X‡š8*²P:/5K6DpJ;þí?HI>@IHÿÿÿ}þé!&7��LþÊ���³&�+5ÿÿ�Á¤¶����)ÿìž)��6�v@="4 1 1*�..((6  78GY44� *�*FY��11FY1%FY�?+�?+�9/+�339/+9933939399333310&&#"�!"&54654&#"'63232655'&$&54632�3ß‹e;FÏöþÃþÎÛÛ *0L•˜Zga\‘“àþÈ¢áÅñ,’ß©¼89r€ç+-þ®þœ¥¦5i+*¶V^X?†GKOæ÷!tÑŒ¡¿þÛþÛå�����Ã��F@"�� ��  MY�?+�3??9333333393310>32&#"!!}>xhu_UB,(5C¶6þÌþPTˆõ°Bå +'`þœŽýÕ/‡��3ÿìË^��(�l@5 ##  * )!!  FY &FY��?3+�39?+�339/3339939393993310"'##"547!57!!4'!32655!326FíS RîÝå?þú®êþþ?å3@ü >\gTLLTg\ÒÒü²ÑfåѲüþò©Ôɰ–‘s‡‰‰‡sÿÿ�¸��Óu&�0���vÃT�³&�+5ÿÿ� ��B!&�P���vî���³,&�+5ÿÿ��ý¨…¼&�$���[s��ÿÿ�Vý¨;u&�D���[���ÿÿþrÿì9Í�&�2R��\ýù����Xý¨Nÿƒ� �� @ � �/39/393Ì210#"&546324&#"326NŽppˆ‡qnž6**600*6þ˜l„€nl„i-33--44��yh+Ç���1@ ��?Æ2ÔÄ9393Â9Ð2Á210467#"&767!#y˜IE%-%?BCJZ**¥‰òRpJ%%-J'C“W^Õ�ÿÿ�)���&�I���'�I����L/��ÿÿ�)����&�I���'�I����O/����wÿì×����H@# � ��   LY @ LY�?+�?Î+�993/3393339910�! ��! 65!3 !"çþ˜þ°þ°þ˜jRZ²]-$Ž{=ûÕº¹sþ¹¼¢Ýþ•þz†mkƒË=Õ±É2ŸÖõøíîùü.��\ÿìÍ��"�#�H@# �$��#   GY@ GY�?+�?Î+�993/3393339910�!"&5�!265!32654&#"˜þàþÿ¡ö„pÈG•-WŽi4üûm{zkl{zla1þïþ̰0EE-ð†k}š¦ª©§¦¦¥ý(��®ÿì)���9@  @LY�?+�?Î39/3/333Â23310665!# �5!3265^JF- k¶Š‘þî»þæþÈ5˜˜‰ã¶¼–jš§gý¢ô‚!û®üiœ“™˜•úJ��šÿìs���J@%  @KY� GY��??+�9?3Æ9/+/3393Ê23310!'##"&5!3265!665!3¸)0³sÅÈ1V^€r1GN- l·Ž MVÓÆÙýsyy«Æu“tœ¨füÏÿÿüÙþW!��CúÊ��ÿÿüÐÙÿ!��vû„��ÿÿûà×ÿ�Rû���üÙÃþ ¤��· �/ÉÌ23910#'6654&#"56632þ ¢ ®K6*"AJi)Œ‹Ïœ)G“ 3% "¨ o��üÙþRþ%ÿ}��±�/É10432#"üÙ¦¦TR¦þç––GNÿÿ�¸��s&�(���CÿzR�³&�+5ÿÿ�¸��Ýs&²���C�TR�³&�+5ÿÿ�\ÿìb!&�H���C©��³$&�+5ÿÿ� ��#!&Ò���C ��³&�+5��wÿì=É�2�b@1++((00 4#3))�  MY-&&LY �?3+�39?3+�3339/33339393310"'663 ��!"&'# ��!2&&#"327!3254&þ'ZDl@°K )þ¸þÖt²MM®tþ×þ·) J¯BlD[&€Žº°SU6Hl°¸Ó!-×1<þŠþ­þ‰þcHKKHœxRw:3×-!óâûþ÷EŒþtE ûáô����Å^��U@)�      ��?2?33?3993333939339993310!!367!36!!‘þu@Â! g]4ÈÐþ^ýdS’lý”䛓"þýÞÌ}þƒ��������O@( � MY LY   �LY�?+�?99//+�3+�3333333339103 !!!5!5!!32654&##5{8ý¤þVÿ��5yþ‡i’”´PdßèÔþ7dæÊÊæüšefeY�����'���J@& � FYJY JY�?+�?Æ39/++�3333333339103 !!!5!5!!4&##323×øþýëþþ1g9hgÑÕËyˤ¦þœyåÉÉåýçA:þø���¸ÿìRË�"�U@,�!$#LYLY! LY �?+�??9/3+�3?+333399339910!3267# �#!!3�!2&&#"!fýš ЪaÁrhÉwþÅþÎþÊ6×,|$æÛdZ´W£ÐdwµÔ(%þü(#N=ý‰¶ýÃ:gü':®¢�� ÿìs� �W@- " ! FY  FY �FY��?+�??9/3+�3?+333393339910"$'#!!36$32&#"!!327ÅðþòÝþÏ1Ýé[¿MVxdk¦þZ ts˜°ˆòïþ3^þRáâ,$Ñ?qpã€~PîE�����×¼� ��E@#   ��  MY  ��?33?39/+3333999222210!#!#!!!'ªÑdþïfÏþÑ-{/üu9† wý‰wý‰¼úDdYE4������^� ��L@% �   � JY  �?33?39/+�3333393333999910#!#!!!!'&'bNþøP—þÛ×oÙþÛþ #H¦þZ¦þZ^û¢uPœW]���¸�� ¼���t@9 �� �   LY � �?3333?9/+�3393?3333339/39333399999910!#!#!!!!!!!'&'ÝÑdþðgÏþÑðþËþÊ6—Û{/üu93\ wý‰wý‰wý‰¶ýÃCúDd‰î'4�� ��7^���x@;�  �FY   �?3333??39/+�339333339/339333399999910#!#!!!!!!!3&'oBþøB¦þÜÂþÍþø‘·nÙþÛþ#ê`Ëþ5Ëþ5Íþ3^þR®û¢šÚDI���)��F¶���„@B�   � ��LY MY ��?22?9/33+�39333+�93333393339393393931036675!!&&'!!)˜:¨„þ‰þ‡¨9˜þÈ{)TCþÍGV({×þþųº$Õ‹‹þ+%¿­þ;|dýqe{þ{9�����ç^���„@B�   � ��JY JY ��?22?9/33+�39333+�93333393339393393931036675!!&&'!7!u(}[þß4þÛYy*tþþ^8/þø6<^h´þšZ} mjjþ‘ {þ¦'MB þ?à DNþÙ®á��¸��m¶��"�#�©@V" � ! ��% $#% " "LYMY LY   ��?333??9/39/++�39333+�93Ä3333233933393933939310!67!!!!5!!&&'!!P—,3þ¨þÊ6Cþ¦þ‡¨9˜þÈ{)TCþÍGV({×þþýÅÅ3ý‰¶ýò‹‹þ+%¿­þ;|dýqe{þ{9ûL�� ��ö^��"�#�¨@U" � ! ��% $#% " "JYJY FY  ��?333??9/9/+�3+�39333+�93Ä3333233933393933939310!67!!!!5!!&&'!7!#u þÓþøÏÿ�3þÛYz)uþþ^ÃJ0>]_¨¸·Ÿ¸Ëþ¯þØ\b)‘YhšŒZ6µï²Íìÿìíè‰{èÔ……ϾˆS·w`ƒÑ6›NWeð— š"¸€Œ¹·‘Éë')X)嬓­œenahòXfKYwÏ6Lwƒ(›dN.���þ/#d�L�ª@Y4* 99 GEECCIIIB@ *0N?@@**M?{H3"6TJ~†ihoþÜþñlaKYUON0ˆV4D³|±±æí„„œ¢vp­‘jzMÃPZA>]WÄ9˜RVcd˜ o"a_{ "}e¨²'44*)噚¯¥@ADAÓ8=66&!Õ! l_)šfL-�ÿÿ�m��–¶u��ÿÿ�þF•����wÿìçÍ� ���?@ ��LY LY  LY�?+�?+�9/+33339910�! ��! �267!"!&&çþ˜þ°þ°þ˜iQQeýH£½ý·¬žºà·Ýþ•þz†mmþ|ü§À½´ÉÛ®®©³��\ÿì˜s� ���?@ ��JY GY GY�?+�?+�9/+33333310�!"&5�!2267!"!&&˜þàþÿ¡ö„¡ö„ýãapþ>ndbpÂm1þïþ̰0Œþúþ�ttttœqppq������¦Ã��A@  ��LY �?3??+�933333393310"!!67>32&B.@*þ˜þ®þ9!+6ª7\xVtF3ÁGvûü¶üs ‹€««œK'ò������Ñf��@@  ��  GY ��??+�?3933333393310!367>32&#"!?Í2 ,{3LkULH+' 3þÌþÉ^ýŽ£Oo|XŽj1ì,7üòÿÿ����¦s&€��v'R� ´)&�+55ÿÿ����Ñ!&��vË��� ´*&�+55��wþ Í��"�.�k@7  0)�0#/ ,LY &LY  MY ��?2?+�3?393?+�?+33Ä222333939310!367!#"'532677�! ��! �326&#"�NÓ  ÏGþ'Añ¡NM7AQy"ýÚþ¶þºþ¼þµLECKü ¤¬­£¡­¯£^ý‹Rpg[uû¯®ò cd7Õþþ‚{xxvþ‡þ‰þÿìåéî��\þ )s��"�0�k@7  2##�2**1- GY-&GY&  GY ��?2?+�3?393?+�?+33Ä222333939310!367!#"'53267732654&#"�!"&5�!2œMÓ  ÏHþ'Añ¡MO9@Qy"û;m{zkl{zlþàþÿ¡ö„¡ö„^ý‹Kwg[uû¯®ò cd7)¦ª©§¦¦¥§þïþ̰0Œþú���wÿƒ91��(�P@*#'  !!��* )#' 'LY@ LY�?3É+�3?É3+�3333393910�#"'$��%6632�6632$%#"'9þÓþâ#qvþáþÏ.$D=5H1û}Ž‹E0-Eþí'ff)þëÝþ¿þ‚(ss$FC{&<2,B&þ„þ¼Æò%**J“ŽMKKM���\ÿ‘´��+�T@, $$** !!��- ,'$**FY@ FY �?3É+�33?É3+�33333393910#"&'&54763266326654&'#"&'ïæ H69G ßòóænjàýüž99+>SOPI>=6D–1ëþà&5:;6'%ãí#!RR"þÛêþò>'+!3±~‚¥/816A��wÿì=�1�G�Y� @PLO=þŠþ­þ‰þcGRKNœxRw>/×-!óâøþðpcenöáôAÂ6) 3;1bt6&-&þ³]ŒV:5:Z���\ÿìÃR�)�@�R�¤@RDH54MH+AAHH# (##TS@,AHHEPEDDP,P,P15511;@   FY (�@�FY&��?2+�Ê39?3+�333Ì23/99//9/393333339333933ØÊ2910 �32&&#"3273265#"'632�!"'#"'&&#"#54>32356654.54632fþþþøìõ•|V?B%ºwl’‚‚“mvº'A>V|”öìþúþû­|z&´ˆg2.+ ¶":fT:pw…Nþþ¶2B%,%NGNT)&<Ñ þª²º‡‡»±VÑ<þâþæþÝþÔppíÂ6) 4;2Ee>$&-&þ²^‹V ;4:Y���wÿì=B�2�@�@H?<;4788;;++((00 B#A4??@95=<8<<@@))�  MY-&&LY �?3+�39?3+�3339/Î23É2293333393933Ê2Ê2310"'663 ��!"&'# ��!2&&#"327!3254&#'##'##'5þ'ZDX<›P )þ¸þÖk²TM°tþ×þ·) QšþŠþ­þ‰þcGRKNœxRw>/×-!óâûþ÷J‡þƒT ûáôoY¬gggg¬Y�����Ť��*�‡@A  )&%!""%%�    ,+))&#''"&&* ��?223?33Î23333933333939339993393Ê2Ê2�9910!!366!36!!#'##'##'5 þf@Â% ¥‹?É j`4Çßþã…˜'R71š171š17P^ýƒov6œuý”>?;—Š/þŒýÿéþ¤X¬ffff¬X���wþ#Ë��3@  LY�LY��?+�?+�?3339310 �4$32&&#"!267!Zþ™þ„²Mâáêe[¹ZÃמ:³Nþ˃jãW¸gü':þúìþý��\þðs��3@ GY�GY��?+�?+�?3339310&��!2&#"327!Züþþ!¸­X­h~rw}ƒþÏ* PèB©©œ¬%ý ���hÿúy ��¶�?/9910%'%7%7%LGþã´´þåFÆþäG¶¶Jþå°¦{¤þÇJ;¤{¤Z¤}¤9IþĤ{¤���´{ÅÍ�� @ � ��/É3È93É210#"&543!632#‹6083mË bm69Ù+3G8u^s9H���ô× ��@  �/Ì29/39Ê2102>32#&&#"##5N…wp:in?¶ +.IJ†·œ%-&6ua1;47Ã�ÍÃX��$@  � �/Ì39393È9102&&546oGN%-%D1~·UX:5 : V‰`MY��ËÃX��$@ ���/3Ì9393È91056654.54632¶0E%-%NGNT²^‹V ; 5:Y���)þÁÁ‘� ��(�6�D�R�_�m�š@JP4,H,,ck:&B&&^ k^VkVnod^WjgS``gIA;O7EE>LLZ-%3))"00ZZgg  ��/3Ê2/3Ê29/39/333Ê2229/333Ê22233Ê22299È93È293È2È93È2102#&&#"#62#&&#"#662#&&#"#66!2#&&#"#662#&&#"#66!2#&&#"#662#&&#"#6!2#&&#"#66é]qO�b@3/2),�!$6::$,2?@ #037;(++;3# �/3/393333339993333333210#6736673#&'5&&'5'766'677'&&&'7BF$a5‹ÑIa4‹¼GÈAÝûZB¿OÝìE±xbC¾ûE±xb›˜C{LbR×C‚&b'Z1B¿OݦGÈAÜ‚þ!Ia5‹ÑF$a5‹ÝDnXb'XúüDnXbYîFÆcbŒûxF2Ã4bEÂ���¸þV+‘��!�"�[@-! #  " LY � '!��?2Þ2Í2??993+/33Á293339993Â210!3!!!!4#!#"&'!3267¸ £sN²þ¨¼þìýZþ‹wôæíãYsce {¶ý>½×VûTýLª¾û ‘»«¤ÂgS[_øo��� þoN?�� �!�_@/  "  ! @  FY  #�??+�??399Þ2Í2/33Á293339993À210!!!!47!#"&&'!3267Ço+’þÞ‰þÙýþþ’)óæ¡Ëg  Yqgd^þFFþðüý‘¾wÙüò^ὩJ–†lN_[ùÁ��/��¾¶���N@(� LY  LY�  LY �?+�?99//3+�3+33333333910!!3 !!#535!32654&##î+þÕz8ý¤þV‰‰6h’”´OþœèÔþ7!þ—ûHefeY����¢���P@) �JYJY��JY��??+�9/39/++�33333333391035!!!3 !!#4&##32œ1yþ‡×øþýìœmhgÑÕË5ß߯þ?¤¦þœoüñA:þø���¸��ª¶���u@; � � � LY LY  �??9/Ê2++�99399399333393293999910'##!! 37'7654&##ª_]X˜sVr…þÊÓ ýD‘:šR)wîÉ>}p¤ýø¶åþ5Rou5Zmh�� þ´s��(�@A!"$"##&&* )!$##"�&"&GY ��GY��?Ê2+�???99+�9939939933992393393999910"'#!336632'"337'7654&ÅpþÏø+6¢cÆà‘^žl4—qhktf§ReŒþ;J‘SSþÎþðþÑ {v‹“‹ !´œ{dNl¥¥���/��P¶� �A@ ��  LY  LY �??+�9/3+�3333333910!!!!#53Pýž‘þoþʉ‰¶þþšþý¬Tþd����¾^� �A@ ��  FY  GY �??+�9/3+�3333333910!!!!#53¾þ�Lþ´þω‰^øÙëþ^¢ëÑ���¸þ�y¶��U@+  �LY �LY LY�??+�9/+�?+�393333393910"!!!632#"&'32654&m5Jþʘýžk•Á1™‹û™n‹J…‹¢æ ýô¶þþo ªþÑÍÃþס/ͰÄÈ��� þ ‰^��Y@-� ��� HYGY  HY�?+�3??+�9/+�93333339910%#"'3265!"!!!632‰zà“Žr-y1t}þÅ*.þÏ1þ�JKžûŠD³þþ…3£—1þ^øñ Œþü����þV¶���n@6 �  LY �� '�?33??39333333+�3/333333333933333910!!!!!#!!!þ?Ù!Ù@þR=þÕ¨þþßþþ´‹ø¾ý<Äý<ÄýBþýLªåýåý���þoX^���i@4�   � FY  #�??+�?3?33933333/333333393333333910!!!!#!!!ðŽ;þd þîþVþäþVþºÃþd;š?ýáýèþ™ý‘7ýÉ7ýÉFû¢�ÿÿ�^þ×Ë&±���ž��ÿÿ�Nþ#s&Ñ���1����¸þVã¶���J@$�    �LY'�??33+�?3933/333333233910!!#!!!œGþÕ¸ýîþÊ6 Jýë1 ýLªåý¶ý<ÄýBý��� þo5^���M@& �  �  FY#�??+�3??3933/333333233910!3!#!!}PþE)úþî‰þ7þÏ1#^ýèþ™ý‘7ýÉ^ýáýÁ���¸��P¶��a@/  ��  ��  �?3?9333323339333333�9999910!!773!!#j|þÊ6z†Xþþ ÿ�†dZýö¶ýc¬bºGýyüÑœÞ��� ��Ë^��U@)        ��?2?39333333Ê2233333�999910!737!!#'! c‘¸<þEâþº×‘aþä^ýá{<éýèýº °dyýÉ����%¶��`@/      �LY�� �?33?9/3+�3393933333333322931035!3#7!!!#‰6‰‰zŒXþþ þƒþʉ/‡‡þþè¬ñýyüÑh^ýö1�����ö��h@3    �JY�� ��??9/3+�3?39393333333933339931035!!!37!!!#œ1;þÅ…9XþD×þ þ¾ƒþÏœs¡¡Çþ²þªTþý‡Åiþ¤¬�����ݶ���Q@'  �  � ��LY��?+�3?3939333333Ä2339910!7!!!!{{‹Xþþ þƒþËþº¶ýc¬ñýyüÑh^ýö´ûL�����^� � �F@"� � � FY��?+�??3933Ä23333333310!!!!!¬;þFáþ»þ7þãþœÄ^ýáýèýº7ýÉyü‡���¸þV‘¶���F@#   ��� LY  �LY '�??3+�?39/+/3333333310!!!!!!!!f+þÕþËý½þÊ6C5¹ ýLªwý‰¶ýÃ=úJ�� þoÁ^���G@$   FY  FY #�??+�??39/+/3333333310!!!!!!!Ѫ1þíþÍþVþϬ^þR®üý‘Íþ3^û¢��¸��¬¶� ��B@!�   LY  LY �?3??+�9/+/333333310!!!!!!!¬þºþËý½þÊ6C{´ûLwý‰¶ýÃ=úJ�� ��^� ��A@    FY FY�?+�?39/+�?/333333310!!!!!!Ѫ•þœþÏþVþϬ^þR®åü‡Íþ3^û¢���¸þ�š¶� �]@/ �"!LYLY LY �?+�3?3?+�9/+�933333933910632#"&'32654&#"!!!!s¦¹"’‹û™m‡P…ƒªÕå:bþËþþÊ\#«þÓÎÃþס/Õ¨ÄÈýü´ûL¶��� þ Õ^��]@/@ �� �  HYFY  HY�?+�3?3?+�9/+�9333339Ê3310%#"'32654&##!!!!72Õyà•Žr-y1s›–þÏþ‰þÏÙP—ñ„D³ÿ�‡3£—•œþ‡yü‡^þþý���wÿ¬úÍ�)�4�z@? 22/*/$$**��65$/�*/*,2',MY'LY 22!LY MY �?Ä++�3399?+�?+�993333333993933310327#"'# ��!2&# 327&&546324#"66Íbq.BLD>t­‘h’þÊþE>8’.N\Nþ¶È±?MÇ¿»Ðþëp7>8&=J¦÷pñb"†W}‡ðþæûLó}Úãòßé{jz¯18¹���\ÿ¸ús�*�3�Ž@H" 220+0%%+++��54�+%0+0.2(.FY((GY 22 ""  GY JY �?Ä+�3+�3/399?+�9/+�9933333339393933310327#"'#"��32&#"327&&546324&#"6ÝVN*;@HT“b†íþåù*y0CX8ohol *¦¥˜²ñ,-ZLgüvº4ÓV"74䦸˜¨OM§±¹¥9Hƒ~W@�ÿÿ�wþÑË&�&���9��ÿÿ�\þÝs&�F��� ����)þVy¶� �6@ �� LY�LY'�??+�?+�393333310!!!!!!ì+þÕþÊþsPþs ýLª´þþ��/þo=^� �:@   FY FY#�??+�?+�333933310!!!!!5=þ’þîþÏþ‘^åýfý‘yåÿÿ����þ¶�<�����þ˜^��5@�  �??33?33333933310!!367!åþÍþNP° $"²NþMþì^þI<´`øû¢������þ¶��^@.  ��� LY�  �??39/3939+�33333933339910!!!!!!5!1Nþ?þÁþÌþÁ?þP\Züƒ)þþþò‡����þ˜^��L@%    FY�??33+�3?33339939333310!!!!5!!367!þÝþÍþÝ#þNP° $"²NþMåþùå^þI<´`øû¢���þVɶ���^@.�      �LY'�??3+�3?39333/33332993239333910!!#!!!¤%þÕªþ¬þ¬þ´åþ:V;5Nþ5î ýLª)ý×òÄýòý+ý�� þo^����g@3�   �� FY  #�?3??+�39333?/33333399339399910!!!!#!!3…þ˜ZÙÛZþ”çþîµëìþ¦Œ ;#þœdýÝþ¤ý‘þ��)þVH¶��K@%�@ �  ' �LY �LY��?+�?3+�39/?333993Ê310!!!!!!!!˜þ‘;þi6+þÕ´þþüN´ûTýLª���/þo7^��D@"   FY FY  #�??+�3?3+�3333993Â310!!!!!!#5…þÙ–1þîüþ^åýlyüý‘yå���mþVF¶���@@  ��� LY  �LY'�??+�?39/+/333333310!!!#"&5!3267!+þÕþÊšÍ]Ñã5buR£w6¸ ýLª54&ɶ\ýüjk!)úJ��{þo²^���@@  FY FY #�??+�?39/+/3333333103267!!!!#"&5¬‡X—M1þîþÏj¶U·ÈÄ^þg’( ãüý‘¼8.»­ û¢��m��¶��J@$    LY��?3?9/333+�3933333933310#"&5!367!!#q((Ñã5bm…Y–6þÊn…Ýɶ\ýüneHþ 3úJ5-þ¼��{�� ^��J@$    FY��?3?9/333+�3933333933310#"&5!33367!!#F3·È1‡}N^1þÏiC}Z»­ þg’�ñ)ãû¢¼6ò��¸��f¶��+@ LY ��??39/+3333310!6632!4&#"!¸6“Ö[ÎæþËbuO§vþʶýË3'Ǹý¤jk *ýq�� ��Å^��-@�  FY ��?3?9/+3333310!4#"!!6632“‡«þÏ1j´W·È¤‡Hþ^þD8.»­þ`����ÿìòÍ�!�(�c@3% &*) $�""LY%@ LY ��LY�?+�3?9/3+�È3+�99333333933310%2$7# �#"&547333�! �!"!4&bŠLn}þã¬þÂþ‚?£¥5ê`)%d%\[ûÕ Ò•ŸÅ å¶î]DþêKBU6ŠztYHX8þuþ|GÁÈݳŸ°¢���ÿì`s��%�g@5"�#'&$! !  FY�JY"@��JY�?+�?9/È3+�3+�99393333339333106$32�!3267# �' 54733%"!&&N!Ûòý•‡j»bN¾†þýþÏþ¸)Í`%^| Ãw®Ûêþó’+-ì'(òà`E75Nìsyp|���þVòÍ�$�+�}@@#$$))!( - ,$''%%LY( @LY�"MY�"�?3+�3?9/3+�È3+�99?3333933339939310&�#"&547333�! �!32$7!"!4&¼öþÛ?£¥5ê`)%d%\[ûÕ Ò¼ŠLnmÖ~þןŠå¶(IŠztYHX8þuþ|GÁÈ]Dþê@> þdu³Ÿ°¢���þo`s� �'�‚@C %%$  ) ( #&##!FY JY$@  �!JY�?+�?9/È3+�3+�9939?3333933339939310&&' 5473336$32�!3267!"!&&ݵÑþ¸)Í`!Ûòý•‡j»b²þí…^| Ãw(øÅà`E75NÛêþó’+-ì? þ+syp|ÿÿ�B��Û¶�,��ÿÿ����‹‘&°��6uR�³&�+5ÿÿ����ü?&Ð��6/���³&�+5��¸þ�®¶��X@,  !  LY� �MY  �??39/3+�3?+�33333393399310"!!7!32#"&'32654&¨KoþÊ6‘‰Xý¿È/”‹û™n‹J…ŽŸâþ�¶ý@ÏñýPœþäÁÃþס/ͰÃÉ�� þ ø^��X@,� ��GY  HY�?+�3??39/3+�33333339939310%#"'32654&#"!!!2�øyà•Žr-y1t~ž™2zþÏ1²Xþ'åD³ÿ�‡3¥•” þ¨^þíþ þÛ��þV‹¶���I@%  �'LY LYLY�?+�?+�?+�?/333Á2939310!!!'"'5326!!!=þËþš>_¶›T@:35>7[ ›N²þ¨u´†þþc¨þaW ûTýLª���þo´^���N@(���FY HY �FY#�??+�?+�3?+/33Á29339310%!!!!#"'5326!‰+‘þ݉þÏþç \™|jD119M=N ßý‘yþ‰þ¥ ô¤Oû¢��¸þ�f¶��C@" ��LY  LY�?+�3??39/+333333910%#"'3265!!!!!f†÷¡¿…K„R~Žý½þÊ6C5Z±þì•/Á¬úý‰¶ýÃ=��� þ ¬^��E@"  HY �FY�??39/+�?+�3333Ä33310!!!!�#"&'3267ÑþÏ1ª1þøèLv@prloÍþ3^þR®û¹÷þê :”ž���¸þV´¶���L@&   ��� LY  �LY '�??3+�?39/+/33Á293333310!!!!!!!!fN²þ¨¼þËý½þÊ6C5¹ ýLªwý‰¶ýÃ=úJ�� þo×^���M@'    FY  FY #�??+�??39/+/33Á293333310!!!!!!!Ѫ1+‘þ݉þÏþVþϬ^þR®üý‘Íþ3^û¢��mþV¶��=@��  LY  'LY�?+�??39/+333333310!!!3#"&5!3267!þþþÕ÷šÍ]Ñã5buR£w6þV´+4&ɶ\ýüjk!)��{þo ^��=@  FY  # FY �?+�??39/+3333333103267!!!35#"&5¬‡X—M1þüþíæj¶U·È^þg’( ãû¢þopÝ8.»­ ��¸þV!¶���T@*�   '   LY��?33+�?3993?/33Á2933393310!#!!3!!!!46#!#þ  þë¦Zo¦N²þ¨¼þß þ‡L{þ¢uýX¶û¢^ûTýLª´1€û‡��� þoL^���U@+ ��� �FY#�??+�?3?3993/33Á2933393310%!!!#&''!!>!!+’þÞ‰þã6+ÆÙÉ+1þä¤À3 !%,±  ßý‘q>Ólþ ønÇDü^þ#MÈG–ƒn²û¢�ÿÿ�B��Û¶�,��ÿÿ����…‘&�$��6�uR�³&�+5ÿÿ�Vÿì\?&�D��6)��³&&�+5ÿÿ����…V&�$���j�VR� ´#&�+55ÿÿ�Vÿì;&�D���jû�� ´8&�+55ÿÿ����%¶�ˆ��ÿÿ�Vÿìþu�¨��ÿÿ�v��A‘&�(��6�R�³&�+5ÿÿ�\ÿìb?&�H��6��³&�+5��¤ÿìÍ���=@ LY  LY �LY�?+�?+�9/+33333310"6$3 ��! �5!&&267!3”þÁp‹£Zƒþ”þ´þ¨þ¢+ Ó•£Ã ý´Ë[G SEþnþžþžþu‡‡HÀÉü#¶›¯¢���\ÿìws���=@ JY  �JY FY�?+�?+�9/+33333310%267! ��!"�55!&&#"566Zcv þ>t<6þåÿ�ñþñè–†c¹kX¾Åvun}®þÕþïþéþÌ 𔂒&2ì,$ÿÿ�¤ÿìV&á���j�îR� ´1&�+55ÿÿ�\ÿìw&â���jÿ�� ´1&�+55ÿÿ����‹V&°���jXR� ´'&�+55ÿÿ����ü&Ð���j��� ´'&�+55ÿÿ�^ÿì×V&±���j�-R� ´<&�+55ÿÿ�Nÿì#&Ñ���jÎ�� ´>&�+55��9ÿìj¶��P@( ��MY  MY LY�?+�9?+�9/+�33339939310!!! '32654&##hýç¿þPñ�þ»þ×þýÀ]ëh§¥ÐÏ{Z\�Æþd ÜÄÐîO,5irf_���9þV^��R@) �FY  GY FY�?+�9?+�9/+�33333939310!5!#"'32654&##•ý²ÇþFíúþîÁûÀ\ãež¦ÊÆvöéÆþbþà—ßxP-3‡Šƒÿÿ�¸��Ýþ&²��M�ÛR�³&�+5ÿÿ� ��#¬&Ò��Mu��³&�+5ÿÿ�¸��ÝV&²���j�ÝR� ´%&�+55ÿÿ� ��#&Ò���ju�� ´#&�+55ÿÿ�wÿìçV&�2���j�ÃR� ´+&�+55ÿÿ�\ÿì˜&�R���j�� ´/&�+55��wÿìçÍ� ���?@ ��LY LY  LY�?+�?+�9/+33339910�! ��! �267!"!&&çþ˜þ°þ°þ˜iQQeýH£½ý·¬žºà·Ýþ•þz†mmþ|ü§À½´ÉÛ®®©³��\ÿì˜s� ���?@ ��JY GY GY�?+�?+�9/+33333310�!"&5�!2267!"!&&˜þàþÿ¡ö„¡ö„ýãapþ>ndbpÂm1þïþ̰0Œþúþ�ttttœqppq�ÿÿ�wÿìçV&~���j�ÅR� ´/&�+55ÿÿ�\ÿì˜&���j �� ´1&�+55ÿÿ�Hÿì×V&Ç���j�#R� ´/&�+55ÿÿ�Jÿì¼&ç���j—�� ´/&�+55ÿÿ��ÿì9þ&½��M�1R�³&�+5ÿÿ��þ¬&�\��MÜ��³&�+5ÿÿ��ÿì9V&½���j�1R� ´)&�+55ÿÿ��þ&�\���jÜ�� ´,&�+55ÿÿ��ÿì9s&½��S�¸R� ´&&�+55ÿÿ��þ!&�\��SR�� ´)&�+55ÿÿ�m��V&Á���j�VR� ´)&�+55ÿÿ�{�� &á���j#�� ´(&�+55��¸þVT¶� �/@  LY LY'�??+�?+3333310!!!!Týš+þÕþʶÿ�üTýLª¶��� þo¤^� �/@  FY FY#�??+�?+3333310!!!!¤þ-þîþÏ^åýfý‘^ÿÿ�¸��‡V&Å���j5R� ´-&�+55ÿÿ� ��-&å���j�ú��� ´,&�+55ÿÿ�/þP¶&›��€�ì��� ·�>+5ÿÿ�þ¾^&œ���¼��� ·�>+5ÿÿ��þ²¶�&�;��€‰���±¸ÿæ´�>+5�ÿÿ� þ ^�&�[��ã���±¸ÿü´>+5������V¶��a@1      �LY���?3?9/3+�3399333393393333310!!!!!!!!q)þ…V;5Nþ‹'þÓžþžþ¬þ¬þ´þähNýòý²þý–)ý×j��� ��–^��i@5       �FY���?3?9/3+�33993333933939393103!!3#!!#f×þàZÙÛZþÛÙÑ.þ¥ëìþ¦+Ϩ¶þœdþJåþ=þÃ���\��b¶� ��4@�� LYLY�??+�9/+33333104$!3!! #"33\8{5þVý¤ÑP´“’hÉÔè1úJ‡Yefe�ÿÿ�\ÿìq�G����\ÿ캶��&�S@)&&� ( ��' ##LYLY�??9/+�39/+�3933339339104$!3!3265!#"&'#"&#"3265\* s5OVZN1ðílÁ'+®}èï¨H‰][Tb¶Ø÷1û¹BAfqþ-ÃÎN=?Jë®il`fA;���\ÿìÉ� �,�W@- $ .*- � �(GYHY��!GY��?+�3+�?+�999/?333393910"�323&&5!32655!#"&''26754&#"^÷þõÙÃËj 1PXWK-ëèx˜>.ÄZofjqÉb(6¤&*fûiKFfqùþÁÄÍ=L7Ró‰¢!¶šþ®¥¥���ÿì Ë�(�R@)��$ *$)MY&&!MY& LY�?+�?+�9/3/+�9333933339103265!#"&54&##53 54&#"'6!2ô¦–±¶SUYO1ðéêôùªªXkqœ™›Èæo‰À$«‘eYfqþ-ÅÌäÚjmÙÑNXdλ���9ÿì\s�(�V@+'" * )('('JY (( HY  FY �?+�?+�99//9+�9333933339102654&#"'663232655!#"&54&##5“ž‡erM²OZxׄËòÑí¨WK-ëäÝü’ˆš°8=66%"Õ.& ‰½9 '½zfqùþÁÅÌ™efÓ���þVsË��[@.��  !  MY MY LY  '�??+�?+�9/+�99333333933910!!!4&##53 54#"'6!2¦–±¶+þÕþÊÑȶ¶uî§¥›Ñ*ño‰À$«‘ ýLªªjmÙѦdλ��Nþo-s�"�]@/" $ #!""!JY "" FY# FY �?+�??+�9/9+�99333333939102654&#"'6632!!!4&##5²ªjzMÃPZwàŠÑüÑoþîþ×™¡¤°8=66&!Õ-' ‰½9 "}egý‘FNIÓ��ÿê–¶�!�D@!�   #" LY LY �?3+�3?+�9/3339933103265!#"&5!'"'5326!OWZN2ðêëòþÃ>_¶›T@:35>7[ rwHCfqþ-ÅÌÈÃ=†þþc¨þaW ���ÿìá^��D@!� ! FYHY �?3+�3?+�9/33399331032655!#"&5##"'5326!jPXWK-ëäëîú \™|jD119M=/yJCfqùþÁÅÌÈÅ�þ‰þ¥ ô¤O���¸ÿ쪶��Z@-�  ��LY LY �?+�??99//+�3933333333933103265!#"&'!!!!!=IUUI1ëäæëýæþÊ65}KFfqþ-ÄÍÈÁý‰¶ýÃ=��� ÿì^��Z@-  FY  HY�?+�??99//+�393333333393310!!32655!#"&'5!!Ñ•2NQUI-éâçêþkþÏ^þR®ýHCfqùþÁÆËÉÂVþ3^��wÿìðË��@@! LY�LY�� LY�?+�?9/++�333393910!�! �4$3 &&#"32655!5»þ¯þ»þœþ¯Mãäkr¿h½×ÙÓš®þ‹5{þšþ˜ŠgåTµkú9*þøêëþ§—��\ÿìòs��@@!   GY�FY�� GY�?+�?9/++�333393910!! ��!2&&#"!265!–\ý¼þæþÈE,âÄ\KµH£›“þܘ]ý±*0Vê#'§³þºtc���)ÿìb¶��?@� � LY  LY �?+�?9/+�3333939103265!#"&5!!!ìKVXL1íæëîþsPþs}KFfqþ-ÅÌ˾?þþ���/ÿìF^��?@  FY HY�?+�?9/+�333393910!32655!#"&'!5=þ’PXVL-ëäéîþ‘^åþKFfqùþÁÅÌÇÄå���XÿìÑË�(�R@)% ��"*"")%MYLY LY�?+�?+�9/+�93333933991046632&#"33#"32$7! $54675&&…ŠúŸ°v‡ÀÎ……Õèz‰ê릪€ aÁþ¿þßþ¶Ì·Ÿ·`i§[COåwQKfXòhaga1/þíOêÊ’·¹��Nÿì%s�$�N@'!! &%$$JY$$ FY FY �?+�?+�9/+�93339993310#"!267! $54675&54$32&#"3H¨’“�oáX¬þúþöþç€Õëoæ[R©­ã…°ÓDIy.(ôM¥¤k† 1ј,(ÕGhB7ÿÿ�þb¶�&µ��€9��� ·���>+5ÿÿ��þ®^�&Õ��…��� ·���>+5ÿÿ��þR…¼&�$���gD��ÿÿ�VþR;u&�D���gÇ��ÿÿ����…ö&�$��f#R�³&�+5ÿÿ�Vÿì;¤&�D��fË���³'&�+5ÿÿ����…Ñ&�$��w!R� ´&�+55ÿÿ�Vÿìþ&�D��wÅ��� ´)&�+55ÿÿ����…Ñ&�$��xR� ´&�+55ÿÿÿÓÿì;&�D��xÇ��� ´0&�+55ÿÿ����…J&�$��y!R� ´'&�+55ÿÿ�Vÿì¨ø&�D��yÉ��� ´<&�+55ÿÿ����…b&�$��zR� ´,&�+55ÿÿ�Vÿì;&�D��zÅ��� ´A&�+55ÿÿ��þR…s&�$���'K�XRgD��� ´&�+5ÿÿ�VþR; &�D���&KûÿgÓ��� ´##&�+5ÿÿ����…&�$��{)R� ´&�+55ÿÿ�Vÿì;Á&�D��{Í��� ´.&�+55ÿÿ����…&�$��|'R� ´ &�+55ÿÿ�Vÿì;Á&�D��|Ë��� ´5&�+55ÿÿ����…X&�$��}'R� ´+&�+55ÿÿ�Vÿì;&�D��}Í��� ´@&�+55ÿÿ����…b&�$��~'R� ´&�+55ÿÿ�Vÿì;&�D��~Í��� ´,&�+55���þR…}�� ��$��!!!!&'#"&'3327432#"7jýëjþ²{ýþ“%!œF ܦ®Ïª/UUÎþ¦¦TR¦\þ¤¼úD`Ù|$€þš¸¶œ/6}÷j––GNÿÿ�VþR;+&�D���'gÉ��Nû��³/&�+5ÿÿ�¸þR¶&�(���gÛ��ÿÿ�\þRbs&�H���gÝ��ÿÿ�¸��ö&�(��fÅR�³&�+5ÿÿ�\ÿìb¤&�H��fÛ���³ &�+5ÿÿ�¸��`&�(��RÿïR�³&�+5ÿÿ�\ÿìb&�H��Rû��³ &�+5ÿÿ�¸��õÑ&�(��w¼R� ´&�+55ÿÿ�\ÿì&�H��wË��� ´"&�+55ÿÿÿÍ��Ñ&�(��xÁR� ´&�+55ÿÿÿßÿìb&�H��xÓ��� ´)&�+55ÿÿ�¸��›J&�(��y¼R� ´%&�+55ÿÿ�\ÿìªø&�H��yË��� ´5&�+55ÿÿ�¸��b&�(��z¼R� ´*&�+55ÿÿ�\ÿìb&�H��zË��� ´:&�+55ÿÿ�«þRs&�(���'KÿñRgÛ���³&�+5ÿÿ�\þTb!&�H���&Kó�gÝ��³)&�+5ÿÿ�B��Ûö&�,��fîR�³&�+5ÿÿ�u��<¤&�ó��fœ���³&�+5ÿÿ�BþRÛ¶&�,���g��ÿÿ�‘þRß&�L���g¸��ÿÿ�wþRçÍ&�2���g°��ÿÿ�\þR˜s&�R���gø��ÿÿ�wÿìçö&�2��f‘R�³&�+5ÿÿ�\ÿ오&�R��fÛ���³&�+5ÿÿ�wÿìçÑ&�2��w…R� ´&�+55ÿÿ�\ÿì &�R��wÑ��� ´ &�+55ÿÿ�wÿìçÑ&�2��x‡R� ´#&�+55ÿÿÿßÿì˜&�R��xÓ��� ´'&�+55ÿÿ�wÿìçJ&�2��y…R� ´/&�+55ÿÿ�\ÿì°ø&�R��yÑ��� ´3&�+55ÿÿ�wÿìçb&�2��z‡R� ´4&�+55ÿÿ�\ÿì˜&�R��zÕ��� ´8&�+55ÿÿ�wþRçs&�2���'g°��K�ÁR�³&�+5ÿÿ�\þR˜!&�R���'gü��K ��³#&�+5ÿÿ�wÿì×s&_���vR�³&�+5ÿÿ�\ÿìÍ!&`���v}��³$&�+5ÿÿ�wÿì×s&_���C�dR�³'&�+5ÿÿ�\ÿìÍ!&`���C¥��³,&�+5ÿÿ�wÿì×ö&_��f¦R�³"&�+5ÿÿ�\ÿìͤ&`��fç���³'&�+5ÿÿ�wÿì×`&_��R�ËR�³"&�+5ÿÿ�\ÿìÍ&`��R��³'&�+5ÿÿ�wþR×&_���g²��ÿÿ�\þRÍ&`���gþ��ÿÿ�®þR^¶&�8���g‡��ÿÿ�šþR¢^&�X���g��ÿÿ�®ÿì^ö&�8��f^R�³&�+5ÿÿ�šÿ좤&�X��fø���³&�+5ÿÿ�®ÿì)s&a���vR�³&&�+5ÿÿ�šÿìs!&b���v�ª���³ &�+5ÿÿ�®ÿì)s&a���C�R�³&&�+5ÿÿ�šÿìs!&b���C£��³ &�+5ÿÿ�®ÿì)ö&a��fdR�³!&�+5ÿÿ�šÿìs¤&b��fþ���³$&�+5ÿÿ�®ÿì)`&a��R�šR�³!&�+5ÿÿ�šÿìs&b��R3��³#&�+5ÿÿ�®þR)&a���g}��ÿÿ�šþRs&b���g��ÿÿ��þRþ¶&�<���gþ��ÿÿ��þ^&�\���gV��ÿÿ����þö&�<��fÙR�³ &�+5ÿÿ��þ¤&�\��f¢���³&�+5ÿÿ����þ`&�<��R�R�³ &�+5ÿÿ��þ&�\��Rà��³&�+5ÿÿ�\þ¼ &�Ó����B�Ù���ûÙþç!� ��¶ ��/2Í]210&&'5!!&&'5!þF>Ú"-!d)ýÑIÑ-!d)Ù1Ë7H­89È2H­8�ü-Ù�9� ��@   �/3Í]29/Ì10#&'#567!'673#é¢pcra¢pg;5‡YU5ñC ˜ÙK[eA‚–N«Â[nYu�û Ùÿ� ��@  �/Í]239/Í1067!#&'#7#&'53ü/pg<1~(¢arji¢X—¤@ò6Sô‚–H¤,Ae`FÃwWpY��ü-Ùÿßø�� �%@ �/3Í]29/Ì239310#'6654&#"5632#&'#567!!} 7B%+#%F^qÈ¢pcra¢pg;5‡`r=t Hþ)K[eA‚–N«�ü1Ùÿ��%�)@�� ! !!!�/3Ý]2Ì2/39/3/310".#"#663232673#&'#567!þ7$KHC(+ q kS%MHB))q j“ŽZS•ªB0€<!1o‚$0t}þºGQJN¤`E„;�ü1ÙÿÁ���@   �/Ý]2Ì3910673#%#"&'33267ý7F/Ý\sƒÏ ॺ–sXXr øi`naNž´¬¦WS^L�ü1ÙÿÁ���@   �/3Ý]2Ì910#&'53%32673#"&'þ�ƒjeÝ/FþËrY[p• ¹¤¡Ã ÝUz`i3K_WS§«³Ÿ�ü1Ùÿ�� �-@   �/33/Ì]2393Í]210#'6654#"563232673#"&'þ126k 3';5FVdþšrY[p• ¹¤¡Ã 4A)n )hC˜K_WS§«³Ÿ��ü1Ùÿ� �$�+@"@ H"     �/3Ý]2Æ2/3Ì+2/3103273#"&'%".#"#663232673üËjbÅ• ¸¥£Á $KHC+( q b\%MHB))q h;F’—ŸŠ1$.dy$0mp��� þ ����+@ ���� �/?39393393104&'3#"'5326ÍNF³OB#ŠpJR<7#-ã4mB54&#"'6632!?üpZ,PTT X¡‰ë‡×òz³¼ ÏmTP03@MHÆwZ° x³{���Nþ¨B‡�&�V@-��"( ""'PY |  $$OY$& OY %�?+�?+�9/_^]+�93333933910!"'32654&##532654#"'6!2¦–±¶þÎþäî¸UÌd™’¨¸oqªÐH•[Èã+‰À$«‘ÓëO+6hsgVíYl¦0;Õ¸��þ¨ys� ��F@"  ��PY %�??39/3+�3933393339910%#!!5!3!5467#y°þÄýŠ•°þ#VéþsÌŒü—Û2Û"0†%þÑ���dþ¨5r��P@(�OY��NY OY%�?+�?+�9/+�33333933993102�!"'3 54!"'!!76fÔûþÒþçô–OÒ^þÛ5€({7ýö#=bîÏõþøO *5èÝ Béþúþáÿÿ�HÿìPÇ�����7þ¼Pp��,@��NY�$�??+�93339310!!ã%ý/ý×þ¼°Âû�ÿÿ�HÿìJÉ�����BþªJ…��%�F@# ##��&'  PYOY&PY%�?+�?+�9/+�999399210�!"'532667##"&54�32%"32654&Jþ”þ‚CT\›Èj :˜r¿Ü æ¢ó‚ýï`lbd^†}þPþVø[ë^LõÚë˜þßÁ„|j|{Pw¤ÿÿ�)��Ž�&�I����I����RÝ…Á�#�4�@@4$,+,-0&(() �)200//)562&&,*$$)�  0))*5-*�?33/339/33/3399333/993939Ê223399333310#"'532654&'.54632&&#"##33#7#‹uoXsX-/#%lH'€r_p48<'%-*JiF‡¦¤ø ªï¨®²dq+6'#&5ÿ®�7@ÿ®�7Cÿq�7Dÿ\�7Fÿ\�7Gÿ×�7Hÿq�7Jÿ…�7ûÿ×�7ýÿ×�7ÿ®�7ÿ®�7ÿ®�7ÿ…�7 ÿ…�7Wÿš�7Xÿq�7Yÿ\�7_ÿ×�7`ÿq�7bÿš�7ÿq�7ÿ\�7ÿq�7 ÿ\�7!ÿq�7"ÿ\�7#ÿq�7%ÿq�7&ÿ\�7'ÿq�7(ÿ\�7)ÿq�7*ÿ\�7+ÿq�7,ÿ\�7-ÿq�7.ÿ\�7/ÿq�70ÿ\�71ÿq�72ÿ\�73ÿq�74ÿ\�76ÿq�78ÿq�7:ÿq�7<ÿq�7@ÿq�7Bÿq�7Dÿq�7Iÿ×�7Jÿq�7Kÿ×�7Lÿq�7Mÿ×�7Nÿq�7Oÿ×�7Qÿ×�7Rÿq�7Sÿ×�7Tÿq�7Uÿ×�7Vÿq�7Wÿ×�7Xÿq�7Yÿ×�7Zÿq�7[ÿ×�7\ÿq�7]ÿ×�7^ÿq�7_ÿ×�7`ÿq�7bÿš�7dÿš�7fÿš�7hÿš�7jÿš�7lÿš�7nÿš�7pÿ×�7�)�8�ÿ×�8�ÿ×�8�$ÿì�8�‚ÿì�8�ƒÿì�8�„ÿì�8�…ÿì�8�†ÿì�8�‡ÿì�8�Âÿì�8�Äÿì�8�Æÿì�8Cÿì�8ÿ×�8 ÿ×�8Xÿì�8ÿì�8ÿì�8!ÿì�8#ÿì�8%ÿì�8'ÿì�8)ÿì�8+ÿì�8-ÿì�8/ÿì�81ÿì�83ÿì�9�ÿš�9�ÿš�9�"�)�9�$ÿ®�9�&ÿì�9�*ÿì�9�2ÿì�9�4ÿì�9�Dÿ×�9�Fÿ×�9�Gÿ×�9�Hÿ×�9�Jÿì�9�Pÿì�9�Qÿì�9�Rÿ×�9�Sÿì�9�Tÿ×�9�Uÿì�9�Vÿì�9�Xÿì�9�‚ÿ®�9�ƒÿ®�9�„ÿ®�9�…ÿ®�9�†ÿ®�9�‡ÿ®�9�‰ÿì�9�”ÿì�9�•ÿì�9�–ÿì�9�—ÿì�9�˜ÿì�9�šÿì�9�¢ÿ×�9�£ÿ×�9�¤ÿ×�9�¥ÿ×�9�¦ÿ×�9�§ÿ×�9�¨ÿ×�9�©ÿ×�9�ªÿ×�9�«ÿ×�9�¬ÿ×�9�­ÿ×�9�´ÿ×�9�µÿ×�9�¶ÿ×�9�·ÿ×�9�¸ÿ×�9�ºÿ×�9�»ÿì�9�¼ÿì�9�½ÿì�9�¾ÿì�9�Âÿ®�9�Ãÿ×�9�Äÿ®�9�Åÿ×�9�Æÿ®�9�Çÿ×�9�Èÿì�9�Éÿ×�9�Êÿì�9�Ëÿ×�9�Ìÿì�9�Íÿ×�9�Îÿì�9�Ïÿ×�9�Ñÿ×�9�Óÿ×�9�Õÿ×�9�×ÿ×�9�Ùÿ×�9�Ûÿ×�9�Ýÿ×�9�Þÿì�9�ßÿì�9�àÿì�9�áÿì�9�âÿì�9�ãÿì�9�äÿì�9�åÿì�9�úÿì�9ÿì�9ÿì�9 ÿì�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿ×�9ÿì�9ÿì�9ÿì�9!ÿì�9+ÿì�9-ÿì�9/ÿì�91ÿì�93ÿì�95ÿì�9Cÿ®�9Dÿ×�9Fÿ×�9Gÿì�9Hÿ×�9Jÿì�9ÿš�9 ÿš�9Wÿì�9Xÿ®�9Yÿ×�9_ÿì�9`ÿ×�9bÿì�9ÿ®�9ÿ×�9ÿ®�9 ÿ×�9!ÿ®�9"ÿ×�9#ÿ®�9%ÿ®�9&ÿ×�9'ÿ®�9(ÿ×�9)ÿ®�9*ÿ×�9+ÿ®�9,ÿ×�9-ÿ®�9.ÿ×�9/ÿ®�90ÿ×�91ÿ®�92ÿ×�93ÿ®�94ÿ×�96ÿ×�98ÿ×�9:ÿ×�9<ÿ×�9@ÿ×�9Bÿ×�9Dÿ×�9Iÿì�9Jÿ×�9Kÿì�9Lÿ×�9Mÿì�9Nÿ×�9Oÿì�9Qÿì�9Rÿ×�9Sÿì�9Tÿ×�9Uÿì�9Vÿ×�9Wÿì�9Xÿ×�9Yÿì�9Zÿ×�9[ÿì�9\ÿ×�9]ÿì�9^ÿ×�9_ÿì�9`ÿ×�9bÿì�9dÿì�9fÿì�9hÿì�9jÿì�9lÿì�9nÿì�:�ÿš�:�ÿš�:�"�)�:�$ÿ®�:�&ÿì�:�*ÿì�:�2ÿì�:�4ÿì�:�Dÿ×�:�Fÿ×�:�Gÿ×�:�Hÿ×�:�Jÿì�:�Pÿì�:�Qÿì�:�Rÿ×�:�Sÿì�:�Tÿ×�:�Uÿì�:�Vÿì�:�Xÿì�:�‚ÿ®�:�ƒÿ®�:�„ÿ®�:�…ÿ®�:�†ÿ®�:�‡ÿ®�:�‰ÿì�:�”ÿì�:�•ÿì�:�–ÿì�:�—ÿì�:�˜ÿì�:�šÿì�:�¢ÿ×�:�£ÿ×�:�¤ÿ×�:�¥ÿ×�:�¦ÿ×�:�§ÿ×�:�¨ÿ×�:�©ÿ×�:�ªÿ×�:�«ÿ×�:�¬ÿ×�:�­ÿ×�:�´ÿ×�:�µÿ×�:�¶ÿ×�:�·ÿ×�:�¸ÿ×�:�ºÿ×�:�»ÿì�:�¼ÿì�:�½ÿì�:�¾ÿì�:�Âÿ®�:�Ãÿ×�:�Äÿ®�:�Åÿ×�:�Æÿ®�:�Çÿ×�:�Èÿì�:�Éÿ×�:�Êÿì�:�Ëÿ×�:�Ìÿì�:�Íÿ×�:�Îÿì�:�Ïÿ×�:�Ñÿ×�:�Óÿ×�:�Õÿ×�:�×ÿ×�:�Ùÿ×�:�Ûÿ×�:�Ýÿ×�:�Þÿì�:�ßÿì�:�àÿì�:�áÿì�:�âÿì�:�ãÿì�:�äÿì�:�åÿì�:�úÿì�:ÿì�:ÿì�: ÿì�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿ×�:ÿì�:ÿì�:ÿì�:!ÿì�:+ÿì�:-ÿì�:/ÿì�:1ÿì�:3ÿì�:5ÿì�:Cÿ®�:Dÿ×�:Fÿ×�:Gÿì�:Hÿ×�:Jÿì�:ÿš�: ÿš�:Wÿì�:Xÿ®�:Yÿ×�:_ÿì�:`ÿ×�:bÿì�:ÿ®�:ÿ×�:ÿ®�: ÿ×�:!ÿ®�:"ÿ×�:#ÿ®�:%ÿ®�:&ÿ×�:'ÿ®�:(ÿ×�:)ÿ®�:*ÿ×�:+ÿ®�:,ÿ×�:-ÿ®�:.ÿ×�:/ÿ®�:0ÿ×�:1ÿ®�:2ÿ×�:3ÿ®�:4ÿ×�:6ÿ×�:8ÿ×�::ÿ×�:<ÿ×�:@ÿ×�:Bÿ×�:Dÿ×�:Iÿì�:Jÿ×�:Kÿì�:Lÿ×�:Mÿì�:Nÿ×�:Oÿì�:Qÿì�:Rÿ×�:Sÿì�:Tÿ×�:Uÿì�:Vÿ×�:Wÿì�:Xÿ×�:Yÿì�:Zÿ×�:[ÿì�:\ÿ×�:]ÿì�:^ÿ×�:_ÿì�:`ÿ×�:bÿì�:dÿì�:fÿì�:hÿì�:jÿì�:lÿì�:nÿì�;�&ÿ×�;�*ÿ×�;�2ÿ×�;�4ÿ×�;�‰ÿ×�;�”ÿ×�;�•ÿ×�;�–ÿ×�;�—ÿ×�;�˜ÿ×�;�šÿ×�;�Èÿ×�;�Êÿ×�;�Ìÿ×�;�Îÿ×�;�Þÿ×�;�àÿ×�;�âÿ×�;�äÿ×�;ÿ×�;ÿ×�;ÿ×�;ÿ×�;Gÿ×�;_ÿ×�;Iÿ×�;Kÿ×�;Mÿ×�;Oÿ×�;Qÿ×�;Sÿ×�;Uÿ×�;Wÿ×�;Yÿ×�;[ÿ×�;]ÿ×�;_ÿ×�<�ÿ…�<�ÿ…�<�"�)�<�$ÿ…�<�&ÿ×�<�*ÿ×�<�2ÿ×�<�4ÿ×�<�Dÿš�<�Fÿš�<�Gÿš�<�Hÿš�<�Jÿ×�<�PÿÃ�<�QÿÃ�<�Rÿš�<�SÿÃ�<�Tÿš�<�UÿÃ�<�Vÿ®�<�XÿÃ�<�]ÿ×�<�‚ÿ…�<�ƒÿ…�<�„ÿ…�<�…ÿ…�<�†ÿ…�<�‡ÿ…�<�‰ÿ×�<�”ÿ×�<�•ÿ×�<�–ÿ×�<�—ÿ×�<�˜ÿ×�<�šÿ×�<�¢ÿš�<�£ÿš�<�¤ÿš�<�¥ÿš�<�¦ÿš�<�§ÿš�<�¨ÿš�<�©ÿš�<�ªÿš�<�«ÿš�<�¬ÿš�<�­ÿš�<�´ÿš�<�µÿš�<�¶ÿš�<�·ÿš�<�¸ÿš�<�ºÿš�<�»ÿÃ�<�¼ÿÃ�<�½ÿÃ�<�¾ÿÃ�<�Âÿ…�<�Ãÿš�<�Äÿ…�<�Åÿš�<�Æÿ…�<�Çÿš�<�Èÿ×�<�Éÿš�<�Êÿ×�<�Ëÿš�<�Ìÿ×�<�Íÿš�<�Îÿ×�<�Ïÿš�<�Ñÿš�<�Óÿš�<�Õÿš�<�×ÿš�<�Ùÿš�<�Ûÿš�<�Ýÿš�<�Þÿ×�<�ßÿ×�<�àÿ×�<�áÿ×�<�âÿ×�<�ãÿ×�<�äÿ×�<�åÿ×�<�úÿÃ�<ÿÃ�<ÿÃ�< ÿÃ�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿ×�<ÿš�<ÿÃ�<ÿÃ�<ÿ®�<!ÿ®�<+ÿÃ�<-ÿÃ�</ÿÃ�<1ÿÃ�<3ÿÃ�<5ÿÃ�<<ÿ×�<>ÿ×�<@ÿ×�<Cÿ…�<Dÿš�<Fÿš�<Gÿ×�<Hÿš�<Jÿ®�<ÿ…�< ÿ…�<WÿÃ�<Xÿ…�<Yÿš�<_ÿ×�<`ÿš�<bÿÃ�<ÿ…�<ÿš�<ÿ…�< ÿš�<!ÿ…�<"ÿš�<#ÿ…�<%ÿ…�<&ÿš�<'ÿ…�<(ÿš�<)ÿ…�<*ÿš�<+ÿ…�<,ÿš�<-ÿ…�<.ÿš�</ÿ…�<0ÿš�<1ÿ…�<2ÿš�<3ÿ…�<4ÿš�<6ÿš�<8ÿš�<:ÿš�<<ÿš�<@ÿš�<Bÿš�<Dÿš�<Iÿ×�<Jÿš�<Kÿ×�<Lÿš�<Mÿ×�<Nÿš�<Oÿ×�<Qÿ×�<Rÿš�<Sÿ×�<Tÿš�<Uÿ×�<Vÿš�<Wÿ×�<Xÿš�<Yÿ×�<Zÿš�<[ÿ×�<\ÿš�<]ÿ×�<^ÿš�<_ÿ×�<`ÿš�<bÿÃ�<dÿÃ�<fÿÃ�<hÿÃ�<jÿÃ�<lÿÃ�<nÿÃ�=�&ÿì�=�*ÿì�=�2ÿì�=�4ÿì�=�‰ÿì�=�”ÿì�=�•ÿì�=�–ÿì�=�—ÿì�=�˜ÿì�=�šÿì�=�Èÿì�=�Êÿì�=�Ìÿì�=�Îÿì�=�Þÿì�=�àÿì�=�âÿì�=�äÿì�=ÿì�=ÿì�=ÿì�=ÿì�=Gÿì�=_ÿì�=Iÿì�=Kÿì�=Mÿì�=Oÿì�=Qÿì�=Sÿì�=Uÿì�=Wÿì�=Yÿì�=[ÿì�=]ÿì�=_ÿì�>�-�¸�D�ÿì�D� ÿì�Dÿì�D ÿì�E�ÿì�E� ÿì�E�Yÿ×�E�Zÿ×�E�[ÿ×�E�\ÿ×�E�]ÿì�E�¿ÿ×�E7ÿ×�E<ÿì�E>ÿì�E@ÿì�Eûÿ×�Eýÿ×�Eÿì�E ÿì�Epÿ×�F��)�F� �)�F�)�F �)�H�ÿì�H� ÿì�H�Yÿ×�H�Zÿ×�H�[ÿ×�H�\ÿ×�H�]ÿì�H�¿ÿ×�H7ÿ×�H<ÿì�H>ÿì�H@ÿì�Hûÿ×�Hýÿ×�Hÿì�H ÿì�Hpÿ×�I��{�I� �{�I�{�I �{�K�ÿì�K� ÿì�Kÿì�K ÿì�N�Fÿ×�N�Gÿ×�N�Hÿ×�N�Rÿ×�N�Tÿ×�N�¢ÿ×�N�©ÿ×�N�ªÿ×�N�«ÿ×�N�¬ÿ×�N�­ÿ×�N�´ÿ×�N�µÿ×�N�¶ÿ×�N�·ÿ×�N�¸ÿ×�N�ºÿ×�N�Éÿ×�N�Ëÿ×�N�Íÿ×�N�Ïÿ×�N�Ñÿ×�N�Óÿ×�N�Õÿ×�N�×ÿ×�N�Ùÿ×�N�Ûÿ×�N�Ýÿ×�Nÿ×�Nÿ×�Nÿ×�Nÿ×�NHÿ×�N`ÿ×�N6ÿ×�N8ÿ×�N:ÿ×�N<ÿ×�N@ÿ×�NBÿ×�NDÿ×�NJÿ×�NLÿ×�NNÿ×�NRÿ×�NTÿ×�NVÿ×�NXÿ×�NZÿ×�N\ÿ×�N^ÿ×�N`ÿ×�P�ÿì�P� ÿì�Pÿì�P ÿì�Q�ÿì�Q� ÿì�Qÿì�Q ÿì�R�ÿì�R� ÿì�R�Yÿ×�R�Zÿ×�R�[ÿ×�R�\ÿ×�R�]ÿì�R�¿ÿ×�R7ÿ×�R<ÿì�R>ÿì�R@ÿì�Rûÿ×�Rýÿ×�Rÿì�R ÿì�Rpÿ×�S�ÿì�S� ÿì�S�Yÿ×�S�Zÿ×�S�[ÿ×�S�\ÿ×�S�]ÿì�S�¿ÿ×�S7ÿ×�S<ÿì�S>ÿì�S@ÿì�Sûÿ×�Sýÿ×�Sÿì�S ÿì�Spÿ×�U��R�U� �R�U�Dÿ×�U�Fÿ×�U�Gÿ×�U�Hÿ×�U�Jÿì�U�Rÿ×�U�Tÿ×�U�¢ÿ×�U�£ÿ×�U�¤ÿ×�U�¥ÿ×�U�¦ÿ×�U�§ÿ×�U�¨ÿ×�U�©ÿ×�U�ªÿ×�U�«ÿ×�U�¬ÿ×�U�­ÿ×�U�´ÿ×�U�µÿ×�U�¶ÿ×�U�·ÿ×�U�¸ÿ×�U�ºÿ×�U�Ãÿ×�U�Åÿ×�U�Çÿ×�U�Éÿ×�U�Ëÿ×�U�Íÿ×�U�Ïÿ×�U�Ñÿ×�U�Óÿ×�U�Õÿ×�U�×ÿ×�U�Ùÿ×�U�Ûÿ×�U�Ýÿ×�U�ßÿì�U�áÿì�U�ãÿì�U�åÿì�Uÿ×�Uÿ×�Uÿ×�Uÿ×�UDÿ×�UFÿ×�UHÿ×�U�R�U �R�UYÿ×�U`ÿ×�Uÿ×�U ÿ×�U"ÿ×�U&ÿ×�U(ÿ×�U*ÿ×�U,ÿ×�U.ÿ×�U0ÿ×�U2ÿ×�U4ÿ×�U6ÿ×�U8ÿ×�U:ÿ×�U<ÿ×�U@ÿ×�UBÿ×�UDÿ×�UJÿ×�ULÿ×�UNÿ×�URÿ×�UTÿ×�UVÿ×�UXÿ×�UZÿ×�U\ÿ×�U^ÿ×�U`ÿ×�W��)�W� �)�W�)�W �)�Y��R�Y� �R�Y�ÿ®�Y�ÿ®�Y�"�)�Y�R�Yÿ®�Y �R�Y ÿ®�Z��R�Z� �R�Z�ÿ®�Z�ÿ®�Z�"�)�Z�R�Zÿ®�Z �R�Z ÿ®�[�Fÿ×�[�Gÿ×�[�Hÿ×�[�Rÿ×�[�Tÿ×�[�¢ÿ×�[�©ÿ×�[�ªÿ×�[�«ÿ×�[�¬ÿ×�[�­ÿ×�[�´ÿ×�[�µÿ×�[�¶ÿ×�[�·ÿ×�[�¸ÿ×�[�ºÿ×�[�Éÿ×�[�Ëÿ×�[�Íÿ×�[�Ïÿ×�[�Ñÿ×�[�Óÿ×�[�Õÿ×�[�×ÿ×�[�Ùÿ×�[�Ûÿ×�[�Ýÿ×�[ÿ×�[ÿ×�[ÿ×�[ÿ×�[Hÿ×�[`ÿ×�[6ÿ×�[8ÿ×�[:ÿ×�[<ÿ×�[@ÿ×�[Bÿ×�[Dÿ×�[Jÿ×�[Lÿ×�[Nÿ×�[Rÿ×�[Tÿ×�[Vÿ×�[Xÿ×�[Zÿ×�[\ÿ×�[^ÿ×�[`ÿ×�\��R�\� �R�\�ÿ®�\�ÿ®�\�"�)�\�R�\ÿ®�\ �R�\ ÿ®�^�-�¸�‚�ÿq�‚� ÿq�‚�&ÿ×�‚�*ÿ×�‚�- �‚�2ÿ×�‚�4ÿ×�‚�7ÿq�‚�9ÿ®�‚�:ÿ®�‚�<ÿ…�‚�‰ÿ×�‚�”ÿ×�‚�•ÿ×�‚�–ÿ×�‚�—ÿ×�‚�˜ÿ×�‚�šÿ×�‚�Ÿÿ…�‚�Èÿ×�‚�Êÿ×�‚�Ìÿ×�‚�Îÿ×�‚�Þÿ×�‚�àÿ×�‚�âÿ×�‚�äÿ×�‚ÿ×�‚ÿ×�‚ÿ×�‚ÿ×�‚$ÿq�‚&ÿq�‚6ÿ®�‚8ÿ…�‚:ÿ…�‚Gÿ×�‚úÿ®�‚üÿ®�‚þÿ®�‚�ÿ…�‚ÿq�‚ ÿq�‚_ÿ×�‚Iÿ×�‚Kÿ×�‚Mÿ×�‚Oÿ×�‚Qÿ×�‚Sÿ×�‚Uÿ×�‚Wÿ×�‚Yÿ×�‚[ÿ×�‚]ÿ×�‚_ÿ×�‚oÿ…�‚qÿ…�‚sÿ…�‚ÿq�ƒ�ÿq�ƒ� ÿq�ƒ�&ÿ×�ƒ�*ÿ×�ƒ�- �ƒ�2ÿ×�ƒ�4ÿ×�ƒ�7ÿq�ƒ�9ÿ®�ƒ�:ÿ®�ƒ�<ÿ…�ƒ�‰ÿ×�ƒ�”ÿ×�ƒ�•ÿ×�ƒ�–ÿ×�ƒ�—ÿ×�ƒ�˜ÿ×�ƒ�šÿ×�ƒ�Ÿÿ…�ƒ�Èÿ×�ƒ�Êÿ×�ƒ�Ìÿ×�ƒ�Îÿ×�ƒ�Þÿ×�ƒ�àÿ×�ƒ�âÿ×�ƒ�äÿ×�ƒÿ×�ƒÿ×�ƒÿ×�ƒÿ×�ƒ$ÿq�ƒ&ÿq�ƒ6ÿ®�ƒ8ÿ…�ƒ:ÿ…�ƒGÿ×�ƒúÿ®�ƒüÿ®�ƒþÿ®�ƒ�ÿ…�ƒÿq�ƒ ÿq�ƒ_ÿ×�ƒIÿ×�ƒKÿ×�ƒMÿ×�ƒOÿ×�ƒQÿ×�ƒSÿ×�ƒUÿ×�ƒWÿ×�ƒYÿ×�ƒ[ÿ×�ƒ]ÿ×�ƒ_ÿ×�ƒoÿ…�ƒqÿ…�ƒsÿ…�ƒÿq�„�ÿq�„� ÿq�„�&ÿ×�„�*ÿ×�„�- �„�2ÿ×�„�4ÿ×�„�7ÿq�„�9ÿ®�„�:ÿ®�„�<ÿ…�„�‰ÿ×�„�”ÿ×�„�•ÿ×�„�–ÿ×�„�—ÿ×�„�˜ÿ×�„�šÿ×�„�Ÿÿ…�„�Èÿ×�„�Êÿ×�„�Ìÿ×�„�Îÿ×�„�Þÿ×�„�àÿ×�„�âÿ×�„�äÿ×�„ÿ×�„ÿ×�„ÿ×�„ÿ×�„$ÿq�„&ÿq�„6ÿ®�„8ÿ…�„:ÿ…�„Gÿ×�„úÿ®�„üÿ®�„þÿ®�„�ÿ…�„ÿq�„ ÿq�„_ÿ×�„Iÿ×�„Kÿ×�„Mÿ×�„Oÿ×�„Qÿ×�„Sÿ×�„Uÿ×�„Wÿ×�„Yÿ×�„[ÿ×�„]ÿ×�„_ÿ×�„oÿ…�„qÿ…�„sÿ…�„ÿq�…�ÿq�…� ÿq�…�&ÿ×�…�*ÿ×�…�- �…�2ÿ×�…�4ÿ×�…�7ÿq�…�9ÿ®�…�:ÿ®�…�<ÿ…�…�‰ÿ×�…�”ÿ×�…�•ÿ×�…�–ÿ×�…�—ÿ×�…�˜ÿ×�…�šÿ×�…�Ÿÿ…�…�Èÿ×�…�Êÿ×�…�Ìÿ×�…�Îÿ×�…�Þÿ×�…�àÿ×�…�âÿ×�…�äÿ×�…ÿ×�…ÿ×�…ÿ×�…ÿ×�…$ÿq�…&ÿq�…6ÿ®�…8ÿ…�…:ÿ…�…Gÿ×�…úÿ®�…üÿ®�…þÿ®�…�ÿ…�…ÿq�… ÿq�…_ÿ×�…Iÿ×�…Kÿ×�…Mÿ×�…Oÿ×�…Qÿ×�…Sÿ×�…Uÿ×�…Wÿ×�…Yÿ×�…[ÿ×�…]ÿ×�…_ÿ×�…oÿ…�…qÿ…�…sÿ…�…ÿq�†�ÿq�†� ÿq�†�&ÿ×�†�*ÿ×�†�- �†�2ÿ×�†�4ÿ×�†�7ÿq�†�9ÿ®�†�:ÿ®�†�<ÿ…�†�‰ÿ×�†�”ÿ×�†�•ÿ×�†�–ÿ×�†�—ÿ×�†�˜ÿ×�†�šÿ×�†�Ÿÿ…�†�Èÿ×�†�Êÿ×�†�Ìÿ×�†�Îÿ×�†�Þÿ×�†�àÿ×�†�âÿ×�†�äÿ×�†ÿ×�†ÿ×�†ÿ×�†ÿ×�†$ÿq�†&ÿq�†6ÿ®�†8ÿ…�†:ÿ…�†Gÿ×�†úÿ®�†üÿ®�†þÿ®�†�ÿ…�†ÿq�† ÿq�†_ÿ×�†Iÿ×�†Kÿ×�†Mÿ×�†Oÿ×�†Qÿ×�†Sÿ×�†Uÿ×�†Wÿ×�†Yÿ×�†[ÿ×�†]ÿ×�†_ÿ×�†oÿ…�†qÿ…�†sÿ…�†ÿq�‡�ÿq�‡� ÿq�‡�&ÿ×�‡�*ÿ×�‡�- �‡�2ÿ×�‡�4ÿ×�‡�7ÿq�‡�9ÿ®�‡�:ÿ®�‡�<ÿ…�‡�‰ÿ×�‡�”ÿ×�‡�•ÿ×�‡�–ÿ×�‡�—ÿ×�‡�˜ÿ×�‡�šÿ×�‡�Ÿÿ…�‡�Èÿ×�‡�Êÿ×�‡�Ìÿ×�‡�Îÿ×�‡�Þÿ×�‡�àÿ×�‡�âÿ×�‡�äÿ×�‡ÿ×�‡ÿ×�‡ÿ×�‡ÿ×�‡$ÿq�‡&ÿq�‡6ÿ®�‡8ÿ…�‡:ÿ…�‡Gÿ×�‡úÿ®�‡üÿ®�‡þÿ®�‡�ÿ…�‡ÿq�‡ ÿq�‡_ÿ×�‡Iÿ×�‡Kÿ×�‡Mÿ×�‡Oÿ×�‡Qÿ×�‡Sÿ×�‡Uÿ×�‡Wÿ×�‡Yÿ×�‡[ÿ×�‡]ÿ×�‡_ÿ×�‡oÿ…�‡qÿ…�‡sÿ…�‡ÿq�ˆ�-�{�‰�&ÿ×�‰�*ÿ×�‰�2ÿ×�‰�4ÿ×�‰�‰ÿ×�‰�”ÿ×�‰�•ÿ×�‰�–ÿ×�‰�—ÿ×�‰�˜ÿ×�‰�šÿ×�‰�Èÿ×�‰�Êÿ×�‰�Ìÿ×�‰�Îÿ×�‰�Þÿ×�‰�àÿ×�‰�âÿ×�‰�äÿ×�‰ÿ×�‰ÿ×�‰ÿ×�‰ÿ×�‰Gÿ×�‰_ÿ×�‰Iÿ×�‰Kÿ×�‰Mÿ×�‰Oÿ×�‰Qÿ×�‰Sÿ×�‰Uÿ×�‰Wÿ×�‰Yÿ×�‰[ÿ×�‰]ÿ×�‰_ÿ×�Š�-�{�‹�-�{�Œ�-�{��-�{�’�ÿ®�’�ÿ®�’�$ÿ×�’�7ÿÃ�’�9ÿì�’�:ÿì�’�;ÿ×�’�<ÿì�’�=ÿì�’�‚ÿ×�’�ƒÿ×�’�„ÿ×�’�…ÿ×�’�†ÿ×�’�‡ÿ×�’�Ÿÿì�’�Âÿ×�’�Äÿ×�’�Æÿ×�’$ÿÃ�’&ÿÃ�’6ÿì�’8ÿì�’:ÿì�’;ÿì�’=ÿì�’?ÿì�’Cÿ×�’ ÿì�’úÿì�’üÿì�’þÿì�’�ÿì�’ÿ®�’ ÿ®�’Xÿ×�’ÿ×�’ÿ×�’!ÿ×�’#ÿ×�’%ÿ×�’'ÿ×�’)ÿ×�’+ÿ×�’-ÿ×�’/ÿ×�’1ÿ×�’3ÿ×�’oÿì�’qÿì�’sÿì�’ÿÃ�”�ÿ®�”�ÿ®�”�$ÿ×�”�7ÿÃ�”�9ÿì�”�:ÿì�”�;ÿ×�”�<ÿì�”�=ÿì�”�‚ÿ×�”�ƒÿ×�”�„ÿ×�”�…ÿ×�”�†ÿ×�”�‡ÿ×�”�Ÿÿì�”�Âÿ×�”�Äÿ×�”�Æÿ×�”$ÿÃ�”&ÿÃ�”6ÿì�”8ÿì�”:ÿì�”;ÿì�”=ÿì�”?ÿì�”Cÿ×�” ÿì�”úÿì�”üÿì�”þÿì�”�ÿì�”ÿ®�” ÿ®�”Xÿ×�”ÿ×�”ÿ×�”!ÿ×�”#ÿ×�”%ÿ×�”'ÿ×�”)ÿ×�”+ÿ×�”-ÿ×�”/ÿ×�”1ÿ×�”3ÿ×�”oÿì�”qÿì�”sÿì�”ÿÃ�•�ÿ®�•�ÿ®�•�$ÿ×�•�7ÿÃ�•�9ÿì�•�:ÿì�•�;ÿ×�•�<ÿì�•�=ÿì�•�‚ÿ×�•�ƒÿ×�•�„ÿ×�•�…ÿ×�•�†ÿ×�•�‡ÿ×�•�Ÿÿì�•�Âÿ×�•�Äÿ×�•�Æÿ×�•$ÿÃ�•&ÿÃ�•6ÿì�•8ÿì�•:ÿì�•;ÿì�•=ÿì�•?ÿì�•Cÿ×�• ÿì�•úÿì�•üÿì�•þÿì�•�ÿì�•ÿ®�• ÿ®�•Xÿ×�•ÿ×�•ÿ×�•!ÿ×�•#ÿ×�•%ÿ×�•'ÿ×�•)ÿ×�•+ÿ×�•-ÿ×�•/ÿ×�•1ÿ×�•3ÿ×�•oÿì�•qÿì�•sÿì�•ÿÃ�–�ÿ®�–�ÿ®�–�$ÿ×�–�7ÿÃ�–�9ÿì�–�:ÿì�–�;ÿ×�–�<ÿì�–�=ÿì�–�‚ÿ×�–�ƒÿ×�–�„ÿ×�–�…ÿ×�–�†ÿ×�–�‡ÿ×�–�Ÿÿì�–�Âÿ×�–�Äÿ×�–�Æÿ×�–$ÿÃ�–&ÿÃ�–6ÿì�–8ÿì�–:ÿì�–;ÿì�–=ÿì�–?ÿì�–Cÿ×�– ÿì�–úÿì�–üÿì�–þÿì�–�ÿì�–ÿ®�– ÿ®�–Xÿ×�–ÿ×�–ÿ×�–!ÿ×�–#ÿ×�–%ÿ×�–'ÿ×�–)ÿ×�–+ÿ×�–-ÿ×�–/ÿ×�–1ÿ×�–3ÿ×�–oÿì�–qÿì�–sÿì�–ÿÃ�—�ÿ®�—�ÿ®�—�$ÿ×�—�7ÿÃ�—�9ÿì�—�:ÿì�—�;ÿ×�—�<ÿì�—�=ÿì�—�‚ÿ×�—�ƒÿ×�—�„ÿ×�—�…ÿ×�—�†ÿ×�—�‡ÿ×�—�Ÿÿì�—�Âÿ×�—�Äÿ×�—�Æÿ×�—$ÿÃ�—&ÿÃ�—6ÿì�—8ÿì�—:ÿì�—;ÿì�—=ÿì�—?ÿì�—Cÿ×�— ÿì�—úÿì�—üÿì�—þÿì�—�ÿì�—ÿ®�— ÿ®�—Xÿ×�—ÿ×�—ÿ×�—!ÿ×�—#ÿ×�—%ÿ×�—'ÿ×�—)ÿ×�—+ÿ×�—-ÿ×�—/ÿ×�—1ÿ×�—3ÿ×�—oÿì�—qÿì�—sÿì�—ÿÃ�˜�ÿ®�˜�ÿ®�˜�$ÿ×�˜�7ÿÃ�˜�9ÿì�˜�:ÿì�˜�;ÿ×�˜�<ÿì�˜�=ÿì�˜�‚ÿ×�˜�ƒÿ×�˜�„ÿ×�˜�…ÿ×�˜�†ÿ×�˜�‡ÿ×�˜�Ÿÿì�˜�Âÿ×�˜�Äÿ×�˜�Æÿ×�˜$ÿÃ�˜&ÿÃ�˜6ÿì�˜8ÿì�˜:ÿì�˜;ÿì�˜=ÿì�˜?ÿì�˜Cÿ×�˜ ÿì�˜úÿì�˜üÿì�˜þÿì�˜�ÿì�˜ÿ®�˜ ÿ®�˜Xÿ×�˜ÿ×�˜ÿ×�˜!ÿ×�˜#ÿ×�˜%ÿ×�˜'ÿ×�˜)ÿ×�˜+ÿ×�˜-ÿ×�˜/ÿ×�˜1ÿ×�˜3ÿ×�˜oÿì�˜qÿì�˜sÿì�˜ÿÃ�š�ÿ®�š�ÿ®�š�$ÿ×�š�7ÿÃ�š�9ÿì�š�:ÿì�š�;ÿ×�š�<ÿì�š�=ÿì�š�‚ÿ×�š�ƒÿ×�š�„ÿ×�š�…ÿ×�š�†ÿ×�š�‡ÿ×�š�Ÿÿì�š�Âÿ×�š�Äÿ×�š�Æÿ×�š$ÿÃ�š&ÿÃ�š6ÿì�š8ÿì�š:ÿì�š;ÿì�š=ÿì�š?ÿì�šCÿ×�š ÿì�šúÿì�šüÿì�šþÿì�š�ÿì�šÿ®�š ÿ®�šXÿ×�šÿ×�šÿ×�š!ÿ×�š#ÿ×�š%ÿ×�š'ÿ×�š)ÿ×�š+ÿ×�š-ÿ×�š/ÿ×�š1ÿ×�š3ÿ×�šoÿì�šqÿì�šsÿì�šÿÃ�›�ÿ×�›�ÿ×�›�$ÿì�›�‚ÿì�›�ƒÿì�›�„ÿì�›�…ÿì�›�†ÿì�›�‡ÿì�›�Âÿì�›�Äÿì�›�Æÿì�›Cÿì�›ÿ×�› ÿ×�›Xÿì�›ÿì�›ÿì�›!ÿì�›#ÿì�›%ÿì�›'ÿì�›)ÿì�›+ÿì�›-ÿì�›/ÿì�›1ÿì�›3ÿì�œ�ÿ×�œ�ÿ×�œ�$ÿì�œ�‚ÿì�œ�ƒÿì�œ�„ÿì�œ�…ÿì�œ�†ÿì�œ�‡ÿì�œ�Âÿì�œ�Äÿì�œ�Æÿì�œCÿì�œÿ×�œ ÿ×�œXÿì�œÿì�œÿì�œ!ÿì�œ#ÿì�œ%ÿì�œ'ÿì�œ)ÿì�œ+ÿì�œ-ÿì�œ/ÿì�œ1ÿì�œ3ÿì��ÿ×��ÿ×��$ÿì��‚ÿì��ƒÿì��„ÿì��…ÿì��†ÿì��‡ÿì��Âÿì��Äÿì��Æÿì�Cÿì�ÿ×� ÿ×�Xÿì�ÿì�ÿì�!ÿì�#ÿì�%ÿì�'ÿì�)ÿì�+ÿì�-ÿì�/ÿì�1ÿì�3ÿì�ž�ÿ×�ž�ÿ×�ž�$ÿì�ž�‚ÿì�ž�ƒÿì�ž�„ÿì�ž�…ÿì�ž�†ÿì�ž�‡ÿì�ž�Âÿì�ž�Äÿì�ž�Æÿì�žCÿì�žÿ×�ž ÿ×�žXÿì�žÿì�žÿì�ž!ÿì�ž#ÿì�ž%ÿì�ž'ÿì�ž)ÿì�ž+ÿì�ž-ÿì�ž/ÿì�ž1ÿì�ž3ÿì�Ÿ�ÿ…�Ÿ�ÿ…�Ÿ�"�)�Ÿ�$ÿ…�Ÿ�&ÿ×�Ÿ�*ÿ×�Ÿ�2ÿ×�Ÿ�4ÿ×�Ÿ�Dÿš�Ÿ�Fÿš�Ÿ�Gÿš�Ÿ�Hÿš�Ÿ�Jÿ×�Ÿ�PÿÃ�Ÿ�QÿÃ�Ÿ�Rÿš�Ÿ�SÿÃ�Ÿ�Tÿš�Ÿ�UÿÃ�Ÿ�Vÿ®�Ÿ�XÿÃ�Ÿ�]ÿ×�Ÿ�‚ÿ…�Ÿ�ƒÿ…�Ÿ�„ÿ…�Ÿ�…ÿ…�Ÿ�†ÿ…�Ÿ�‡ÿ…�Ÿ�‰ÿ×�Ÿ�”ÿ×�Ÿ�•ÿ×�Ÿ�–ÿ×�Ÿ�—ÿ×�Ÿ�˜ÿ×�Ÿ�šÿ×�Ÿ�¢ÿš�Ÿ�£ÿš�Ÿ�¤ÿš�Ÿ�¥ÿš�Ÿ�¦ÿš�Ÿ�§ÿš�Ÿ�¨ÿš�Ÿ�©ÿš�Ÿ�ªÿš�Ÿ�«ÿš�Ÿ�¬ÿš�Ÿ�­ÿš�Ÿ�´ÿš�Ÿ�µÿš�Ÿ�¶ÿš�Ÿ�·ÿš�Ÿ�¸ÿš�Ÿ�ºÿš�Ÿ�»ÿÃ�Ÿ�¼ÿÃ�Ÿ�½ÿÃ�Ÿ�¾ÿÃ�Ÿ�Âÿ…�Ÿ�Ãÿš�Ÿ�Äÿ…�Ÿ�Åÿš�Ÿ�Æÿ…�Ÿ�Çÿš�Ÿ�Èÿ×�Ÿ�Éÿš�Ÿ�Êÿ×�Ÿ�Ëÿš�Ÿ�Ìÿ×�Ÿ�Íÿš�Ÿ�Îÿ×�Ÿ�Ïÿš�Ÿ�Ñÿš�Ÿ�Óÿš�Ÿ�Õÿš�Ÿ�×ÿš�Ÿ�Ùÿš�Ÿ�Ûÿš�Ÿ�Ýÿš�Ÿ�Þÿ×�Ÿ�ßÿ×�Ÿ�àÿ×�Ÿ�áÿ×�Ÿ�âÿ×�Ÿ�ãÿ×�Ÿ�äÿ×�Ÿ�åÿ×�Ÿ�úÿÃ�ŸÿÃ�ŸÿÃ�Ÿ ÿÃ�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�Ÿÿ×�Ÿÿš�ŸÿÃ�ŸÿÃ�Ÿÿ®�Ÿ!ÿ®�Ÿ+ÿÃ�Ÿ-ÿÃ�Ÿ/ÿÃ�Ÿ1ÿÃ�Ÿ3ÿÃ�Ÿ5ÿÃ�Ÿ<ÿ×�Ÿ>ÿ×�Ÿ@ÿ×�ŸCÿ…�ŸDÿš�ŸFÿš�ŸGÿ×�ŸHÿš�ŸJÿ®�Ÿÿ…�Ÿ ÿ…�ŸWÿÃ�ŸXÿ…�ŸYÿš�Ÿ_ÿ×�Ÿ`ÿš�ŸbÿÃ�Ÿÿ…�Ÿÿš�Ÿÿ…�Ÿ ÿš�Ÿ!ÿ…�Ÿ"ÿš�Ÿ#ÿ…�Ÿ%ÿ…�Ÿ&ÿš�Ÿ'ÿ…�Ÿ(ÿš�Ÿ)ÿ…�Ÿ*ÿš�Ÿ+ÿ…�Ÿ,ÿš�Ÿ-ÿ…�Ÿ.ÿš�Ÿ/ÿ…�Ÿ0ÿš�Ÿ1ÿ…�Ÿ2ÿš�Ÿ3ÿ…�Ÿ4ÿš�Ÿ6ÿš�Ÿ8ÿš�Ÿ:ÿš�Ÿ<ÿš�Ÿ@ÿš�ŸBÿš�ŸDÿš�ŸIÿ×�ŸJÿš�ŸKÿ×�ŸLÿš�ŸMÿ×�ŸNÿš�ŸOÿ×�ŸQÿ×�ŸRÿš�ŸSÿ×�ŸTÿš�ŸUÿ×�ŸVÿš�ŸWÿ×�ŸXÿš�ŸYÿ×�ŸZÿš�Ÿ[ÿ×�Ÿ\ÿš�Ÿ]ÿ×�Ÿ^ÿš�Ÿ_ÿ×�Ÿ`ÿš�ŸbÿÃ�ŸdÿÃ�ŸfÿÃ�ŸhÿÃ�ŸjÿÃ�ŸlÿÃ�ŸnÿÃ� �þö� �þö� �$ÿš� �;ÿ×� �=ÿì� �‚ÿš� �ƒÿš� �„ÿš� �…ÿš� �†ÿš� �‡ÿš� �Âÿš� �Äÿš� �Æÿš� ;ÿì� =ÿì� ?ÿì� Cÿš� þö�  þö� Xÿš� ÿš� ÿš� !ÿš� #ÿš� %ÿš� 'ÿš� )ÿš� +ÿš� -ÿš� /ÿš� 1ÿš� 3ÿš�¢�ÿì�¢� ÿì�¢ÿì�¢ ÿì�£�ÿì�£� ÿì�£ÿì�£ ÿì�¤�ÿì�¤� ÿì�¤ÿì�¤ ÿì�¥�ÿì�¥� ÿì�¥ÿì�¥ ÿì�¦�ÿì�¦� ÿì�¦ÿì�¦ ÿì�§�ÿì�§� ÿì�§ÿì�§ ÿì�ª�ÿì�ª� ÿì�ª�Yÿ×�ª�Zÿ×�ª�[ÿ×�ª�\ÿ×�ª�]ÿì�ª�¿ÿ×�ª7ÿ×�ª<ÿì�ª>ÿì�ª@ÿì�ªûÿ×�ªýÿ×�ªÿì�ª ÿì�ªpÿ×�«�ÿì�«� ÿì�«�Yÿ×�«�Zÿ×�«�[ÿ×�«�\ÿ×�«�]ÿì�«�¿ÿ×�«7ÿ×�«<ÿì�«>ÿì�«@ÿì�«ûÿ×�«ýÿ×�«ÿì�« ÿì�«pÿ×�¬�ÿì�¬� ÿì�¬�Yÿ×�¬�Zÿ×�¬�[ÿ×�¬�\ÿ×�¬�]ÿì�¬�¿ÿ×�¬7ÿ×�¬<ÿì�¬>ÿì�¬@ÿì�¬ûÿ×�¬ýÿ×�¬ÿì�¬ ÿì�¬pÿ×�­�ÿì�­� ÿì�­�Yÿ×�­�Zÿ×�­�[ÿ×�­�\ÿ×�­�]ÿì�­�¿ÿ×�­7ÿ×�­<ÿì�­>ÿì�­@ÿì�­ûÿ×�­ýÿ×�­ÿì�­ ÿì�­pÿ×�²�ÿì�²� ÿì�²�Yÿ×�²�Zÿ×�²�[ÿ×�²�\ÿ×�²�]ÿì�²�¿ÿ×�²7ÿ×�²<ÿì�²>ÿì�²@ÿì�²ûÿ×�²ýÿ×�²ÿì�² ÿì�²pÿ×�´�ÿì�´� ÿì�´�Yÿ×�´�Zÿ×�´�[ÿ×�´�\ÿ×�´�]ÿì�´�¿ÿ×�´7ÿ×�´<ÿì�´>ÿì�´@ÿì�´ûÿ×�´ýÿ×�´ÿì�´ ÿì�´pÿ×�µ�ÿì�µ� ÿì�µ�Yÿ×�µ�Zÿ×�µ�[ÿ×�µ�\ÿ×�µ�]ÿì�µ�¿ÿ×�µ7ÿ×�µ<ÿì�µ>ÿì�µ@ÿì�µûÿ×�µýÿ×�µÿì�µ ÿì�µpÿ×�¶�ÿì�¶� ÿì�¶�Yÿ×�¶�Zÿ×�¶�[ÿ×�¶�\ÿ×�¶�]ÿì�¶�¿ÿ×�¶7ÿ×�¶<ÿì�¶>ÿì�¶@ÿì�¶ûÿ×�¶ýÿ×�¶ÿì�¶ ÿì�¶pÿ×�¸�ÿ×�¸� ÿ×�¸ÿ×�¸ ÿ×�º�ÿì�º� ÿì�º�Yÿ×�º�Zÿ×�º�[ÿ×�º�\ÿ×�º�]ÿì�º�¿ÿ×�º7ÿ×�º<ÿì�º>ÿì�º@ÿì�ºûÿ×�ºýÿ×�ºÿì�º ÿì�ºpÿ×�¿��R�¿� �R�¿�ÿ®�¿�ÿ®�¿�"�)�¿�R�¿ÿ®�¿ �R�¿ ÿ®�À�ÿì�À� ÿì�À�Yÿ×�À�Zÿ×�À�[ÿ×�À�\ÿ×�À�]ÿì�À�¿ÿ×�À7ÿ×�À<ÿì�À>ÿì�À@ÿì�Àûÿ×�Àýÿ×�Àÿì�À ÿì�Àpÿ×�Á��R�Á� �R�Á�ÿ®�Á�ÿ®�Á�"�)�Á�R�Áÿ®�Á �R�Á ÿ®�Â�ÿq�Â� ÿq�Â�&ÿ×�Â�*ÿ×�Â�- �Â�2ÿ×�Â�4ÿ×�Â�7ÿq�Â�9ÿ®�Â�:ÿ®�Â�<ÿ…�Â�‰ÿ×�Â�”ÿ×�Â�•ÿ×�Â�–ÿ×�Â�—ÿ×�Â�˜ÿ×�Â�šÿ×�Â�Ÿÿ…�Â�Èÿ×�Â�Êÿ×�Â�Ìÿ×�Â�Îÿ×�Â�Þÿ×�Â�àÿ×�Â�âÿ×�Â�äÿ×�Âÿ×�Âÿ×�Âÿ×�Âÿ×�Â$ÿq�Â&ÿq�Â6ÿ®�Â8ÿ…�Â:ÿ…�ÂGÿ×�Âúÿ®�Âüÿ®�Âþÿ®�Â�ÿ…�Âÿq� ÿq�Â_ÿ×�ÂIÿ×�ÂKÿ×�ÂMÿ×�ÂOÿ×�ÂQÿ×�ÂSÿ×�ÂUÿ×�ÂWÿ×�ÂYÿ×�Â[ÿ×�Â]ÿ×�Â_ÿ×�Âoÿ…�Âqÿ…�Âsÿ…�Âÿq�Ã�ÿì�Ã� ÿì�Ãÿì�à ÿì�Ä�ÿq�Ä� ÿq�Ä�&ÿ×�Ä�*ÿ×�Ä�- �Ä�2ÿ×�Ä�4ÿ×�Ä�7ÿq�Ä�9ÿ®�Ä�:ÿ®�Ä�<ÿ…�Ä�‰ÿ×�Ä�”ÿ×�Ä�•ÿ×�Ä�–ÿ×�Ä�—ÿ×�Ä�˜ÿ×�Ä�šÿ×�Ä�Ÿÿ…�Ä�Èÿ×�Ä�Êÿ×�Ä�Ìÿ×�Ä�Îÿ×�Ä�Þÿ×�Ä�àÿ×�Ä�âÿ×�Ä�äÿ×�Äÿ×�Äÿ×�Äÿ×�Äÿ×�Ä$ÿq�Ä&ÿq�Ä6ÿ®�Ä8ÿ…�Ä:ÿ…�ÄGÿ×�Äúÿ®�Äüÿ®�Äþÿ®�Ä�ÿ…�Äÿq�Ä ÿq�Ä_ÿ×�ÄIÿ×�ÄKÿ×�ÄMÿ×�ÄOÿ×�ÄQÿ×�ÄSÿ×�ÄUÿ×�ÄWÿ×�ÄYÿ×�Ä[ÿ×�Ä]ÿ×�Ä_ÿ×�Äoÿ…�Äqÿ…�Äsÿ…�Äÿq�Å�ÿì�Å� ÿì�Åÿì�Å ÿì�Æ�ÿq�Æ� ÿq�Æ�&ÿ×�Æ�*ÿ×�Æ�- �Æ�2ÿ×�Æ�4ÿ×�Æ�7ÿq�Æ�9ÿ®�Æ�:ÿ®�Æ�<ÿ…�Æ�‰ÿ×�Æ�”ÿ×�Æ�•ÿ×�Æ�–ÿ×�Æ�—ÿ×�Æ�˜ÿ×�Æ�šÿ×�Æ�Ÿÿ…�Æ�Èÿ×�Æ�Êÿ×�Æ�Ìÿ×�Æ�Îÿ×�Æ�Þÿ×�Æ�àÿ×�Æ�âÿ×�Æ�äÿ×�Æÿ×�Æÿ×�Æÿ×�Æÿ×�Æ$ÿq�Æ&ÿq�Æ6ÿ®�Æ8ÿ…�Æ:ÿ…�ÆGÿ×�Æúÿ®�Æüÿ®�Æþÿ®�Æ�ÿ…�Æÿq�Æ ÿq�Æ_ÿ×�ÆIÿ×�ÆKÿ×�ÆMÿ×�ÆOÿ×�ÆQÿ×�ÆSÿ×�ÆUÿ×�ÆWÿ×�ÆYÿ×�Æ[ÿ×�Æ]ÿ×�Æ_ÿ×�Æoÿ…�Æqÿ…�Æsÿ…�Æÿq�Ç�ÿì�Ç� ÿì�Çÿì�Ç ÿì�È�&ÿ×�È�*ÿ×�È�2ÿ×�È�4ÿ×�È�‰ÿ×�È�”ÿ×�È�•ÿ×�È�–ÿ×�È�—ÿ×�È�˜ÿ×�È�šÿ×�È�Èÿ×�È�Êÿ×�È�Ìÿ×�È�Îÿ×�È�Þÿ×�È�àÿ×�È�âÿ×�È�äÿ×�Èÿ×�Èÿ×�Èÿ×�Èÿ×�ÈGÿ×�È_ÿ×�ÈIÿ×�ÈKÿ×�ÈMÿ×�ÈOÿ×�ÈQÿ×�ÈSÿ×�ÈUÿ×�ÈWÿ×�ÈYÿ×�È[ÿ×�È]ÿ×�È_ÿ×�Ê�&ÿ×�Ê�*ÿ×�Ê�2ÿ×�Ê�4ÿ×�Ê�‰ÿ×�Ê�”ÿ×�Ê�•ÿ×�Ê�–ÿ×�Ê�—ÿ×�Ê�˜ÿ×�Ê�šÿ×�Ê�Èÿ×�Ê�Êÿ×�Ê�Ìÿ×�Ê�Îÿ×�Ê�Þÿ×�Ê�àÿ×�Ê�âÿ×�Ê�äÿ×�Êÿ×�Êÿ×�Êÿ×�Êÿ×�ÊGÿ×�Ê_ÿ×�ÊIÿ×�ÊKÿ×�ÊMÿ×�ÊOÿ×�ÊQÿ×�ÊSÿ×�ÊUÿ×�ÊWÿ×�ÊYÿ×�Ê[ÿ×�Ê]ÿ×�Ê_ÿ×�Ì�&ÿ×�Ì�*ÿ×�Ì�2ÿ×�Ì�4ÿ×�Ì�‰ÿ×�Ì�”ÿ×�Ì�•ÿ×�Ì�–ÿ×�Ì�—ÿ×�Ì�˜ÿ×�Ì�šÿ×�Ì�Èÿ×�Ì�Êÿ×�Ì�Ìÿ×�Ì�Îÿ×�Ì�Þÿ×�Ì�àÿ×�Ì�âÿ×�Ì�äÿ×�Ìÿ×�Ìÿ×�Ìÿ×�Ìÿ×�ÌGÿ×�Ì_ÿ×�ÌIÿ×�ÌKÿ×�ÌMÿ×�ÌOÿ×�ÌQÿ×�ÌSÿ×�ÌUÿ×�ÌWÿ×�ÌYÿ×�Ì[ÿ×�Ì]ÿ×�Ì_ÿ×�Î�&ÿ×�Î�*ÿ×�Î�2ÿ×�Î�4ÿ×�Î�‰ÿ×�Î�”ÿ×�Î�•ÿ×�Î�–ÿ×�Î�—ÿ×�Î�˜ÿ×�Î�šÿ×�Î�Èÿ×�Î�Êÿ×�Î�Ìÿ×�Î�Îÿ×�Î�Þÿ×�Î�àÿ×�Î�âÿ×�Î�äÿ×�Îÿ×�Îÿ×�Îÿ×�Îÿ×�ÎGÿ×�Î_ÿ×�ÎIÿ×�ÎKÿ×�ÎMÿ×�ÎOÿ×�ÎQÿ×�ÎSÿ×�ÎUÿ×�ÎWÿ×�ÎYÿ×�Î[ÿ×�Î]ÿ×�Î_ÿ×�Ð�ÿ®�Ð�ÿ®�Ð�$ÿ×�Ð�7ÿÃ�Ð�9ÿì�Ð�:ÿì�Ð�;ÿ×�Ð�<ÿì�Ð�=ÿì�Ð�‚ÿ×�Ð�ƒÿ×�Ð�„ÿ×�Ð�…ÿ×�Ð�†ÿ×�Ð�‡ÿ×�Ð�Ÿÿì�Ð�Âÿ×�Ð�Äÿ×�Ð�Æÿ×�Ð$ÿÃ�Ð&ÿÃ�Ð6ÿì�Ð8ÿì�Ð:ÿì�Ð;ÿì�Ð=ÿì�Ð?ÿì�ÐCÿ×�Рÿì�Ðúÿì�Ðüÿì�Ðþÿì�Ð�ÿì�Ðÿ®�Ð ÿ®�ÐXÿ×�Ðÿ×�Ðÿ×�Ð!ÿ×�Ð#ÿ×�Ð%ÿ×�Ð'ÿ×�Ð)ÿ×�Ð+ÿ×�Ð-ÿ×�Ð/ÿ×�Ð1ÿ×�Ð3ÿ×�Ðoÿì�Ðqÿì�Ðsÿì�ÐÿÃ�Ñ��R�Ñ� �R�Ñ� ��Ñ�"�¤�Ñ�@��Ñ�E�=�Ñ�K�=�Ñ�N�=�Ñ�O�=�Ñ�`��Ñ�ç�=�Ñ�é�{�Ñ�R�Ñ �R�Ò�ÿ®�Ò�ÿ®�Ò�$ÿ×�Ò�7ÿÃ�Ò�9ÿì�Ò�:ÿì�Ò�;ÿ×�Ò�<ÿì�Ò�=ÿì�Ò�‚ÿ×�Ò�ƒÿ×�Ò�„ÿ×�Ò�…ÿ×�Ò�†ÿ×�Ò�‡ÿ×�Ò�Ÿÿì�Ò�Âÿ×�Ò�Äÿ×�Ò�Æÿ×�Ò$ÿÃ�Ò&ÿÃ�Ò6ÿì�Ò8ÿì�Ò:ÿì�Ò;ÿì�Ò=ÿì�Ò?ÿì�ÒCÿ×�Ò ÿì�Òúÿì�Òüÿì�Òþÿì�Ò�ÿì�Òÿ®�Ò ÿ®�ÒXÿ×�Òÿ×�Òÿ×�Ò!ÿ×�Ò#ÿ×�Ò%ÿ×�Ò'ÿ×�Ò)ÿ×�Ò+ÿ×�Ò-ÿ×�Ò/ÿ×�Ò1ÿ×�Ò3ÿ×�Òoÿì�Òqÿì�Òsÿì�ÒÿÃ�Ô�-�{�Õ�ÿì�Õ� ÿì�Õ�Yÿ×�Õ�Zÿ×�Õ�[ÿ×�Õ�\ÿ×�Õ�]ÿì�Õ�¿ÿ×�Õ7ÿ×�Õ<ÿì�Õ>ÿì�Õ@ÿì�Õûÿ×�Õýÿ×�Õÿì�Õ ÿì�Õpÿ×�Ö�-�{�×�ÿì�×� ÿì�×�Yÿ×�×�Zÿ×�×�[ÿ×�×�\ÿ×�×�]ÿì�×�¿ÿ×�×7ÿ×�×<ÿì�×>ÿì�×@ÿì�×ûÿ×�×ýÿ×�×ÿì�× ÿì�×pÿ×�Ø�-�{�Ù�ÿì�Ù� ÿì�Ù�Yÿ×�Ù�Zÿ×�Ù�[ÿ×�Ù�\ÿ×�Ù�]ÿì�Ù�¿ÿ×�Ù7ÿ×�Ù<ÿì�Ù>ÿì�Ù@ÿì�Ùûÿ×�Ùýÿ×�Ùÿì�Ù ÿì�Ùpÿ×�Ú�-�{�Û�ÿì�Û� ÿì�Û�Yÿ×�Û�Zÿ×�Û�[ÿ×�Û�\ÿ×�Û�]ÿì�Û�¿ÿ×�Û7ÿ×�Û<ÿì�Û>ÿì�Û@ÿì�Ûûÿ×�Ûýÿ×�Ûÿì�Û ÿì�Ûpÿ×�Ü�-�{�Ý�ÿì�Ý� ÿì�Ý�Yÿ×�Ý�Zÿ×�Ý�[ÿ×�Ý�\ÿ×�Ý�]ÿì�Ý�¿ÿ×�Ý7ÿ×�Ý<ÿì�Ý>ÿì�Ý@ÿì�Ýûÿ×�Ýýÿ×�Ýÿì�Ý ÿì�Ýpÿ×�ç�ÿì�ç� ÿì�çÿì�ç ÿì�ø�&ÿ×�ø�*ÿ×�ø�2ÿ×�ø�4ÿ×�ø�‰ÿ×�ø�”ÿ×�ø�•ÿ×�ø�–ÿ×�ø�—ÿ×�ø�˜ÿ×�ø�šÿ×�ø�Èÿ×�ø�Êÿ×�ø�Ìÿ×�ø�Îÿ×�ø�Þÿ×�ø�àÿ×�ø�âÿ×�ø�äÿ×�øÿ×�øÿ×�øÿ×�øÿ×�øGÿ×�ø_ÿ×�øIÿ×�øKÿ×�øMÿ×�øOÿ×�øQÿ×�øSÿ×�øUÿ×�øWÿ×�øYÿ×�ø[ÿ×�ø]ÿ×�ø_ÿ×�ù�Fÿ×�ù�Gÿ×�ù�Hÿ×�ù�Rÿ×�ù�Tÿ×�ù�¢ÿ×�ù�©ÿ×�ù�ªÿ×�ù�«ÿ×�ù�¬ÿ×�ù�­ÿ×�ù�´ÿ×�ù�µÿ×�ù�¶ÿ×�ù�·ÿ×�ù�¸ÿ×�ù�ºÿ×�ù�Éÿ×�ù�Ëÿ×�ù�Íÿ×�ù�Ïÿ×�ù�Ñÿ×�ù�Óÿ×�ù�Õÿ×�ù�×ÿ×�ù�Ùÿ×�ù�Ûÿ×�ù�Ýÿ×�ùÿ×�ùÿ×�ùÿ×�ùÿ×�ùHÿ×�ù`ÿ×�ù6ÿ×�ù8ÿ×�ù:ÿ×�ù<ÿ×�ù@ÿ×�ùBÿ×�ùDÿ×�ùJÿ×�ùLÿ×�ùNÿ×�ùRÿ×�ùTÿ×�ùVÿ×�ùXÿ×�ùZÿ×�ù\ÿ×�ù^ÿ×�ù`ÿ×�ú�Fÿ×�ú�Gÿ×�ú�Hÿ×�ú�Rÿ×�ú�Tÿ×�ú�¢ÿ×�ú�©ÿ×�ú�ªÿ×�ú�«ÿ×�ú�¬ÿ×�ú�­ÿ×�ú�´ÿ×�ú�µÿ×�ú�¶ÿ×�ú�·ÿ×�ú�¸ÿ×�ú�ºÿ×�ú�Éÿ×�ú�Ëÿ×�ú�Íÿ×�ú�Ïÿ×�ú�Ñÿ×�ú�Óÿ×�ú�Õÿ×�ú�×ÿ×�ú�Ùÿ×�ú�Ûÿ×�ú�Ýÿ×�úÿ×�úÿ×�úÿ×�úÿ×�úHÿ×�ú`ÿ×�ú6ÿ×�ú8ÿ×�ú:ÿ×�ú<ÿ×�ú@ÿ×�úBÿ×�úDÿ×�úJÿ×�úLÿ×�úNÿ×�úRÿ×�úTÿ×�úVÿ×�úXÿ×�úZÿ×�ú\ÿ×�ú^ÿ×�ú`ÿ×�û�ÿ\�û� ÿ\�û�&ÿ×�û�*ÿ×�û�2ÿ×�û�4ÿ×�û�7ÿ×�û�8ÿì�û�9ÿ×�û�:ÿ×�û�<ÿÃ�û�‰ÿ×�û�”ÿ×�û�•ÿ×�û�–ÿ×�û�—ÿ×�û�˜ÿ×�û�šÿ×�û�›ÿì�û�œÿì�û�ÿì�û�žÿì�û�ŸÿÃ�û�Èÿ×�û�Êÿ×�û�Ìÿ×�û�Îÿ×�û�Þÿ×�û�àÿ×�û�âÿ×�û�äÿ×�ûÿ×�ûÿ×�ûÿ×�ûÿ×�û$ÿ×�û&ÿ×�û*ÿì�û,ÿì�û.ÿì�û0ÿì�û2ÿì�û4ÿì�û6ÿ×�û8ÿÃ�û:ÿÃ�ûGÿ×�ûúÿ×�ûüÿ×�ûþÿ×�û�ÿÃ�ûÿ\�û ÿ\�û_ÿ×�ûaÿì�ûIÿ×�ûKÿ×�ûMÿ×�ûOÿ×�ûQÿ×�ûSÿ×�ûUÿ×�ûWÿ×�ûYÿ×�û[ÿ×�û]ÿ×�û_ÿ×�ûaÿì�ûcÿì�ûeÿì�ûgÿì�ûiÿì�ûkÿì�ûmÿì�ûoÿÃ�ûqÿÃ�ûsÿÃ�ûÿ×�ý�ÿ\�ý� ÿ\�ý�&ÿ×�ý�*ÿ×�ý�2ÿ×�ý�4ÿ×�ý�7ÿ×�ý�8ÿì�ý�9ÿ×�ý�:ÿ×�ý�<ÿÃ�ý�‰ÿ×�ý�”ÿ×�ý�•ÿ×�ý�–ÿ×�ý�—ÿ×�ý�˜ÿ×�ý�šÿ×�ý�›ÿì�ý�œÿì�ý�ÿì�ý�žÿì�ý�ŸÿÃ�ý�Èÿ×�ý�Êÿ×�ý�Ìÿ×�ý�Îÿ×�ý�Þÿ×�ý�àÿ×�ý�âÿ×�ý�äÿ×�ýÿ×�ýÿ×�ýÿ×�ýÿ×�ý$ÿ×�ý&ÿ×�ý*ÿì�ý,ÿì�ý.ÿì�ý0ÿì�ý2ÿì�ý4ÿì�ý6ÿ×�ý8ÿÃ�ý:ÿÃ�ýGÿ×�ýúÿ×�ýüÿ×�ýþÿ×�ý�ÿÃ�ýÿ\�ý ÿ\�ý_ÿ×�ýaÿì�ýIÿ×�ýKÿ×�ýMÿ×�ýOÿ×�ýQÿ×�ýSÿ×�ýUÿ×�ýWÿ×�ýYÿ×�ý[ÿ×�ý]ÿ×�ý_ÿ×�ýaÿì�ýcÿì�ýeÿì�ýgÿì�ýiÿì�ýkÿì�ýmÿì�ýoÿÃ�ýqÿÃ�ýsÿÃ�ýÿ×�ÿ�ÿ\�ÿ� ÿ\�ÿ�&ÿ×�ÿ�*ÿ×�ÿ�2ÿ×�ÿ�4ÿ×�ÿ�7ÿ×�ÿ�8ÿì�ÿ�9ÿ×�ÿ�:ÿ×�ÿ�<ÿÃ�ÿ�‰ÿ×�ÿ�”ÿ×�ÿ�•ÿ×�ÿ�–ÿ×�ÿ�—ÿ×�ÿ�˜ÿ×�ÿ�šÿ×�ÿ�›ÿì�ÿ�œÿì�ÿ�ÿì�ÿ�žÿì�ÿ�ŸÿÃ�ÿ�Èÿ×�ÿ�Êÿ×�ÿ�Ìÿ×�ÿ�Îÿ×�ÿ�Þÿ×�ÿ�àÿ×�ÿ�âÿ×�ÿ�äÿ×�ÿÿ×�ÿÿ×�ÿÿ×�ÿÿ×�ÿ$ÿ×�ÿ&ÿ×�ÿ*ÿì�ÿ,ÿì�ÿ.ÿì�ÿ0ÿì�ÿ2ÿì�ÿ4ÿì�ÿ6ÿ×�ÿ8ÿÃ�ÿ:ÿÃ�ÿGÿ×�ÿúÿ×�ÿüÿ×�ÿþÿ×�ÿ�ÿÃ�ÿÿ\�ÿ ÿ\�ÿ_ÿ×�ÿaÿì�ÿIÿ×�ÿKÿ×�ÿMÿ×�ÿOÿ×�ÿQÿ×�ÿSÿ×�ÿUÿ×�ÿWÿ×�ÿYÿ×�ÿ[ÿ×�ÿ]ÿ×�ÿ_ÿ×�ÿaÿì�ÿcÿì�ÿeÿì�ÿgÿì�ÿiÿì�ÿkÿì�ÿmÿì�ÿoÿÃ�ÿqÿÃ�ÿsÿÃ�ÿÿ×���R�� �R�� ���"���@���E�=��K�=��N�=��O�=��`���ç�=��é���R� �R�ÿ\� ÿ\�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7ÿ×�8ÿì�9ÿ×�:ÿ×�<ÿÃ�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�›ÿì�œÿì�ÿì�žÿì�ŸÿÃ�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿ×&ÿ×*ÿì,ÿì.ÿì0ÿì2ÿì4ÿì6ÿ×8ÿÃ:ÿÃGÿ×úÿ×üÿ×þÿ×�ÿÃÿ\ ÿ\_ÿ×aÿìIÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×aÿìcÿìeÿìgÿìiÿìkÿìmÿìoÿÃqÿÃsÿÃÿ×�ÿ\� ÿ\�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7ÿ×�8ÿì�9ÿ×�:ÿ×�<ÿÃ�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�›ÿì�œÿì�ÿì�žÿì�ŸÿÃ�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿ×&ÿ×*ÿì,ÿì.ÿì0ÿì2ÿì4ÿì6ÿ×8ÿÃ:ÿÃGÿ×úÿ×üÿ×þÿ×�ÿÃÿ\ ÿ\_ÿ×aÿìIÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×aÿìcÿìeÿìgÿìiÿìkÿìmÿìoÿÃqÿÃsÿÃÿ×�ÿì� ÿìÿì ÿì�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�ÿ®�ÿ®�$ÿ×�7ÿÃ�9ÿì�:ÿì�;ÿ×�<ÿì�=ÿì�‚ÿ×�ƒÿ×�„ÿ×�…ÿ×�†ÿ×�‡ÿ×�Ÿÿì�Âÿ×�Äÿ×�Æÿ×$ÿÃ&ÿÃ6ÿì8ÿì:ÿì;ÿì=ÿì?ÿìCÿ× ÿìúÿìüÿìþÿì�ÿìÿ® ÿ®Xÿ×ÿ×ÿ×!ÿ×#ÿ×%ÿ×'ÿ×)ÿ×+ÿ×-ÿ×/ÿ×1ÿ×3ÿ×oÿìqÿìsÿìÿÃ�-�{��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×��R� �R�Dÿ×�Fÿ×�Gÿ×�Hÿ×�Jÿì�Rÿ×�Tÿ×�¢ÿ×�£ÿ×�¤ÿ×�¥ÿ×�¦ÿ×�§ÿ×�¨ÿ×�©ÿ×�ªÿ×�«ÿ×�¬ÿ×�­ÿ×�´ÿ×�µÿ×�¶ÿ×�·ÿ×�¸ÿ×�ºÿ×�Ãÿ×�Åÿ×�Çÿ×�Éÿ×�Ëÿ×�Íÿ×�Ïÿ×�Ñÿ×�Óÿ×�Õÿ×�×ÿ×�Ùÿ×�Ûÿ×�Ýÿ×�ßÿì�áÿì�ãÿì�åÿìÿ×ÿ×ÿ×ÿ×Dÿ×Fÿ×Hÿ×�R �RYÿ×`ÿ×ÿ× ÿ×"ÿ×&ÿ×(ÿ×*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ×8ÿ×:ÿ×<ÿ×@ÿ×Bÿ×Dÿ×Jÿ×Lÿ×Nÿ×Rÿ×Tÿ×Vÿ×Xÿ×Zÿ×\ÿ×^ÿ×`ÿ×$�ÿ…$�ÿ®$�ÿ…$�"�)$�$ÿq$�&ÿ×$�*ÿ×$�2ÿ×$�4ÿ×$�7�)$�Dÿ\$�Fÿq$�Gÿq$�Hÿq$�Jÿq$�Pÿš$�Qÿš$�Rÿq$�Sÿš$�Tÿq$�Uÿš$�Vÿ…$�Xÿš$�Yÿ×$�Zÿ×$�[ÿ×$�\ÿ×$�]ÿ®$�‚ÿq$�ƒÿq$�„ÿq$�…ÿq$�†ÿq$�‡ÿq$�‰ÿ×$�”ÿ×$�•ÿ×$�–ÿ×$�—ÿ×$�˜ÿ×$�šÿ×$�¢ÿq$�£ÿ\$�¤ÿ\$�¥ÿ\$�¦ÿ\$�§ÿ\$�¨ÿ\$�©ÿq$�ªÿq$�«ÿq$�¬ÿq$�­ÿq$�´ÿq$�µÿq$�¶ÿq$�·ÿq$�¸ÿq$�ºÿq$�»ÿš$�¼ÿš$�½ÿš$�¾ÿš$�¿ÿ×$�Âÿq$�Ãÿ\$�Äÿq$�Åÿ\$�Æÿq$�Çÿ\$�Èÿ×$�Éÿq$�Êÿ×$�Ëÿq$�Ìÿ×$�Íÿq$�Îÿ×$�Ïÿq$�Ñÿq$�Óÿq$�Õÿq$�×ÿq$�Ùÿq$�Ûÿq$�Ýÿq$�Þÿ×$�ßÿq$�àÿ×$�áÿq$�âÿ×$�ãÿq$�äÿ×$�åÿq$�úÿš$ÿš$ÿš$ ÿš$ÿ×$ÿq$ÿ×$ÿq$ÿ×$ÿq$ÿ×$ÿq$ÿš$ÿš$ÿ…$!ÿ…$$�)$&�)$+ÿš$-ÿš$/ÿš$1ÿš$3ÿš$5ÿš$7ÿ×$<ÿ®$>ÿ®$@ÿ®$Cÿq$Dÿ\$Fÿ\$Gÿ×$Hÿq$Jÿ…$ûÿ×$ýÿ×$ÿ®$ÿ®$ÿ®$ÿ…$ ÿ…$Wÿš$Xÿq$Yÿ\$_ÿ×$`ÿq$bÿš$ÿq$ÿ\$ÿq$ ÿ\$!ÿq$"ÿ\$#ÿq$%ÿq$&ÿ\$'ÿq$(ÿ\$)ÿq$*ÿ\$+ÿq$,ÿ\$-ÿq$.ÿ\$/ÿq$0ÿ\$1ÿq$2ÿ\$3ÿq$4ÿ\$6ÿq$8ÿq$:ÿq$<ÿq$@ÿq$Bÿq$Dÿq$Iÿ×$Jÿq$Kÿ×$Lÿq$Mÿ×$Nÿq$Oÿ×$Qÿ×$Rÿq$Sÿ×$Tÿq$Uÿ×$Vÿq$Wÿ×$Xÿq$Yÿ×$Zÿq$[ÿ×$\ÿq$]ÿ×$^ÿq$_ÿ×$`ÿq$bÿš$dÿš$fÿš$hÿš$jÿš$lÿš$nÿš$pÿ×$�)%��)%� �)%�)% �)&�ÿ…&�ÿ®&�ÿ…&�"�)&�$ÿq&�&ÿ×&�*ÿ×&�2ÿ×&�4ÿ×&�7�)&�Dÿ\&�Fÿq&�Gÿq&�Hÿq&�Jÿq&�Pÿš&�Qÿš&�Rÿq&�Sÿš&�Tÿq&�Uÿš&�Vÿ…&�Xÿš&�Yÿ×&�Zÿ×&�[ÿ×&�\ÿ×&�]ÿ®&�‚ÿq&�ƒÿq&�„ÿq&�…ÿq&�†ÿq&�‡ÿq&�‰ÿ×&�”ÿ×&�•ÿ×&�–ÿ×&�—ÿ×&�˜ÿ×&�šÿ×&�¢ÿq&�£ÿ\&�¤ÿ\&�¥ÿ\&�¦ÿ\&�§ÿ\&�¨ÿ\&�©ÿq&�ªÿq&�«ÿq&�¬ÿq&�­ÿq&�´ÿq&�µÿq&�¶ÿq&�·ÿq&�¸ÿq&�ºÿq&�»ÿš&�¼ÿš&�½ÿš&�¾ÿš&�¿ÿ×&�Âÿq&�Ãÿ\&�Äÿq&�Åÿ\&�Æÿq&�Çÿ\&�Èÿ×&�Éÿq&�Êÿ×&�Ëÿq&�Ìÿ×&�Íÿq&�Îÿ×&�Ïÿq&�Ñÿq&�Óÿq&�Õÿq&�×ÿq&�Ùÿq&�Ûÿq&�Ýÿq&�Þÿ×&�ßÿq&�àÿ×&�áÿq&�âÿ×&�ãÿq&�äÿ×&�åÿq&�úÿš&ÿš&ÿš& ÿš&ÿ×&ÿq&ÿ×&ÿq&ÿ×&ÿq&ÿ×&ÿq&ÿš&ÿš&ÿ…&!ÿ…&$�)&&�)&+ÿš&-ÿš&/ÿš&1ÿš&3ÿš&5ÿš&7ÿ×&<ÿ®&>ÿ®&@ÿ®&Cÿq&Dÿ\&Fÿ\&Gÿ×&Hÿq&Jÿ…&ûÿ×&ýÿ×&ÿ®&ÿ®&ÿ®&ÿ…& ÿ…&Wÿš&Xÿq&Yÿ\&_ÿ×&`ÿq&bÿš&ÿq&ÿ\&ÿq& ÿ\&!ÿq&"ÿ\&#ÿq&%ÿq&&ÿ\&'ÿq&(ÿ\&)ÿq&*ÿ\&+ÿq&,ÿ\&-ÿq&.ÿ\&/ÿq&0ÿ\&1ÿq&2ÿ\&3ÿq&4ÿ\&6ÿq&8ÿq&:ÿq&<ÿq&@ÿq&Bÿq&Dÿq&Iÿ×&Jÿq&Kÿ×&Lÿq&Mÿ×&Nÿq&Oÿ×&Qÿ×&Rÿq&Sÿ×&Tÿq&Uÿ×&Vÿq&Wÿ×&Xÿq&Yÿ×&Zÿq&[ÿ×&\ÿq&]ÿ×&^ÿq&_ÿ×&`ÿq&bÿš&dÿš&fÿš&hÿš&jÿš&lÿš&nÿš&pÿ×&�)'��)'� �)'�)' �)(�ÿ…(�ÿ®(�ÿ…(�"�)(�$ÿq(�&ÿ×(�*ÿ×(�2ÿ×(�4ÿ×(�7�)(�Dÿ\(�Fÿq(�Gÿq(�Hÿq(�Jÿq(�Pÿš(�Qÿš(�Rÿq(�Sÿš(�Tÿq(�Uÿš(�Vÿ…(�Xÿš(�Yÿ×(�Zÿ×(�[ÿ×(�\ÿ×(�]ÿ®(�‚ÿq(�ƒÿq(�„ÿq(�…ÿq(�†ÿq(�‡ÿq(�‰ÿ×(�”ÿ×(�•ÿ×(�–ÿ×(�—ÿ×(�˜ÿ×(�šÿ×(�¢ÿq(�£ÿ\(�¤ÿ\(�¥ÿ\(�¦ÿ\(�§ÿ\(�¨ÿ\(�©ÿq(�ªÿq(�«ÿq(�¬ÿq(�­ÿq(�´ÿq(�µÿq(�¶ÿq(�·ÿq(�¸ÿq(�ºÿq(�»ÿš(�¼ÿš(�½ÿš(�¾ÿš(�¿ÿ×(�Âÿq(�Ãÿ\(�Äÿq(�Åÿ\(�Æÿq(�Çÿ\(�Èÿ×(�Éÿq(�Êÿ×(�Ëÿq(�Ìÿ×(�Íÿq(�Îÿ×(�Ïÿq(�Ñÿq(�Óÿq(�Õÿq(�×ÿq(�Ùÿq(�Ûÿq(�Ýÿq(�Þÿ×(�ßÿq(�àÿ×(�áÿq(�âÿ×(�ãÿq(�äÿ×(�åÿq(�úÿš(ÿš(ÿš( ÿš(ÿ×(ÿq(ÿ×(ÿq(ÿ×(ÿq(ÿ×(ÿq(ÿš(ÿš(ÿ…(!ÿ…($�)(&�)(+ÿš(-ÿš(/ÿš(1ÿš(3ÿš(5ÿš(7ÿ×(<ÿ®(>ÿ®(@ÿ®(Cÿq(Dÿ\(Fÿ\(Gÿ×(Hÿq(Jÿ…(ûÿ×(ýÿ×(ÿ®(ÿ®(ÿ®(ÿ…( ÿ…(Wÿš(Xÿq(Yÿ\(_ÿ×(`ÿq(bÿš(ÿq(ÿ\(ÿq( ÿ\(!ÿq("ÿ\(#ÿq(%ÿq(&ÿ\('ÿq((ÿ\()ÿq(*ÿ\(+ÿq(,ÿ\(-ÿq(.ÿ\(/ÿq(0ÿ\(1ÿq(2ÿ\(3ÿq(4ÿ\(6ÿq(8ÿq(:ÿq(<ÿq(@ÿq(Bÿq(Dÿq(Iÿ×(Jÿq(Kÿ×(Lÿq(Mÿ×(Nÿq(Oÿ×(Qÿ×(Rÿq(Sÿ×(Tÿq(Uÿ×(Vÿq(Wÿ×(Xÿq(Yÿ×(Zÿq([ÿ×(\ÿq(]ÿ×(^ÿq(_ÿ×(`ÿq(bÿš(dÿš(fÿš(hÿš(jÿš(lÿš(nÿš(pÿ×(�)*�ÿ×*�ÿ×*�$ÿì*�‚ÿì*�ƒÿì*�„ÿì*�…ÿì*�†ÿì*�‡ÿì*�Âÿì*�Äÿì*�Æÿì*Cÿì*ÿ×* ÿ×*Xÿì*ÿì*ÿì*!ÿì*#ÿì*%ÿì*'ÿì*)ÿì*+ÿì*-ÿì*/ÿì*1ÿì*3ÿì,�ÿ×,�ÿ×,�$ÿì,�‚ÿì,�ƒÿì,�„ÿì,�…ÿì,�†ÿì,�‡ÿì,�Âÿì,�Äÿì,�Æÿì,Cÿì,ÿ×, ÿ×,Xÿì,ÿì,ÿì,!ÿì,#ÿì,%ÿì,'ÿì,)ÿì,+ÿì,-ÿì,/ÿì,1ÿì,3ÿì.�ÿ×.�ÿ×.�$ÿì.�‚ÿì.�ƒÿì.�„ÿì.�…ÿì.�†ÿì.�‡ÿì.�Âÿì.�Äÿì.�Æÿì.Cÿì.ÿ×. ÿ×.Xÿì.ÿì.ÿì.!ÿì.#ÿì.%ÿì.'ÿì.)ÿì.+ÿì.-ÿì./ÿì.1ÿì.3ÿì0�ÿ×0�ÿ×0�$ÿì0�‚ÿì0�ƒÿì0�„ÿì0�…ÿì0�†ÿì0�‡ÿì0�Âÿì0�Äÿì0�Æÿì0Cÿì0ÿ×0 ÿ×0Xÿì0ÿì0ÿì0!ÿì0#ÿì0%ÿì0'ÿì0)ÿì0+ÿì0-ÿì0/ÿì01ÿì03ÿì2�ÿ×2�ÿ×2�$ÿì2�‚ÿì2�ƒÿì2�„ÿì2�…ÿì2�†ÿì2�‡ÿì2�Âÿì2�Äÿì2�Æÿì2Cÿì2ÿ×2 ÿ×2Xÿì2ÿì2ÿì2!ÿì2#ÿì2%ÿì2'ÿì2)ÿì2+ÿì2-ÿì2/ÿì21ÿì23ÿì4�ÿ×4�ÿ×4�$ÿì4�‚ÿì4�ƒÿì4�„ÿì4�…ÿì4�†ÿì4�‡ÿì4�Âÿì4�Äÿì4�Æÿì4Cÿì4ÿ×4 ÿ×4Xÿì4ÿì4ÿì4!ÿì4#ÿì4%ÿì4'ÿì4)ÿì4+ÿì4-ÿì4/ÿì41ÿì43ÿì6�ÿš6�ÿš6�"�)6�$ÿ®6�&ÿì6�*ÿì6�2ÿì6�4ÿì6�Dÿ×6�Fÿ×6�Gÿ×6�Hÿ×6�Jÿì6�Pÿì6�Qÿì6�Rÿ×6�Sÿì6�Tÿ×6�Uÿì6�Vÿì6�Xÿì6�‚ÿ®6�ƒÿ®6�„ÿ®6�…ÿ®6�†ÿ®6�‡ÿ®6�‰ÿì6�”ÿì6�•ÿì6�–ÿì6�—ÿì6�˜ÿì6�šÿì6�¢ÿ×6�£ÿ×6�¤ÿ×6�¥ÿ×6�¦ÿ×6�§ÿ×6�¨ÿ×6�©ÿ×6�ªÿ×6�«ÿ×6�¬ÿ×6�­ÿ×6�´ÿ×6�µÿ×6�¶ÿ×6�·ÿ×6�¸ÿ×6�ºÿ×6�»ÿì6�¼ÿì6�½ÿì6�¾ÿì6�Âÿ®6�Ãÿ×6�Äÿ®6�Åÿ×6�Æÿ®6�Çÿ×6�Èÿì6�Éÿ×6�Êÿì6�Ëÿ×6�Ìÿì6�Íÿ×6�Îÿì6�Ïÿ×6�Ñÿ×6�Óÿ×6�Õÿ×6�×ÿ×6�Ùÿ×6�Ûÿ×6�Ýÿ×6�Þÿì6�ßÿì6�àÿì6�áÿì6�âÿì6�ãÿì6�äÿì6�åÿì6�úÿì6ÿì6ÿì6 ÿì6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿ×6ÿì6ÿì6ÿì6!ÿì6+ÿì6-ÿì6/ÿì61ÿì63ÿì65ÿì6Cÿ®6Dÿ×6Fÿ×6Gÿì6Hÿ×6Jÿì6ÿš6 ÿš6Wÿì6Xÿ®6Yÿ×6_ÿì6`ÿ×6bÿì6ÿ®6ÿ×6ÿ®6 ÿ×6!ÿ®6"ÿ×6#ÿ®6%ÿ®6&ÿ×6'ÿ®6(ÿ×6)ÿ®6*ÿ×6+ÿ®6,ÿ×6-ÿ®6.ÿ×6/ÿ®60ÿ×61ÿ®62ÿ×63ÿ®64ÿ×66ÿ×68ÿ×6:ÿ×6<ÿ×6@ÿ×6Bÿ×6Dÿ×6Iÿì6Jÿ×6Kÿì6Lÿ×6Mÿì6Nÿ×6Oÿì6Qÿì6Rÿ×6Sÿì6Tÿ×6Uÿì6Vÿ×6Wÿì6Xÿ×6Yÿì6Zÿ×6[ÿì6\ÿ×6]ÿì6^ÿ×6_ÿì6`ÿ×6bÿì6dÿì6fÿì6hÿì6jÿì6lÿì6nÿì7��R7� �R7�ÿ®7�ÿ®7�"�)7�R7ÿ®7 �R7 ÿ®8�ÿ…8�ÿ…8�"�)8�$ÿ…8�&ÿ×8�*ÿ×8�2ÿ×8�4ÿ×8�Dÿš8�Fÿš8�Gÿš8�Hÿš8�Jÿ×8�PÿÃ8�QÿÃ8�Rÿš8�SÿÃ8�Tÿš8�UÿÃ8�Vÿ®8�XÿÃ8�]ÿ×8�‚ÿ…8�ƒÿ…8�„ÿ…8�…ÿ…8�†ÿ…8�‡ÿ…8�‰ÿ×8�”ÿ×8�•ÿ×8�–ÿ×8�—ÿ×8�˜ÿ×8�šÿ×8�¢ÿš8�£ÿš8�¤ÿš8�¥ÿš8�¦ÿš8�§ÿš8�¨ÿš8�©ÿš8�ªÿš8�«ÿš8�¬ÿš8�­ÿš8�´ÿš8�µÿš8�¶ÿš8�·ÿš8�¸ÿš8�ºÿš8�»ÿÃ8�¼ÿÃ8�½ÿÃ8�¾ÿÃ8�Âÿ…8�Ãÿš8�Äÿ…8�Åÿš8�Æÿ…8�Çÿš8�Èÿ×8�Éÿš8�Êÿ×8�Ëÿš8�Ìÿ×8�Íÿš8�Îÿ×8�Ïÿš8�Ñÿš8�Óÿš8�Õÿš8�×ÿš8�Ùÿš8�Ûÿš8�Ýÿš8�Þÿ×8�ßÿ×8�àÿ×8�áÿ×8�âÿ×8�ãÿ×8�äÿ×8�åÿ×8�úÿÃ8ÿÃ8ÿÃ8 ÿÃ8ÿ×8ÿš8ÿ×8ÿš8ÿ×8ÿš8ÿ×8ÿš8ÿÃ8ÿÃ8ÿ®8!ÿ®8+ÿÃ8-ÿÃ8/ÿÃ81ÿÃ83ÿÃ85ÿÃ8<ÿ×8>ÿ×8@ÿ×8Cÿ…8Dÿš8Fÿš8Gÿ×8Hÿš8Jÿ®8ÿ…8 ÿ…8WÿÃ8Xÿ…8Yÿš8_ÿ×8`ÿš8bÿÃ8ÿ…8ÿš8ÿ…8 ÿš8!ÿ…8"ÿš8#ÿ…8%ÿ…8&ÿš8'ÿ…8(ÿš8)ÿ…8*ÿš8+ÿ…8,ÿš8-ÿ…8.ÿš8/ÿ…80ÿš81ÿ…82ÿš83ÿ…84ÿš86ÿš88ÿš8:ÿš8<ÿš8@ÿš8Bÿš8Dÿš8Iÿ×8Jÿš8Kÿ×8Lÿš8Mÿ×8Nÿš8Oÿ×8Qÿ×8Rÿš8Sÿ×8Tÿš8Uÿ×8Vÿš8Wÿ×8Xÿš8Yÿ×8Zÿš8[ÿ×8\ÿš8]ÿ×8^ÿš8_ÿ×8`ÿš8bÿÃ8dÿÃ8fÿÃ8hÿÃ8jÿÃ8lÿÃ8nÿÃ9��R9� �R9�ÿ®9�ÿ®9�"�)9�R9ÿ®9 �R9 ÿ®:�ÿ…:�ÿ…:�"�):�$ÿ…:�&ÿ×:�*ÿ×:�2ÿ×:�4ÿ×:�Dÿš:�Fÿš:�Gÿš:�Hÿš:�Jÿ×:�PÿÃ:�QÿÃ:�Rÿš:�SÿÃ:�Tÿš:�UÿÃ:�Vÿ®:�XÿÃ:�]ÿ×:�‚ÿ…:�ƒÿ…:�„ÿ…:�…ÿ…:�†ÿ…:�‡ÿ…:�‰ÿ×:�”ÿ×:�•ÿ×:�–ÿ×:�—ÿ×:�˜ÿ×:�šÿ×:�¢ÿš:�£ÿš:�¤ÿš:�¥ÿš:�¦ÿš:�§ÿš:�¨ÿš:�©ÿš:�ªÿš:�«ÿš:�¬ÿš:�­ÿš:�´ÿš:�µÿš:�¶ÿš:�·ÿš:�¸ÿš:�ºÿš:�»ÿÃ:�¼ÿÃ:�½ÿÃ:�¾ÿÃ:�Âÿ…:�Ãÿš:�Äÿ…:�Åÿš:�Æÿ…:�Çÿš:�Èÿ×:�Éÿš:�Êÿ×:�Ëÿš:�Ìÿ×:�Íÿš:�Îÿ×:�Ïÿš:�Ñÿš:�Óÿš:�Õÿš:�×ÿš:�Ùÿš:�Ûÿš:�Ýÿš:�Þÿ×:�ßÿ×:�àÿ×:�áÿ×:�âÿ×:�ãÿ×:�äÿ×:�åÿ×:�úÿÃ:ÿÃ:ÿÃ: ÿÃ:ÿ×:ÿš:ÿ×:ÿš:ÿ×:ÿš:ÿ×:ÿš:ÿÃ:ÿÃ:ÿ®:!ÿ®:+ÿÃ:-ÿÃ:/ÿÃ:1ÿÃ:3ÿÃ:5ÿÃ:<ÿ×:>ÿ×:@ÿ×:Cÿ…:Dÿš:Fÿš:Gÿ×:Hÿš:Jÿ®:ÿ…: ÿ…:WÿÃ:Xÿ…:Yÿš:_ÿ×:`ÿš:bÿÃ:ÿ…:ÿš:ÿ…: ÿš:!ÿ…:"ÿš:#ÿ…:%ÿ…:&ÿš:'ÿ…:(ÿš:)ÿ…:*ÿš:+ÿ…:,ÿš:-ÿ…:.ÿš:/ÿ…:0ÿš:1ÿ…:2ÿš:3ÿ…:4ÿš:6ÿš:8ÿš::ÿš:<ÿš:@ÿš:Bÿš:Dÿš:Iÿ×:Jÿš:Kÿ×:Lÿš:Mÿ×:Nÿš:Oÿ×:Qÿ×:Rÿš:Sÿ×:Tÿš:Uÿ×:Vÿš:Wÿ×:Xÿš:Yÿ×:Zÿš:[ÿ×:\ÿš:]ÿ×:^ÿš:_ÿ×:`ÿš:bÿÃ:dÿÃ:fÿÃ:hÿÃ:jÿÃ:lÿÃ:nÿÃ;�&ÿì;�*ÿì;�2ÿì;�4ÿì;�‰ÿì;�”ÿì;�•ÿì;�–ÿì;�—ÿì;�˜ÿì;�šÿì;�Èÿì;�Êÿì;�Ìÿì;�Îÿì;�Þÿì;�àÿì;�âÿì;�äÿì;ÿì;ÿì;ÿì;ÿì;Gÿì;_ÿì;Iÿì;Kÿì;Mÿì;Oÿì;Qÿì;Sÿì;Uÿì;Wÿì;Yÿì;[ÿì;]ÿì;_ÿì=�&ÿì=�*ÿì=�2ÿì=�4ÿì=�‰ÿì=�”ÿì=�•ÿì=�–ÿì=�—ÿì=�˜ÿì=�šÿì=�Èÿì=�Êÿì=�Ìÿì=�Îÿì=�Þÿì=�àÿì=�âÿì=�äÿì=ÿì=ÿì=ÿì=ÿì=Gÿì=_ÿì=Iÿì=Kÿì=Mÿì=Oÿì=Qÿì=Sÿì=Uÿì=Wÿì=Yÿì=[ÿì=]ÿì=_ÿì?�&ÿì?�*ÿì?�2ÿì?�4ÿì?�‰ÿì?�”ÿì?�•ÿì?�–ÿì?�—ÿì?�˜ÿì?�šÿì?�Èÿì?�Êÿì?�Ìÿì?�Îÿì?�Þÿì?�àÿì?�âÿì?�äÿì?ÿì?ÿì?ÿì?ÿì?Gÿì?_ÿì?Iÿì?Kÿì?Mÿì?Oÿì?Qÿì?Sÿì?Uÿì?Wÿì?Yÿì?[ÿì?]ÿì?_ÿìC�ÿqC� ÿqC�&ÿ×C�*ÿ×C�- C�2ÿ×C�4ÿ×C�7ÿqC�9ÿ®C�:ÿ®C�<ÿ…C�‰ÿ×C�”ÿ×C�•ÿ×C�–ÿ×C�—ÿ×C�˜ÿ×C�šÿ×C�Ÿÿ…C�Èÿ×C�Êÿ×C�Ìÿ×C�Îÿ×C�Þÿ×C�àÿ×C�âÿ×C�äÿ×Cÿ×Cÿ×Cÿ×Cÿ×C$ÿqC&ÿqC6ÿ®C8ÿ…C:ÿ…CGÿ×Cúÿ®Cüÿ®Cþÿ®C�ÿ…CÿqC ÿqC_ÿ×CIÿ×CKÿ×CMÿ×COÿ×CQÿ×CSÿ×CUÿ×CWÿ×CYÿ×C[ÿ×C]ÿ×C_ÿ×Coÿ…Cqÿ…Csÿ…CÿqD�ÿìD� ÿìDÿìD ÿìE�-�{G�ÿ®G�ÿ®G�$ÿ×G�7ÿÃG�9ÿìG�:ÿìG�;ÿ×G�<ÿìG�=ÿìG�‚ÿ×G�ƒÿ×G�„ÿ×G�…ÿ×G�†ÿ×G�‡ÿ×G�ŸÿìG�Âÿ×G�Äÿ×G�Æÿ×G$ÿÃG&ÿÃG6ÿìG8ÿìG:ÿìG;ÿìG=ÿìG?ÿìGCÿ×G ÿìGúÿìGüÿìGþÿìG�ÿìGÿ®G ÿ®GXÿ×Gÿ×Gÿ×G!ÿ×G#ÿ×G%ÿ×G'ÿ×G)ÿ×G+ÿ×G-ÿ×G/ÿ×G1ÿ×G3ÿ×GoÿìGqÿìGsÿìGÿÃV�ÿqV� ÿqVfÿ×Vmÿ×VqÿqVrÿ…Vsÿ×Vuÿ®Vxÿ…VÿqV ÿqVTÿ…[�ÿ®[�ÿ®[Vÿ×[_ÿ×[bÿ×[dÿì[iÿ×[pÿì[qÿÃ[rÿì[tÿ×[uÿì[xÿì[ˆÿì[ÿ®[ ÿ®[Tÿì\�ÿ…\�ÿ…\Vÿ…\_ÿ…\bÿ…\fÿ×\iÿ…\mÿ×\sÿÃ\vÿì\yÿš\zÿ®\{ÿÃ\|ÿÃ\}ÿÃ\~ÿš\ÿÃ\‚ÿ®\„ÿÃ\†ÿÃ\‡ÿÃ\‰ÿÃ\Œÿš\Žÿš\ÿš\ÿš\’ÿÃ\“ÿš\•ÿÃ\–ÿÃ\˜ÿÃ\™ÿš\šÿÃ\›ÿÃ\ÿ…\ ÿ…\!ÿì]qÿ×]rÿì]xÿì]Tÿì^�ÿ×^� ÿ×^ÿ×^ ÿ×_�ÿq_� ÿq_fÿ×_mÿ×_qÿq_rÿ…_sÿ×_uÿ®_xÿ…_ÿq_ ÿq_Tÿ…`�ÿ®`�ÿ®`Vÿ×`_ÿ×`bÿ×`iÿ×`tÿ×`ÿ®` ÿ®a�ÿ…a�ÿ®a�ÿ…aVÿ\a_ÿ\abÿ\afÿÃaiÿ\amÿÃasÿšavÿÃayÿqazÿša{ÿša|ÿ®a}ÿša~ÿqa€ÿ×aÿÃa‚ÿša„ÿša†ÿ®a‡ÿša‰ÿšaŠÿ×aŒÿqaŽÿšaÿqaÿqa’ÿša“ÿqa”ÿ×a•ÿša–ÿša˜ÿša™ÿqašÿša›ÿšaÿ®aÿ®aÿ®aÿ…a ÿ…a!ÿÃaSÿ×b�ÿqb� ÿqbfÿ×bmÿ×bqÿqbrÿ…bsÿ×buÿ®bxÿ…bÿqb ÿqbTÿ…dfÿìdmÿìdsÿÃf�ÿ®f�ÿ®fVÿ×f_ÿ×fbÿ×fdÿìfiÿ×fpÿìfqÿÃfrÿìftÿ×fuÿìfxÿìfˆÿìfÿ®f ÿ®fTÿìhfÿ×hmÿ×hsÿÃhÿìh‘ÿìi�ÿqi� ÿqifÿ×imÿ×iqÿqirÿ…isÿ×iuÿ®ixÿ…iÿqi ÿqiTÿ…m�ÿ®m�ÿ®mVÿ×m_ÿ×mbÿ×mdÿìmiÿ×mpÿìmqÿÃmrÿìmtÿ×muÿìmxÿìmˆÿìmÿ®m ÿ®mTÿìo�þöo�þöoVÿšo_ÿšobÿšodÿìoiÿšotÿ×oˆÿ×oþöo þöq�ÿ…q�ÿ®q�ÿ…qVÿ\q_ÿ\qbÿ\qfÿÃqiÿ\qmÿÃqsÿšqvÿÃqyÿqqzÿšq{ÿšq|ÿ®q}ÿšq~ÿqq€ÿ×qÿÃq‚ÿšq„ÿšq†ÿ®q‡ÿšq‰ÿšqŠÿ×qŒÿqqŽÿšqÿqqÿqq’ÿšq“ÿqq”ÿ×q•ÿšq–ÿšq˜ÿšq™ÿqqšÿšq›ÿšqÿ®qÿ®qÿ®qÿ…q ÿ…q!ÿÃqSÿ×r�ÿ…r�ÿ…rVÿ…r_ÿ…rbÿ…rfÿ×riÿ…rmÿ×rsÿÃrvÿìryÿšrzÿ®r{ÿÃr|ÿÃr}ÿÃr~ÿšrÿÃr‚ÿ®r„ÿÃr†ÿÃr‡ÿÃr‰ÿÃrŒÿšrŽÿšrÿšrÿšr’ÿÃr“ÿšr•ÿÃr–ÿÃr˜ÿÃr™ÿšršÿÃr›ÿÃrÿ…r ÿ…r!ÿìs�ÿšs�ÿšsVÿ×s_ÿ×sbÿ×sdÿÃsiÿ×spÿìsqÿ®srÿÃstÿìsxÿÃsˆÿìsÿšs ÿšsTÿÃtfÿ×tmÿ×tsÿÃtÿìt‘ÿìu�ÿ…u�ÿ…uVÿ®u_ÿ®ubÿ®ufÿìuiÿ®umÿìuÿ…u ÿ…vqÿ×vrÿìvxÿìvTÿìx�ÿ…x�ÿ…xVÿ…x_ÿ…xbÿ…xfÿ×xiÿ…xmÿ×xsÿÃxvÿìxyÿšxzÿ®x{ÿÃx|ÿÃx}ÿÃx~ÿšxÿÃx‚ÿ®x„ÿÃx†ÿÃx‡ÿÃx‰ÿÃxŒÿšxŽÿšxÿšxÿšx’ÿÃx“ÿšx•ÿÃx–ÿÃx˜ÿÃx™ÿšxšÿÃx›ÿÃxÿ…x ÿ…x!ÿìyˆ�){�ÿì{� ÿì{ÿì{ ÿì|�ÿ®|� ÿ®|ÿì|‘ÿì|ÿ®| ÿ®~ˆ�)€�ÿ®€�ÿ®€ˆÿì€ÿ®€ ÿ®ƒ�ÿšƒyÿ׃~ÿ׃ÿ׃Œÿ׃ÿ׃ÿ׃ÿ׃‘ÿ׃“ÿ׃™ÿ׃ÿšƒÿšƒÿš„�ÿì„� ÿì„ÿì„ ÿì…�ÿ×…�ÿ×…ÿ×… ÿ׆�ÿ®†� ÿ®†ÿ솑ÿì†ÿ®† ÿ®‡yÿׇ~ÿׇŒÿׇÿׇÿׇ“ÿׇ™ÿ׈�ÿ…ˆ� ÿ…ˆyÿìˆ~ÿ숀ÿ׈Šÿ׈Œÿìˆÿ׈ÿìˆÿ숑ÿ׈“ÿ숙ÿìˆÿ…ˆ ÿ…Š�ÿ®Š�ÿ®ŠˆÿìŠÿ®Š ÿ®Œ�ÿìŒ� ÿ쌀ÿ׌Šÿ׌ÿìŒ ÿìŽ�ÿìŽ� ÿ쎀ÿ׎Šÿ׎ÿìŽ ÿì�ÿì�ÿìÿì ÿì“�ÿì“� ÿì“€ÿדŠÿדÿì“ ÿì”�ÿÔ�ÿ×”�ÿÔyÿ×”~ÿ×”ÿ×”Œÿ×”ÿ×”ÿ×”“ÿ×”™ÿ×”ÿ×”ÿ×”ÿ×”ÿÔ ÿ×�ÿ×—� ÿ×—ÿ×— ÿ×™�ÿì™� ÿ와ÿ×™Šÿ×™ÿì™ ÿì�ÿ®� ÿ®ÿ…¦ÿ…¨ÿ×¼ÿš½ÿ×ÁÿšÄÿ…Üÿ×Ýÿ×áÿ×äÿ×öÿ×ÿ® ÿ®nÿ®|ÿš€ÿ®‚ÿ®—ÿ®›ÿ®§ÿ®©ÿ…ªÿ×µÿš¶ÿ×·ÿš¸ÿ×¹ÿšºÿ×½ÿ…¾ÿ׿ÿšÀÿ×ÁÿšÂÿ×ÔÿšÕÿ×÷ÿ×øÿ×ùÿ×úÿ×ûÿ×üÿ×ýÿšþÿ×ÿ® ÿšÿÃÿšÿÃÿ…ÿמ�ÿ…ž�ÿ®ž�ÿ…žŸÿמ¤ÿšžªÿqž®ÿšžµÿšž¸ÿמ»ÿמ¼�)ž¾ÿ®žÌÿšžÍÿšžÎÿ…žÏÿqžÐÿמÑÿמÒÿšžÓÿšžÔÿšžÕÿ…žÖÿšž×ÿšžØÿqžÙÿšžÚÿšžÛÿqžÜÿ®žÝÿ®žÞÿqžßÿמàÿšžáÿšžâÿšžãÿšžäÿ®žåÿšžæÿšžçÿמèÿšžéÿÞêÿqžìÿšžíÿqžîÿ…žòÿ…žóÿšžõÿšžöÿ®ž÷ÿšžùÿšžÿ®žÿ®žÿ®žÿ…ž ÿ…žjÿqžkÿšžlÿמmÿמqÿšžrÿqžsÿ…žuÿšžwÿšžyÿšž}ÿšž~ÿמÿqžÿמƒÿמ„ÿמ…ÿqž†ÿמ‡ÿqžˆÿמ‰ÿqžŠÿמ‹ÿמŒÿמÿqž–ÿšžšÿšžžÿšž ÿמ¢ÿמ¤ÿšž¦ÿšžªÿ®ž¬ÿšž®ÿšž°ÿšž±ÿמ²ÿqž³ÿמ´ÿqžµ�)ž¶ÿ®ž¸ÿ®žºÿ®ž¼ÿמ¾ÿ®žÀÿšžÂÿšžÄÿšžÅÿšžÆÿqžÇÿšžÈÿqžËÿמÍÿšžÎÿšžÏÿ…žÑÿšžÓÿšžÕÿšž×ÿšžÙÿqžÛÿqžÝÿqžàÿqžæÿמèÿמêÿÞìÿšžîÿšžïÿמðÿqžñÿמòÿqžóÿמôÿqžöÿמøÿ®žúÿ®žüÿ®žþÿšž�ÿšžÿšžÿמÿמ ÿqž ÿqž ÿqž ÿqžÿšžÿšžÿšžÿ…žÿšžÿמÿqžÿ®žÿqžÿšžÿ…ŸŸÿן¸ÿן»ÿן¾ÿןáÿןlÿן~ÿן„ÿן†ÿןˆÿןŠÿןŒÿן±ÿן³ÿןÀÿןÂÿןÅÿןÇÿןÕÿןïÿןñÿןóÿןþÿן ÿן ÿןÿןÿןÿ× ÿ× ÿפ�ÿ®¤� ÿ®¤ÿ…¤¦ÿ…¤¨ÿפ¼ÿš¤½ÿפÁÿš¤Äÿ…¤ÜÿפÝÿפáÿפäÿפöÿפÿ®¤ ÿ®¤nÿ®¤|ÿš¤€ÿ®¤‚ÿ®¤—ÿ®¤›ÿ®¤§ÿ®¤©ÿ…¤ªÿפµÿš¤¶ÿפ·ÿš¤¸ÿפ¹ÿš¤ºÿפ½ÿ…¤¾ÿפ¿ÿš¤ÀÿפÁÿš¤ÂÿפÔÿš¤Õÿפ÷ÿפøÿפùÿפúÿפûÿפüÿפýÿš¤þÿפÿ®¤ ÿš¤ÿäÿš¤ÿäÿ…¤ÿ×¥�ÿ®¥� ÿ®¥ÿ…¥¦ÿ…¥¨ÿ×¥¼ÿš¥½ÿ×¥Áÿš¥Äÿ…¥Üÿ×¥Ýÿ×¥áÿ×¥äÿ×¥öÿ×¥ÿ®¥ ÿ®¥nÿ®¥|ÿš¥€ÿ®¥‚ÿ®¥—ÿ®¥›ÿ®¥§ÿ®¥©ÿ…¥ªÿ×¥µÿš¥¶ÿ×¥·ÿš¥¸ÿ×¥¹ÿš¥ºÿ×¥½ÿ…¥¾ÿ×¥¿ÿš¥Àÿ×¥Áÿš¥Âÿ×¥Ôÿš¥Õÿ×¥÷ÿ×¥øÿ×¥ùÿ×¥úÿ×¥ûÿ×¥üÿ×¥ýÿš¥þÿ×¥ÿ®¥ ÿš¥ÿÃ¥ÿš¥ÿÃ¥ÿ…¥ÿצ�ÿ®¦� ÿ®¦ÿ…¦¦ÿ…¦¨ÿצ¼ÿš¦½ÿצÁÿš¦Äÿ…¦ÜÿצÝÿצáÿצäÿצöÿצÿ®¦ ÿ®¦nÿ®¦|ÿš¦€ÿ®¦‚ÿ®¦—ÿ®¦›ÿ®¦§ÿ®¦©ÿ…¦ªÿצµÿš¦¶ÿצ·ÿš¦¸ÿצ¹ÿš¦ºÿצ½ÿ…¦¾ÿצ¿ÿš¦ÀÿצÁÿš¦ÂÿצÔÿš¦Õÿצ÷ÿצøÿצùÿצúÿצûÿצüÿצýÿš¦þÿצÿ®¦ ÿš¦ÿæÿš¦ÿæÿ…¦ÿ×§Ÿÿ×§¸ÿ×§»ÿ×§¾ÿ×§Áÿ×§áÿ×§lÿ×§|ÿ×§~ÿ×§„ÿ×§†ÿ×§ˆÿ×§Šÿ×§Œÿ×§±ÿ×§³ÿ×§¿ÿ×§Àÿ×§Áÿ×§Âÿ×§Åÿš§Çÿš§Ôÿ×§Õÿ×§ïÿ×§ñÿ×§óÿ×§ýÿ×§þÿ×§ ÿ×§ ÿ×§ÿ×§ÿ×§ÿ×§ÿì¨�ÿ…¨�ÿ…¨Ÿÿ쨤ÿš¨ªÿq¨®ÿš¨µÿš¨¸ÿ쨻ÿ쨾ÿèÉÿì¨Îÿ®¨ÏÿרÕÿ®¨ØÿרÛÿרÞÿרáÿרêÿרë�f¨íÿרîÿì¨òÿ®¨ô�f¨ÿ…¨ ÿ…¨jÿרlÿì¨rÿq¨sÿ®¨~ÿì¨ÿר„ÿ쨅ÿר†ÿ쨇ÿרˆÿ쨉ÿרŠÿ쨌ÿì¨ÿר˜�f¨¨�f¨±ÿ쨲ÿר³ÿ쨴ÿרÀÿרÂÿרÅÿרÆÿèÇÿרÈÿèÎÿš¨Ïÿ®¨ÕÿרÙÿq¨Ûÿq¨Ýÿq¨àÿרïÿì¨ðÿרñÿì¨òÿרóÿì¨ôÿרþÿר ÿq¨ ÿר ÿq¨ ÿרÿš¨ÿ®¨ÿì¨ÿרÿרÿš¨ÿ®ª�ÿqª� ÿqªÿšª¦ÿšª¼ÿqª¾ÿתÁÿšªÄÿšªÜÿתáÿתäÿתÿqª ÿqªnÿת|ÿšª€ÿ®ª‚ÿ®ª—ÿת›ÿת§ÿת©ÿšªªÿתµÿqª¶ÿת·ÿ…ª¹ÿ…ª½ÿšª¾ÿת¿ÿšªÀÿתÁÿšªÂÿתÅÿšªÇÿšªÔÿšªÕÿתáÿתãÿתýÿšªþÿתÿת ÿqªÿתÿqªÿתÿšªÿ׫�ÿ׫� ÿ׫ªÿì«Áÿ׫ÿ׫ ÿ׫rÿì«|ÿ׫¿ÿ׫Áÿ׫Åÿ׫Çÿ׫Ôÿ׫Ùÿì«Ûÿì«Ýÿì«ýÿ׬�ÿ®¬�ÿ®¬ÿ®¬ ÿ®¬€ÿ쬂ÿ쬷ÿ쬹ÿì¬ ÿ׬ÿ×­�ÿ…­�ÿ®­�ÿ…­Ÿÿ×­¤ÿš­ªÿq­®ÿš­µÿš­¸ÿ×­»ÿ×­¼�)­¾ÿ®­Ìÿš­Íÿš­Îÿ…­Ïÿq­Ðÿ×­Ñÿ×­Òÿš­Óÿš­Ôÿš­Õÿ…­Öÿš­×ÿš­Øÿq­Ùÿš­Úÿš­Ûÿq­Üÿ®­Ýÿ®­Þÿq­ßÿ×­àÿš­áÿš­âÿš­ãÿš­äÿ®­åÿš­æÿš­çÿ×­èÿš­éÿíêÿq­ìÿš­íÿq­îÿ…­òÿ…­óÿš­õÿš­öÿ®­÷ÿš­ùÿš­ÿ®­ÿ®­ÿ®­ÿ…­ ÿ…­jÿq­kÿš­lÿ×­mÿ×­qÿš­rÿq­sÿ…­uÿš­wÿš­yÿš­}ÿš­~ÿ×­ÿq­ÿ×­ƒÿ×­„ÿ×­…ÿq­†ÿ×­‡ÿq­ˆÿ×­‰ÿq­Šÿ×­‹ÿ×­Œÿ×­ÿq­–ÿš­šÿš­žÿš­ ÿ×­¢ÿ×­¤ÿš­¦ÿš­ªÿ®­¬ÿš­®ÿš­°ÿš­±ÿ×­²ÿq­³ÿ×­´ÿq­µ�)­¶ÿ®­¸ÿ®­ºÿ®­¼ÿ×­¾ÿ®­Àÿš­Âÿš­Äÿš­Åÿš­Æÿq­Çÿš­Èÿq­Ëÿ×­Íÿš­Îÿš­Ïÿ…­Ñÿš­Óÿš­Õÿš­×ÿš­Ùÿq­Ûÿq­Ýÿq­àÿq­æÿ×­èÿ×­êÿíìÿš­îÿš­ïÿ×­ðÿq­ñÿ×­òÿq­óÿ×­ôÿq­öÿ×­øÿ®­úÿ®­üÿ®­þÿš­�ÿš­ÿš­ÿ×­ÿ×­ ÿq­ ÿq­ ÿq­ ÿq­ÿš­ÿš­ÿš­ÿ…­ÿš­ÿ×­ÿq­ÿ®­ÿq­ÿš­ÿ…®£�á®ê�)®ÿ×®ÿ×°Ÿÿ×°¸ÿ×°»ÿ×°¾ÿ×°Áÿ×°áÿ×°lÿ×°|ÿ×°~ÿ×°„ÿ×°†ÿ×°ˆÿ×°Šÿ×°Œÿ×°±ÿ×°³ÿ×°¿ÿ×°Àÿ×°Áÿ×°Âÿ×°Åÿš°Çÿš°Ôÿ×°Õÿ×°ïÿ×°ñÿ×°óÿ×°ýÿ×°þÿ×° ÿ×° ÿ×°ÿ×°ÿ×°ÿ×°ÿì±�ÿ®±�ÿ®±ÿ®± ÿ®±€ÿ챂ÿì±·ÿì±¹ÿì± ÿ×±ÿ×´Ÿÿ×´¸ÿ×´»ÿ×´¾ÿ×´Áÿ×´áÿ×´lÿ×´|ÿ×´~ÿ×´„ÿ×´†ÿ×´ˆÿ×´Šÿ×´Œÿ×´±ÿ×´³ÿ×´¿ÿ×´Àÿ×´Áÿ×´Âÿ×´Åÿš´Çÿš´Ôÿ×´Õÿ×´ïÿ×´ñÿ×´óÿ×´ýÿ×´þÿ×´ ÿ×´ ÿ×´ÿ×´ÿ×´ÿ×´ÿì¸�ÿ®¸�ÿ®¸ÿ츤ÿ׸¦ÿ츨ÿ׸ªÿ׸®ÿ׸°ÿ׸±ÿ층ÿ׸¼ÿø½ÿ׸¿ÿ׸Áÿ׸Äÿì¸Çÿì¸Îÿì¸Õÿì¸òÿì¸ÿ®¸ ÿ®¸rÿ׸sÿì¸zÿì¸|ÿ׸€ÿ츂ÿ츟ÿ׸¡ÿ츩ÿ층ÿø·ÿ츹ÿ츻ÿ׸½ÿ츿ÿ׸Áÿ׸Êÿ׸Îÿ׸Ïÿì¸Ôÿ׸Ùÿ׸Ûÿ׸Ýÿ׸åÿ׸çÿì¸õÿì¸÷ÿ׸ùÿ׸ûÿ׸ýÿ׸ÿ׸ÿ׸ ÿ׸ÿ׸ÿ׸ÿì¸ÿì¸ÿ׸ÿìº�þöº�þöº¤ÿ…ºªÿšº®ÿ…º°ÿ׺µÿ…º¿ÿ׺ÎÿšºÕÿšºòÿšºþöº þöºrÿšºsÿšºvÿ캟ÿ׺»ÿ׺Êÿ׺Îÿ…ºÏÿšºÙÿšºÛÿšºÝÿšºåÿ׺ÿ׺ÿ׺ ÿ®º ÿ®ºÿ…ºÿšºÿ…ºÿš»Ÿÿ×»¸ÿ×»»ÿ×»¾ÿ×»áÿ×»lÿ×»~ÿ×»„ÿ×»†ÿ×»ˆÿ×»Šÿ×»Œÿ×»±ÿ×»³ÿ×»Àÿ×»Âÿ×»Åÿ×»Çÿ×»Õÿ×»ïÿ×»ñÿ×»óÿ×»þÿ×» ÿ×» ÿ×»ÿ×»ÿ×»ÿ×¼�ÿ…¼�ÿ®¼�ÿ…¼Ÿÿ×¼¤ÿš¼ªÿq¼®ÿš¼µÿš¼¸ÿ×¼»ÿ×¼¼�)¼¾ÿ®¼Ìÿš¼Íÿš¼Îÿ…¼Ïÿq¼Ðÿ×¼Ñÿ×¼Òÿš¼Óÿš¼Ôÿš¼Õÿ…¼Öÿš¼×ÿš¼Øÿq¼Ùÿš¼Úÿš¼Ûÿq¼Üÿ®¼Ýÿ®¼Þÿq¼ßÿ×¼àÿš¼áÿš¼âÿš¼ãÿš¼äÿ®¼åÿš¼æÿš¼çÿ×¼èÿš¼éÿüêÿq¼ìÿš¼íÿq¼îÿ…¼òÿ…¼óÿš¼õÿš¼öÿ®¼÷ÿš¼ùÿš¼ÿ®¼ÿ®¼ÿ®¼ÿ…¼ ÿ…¼jÿq¼kÿš¼lÿ×¼mÿ×¼qÿš¼rÿq¼sÿ…¼uÿš¼wÿš¼yÿš¼}ÿš¼~ÿ×¼ÿq¼ÿ×¼ƒÿ×¼„ÿ×¼…ÿq¼†ÿ×¼‡ÿq¼ˆÿ×¼‰ÿq¼Šÿ×¼‹ÿ×¼Œÿ×¼ÿq¼–ÿš¼šÿš¼žÿš¼ ÿ×¼¢ÿ×¼¤ÿš¼¦ÿš¼ªÿ®¼¬ÿš¼®ÿš¼°ÿš¼±ÿ×¼²ÿq¼³ÿ×¼´ÿq¼µ�)¼¶ÿ®¼¸ÿ®¼ºÿ®¼¼ÿ×¼¾ÿ®¼Àÿš¼Âÿš¼Äÿš¼Åÿš¼Æÿq¼Çÿš¼Èÿq¼Ëÿ×¼Íÿš¼Îÿš¼Ïÿ…¼Ñÿš¼Óÿš¼Õÿš¼×ÿš¼Ùÿq¼Ûÿq¼Ýÿq¼àÿq¼æÿ×¼èÿ×¼êÿüìÿš¼îÿš¼ïÿ×¼ðÿq¼ñÿ×¼òÿq¼óÿ×¼ôÿq¼öÿ×¼øÿ®¼úÿ®¼üÿ®¼þÿš¼�ÿš¼ÿš¼ÿ×¼ÿ×¼ ÿq¼ ÿq¼ ÿq¼ ÿq¼ÿš¼ÿš¼ÿš¼ÿ…¼ÿš¼ÿ×¼ÿq¼ÿ®¼ÿq¼ÿš¼ÿ…½�ÿ…½�ÿ…½Ÿÿ콤ÿš½ªÿq½®ÿš½µÿš½¸ÿì½»ÿì½¾ÿýÉÿì½Îÿ®½Ïÿ×½Õÿ®½Øÿ×½Ûÿ×½Þÿ×½áÿ×½êÿ×½ë�f½íÿ×½îÿì½òÿ®½ô�f½ÿ…½ ÿ…½jÿ×½lÿì½rÿq½sÿ®½~ÿì½ÿ×½„ÿì½…ÿ×½†ÿ콇ÿ×½ˆÿ콉ÿ×½Šÿ콌ÿì½ÿ×½˜�f½¨�f½±ÿì½²ÿ×½³ÿì½´ÿ×½Àÿ×½Âÿ×½Åÿ×½ÆÿýÇÿ×½ÈÿýÎÿš½Ïÿ®½Õÿ×½Ùÿq½Ûÿq½Ýÿq½àÿ×½ïÿì½ðÿ×½ñÿì½òÿ×½óÿì½ôÿ×½þÿ×½ ÿq½ ÿ×½ ÿq½ ÿ×½ÿš½ÿ®½ÿì½ÿ×½ÿ×½ÿš½ÿ®¾�ÿ®¾�ÿ®¾ÿ×¾¤ÿ×¾¦ÿ×¾¨ÿþªÿ×¾®ÿ×¾°ÿ×¾±ÿ×¾µÿ×¾¼ÿþ½ÿþ¿ÿ×¾Äÿ×¾Çÿ×¾Îÿì¾Õÿì¾òÿì¾ÿ®¾ ÿ®¾rÿ×¾sÿì¾zÿ×¾€ÿ쾂ÿ쾟ÿ×¾¡ÿ×¾©ÿ×¾µÿþ·ÿþ¹ÿþ»ÿ×¾½ÿ×¾Êÿ×¾Îÿ×¾Ïÿì¾Ùÿ×¾Ûÿ×¾Ýÿ×¾åÿ×¾çÿ×¾õÿ×¾÷ÿþùÿþûÿþÿ×¾ÿ×¾ ÿ×¾ÿ×¾ÿ×¾ÿì¾ÿ×¾ÿ×¾ÿ쿟ÿ׿¸ÿ׿»ÿ׿¾ÿ׿Áÿ׿áÿ׿lÿ׿|ÿ׿~ÿ׿„ÿ׿†ÿ׿ˆÿ׿Šÿ׿Œÿ׿±ÿ׿³ÿ׿¿ÿ׿Àÿ׿Áÿ׿Âÿ׿Åÿš¿Çÿš¿Ôÿ׿Õÿ׿ïÿ׿ñÿ׿óÿ׿ýÿ׿þÿ׿ ÿ׿ ÿ׿ÿ׿ÿ׿ÿ׿ÿìÀ£�áÀê�)Àÿ×Àÿ×ã�áÃê�)Ãÿ×Ãÿ×Ä�ÿ®Ä� ÿ®Äÿ…Ħÿ…Ĩÿ×ļÿšÄ½ÿ×ÄÁÿšÄÄÿ…ÄÜÿ×ÄÝÿ×Äáÿ×Ääÿ×Äöÿ×Äÿ®Ä ÿ®Änÿ®Ä|ÿšÄ€ÿ®Ä‚ÿ®Ä—ÿ®Ä›ÿ®Ä§ÿ®Ä©ÿ…Īÿ×ĵÿšÄ¶ÿ×Ä·ÿšÄ¸ÿ×ĹÿšÄºÿ׼ÿ…ľÿ×Ä¿ÿšÄÀÿ×ÄÁÿšÄÂÿ×ÄÔÿšÄÕÿ×Ä÷ÿ×Äøÿ×Äùÿ×Äúÿ×Äûÿ×Äüÿ×ÄýÿšÄþÿ×Äÿ®Ä ÿšÄÿÃÄÿšÄÿÃÄÿ…Äÿׯ�ÿ®Æ� ÿ®Æÿ…Ʀÿ…ƨÿׯ¼ÿšÆ½ÿׯÁÿšÆÄÿ…ÆÜÿׯÝÿׯáÿׯäÿׯöÿׯÿ®Æ ÿ®Ænÿ®Æ|ÿšÆ€ÿ®Æ‚ÿ®Æ—ÿ®Æ›ÿ®Æ§ÿ®Æ©ÿ…ƪÿׯµÿšÆ¶ÿׯ·ÿšÆ¸ÿׯ¹ÿšÆºÿׯ½ÿ…ƾÿׯ¿ÿšÆÀÿׯÁÿšÆÂÿׯÔÿšÆÕÿׯ÷ÿׯøÿׯùÿׯúÿׯûÿׯüÿׯýÿšÆþÿׯÿ®Æ ÿšÆÿÃÆÿšÆÿÃÆÿ…Æÿ×Ç�ÿ®Ç�ÿ®ÇÿìǤÿ×ǦÿìǨÿ×Ǫÿ×Ç®ÿ×ǰÿ×DZÿìǵÿ×ǼÿÃǽÿ×Ç¿ÿ×ÇÁÿ×ÇÄÿìÇÇÿìÇÎÿìÇÕÿìÇòÿìÇÿ®Ç ÿ®Çrÿ×ÇsÿìÇzÿìÇ|ÿ×Ç€ÿìÇ‚ÿìÇŸÿ×Ç¡ÿìÇ©ÿìǵÿÃÇ·ÿìǹÿìÇ»ÿ×ǽÿìÇ¿ÿ×ÇÁÿ×ÇÊÿ×ÇÎÿ×ÇÏÿìÇÔÿ×ÇÙÿ×ÇÛÿ×ÇÝÿ×Çåÿ×ÇçÿìÇõÿìÇ÷ÿ×Çùÿ×Çûÿ×Çýÿ×Çÿ×Çÿ×Ç ÿ×Çÿ×Çÿ×ÇÿìÇÿìÇÿ×ÇÿìÈ�ÿ®È�ÿ®ÈÿìȤÿ×ȦÿìȨÿ×Ȫÿ×È®ÿ×Ȱÿ×ȱÿìȵÿ×ȼÿÃȽÿ×È¿ÿ×ÈÁÿ×ÈÄÿìÈÇÿìÈÎÿìÈÕÿìÈòÿìÈÿ®È ÿ®Èrÿ×ÈsÿìÈzÿìÈ|ÿ×È€ÿìÈ‚ÿìÈŸÿ×È¡ÿìÈ©ÿìȵÿÃÈ·ÿìȹÿìÈ»ÿ×ȽÿìÈ¿ÿ×ÈÁÿ×ÈÊÿ×ÈÎÿ×ÈÏÿìÈÔÿ×ÈÙÿ×ÈÛÿ×ÈÝÿ×Èåÿ×ÈçÿìÈõÿìÈ÷ÿ×Èùÿ×Èûÿ×Èýÿ×Èÿ×Èÿ×È ÿ×Èÿ×Èÿ×ÈÿìÈÿìÈÿ×ÈÿìÊ�ÿìÊ� ÿìÊÿìÊ ÿìÌé�)Í�ÿšÍ�ÿ×Í�ÿšÍÎÿÃÍÏÿìÍÕÿÃÍØÿìÍÛÿìÍÞÿìÍêÿìÍíÿìÍòÿÃÍÿ×Íÿ×Íÿ×ÍÿšÍ ÿšÍjÿìÍsÿÃÍÿìÍ…ÿì͇ÿì͉ÿìÍÿìͲÿìÍ´ÿìÍÏÿÃÍàÿìÍðÿìÍòÿìÍôÿìÍ ÿìÍ ÿìÍÿÃÍÿìÍÿìÍÿÃÎ�ÿìÎ� ÿìÎÿìÎ ÿìÏ�ÿìÏ� ÿìÏÿìÏ ÿìÐÏÿ×ÐØÿ×ÐÛÿ×ÐÞÿ×Ðáÿ×Ðêÿ×Ðíÿ×Ðjÿ×Ðÿ×Ð…ÿ×Їÿ×Љÿ×Ðÿ×вÿ×дÿ×ÐÀÿ×ÐÂÿ×ÐÆÿ×ÐÈÿ×ÐÕÿ×Ðàÿ×Ððÿ×Ðòÿ×Ðôÿ×Ðþÿ×Ð ÿ×Ð ÿ×Ðÿ×Ðÿ×Ñé�)ÔÏÿ×ÔØÿ×ÔÛÿ×ÔÞÿ×Ôáÿ×Ôêÿ×Ôíÿ×Ôjÿ×Ôÿ×Ô…ÿ×Ô‡ÿ×Ô‰ÿ×Ôÿ×Ô²ÿ×Ô´ÿ×ÔÀÿ×ÔÂÿ×ÔÆÿ×ÔÈÿ×ÔÕÿ×Ôàÿ×Ôðÿ×Ôòÿ×Ôôÿ×Ôþÿ×Ô ÿ×Ô ÿ×Ôÿ×Ôÿר�ÿìØ� ÿìØÐÿרÜÿìØÝÿìØßÿרáÿìØäÿìØöÿìØÿìØ ÿìØ ÿרªÿìØ¶ÿìØ¼ÿר¾ÿìØÀÿìØÂÿìØËÿרÕÿìØæÿרøÿìØúÿìØüÿìØþÿìØÿרÿרÿìØÿìØÿìÚ�ÿìÚ� ÿìÚÐÿ×ÚÜÿìÚÝÿìÚßÿ×ÚáÿìÚäÿìÚöÿìÚÿìÚ ÿìÚ ÿ×ÚªÿìÚ¶ÿìÚ¼ÿ×Ú¾ÿìÚÀÿìÚÂÿìÚËÿ×ÚÕÿìÚæÿ×ÚøÿìÚúÿìÚüÿìÚþÿìÚÿ×Úÿ×ÚÿìÚÿìÚÿìÜ�ÿšÜ�ÿ×Ü�ÿšÜÎÿÃÜÏÿìÜÕÿÃÜØÿìÜÛÿìÜÞÿìÜêÿìÜíÿìÜòÿÃÜÿ×Üÿ×Üÿ×ÜÿšÜ ÿšÜjÿìÜsÿÃÜÿìÜ…ÿì܇ÿì܉ÿìÜÿìܲÿìÜ´ÿìÜÏÿÃÜàÿìÜðÿìÜòÿìÜôÿìÜ ÿìÜ ÿìÜÿÃÜÿìÜÿìÜÿÃÝ�ÿ®Ý�ÿ®ÝÎÿ×ÝÕÿ×Ýòÿ×Ýÿ®Ý ÿ®Ýsÿ×ÝÏÿ×Ýÿ×Ýÿ×Þ�ÿìÞ� ÿìÞÐÿ×ÞÜÿìÞÝÿìÞßÿ×ÞáÿìÞäÿìÞöÿìÞÿìÞ ÿìÞ ÿ×ÞªÿìÞ¶ÿìÞ¼ÿ×Þ¾ÿìÞÀÿìÞÂÿìÞËÿ×ÞÕÿìÞæÿ×ÞøÿìÞúÿìÞüÿìÞþÿìÞÿ×Þÿ×ÞÿìÞÿìÞÿìßÏÿ×ߨÿ×ßÛÿ×ßÞÿ×ßáÿ×ßêÿ×ßíÿ×ßjÿ×ßÿ×ß…ÿ×߇ÿ×߉ÿ×ßÿ×ß²ÿ×ß´ÿ×ßÀÿ×ßÂÿ×߯ÿ×ßÈÿ×ßÕÿ×ßàÿ×ßðÿ×ßòÿ×ßôÿ×ßþÿ×ß ÿ×ß ÿ×ßÿ×ßÿ×à�ÿìà� ÿìàÿìà ÿìã�ÿìã� ÿìãÿìã ÿìä�ÿ…ä� ÿ…äÐÿ×äÜÿšäÝÿÃäßÿ×äáÿ®ääÿšäöÿÃäÿ…ä ÿ…ämÿ×äÿ×äƒÿ×ä‹ÿ×ä ÿ×äªÿšä¶ÿšä¸ÿÃäºÿÃä¼ÿ×ä¾ÿšäÀÿ®äÂÿ®äÆÿ×äÈÿ×äËÿ×äÕÿ®äæÿ×äêÿ×äøÿÃäúÿÃäüÿÃäþÿ®äÿ×äÿ×äÿšäÿšäÿšæ�ÿ…æ� ÿ…æÐÿ׿ÜÿšæÝÿÃæßÿ׿áÿ®æäÿšæöÿÃæÿ…æ ÿ…æmÿ׿ÿ׿ƒÿ׿‹ÿ׿ ÿ׿ªÿšæ¶ÿšæ¸ÿÃæºÿÃæ¼ÿ׿¾ÿšæÀÿ®æÂÿ®æÆÿ׿Èÿ׿Ëÿ׿Õÿ®ææÿ׿êÿ׿øÿÃæúÿÃæüÿÃæþÿ®æÿ׿ÿ׿ÿšæÿšæÿšç�ÿìç� ÿìçÐÿ×çÜÿìçÝÿìçßÿ×çáÿìçäÿìçöÿìçÿìç ÿìç ÿ×çªÿìç¶ÿìç¼ÿ×ç¾ÿìçÀÿìçÂÿìçËÿ×çÕÿìçæÿ×çøÿìçúÿìçüÿìçþÿìçÿ×çÿ×çÿìçÿìçÿìè�ÿìè� ÿìèÐÿ×èÜÿìèÝÿìèßÿ×èáÿìèäÿìèöÿìèÿìè ÿìè ÿ×èªÿìè¶ÿìè¼ÿ×è¾ÿìèÀÿìèÂÿìèËÿ×èÕÿìèæÿ×èøÿìèúÿìèüÿìèþÿìèÿ×èÿ×èÿìèÿìèÿìê�ÿìê� ÿìêÿìê ÿìë�ÿìë� ÿìëÿìë ÿìëÿ×ëÿ×ì�ÿšì�ÿ×ì�ÿšìÎÿÃìÏÿììÕÿÃìØÿììÛÿììÞÿììêÿììíÿììòÿÃìÿ×ìÿ×ìÿ×ìÿšì ÿšìjÿììsÿÃìÿìì…ÿìì‡ÿìì‰ÿììÿìì²ÿìì´ÿììÏÿÃìàÿììðÿììòÿììôÿìì ÿìì ÿììÿÃìÿììÿììÿÃò�ÿ…ò� ÿ…òÐÿ×òÜÿšòÝÿÃòßÿ×òáÿ®òäÿšòöÿÃòÿ…ò ÿ…òmÿ×òÿ×òƒÿ×ò‹ÿ×ò ÿ×òªÿšò¶ÿšò¸ÿÃòºÿÃò¼ÿ×ò¾ÿšòÀÿ®òÂÿ®òÆÿ×òÈÿ×òËÿ×òÕÿ®òæÿ×òêÿ×òøÿÃòúÿÃòüÿÃòþÿ®òÿ×òÿ×òÿšòÿšòÿšó�ÿ…ó� ÿ…óÐÿ×óÜÿšóÝÿÃóßÿ×óáÿ®óäÿšóöÿÃóÿ…ó ÿ…ómÿ×óÿ×óƒÿ×ó‹ÿ×ó ÿ×óªÿšó¶ÿšó¸ÿÃóºÿÃó¼ÿ×ó¾ÿšóÀÿ®óÂÿ®óÆÿ×óÈÿ×óËÿ×óÕÿ®óæÿ×óêÿ×óøÿÃóúÿÃóüÿÃóþÿ®óÿ×óÿ×óÿšóÿšóÿšô�ÿìô� ÿìôÿìô ÿìôÿ×ôÿ×õÏÿ×õØÿ×õÛÿ×õÞÿ×õáÿ×õêÿ×õíÿ×õjÿ×õÿ×õ…ÿ×õ‡ÿ×õ‰ÿ×õÿ×õ²ÿ×õ´ÿ×õÀÿ×õÂÿ×õÆÿ×õÈÿ×õÕÿ×õàÿ×õðÿ×õòÿ×õôÿ×õþÿ×õ ÿ×õ ÿ×õÿ×õÿ×ö�ÿ®ö�ÿ®öÎÿ×öÕÿ×öòÿ×öÿ®ö ÿ®ösÿ×öÏÿ×öÿ×öÿ×ø�ÿ…ø�ÿ®ø�ÿ…øŸÿ×ø¤ÿšøªÿqø®ÿšøµÿšø¸ÿ×ø»ÿ×ø¼�)ø¾ÿ®øÌÿšøÍÿšøÎÿ…øÏÿqøÐÿ×øÑÿ×øÒÿšøÓÿšøÔÿšøÕÿ…øÖÿšø×ÿšøØÿqøÙÿšøÚÿšøÛÿqøÜÿ®øÝÿ®øÞÿqøßÿ×øàÿšøáÿšøâÿšøãÿšøäÿ®øåÿšøæÿšøçÿ×øèÿšøéÿÃøêÿqøìÿšøíÿqøîÿ…øòÿ…øóÿšøõÿšøöÿ®ø÷ÿšøùÿšøÿ®øÿ®øÿ®øÿ…ø ÿ…øjÿqøkÿšølÿ×ømÿ×øqÿšørÿqøsÿ…øuÿšøwÿšøyÿšø}ÿšø~ÿ×øÿqøÿ×øƒÿ×ø„ÿ×ø…ÿqø†ÿ×ø‡ÿqøˆÿ×ø‰ÿqøŠÿ×ø‹ÿ×øŒÿ×øÿqø–ÿšøšÿšøžÿšø ÿ×ø¢ÿ×ø¤ÿšø¦ÿšøªÿ®ø¬ÿšø®ÿšø°ÿšø±ÿ×ø²ÿqø³ÿ×ø´ÿqøµ�)ø¶ÿ®ø¸ÿ®øºÿ®ø¼ÿ×ø¾ÿ®øÀÿšøÂÿšøÄÿšøÅÿšøÆÿqøÇÿšøÈÿqøËÿ×øÍÿšøÎÿšøÏÿ…øÑÿšøÓÿšøÕÿšø×ÿšøÙÿqøÛÿqøÝÿqøàÿqøæÿ×øèÿ×øêÿÃøìÿšøîÿšøïÿ×øðÿqøñÿ×øòÿqøóÿ×øôÿqøöÿ×øøÿ®øúÿ®øüÿ®øþÿšø�ÿšøÿšøÿ×øÿ×ø ÿqø ÿqø ÿqø ÿqøÿšøÿšøÿšøÿ…øÿšøÿ×øÿqøÿ®øÿqøÿšøÿ…ù�ÿšù�ÿ×ù�ÿšùÎÿÃùÏÿìùÕÿÃùØÿìùÛÿìùÞÿìùêÿìùíÿìùòÿÃùÿ×ùÿ×ùÿ×ùÿšù ÿšùjÿìùsÿÃùÿìù…ÿìù‡ÿìù‰ÿìùÿìù²ÿìù´ÿìùÏÿÃùàÿìùðÿìùòÿìùôÿìù ÿìù ÿìùÿÃùÿìùÿìùÿÃú�ÿšú�ÿšú�"�)ú�$ÿ®ú�&ÿìú�*ÿìú�2ÿìú�4ÿìú�Dÿ×ú�Fÿ×ú�Gÿ×ú�Hÿ×ú�Jÿìú�Pÿìú�Qÿìú�Rÿ×ú�Sÿìú�Tÿ×ú�Uÿìú�Vÿìú�Xÿìú�‚ÿ®ú�ƒÿ®ú�„ÿ®ú�…ÿ®ú�†ÿ®ú�‡ÿ®ú�‰ÿìú�”ÿìú�•ÿìú�–ÿìú�—ÿìú�˜ÿìú�šÿìú�¢ÿ×ú�£ÿ×ú�¤ÿ×ú�¥ÿ×ú�¦ÿ×ú�§ÿ×ú�¨ÿ×ú�©ÿ×ú�ªÿ×ú�«ÿ×ú�¬ÿ×ú�­ÿ×ú�´ÿ×ú�µÿ×ú�¶ÿ×ú�·ÿ×ú�¸ÿ×ú�ºÿ×ú�»ÿìú�¼ÿìú�½ÿìú�¾ÿìú�Âÿ®ú�Ãÿ×ú�Äÿ®ú�Åÿ×ú�Æÿ®ú�Çÿ×ú�Èÿìú�Éÿ×ú�Êÿìú�Ëÿ×ú�Ìÿìú�Íÿ×ú�Îÿìú�Ïÿ×ú�Ñÿ×ú�Óÿ×ú�Õÿ×ú�×ÿ×ú�Ùÿ×ú�Ûÿ×ú�Ýÿ×ú�Þÿìú�ßÿìú�àÿìú�áÿìú�âÿìú�ãÿìú�äÿìú�åÿìú�úÿìúÿìúÿìú ÿìúÿìúÿ×úÿìúÿ×úÿìúÿ×úÿìúÿ×úÿìúÿìúÿìú!ÿìú+ÿìú-ÿìú/ÿìú1ÿìú3ÿìú5ÿìúCÿ®úDÿ×úFÿ×úGÿìúHÿ×úJÿìúÿšú ÿšúWÿìúXÿ®úYÿ×ú_ÿìú`ÿ×úbÿìúÿ®úÿ×úÿ®ú ÿ×ú!ÿ®ú"ÿ×ú#ÿ®ú%ÿ®ú&ÿ×ú'ÿ®ú(ÿ×ú)ÿ®ú*ÿ×ú+ÿ®ú,ÿ×ú-ÿ®ú.ÿ×ú/ÿ®ú0ÿ×ú1ÿ®ú2ÿ×ú3ÿ®ú4ÿ×ú6ÿ×ú8ÿ×ú:ÿ×ú<ÿ×ú@ÿ×úBÿ×úDÿ×úIÿìúJÿ×úKÿìúLÿ×úMÿìúNÿ×úOÿìúQÿìúRÿ×úSÿìúTÿ×úUÿìúVÿ×úWÿìúXÿ×úYÿìúZÿ×ú[ÿìú\ÿ×ú]ÿìú^ÿ×ú_ÿìú`ÿ×úbÿìúdÿìúfÿìúhÿìújÿìúlÿìúnÿìû��Rû� �Rû�ÿ®û�ÿ®û�"�)û�Rûÿ®û �Rû ÿ®ü�ÿšü�ÿšü�"�)ü�$ÿ®ü�&ÿìü�*ÿìü�2ÿìü�4ÿìü�Dÿ×ü�Fÿ×ü�Gÿ×ü�Hÿ×ü�Jÿìü�Pÿìü�Qÿìü�Rÿ×ü�Sÿìü�Tÿ×ü�Uÿìü�Vÿìü�Xÿìü�‚ÿ®ü�ƒÿ®ü�„ÿ®ü�…ÿ®ü�†ÿ®ü�‡ÿ®ü�‰ÿìü�”ÿìü�•ÿìü�–ÿìü�—ÿìü�˜ÿìü�šÿìü�¢ÿ×ü�£ÿ×ü�¤ÿ×ü�¥ÿ×ü�¦ÿ×ü�§ÿ×ü�¨ÿ×ü�©ÿ×ü�ªÿ×ü�«ÿ×ü�¬ÿ×ü�­ÿ×ü�´ÿ×ü�µÿ×ü�¶ÿ×ü�·ÿ×ü�¸ÿ×ü�ºÿ×ü�»ÿìü�¼ÿìü�½ÿìü�¾ÿìü�Âÿ®ü�Ãÿ×ü�Äÿ®ü�Åÿ×ü�Æÿ®ü�Çÿ×ü�Èÿìü�Éÿ×ü�Êÿìü�Ëÿ×ü�Ìÿìü�Íÿ×ü�Îÿìü�Ïÿ×ü�Ñÿ×ü�Óÿ×ü�Õÿ×ü�×ÿ×ü�Ùÿ×ü�Ûÿ×ü�Ýÿ×ü�Þÿìü�ßÿìü�àÿìü�áÿìü�âÿìü�ãÿìü�äÿìü�åÿìü�úÿìüÿìüÿìü ÿìüÿìüÿ×üÿìüÿ×üÿìüÿ×üÿìüÿ×üÿìüÿìüÿìü!ÿìü+ÿìü-ÿìü/ÿìü1ÿìü3ÿìü5ÿìüCÿ®üDÿ×üFÿ×üGÿìüHÿ×üJÿìüÿšü ÿšüWÿìüXÿ®üYÿ×ü_ÿìü`ÿ×übÿìüÿ®üÿ×üÿ®ü ÿ×ü!ÿ®ü"ÿ×ü#ÿ®ü%ÿ®ü&ÿ×ü'ÿ®ü(ÿ×ü)ÿ®ü*ÿ×ü+ÿ®ü,ÿ×ü-ÿ®ü.ÿ×ü/ÿ®ü0ÿ×ü1ÿ®ü2ÿ×ü3ÿ®ü4ÿ×ü6ÿ×ü8ÿ×ü:ÿ×ü<ÿ×ü@ÿ×üBÿ×üDÿ×üIÿìüJÿ×üKÿìüLÿ×üMÿìüNÿ×üOÿìüQÿìüRÿ×üSÿìüTÿ×üUÿìüVÿ×üWÿìüXÿ×üYÿìüZÿ×ü[ÿìü\ÿ×ü]ÿìü^ÿ×ü_ÿìü`ÿ×übÿìüdÿìüfÿìühÿìüjÿìülÿìünÿìý��Rý� �Rý�ÿ®ý�ÿ®ý�"�)ý�Rýÿ®ý �Rý ÿ®þ�ÿšþ�ÿšþ�"�)þ�$ÿ®þ�&ÿìþ�*ÿìþ�2ÿìþ�4ÿìþ�Dÿ×þ�Fÿ×þ�Gÿ×þ�Hÿ×þ�Jÿìþ�Pÿìþ�Qÿìþ�Rÿ×þ�Sÿìþ�Tÿ×þ�Uÿìþ�Vÿìþ�Xÿìþ�‚ÿ®þ�ƒÿ®þ�„ÿ®þ�…ÿ®þ�†ÿ®þ�‡ÿ®þ�‰ÿìþ�”ÿìþ�•ÿìþ�–ÿìþ�—ÿìþ�˜ÿìþ�šÿìþ�¢ÿ×þ�£ÿ×þ�¤ÿ×þ�¥ÿ×þ�¦ÿ×þ�§ÿ×þ�¨ÿ×þ�©ÿ×þ�ªÿ×þ�«ÿ×þ�¬ÿ×þ�­ÿ×þ�´ÿ×þ�µÿ×þ�¶ÿ×þ�·ÿ×þ�¸ÿ×þ�ºÿ×þ�»ÿìþ�¼ÿìþ�½ÿìþ�¾ÿìþ�Âÿ®þ�Ãÿ×þ�Äÿ®þ�Åÿ×þ�Æÿ®þ�Çÿ×þ�Èÿìþ�Éÿ×þ�Êÿìþ�Ëÿ×þ�Ìÿìþ�Íÿ×þ�Îÿìþ�Ïÿ×þ�Ñÿ×þ�Óÿ×þ�Õÿ×þ�×ÿ×þ�Ùÿ×þ�Ûÿ×þ�Ýÿ×þ�Þÿìþ�ßÿìþ�àÿìþ�áÿìþ�âÿìþ�ãÿìþ�äÿìþ�åÿìþ�úÿìþÿìþÿìþ ÿìþÿìþÿ×þÿìþÿ×þÿìþÿ×þÿìþÿ×þÿìþÿìþÿìþ!ÿìþ+ÿìþ-ÿìþ/ÿìþ1ÿìþ3ÿìþ5ÿìþCÿ®þDÿ×þFÿ×þGÿìþHÿ×þJÿìþÿšþ ÿšþWÿìþXÿ®þYÿ×þ_ÿìþ`ÿ×þbÿìþÿ®þÿ×þÿ®þ ÿ×þ!ÿ®þ"ÿ×þ#ÿ®þ%ÿ®þ&ÿ×þ'ÿ®þ(ÿ×þ)ÿ®þ*ÿ×þ+ÿ®þ,ÿ×þ-ÿ®þ.ÿ×þ/ÿ®þ0ÿ×þ1ÿ®þ2ÿ×þ3ÿ®þ4ÿ×þ6ÿ×þ8ÿ×þ:ÿ×þ<ÿ×þ@ÿ×þBÿ×þDÿ×þIÿìþJÿ×þKÿìþLÿ×þMÿìþNÿ×þOÿìþQÿìþRÿ×þSÿìþTÿ×þUÿìþVÿ×þWÿìþXÿ×þYÿìþZÿ×þ[ÿìþ\ÿ×þ]ÿìþ^ÿ×þ_ÿìþ`ÿ×þbÿìþdÿìþfÿìþhÿìþjÿìþlÿìþnÿìÿ��Rÿ� �Rÿ�ÿ®ÿ�ÿ®ÿ�"�)ÿ�Rÿÿ®ÿ �Rÿ ÿ®��ÿ…��ÿ…��"�)��$ÿ…��&ÿ×��*ÿ×��2ÿ×��4ÿ×��Dÿš��Fÿš��Gÿš��Hÿš��Jÿ×��PÿÃ��QÿÃ��Rÿš��SÿÃ��Tÿš��UÿÃ��Vÿ®��XÿÃ��]ÿ×��‚ÿ…��ƒÿ…��„ÿ…��…ÿ…��†ÿ…��‡ÿ…��‰ÿ×��”ÿ×��•ÿ×��–ÿ×��—ÿ×��˜ÿ×��šÿ×��¢ÿš��£ÿš��¤ÿš��¥ÿš��¦ÿš��§ÿš��¨ÿš��©ÿš��ªÿš��«ÿš��¬ÿš��­ÿš��´ÿš��µÿš��¶ÿš��·ÿš��¸ÿš��ºÿš��»ÿÃ��¼ÿÃ��½ÿÃ��¾ÿÃ��Âÿ…��Ãÿš��Äÿ…��Åÿš��Æÿ…��Çÿš��Èÿ×��Éÿš��Êÿ×��Ëÿš��Ìÿ×��Íÿš��Îÿ×��Ïÿš��Ñÿš��Óÿš��Õÿš��×ÿš��Ùÿš��Ûÿš��Ýÿš��Þÿ×��ßÿ×��àÿ×��áÿ×��âÿ×��ãÿ×��äÿ×��åÿ×��úÿÃ�ÿÃ�ÿÃ� ÿÃ�ÿ×�ÿš�ÿ×�ÿš�ÿ×�ÿš�ÿ×�ÿš�ÿÃ�ÿÃ�ÿ®�!ÿ®�+ÿÃ�-ÿÃ�/ÿÃ�1ÿÃ�3ÿÃ�5ÿÃ�<ÿ×�>ÿ×�@ÿ×�Cÿ…�Dÿš�Fÿš�Gÿ×�Hÿš�Jÿ®�ÿ…� ÿ…�WÿÃ�Xÿ…�Yÿš�_ÿ×�`ÿš�bÿÃ�ÿ…�ÿš�ÿ…� ÿš�!ÿ…�"ÿš�#ÿ…�%ÿ…�&ÿš�'ÿ…�(ÿš�)ÿ…�*ÿš�+ÿ…�,ÿš�-ÿ…�.ÿš�/ÿ…�0ÿš�1ÿ…�2ÿš�3ÿ…�4ÿš�6ÿš�8ÿš�:ÿš�<ÿš�@ÿš�Bÿš�Dÿš�Iÿ×�Jÿš�Kÿ×�Lÿš�Mÿ×�Nÿš�Oÿ×�Qÿ×�Rÿš�Sÿ×�Tÿš�Uÿ×�Vÿš�Wÿ×�Xÿš�Yÿ×�Zÿš�[ÿ×�\ÿš�]ÿ×�^ÿš�_ÿ×�`ÿš�bÿÃ�dÿÃ�fÿÃ�hÿÃ�jÿÃ�lÿÃ�nÿÃ��R� �R�ÿ®�ÿ®�"�)�Rÿ® �R ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�7ÿ®$ÿ®&ÿ®qÿ®ÿ®¦ÿ®¼ÿ®Äÿ®Üÿ×äÿשÿ®ªÿ×µÿ®¶ÿ×½ÿ®¾ÿ×ÿ®ÿ×ÿ®�$ÿq�7�)�9�)�:�)�<��Dÿ®�Fÿ…�Gÿ…�Hÿ…�JÿÃ�PÿÃ�QÿÃ�Rÿ…�SÿÃ�Tÿ…�UÿÃ�VÿÃ�XÿÃ�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�Ÿ��¢ÿ…�£ÿ®�¤ÿ®�¥ÿ®�¦ÿ®�§ÿ®�¨ÿ®�©ÿ…�ªÿ…�«ÿ…�¬ÿ…�­ÿ…�´ÿ…�µÿ…�¶ÿ…�·ÿ…�¸ÿ…�ºÿ…�»ÿÃ�¼ÿÃ�½ÿÃ�¾ÿÃ�Âÿq�Ãÿ®�Äÿq�Åÿ®�Æÿq�Çÿ®�Éÿ…�Ëÿ…�Íÿ…�Ïÿ…�Ñÿ…�Óÿ…�Õÿ…�×ÿ…�Ùÿ…�Ûÿ…�Ýÿ…�ßÿÃ�áÿÃ�ãÿÃ�åÿÃ�úÿÃÿÃÿà ÿÃÿ…ÿ…ÿ…ÿ…ÿÃÿÃÿÃ!ÿÃ$�)&�)+ÿÃ-ÿÃ/ÿÃ1ÿÃ3ÿÃ5ÿÃ6�)8�:�CÿqDÿ®Fÿ®Hÿ…JÿÃVÿq_ÿqbÿqiÿqyÿ®zÿ×{ÿ×~ÿ®ÿÂÿ׃ÿׄÿׇÿ׉ÿ׌ÿ®ŽÿÃÿ®ÿ®“ÿ®™ÿ®¤ÿ…ªÿq®ÿ…µÿ…Êÿ×ÎÿqÏÿ…ÕÿqØÿ…Ûÿ…Þÿ…êÿ…íÿ…îÿÃòÿqú�)ü�)þ�)��WÿÃXÿqYÿ®`ÿ…bÿÃjÿ…rÿqsÿq}ÿìÿ……ÿ…‡ÿ…‰ÿ…ÿ…²ÿ…´ÿ…Îÿ…ÏÿqÙÿqÚÿ×ÛÿqÜÿ×ÝÿqÞÿ×àÿ…âÿ×äÿ×ðÿ…òÿ…ôÿ… ÿq ÿ… ÿq ÿ…ÿ…ÿqÿ…ÿ…ÿ…ÿqÿqÿ®ÿq ÿ®!ÿq"ÿ®#ÿq%ÿq&ÿ®'ÿq(ÿ®)ÿq*ÿ®+ÿq,ÿ®-ÿq.ÿ®/ÿq0ÿ®1ÿq2ÿ®3ÿq4ÿ®6ÿ…8ÿ…:ÿ…<ÿ…@ÿ…Bÿ…Dÿ…Jÿ…Lÿ…Nÿ…Rÿ…Tÿ…Vÿ…Xÿ…Zÿ…\ÿ…^ÿ…`ÿ…bÿÃdÿÃfÿÃhÿÃjÿÃlÿÃnÿÃo�q�s��)�$ÿq�7�)�9�)�:�)�<��Dÿ®�Fÿ…�Gÿ…�Hÿ…�JÿÃ�PÿÃ�QÿÃ�Rÿ…�SÿÃ�Tÿ…�UÿÃ�VÿÃ�XÿÃ�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�Ÿ��¢ÿ…�£ÿ®�¤ÿ®�¥ÿ®�¦ÿ®�§ÿ®�¨ÿ®�©ÿ…�ªÿ…�«ÿ…�¬ÿ…�­ÿ…�´ÿ…�µÿ…�¶ÿ…�·ÿ…�¸ÿ…�ºÿ…�»ÿÃ�¼ÿÃ�½ÿÃ�¾ÿÃ�Âÿq�Ãÿ®�Äÿq�Åÿ®�Æÿq�Çÿ®�Éÿ…�Ëÿ…�Íÿ…�Ïÿ…�Ñÿ…�Óÿ…�Õÿ…�×ÿ…�Ùÿ…�Ûÿ…�Ýÿ…�ßÿÃ�áÿÃ�ãÿÃ�åÿÃ�úÿÃÿÃÿà ÿÃÿ…ÿ…ÿ…ÿ…ÿÃÿÃÿÃ!ÿÃ$�)&�)+ÿÃ-ÿÃ/ÿÃ1ÿÃ3ÿÃ5ÿÃ6�)8�:�CÿqDÿ®Fÿ®Hÿ…JÿÃVÿq_ÿqbÿqiÿqyÿ®zÿ×{ÿ×~ÿ®ÿÂÿ׃ÿׄÿׇÿ׉ÿ׌ÿ®ŽÿÃÿ®ÿ®“ÿ®™ÿ®¤ÿ…ªÿq®ÿ…µÿ…Êÿ×ÎÿqÏÿ…ÕÿqØÿ…Ûÿ…Þÿ…êÿ…íÿ…îÿÃòÿqú�)ü�)þ�)��WÿÃXÿqYÿ®`ÿ…bÿÃjÿ…rÿqsÿq}ÿìÿ……ÿ…‡ÿ…‰ÿ…ÿ…²ÿ…´ÿ…Îÿ…ÏÿqÙÿqÚÿ×ÛÿqÜÿ×ÝÿqÞÿ×àÿ…âÿ×äÿ×ðÿ…òÿ…ôÿ… ÿq ÿ… ÿq ÿ…ÿ…ÿqÿ…ÿ…ÿ…ÿqÿqÿ®ÿq ÿ®!ÿq"ÿ®#ÿq%ÿq&ÿ®'ÿq(ÿ®)ÿq*ÿ®+ÿq,ÿ®-ÿq.ÿ®/ÿq0ÿ®1ÿq2ÿ®3ÿq4ÿ®6ÿ…8ÿ…:ÿ…<ÿ…@ÿ…Bÿ…Dÿ…Jÿ…Lÿ…Nÿ…Rÿ…Tÿ…Vÿ…Xÿ…Zÿ…\ÿ…^ÿ…`ÿ…bÿÃdÿÃfÿÃhÿÃjÿÃlÿÃnÿÃo�q�s��)�&ÿš�*ÿš�2ÿš�4ÿš�7ÿq�8ÿ×�9ÿ…�:ÿ…�<ÿ…�‰ÿš�”ÿš�•ÿš�–ÿš�—ÿš�˜ÿš�šÿš�›ÿ×�œÿ×�ÿ×�žÿ×�Ÿÿ…�Èÿš�Êÿš�Ìÿš�Îÿš�Þÿš�àÿš�âÿš�äÿšÿšÿšÿšÿš$ÿq&ÿq*ÿ×,ÿ×.ÿ×0ÿ×2ÿ×4ÿ×6ÿ…8ÿ…:ÿ…Gÿšfÿ®mÿ®qÿqrÿ…sÿšuÿ…xÿ……ÿ×ÿqŸÿš¦ÿq¸ÿš»ÿš¼ÿq¾ÿ®Áÿ\ÄÿqÜÿšáÿ…äÿšúÿ…üÿ…þÿ…�ÿ…Tÿ…_ÿšaÿ×lÿš|ÿ\~ÿš€ÿ…‚ÿ…„ÿš†ÿšˆÿšŠÿšŒÿš©ÿqªÿš±ÿš³ÿšµÿq¶ÿš·ÿ…¹ÿ…½ÿq¾ÿš¿ÿ\Àÿ…Áÿ\Âÿ…Åÿ…Çÿ…Ôÿ\Õÿ…ïÿšñÿšóÿšýÿ\þÿ… ÿ…ÿšÿ…ÿšÿšÿqÿšIÿšKÿšMÿšOÿšQÿšSÿšUÿšWÿšYÿš[ÿš]ÿš_ÿšaÿ×cÿ×eÿ×gÿ×iÿ×kÿ×mÿ×oÿ…qÿ…sÿ…ÿq �$ÿq �7�) �9�) �:�) �<� �Dÿ® �Fÿ… �Gÿ… �Hÿ… �Jÿà �Pÿà �Qÿà �Rÿ… �Sÿà �Tÿ… �Uÿà �Vÿà �Xÿà �‚ÿq �ƒÿq �„ÿq �…ÿq �†ÿq �‡ÿq �Ÿ� �¢ÿ… �£ÿ® �¤ÿ® �¥ÿ® �¦ÿ® �§ÿ® �¨ÿ® �©ÿ… �ªÿ… �«ÿ… �¬ÿ… �­ÿ… �´ÿ… �µÿ… �¶ÿ… �·ÿ… �¸ÿ… �ºÿ… �»ÿà �¼ÿà �½ÿà �¾ÿà �Âÿq �Ãÿ® �Äÿq �Åÿ® �Æÿq �Çÿ® �Éÿ… �Ëÿ… �Íÿ… �Ïÿ… �Ñÿ… �Óÿ… �Õÿ… �×ÿ… �Ùÿ… �Ûÿ… �Ýÿ… �ßÿà �áÿà �ãÿà �åÿà �úÿà ÿà ÿà  ÿà ÿ… ÿ… ÿ… ÿ… ÿà ÿà ÿà !ÿà $�) &�) +ÿà -ÿà /ÿà 1ÿà 3ÿà 5ÿà 6�) 8� :� Cÿq Dÿ® Fÿ® Hÿ… Jÿà Vÿq _ÿq bÿq iÿq yÿ® zÿ× {ÿ× ~ÿ® ÿà ‚ÿ× ƒÿ× „ÿ× ‡ÿ× ‰ÿ× Œÿ® Žÿà ÿ® ÿ® “ÿ® ™ÿ® ¤ÿ… ªÿq ®ÿ… µÿ… Êÿ× Îÿq Ïÿ… Õÿq Øÿ… Ûÿ… Þÿ… êÿ… íÿ… îÿà òÿq ú�) ü�) þ�) �� Wÿà Xÿq Yÿ® `ÿ… bÿà jÿ… rÿq sÿq }ÿì ÿ… …ÿ… ‡ÿ… ‰ÿ… ÿ… ²ÿ… ´ÿ… Îÿ… Ïÿq Ùÿq Úÿ× Ûÿq Üÿ× Ýÿq Þÿ× àÿ… âÿ× äÿ× ðÿ… òÿ… ôÿ…  ÿq  ÿ…  ÿq  ÿ… ÿ… ÿq ÿ… ÿ… ÿ… ÿq ÿq ÿ® ÿq  ÿ® !ÿq "ÿ® #ÿq %ÿq &ÿ® 'ÿq (ÿ® )ÿq *ÿ® +ÿq ,ÿ® -ÿq .ÿ® /ÿq 0ÿ® 1ÿq 2ÿ® 3ÿq 4ÿ® 6ÿ… 8ÿ… :ÿ… <ÿ… @ÿ… Bÿ… Dÿ… Jÿ… Lÿ… Nÿ… Rÿ… Tÿ… Vÿ… Xÿ… Zÿ… \ÿ… ^ÿ… `ÿ… bÿà dÿà fÿà hÿà jÿà lÿà nÿà o� q� s� �) �&ÿš �*ÿš �2ÿš �4ÿš �7ÿq �8ÿ× �9ÿ… �:ÿ… �<ÿ… �‰ÿš �”ÿš �•ÿš �–ÿš �—ÿš �˜ÿš �šÿš �›ÿ× �œÿ× �ÿ× �žÿ× �Ÿÿ… �Èÿš �Êÿš �Ìÿš �Îÿš �Þÿš �àÿš �âÿš �äÿš ÿš ÿš ÿš ÿš $ÿq &ÿq *ÿ× ,ÿ× .ÿ× 0ÿ× 2ÿ× 4ÿ× 6ÿ… 8ÿ… :ÿ… Gÿš fÿ® mÿ® qÿq rÿ… sÿš uÿ… xÿ… …ÿ× ÿq Ÿÿš ¦ÿq ¸ÿš »ÿš ¼ÿq ¾ÿ® Áÿ\ Äÿq Üÿš áÿ… äÿš úÿ… üÿ… þÿ… �ÿ… Tÿ… _ÿš aÿ× lÿš |ÿ\ ~ÿš €ÿ… ‚ÿ… „ÿš †ÿš ˆÿš Šÿš Œÿš ©ÿq ªÿš ±ÿš ³ÿš µÿq ¶ÿš ·ÿ… ¹ÿ… ½ÿq ¾ÿš ¿ÿ\ Àÿ… Áÿ\ Âÿ… Åÿ… Çÿ… Ôÿ\ Õÿ… ïÿš ñÿš óÿš ýÿ\ þÿ…  ÿ… ÿš ÿ… ÿš ÿš ÿq ÿš Iÿš Kÿš Mÿš Oÿš Qÿš Sÿš Uÿš Wÿš Yÿš [ÿš ]ÿš _ÿš aÿ× cÿ× eÿ× gÿ× iÿ× kÿ× mÿ× oÿ… qÿ… sÿ… ÿq!qÿ×!rÿì!xÿì!TÿìS�ÿÃS�ÿÃSÿÃS ÿÃT�ÿ…T�ÿ…TVÿ…T_ÿ…Tbÿ…Tfÿ×Tiÿ…Tmÿ×TsÿÃTvÿìTyÿšTzÿ®T{ÿÃT|ÿÃT}ÿÃT~ÿšTÿÃT‚ÿ®T„ÿÃT†ÿÃT‡ÿÃT‰ÿÃTŒÿšTŽÿšTÿšTÿšT’ÿÃT“ÿšT•ÿÃT–ÿÃT˜ÿÃT™ÿšTšÿÃT›ÿÃTÿ…T ÿ…T!ÿìX�ÿqX� ÿqX�&ÿ×X�*ÿ×X�- X�2ÿ×X�4ÿ×X�7ÿqX�9ÿ®X�:ÿ®X�<ÿ…X�‰ÿ×X�”ÿ×X�•ÿ×X�–ÿ×X�—ÿ×X�˜ÿ×X�šÿ×X�Ÿÿ…X�Èÿ×X�Êÿ×X�Ìÿ×X�Îÿ×X�Þÿ×X�àÿ×X�âÿ×X�äÿ×Xÿ×Xÿ×Xÿ×Xÿ×X$ÿqX&ÿqX6ÿ®X8ÿ…X:ÿ…XGÿ×Xúÿ®Xüÿ®Xþÿ®X�ÿ…XÿqX ÿqX_ÿ×XIÿ×XKÿ×XMÿ×XOÿ×XQÿ×XSÿ×XUÿ×XWÿ×XYÿ×X[ÿ×X]ÿ×X_ÿ×Xoÿ…Xqÿ…Xsÿ…XÿqY�ÿìY� ÿìYÿìY ÿìZ�ÿ®Z�ÿ®ZVÿ×Z_ÿ×Zbÿ×ZdÿìZiÿ×ZpÿìZqÿÃZrÿìZtÿ×ZuÿìZxÿìZˆÿìZÿ®Z ÿ®ZTÿì`�I�R`�W�R`�Y�f`�Z�f`�[�f`�\�f`�¿�f`%�R`'�R`7�f`û�f`ý�f`4�R`5�R`]�R`^�R`p�f`�R`�Rb�I�fb�W�fb�Y�fb�Z�fb�[�fb�\�fb�¿�fb%�fb'�fb7�fbû�fbý�fb4�fb5�fb]�fb^�fbp�fb�fb�fj�ÿìj� ÿìjÿìj ÿìl�ÿ®l�ÿ®lÿìl¤ÿ×l¦ÿìl¨ÿ×lªÿ×l®ÿ×l°ÿ×l±ÿìlµÿ×l¼ÿÃl½ÿ×l¿ÿ×lÁÿ×lÄÿìlÇÿìlÎÿìlÕÿìlòÿìlÿ®l ÿ®lrÿ×lsÿìlzÿìl|ÿ×l€ÿìl‚ÿìlŸÿ×l¡ÿìl©ÿìlµÿÃl·ÿìl¹ÿìl»ÿ×l½ÿìl¿ÿ×lÁÿ×lÊÿ×lÎÿ×lÏÿìlÔÿ×lÙÿ×lÛÿ×lÝÿ×låÿ×lçÿìlõÿìl÷ÿ×lùÿ×lûÿ×lýÿ×lÿ×lÿ×l ÿ×lÿ×lÿ×lÿìlÿìlÿ×lÿìm�ÿ®m�ÿ®mÎÿ×mÕÿ×mòÿ×mÿ®m ÿ®msÿ×mÏÿ×mÿ×mÿ×n�ÿ®n� ÿ®nÿ×n¦ÿ×n¼ÿ®nÁÿ®nÄÿ×nÜÿ×näÿ×nÿ®n ÿ®n|ÿ®n€ÿÃn‚ÿÃn©ÿ×nªÿ×nµÿ®n¶ÿ×n·ÿÃn¹ÿÃn½ÿ×n¾ÿ×n¿ÿ®nÁÿ®nÔÿ®nýÿ®n ÿšnÿšnÿ×nÿ×o�ÿ…o� ÿ…oÐÿ×oÜÿšoÝÿÃoßÿ×oáÿ®oäÿšoöÿÃoÿ…o ÿ…omÿ×oÿ×oƒÿ×o‹ÿ×o ÿ×oªÿšo¶ÿšo¸ÿÃoºÿÃo¼ÿ×o¾ÿšoÀÿ®oÂÿ®oÆÿ×oÈÿ×oËÿ×oÕÿ®oæÿ×oêÿ×oøÿÃoúÿÃoüÿÃoþÿ®oÿ×oÿ×oÿšoÿšoÿšpŸÿ×p¸ÿ×p»ÿ×p¾ÿ×páÿ×plÿ×p~ÿ×p„ÿ×p†ÿ×pˆÿ×pŠÿ×pŒÿ×p±ÿ×p³ÿ×pÀÿ×pÂÿ×pÅÿ×pÇÿ×pÕÿ×pïÿ×pñÿ×póÿ×pþÿ×p ÿ×p ÿ×pÿ×pÿ×pÿ×r�ÿqr� ÿqrÿšr¦ÿšr¼ÿqr¾ÿ×rÁÿšrÄÿšrÜÿ×ráÿ×räÿ×rÿqr ÿqrnÿ×r|ÿšr€ÿ®r‚ÿ®r—ÿ×r›ÿ×r§ÿ×r©ÿšrªÿ×rµÿqr¶ÿ×r·ÿ…r¹ÿ…r½ÿšr¾ÿ×r¿ÿšrÀÿ×rÁÿšrÂÿ×rÅÿšrÇÿšrÔÿšrÕÿ×ráÿ×rãÿ×rýÿšrþÿ×rÿ×r ÿqrÿ×rÿqrÿ×rÿšrÿ×s�ÿqs� ÿqsÏÿ×sØÿ×sÛÿ×sÜÿšsÝÿÃsÞÿ×sáÿÃsäÿšsêÿ×síÿ×söÿÃsÿqs ÿqsjÿ×smÿ×s}ÿìsÿ×sÿ×sƒÿ×s…ÿ×s‡ÿ×s‰ÿ×s‹ÿ×sÿ×sªÿšs²ÿ×s´ÿ×s¶ÿšs¸ÿ×sºÿ×s¾ÿšsÀÿÃsÂÿÃsÆÿ×sÈÿ×sÕÿÃsàÿ×sðÿ×sòÿ×sôÿ×søÿÃsúÿÃsüÿÃsþÿÃs ÿ×s ÿ×sÿ…sÿ…sÿ×sÿšsÿ×t�ÿqt� ÿqtÿšt¦ÿšt¼ÿqt¾ÿ×tÁÿštÄÿštÜÿ×táÿ×täÿ×tÿqt ÿqtnÿ×t|ÿšt€ÿ®t‚ÿ®t—ÿ×t›ÿ×t§ÿ×t©ÿštªÿ×tµÿqt¶ÿ×t·ÿ…t¹ÿ…t½ÿšt¾ÿ×t¿ÿštÀÿ×tÁÿštÂÿ×tÅÿštÇÿštÔÿštÕÿ×táÿ×tãÿ×týÿštþÿ×tÿ×t ÿqtÿ×tÿqtÿ×tÿštÿ×u�ÿqu� ÿquÏÿ×uØÿ×uÛÿ×uÜÿšuÝÿÃuÞÿ×uáÿÃuäÿšuêÿ×uíÿ×uöÿÃuÿqu ÿqujÿ×umÿ×u}ÿìuÿ×uÿ×uƒÿ×u…ÿ×u‡ÿ×u‰ÿ×u‹ÿ×uÿ×uªÿšu²ÿ×u´ÿ×u¶ÿšu¸ÿ×uºÿ×u¾ÿšuÀÿÃuÂÿÃuÆÿ×uÈÿ×uÕÿÃuàÿ×uðÿ×uòÿ×uôÿ×uøÿÃuúÿÃuüÿÃuþÿÃu ÿ×u ÿ×uÿ…uÿ…uÿ×uÿšuÿ×v ÿìvÿìx ÿìxÿìz�ÿ®z�ÿ®zÿ®z ÿ®z€ÿìz‚ÿìz·ÿìz¹ÿìz ÿ×zÿ×|�ÿq|�ÿq|¤ÿÃ|ªÿ®|®ÿÃ|µÿÃ|Îÿ×|Õÿ×|òÿ×|ÿq| ÿq|rÿ®|sÿ×|ÎÿÃ|Ïÿ×|Ùÿ®|Ûÿ®|Ýÿ®| ÿ®| ÿ®|ÿÃ|ÿ×|ÿÃ|ÿ×}�ÿì}� ÿì}Ðÿ×}Üÿì}Ýÿì}ßÿ×}áÿì}äÿì}öÿì}ÿì} ÿì} ÿ×}ªÿì}¶ÿì}¼ÿ×}¾ÿì}Àÿì}Âÿì}Ëÿ×}Õÿì}æÿ×}øÿì}úÿì}üÿì}þÿì}ÿ×}ÿ×}ÿì}ÿì}ÿì~�ÿ®~�ÿ®~ÿì~¤ÿ×~¦ÿì~¨ÿ×~ªÿ×~®ÿ×~°ÿ×~±ÿì~µÿ×~¼ÿÃ~½ÿ×~¿ÿ×~Áÿ×~Äÿì~Çÿì~Îÿì~Õÿì~òÿì~ÿ®~ ÿ®~rÿ×~sÿì~zÿì~|ÿ×~€ÿì~‚ÿì~Ÿÿ×~¡ÿì~©ÿì~µÿÃ~·ÿì~¹ÿì~»ÿ×~½ÿì~¿ÿ×~Áÿ×~Êÿ×~Îÿ×~Ïÿì~Ôÿ×~Ùÿ×~Ûÿ×~Ýÿ×~åÿ×~çÿì~õÿì~÷ÿ×~ùÿ×~ûÿ×~ýÿ×~ÿ×~ÿ×~ ÿ×~ÿ×~ÿ×~ÿì~ÿì~ÿ×~ÿì�ÿì� ÿìÐÿ×ÜÿìÝÿìßÿ×áÿìäÿìöÿìÿì ÿì ÿתÿì¶ÿì¼ÿ×¾ÿìÀÿìÂÿìËÿ×Õÿìæÿ×øÿìúÿìüÿìþÿìÿ×ÿ×ÿìÿìÿì€�ÿ…€�ÿ…€Ÿÿ쀤ÿš€ªÿq€®ÿš€µÿš€¸ÿ쀻ÿ쀾ÿÀÉÿì€Îÿ®€Ïÿ×€Õÿ®€Øÿ×€Ûÿ×€Þÿ×€áÿ×€êÿ×€ë�f€íÿ×€îÿì€òÿ®€ô�f€ÿ…€ ÿ…€jÿ×€lÿì€rÿq€sÿ®€~ÿì€ÿ×€„ÿ쀅ÿ×€†ÿ쀇ÿ×€ˆÿ쀉ÿ×€Šÿ쀌ÿì€ÿ×€˜�f€¨�f€±ÿ쀲ÿ×€³ÿ쀴ÿ×€Àÿ×€Âÿ×€Åÿ×€ÆÿÀÇÿ×€ÈÿÀÎÿš€Ïÿ®€Õÿ×€Ùÿq€Ûÿq€Ýÿq€àÿ×€ïÿì€ðÿ×€ñÿì€òÿ×€óÿì€ôÿ×€þÿ×€ ÿq€ ÿ×€ ÿq€ ÿ×€ÿš€ÿ®€ÿì€ÿ×€ÿ×€ÿš€ÿ®�ÿ®�ÿ®Îÿ×Õÿ×òÿ×ÿ® ÿ®sÿ×Ïÿ×ÿ×ÿׂ�ÿ…‚�ÿ…‚Ÿÿ삤ÿš‚ªÿq‚®ÿš‚µÿš‚¸ÿì‚»ÿ삾ÿÂÉÿì‚Îÿ®‚ÏÿׂÕÿ®‚ØÿׂÛÿׂÞÿׂáÿׂêÿׂë�f‚íÿׂîÿì‚òÿ®‚ô�f‚ÿ…‚ ÿ…‚jÿׂlÿì‚rÿq‚sÿ®‚~ÿì‚ÿׂ„ÿì‚…ÿׂ†ÿ삇ÿׂˆÿ삉ÿׂŠÿ삌ÿì‚ÿׂ˜�f‚¨�f‚±ÿ삲ÿׂ³ÿì‚´ÿׂÀÿׂÂÿׂÅÿׂÆÿÂÇÿׂÈÿÂÎÿš‚Ïÿ®‚ÕÿׂÙÿq‚Ûÿq‚Ýÿq‚àÿׂïÿì‚ðÿׂñÿì‚òÿׂóÿì‚ôÿׂþÿׂ ÿq‚ ÿׂ ÿq‚ ÿׂÿš‚ÿ®‚ÿì‚ÿׂÿׂÿš‚ÿ®ƒ�ÿ®ƒ�ÿ®ƒÎÿ׃Õÿ׃òÿ׃ÿ®ƒ ÿ®ƒsÿ׃Ïÿ׃ÿ׃ÿׄ�ÿ®„�ÿ®„ÎÿׄÕÿׄòÿׄÿ®„ ÿ®„sÿׄÏÿׄÿׄÿ×…�ÿ®…�ÿ®…Îÿ×…Õÿ×…òÿ×…ÿ®… ÿ®…sÿ×…Ïÿ×…ÿ×…ÿ׆�ÿ®†�ÿ®†ÿ솤ÿ׆¦ÿ솨ÿ׆ªÿ׆®ÿ׆°ÿ׆±ÿ솵ÿ׆¼ÿƽÿ׆¿ÿ׆Áÿ׆Äÿì†Çÿì†Îÿì†Õÿì†òÿì†ÿ®† ÿ®†rÿ׆sÿì†zÿì†|ÿ׆€ÿ솂ÿ솟ÿ׆¡ÿ솩ÿ솵ÿÆ·ÿ솹ÿ솻ÿ׆½ÿ솿ÿ׆Áÿ׆Êÿ׆Îÿ׆Ïÿì†Ôÿ׆Ùÿ׆Ûÿ׆Ýÿ׆åÿ׆çÿì†õÿì†÷ÿ׆ùÿ׆ûÿ׆ýÿ׆ÿ׆ÿ׆ ÿ׆ÿ׆ÿ׆ÿì†ÿì†ÿ׆ÿì‡�ÿì‡� ÿì‡ÐÿׇÜÿì‡Ýÿì‡ßÿׇáÿì‡äÿì‡öÿì‡ÿì‡ ÿ쇠ÿׇªÿ쇶ÿ쇼ÿׇ¾ÿì‡Àÿì‡Âÿì‡ËÿׇÕÿì‡æÿׇøÿì‡úÿì‡üÿì‡þÿì‡ÿׇÿׇÿì‡ÿì‡ÿìˆ�ÿ®ˆ�ÿ®ˆÿ숤ÿ׈¦ÿ숨ÿ׈ªÿ׈®ÿ׈°ÿ׈±ÿ숵ÿ׈¼ÿȽÿ׈¿ÿ׈Áÿ׈ÄÿìˆÇÿìˆÎÿìˆÕÿìˆòÿìˆÿ®ˆ ÿ®ˆrÿ׈sÿìˆzÿìˆ|ÿ׈€ÿ숂ÿ숟ÿ׈¡ÿ숩ÿ숵ÿÈ·ÿ숹ÿ숻ÿ׈½ÿ숿ÿ׈Áÿ׈Êÿ׈Îÿ׈ÏÿìˆÔÿ׈Ùÿ׈Ûÿ׈Ýÿ׈åÿ׈çÿìˆõÿìˆ÷ÿ׈ùÿ׈ûÿ׈ýÿ׈ÿ׈ÿ׈ ÿ׈ÿ׈ÿ׈ÿìˆÿìˆÿ׈ÿì‰�ÿì‰� ÿì‰Ðÿ׉Üÿì‰Ýÿì‰ßÿ׉áÿì‰äÿì‰öÿì‰ÿì‰ ÿ쉠ÿ׉ªÿ쉶ÿ쉼ÿ׉¾ÿì‰Àÿì‰Âÿì‰Ëÿ׉Õÿì‰æÿ׉øÿì‰úÿì‰üÿì‰þÿì‰ÿ׉ÿ׉ÿì‰ÿì‰ÿìŠ�ÿ®Š�ÿ®Šÿ스ÿ׊¦ÿ슨ÿ׊ªÿ׊®ÿ׊°ÿ׊±ÿ습ÿ׊¼ÿʽÿ׊¿ÿ׊Áÿ׊ÄÿìŠÇÿìŠÎÿìŠÕÿìŠòÿìŠÿ®Š ÿ®Šrÿ׊sÿìŠzÿìŠ|ÿ׊€ÿ슂ÿ슟ÿ׊¡ÿ슩ÿ습ÿÊ·ÿ승ÿ슻ÿ׊½ÿ슿ÿ׊Áÿ׊Êÿ׊Îÿ׊ÏÿìŠÔÿ׊Ùÿ׊Ûÿ׊Ýÿ׊åÿ׊çÿìŠõÿìŠ÷ÿ׊ùÿ׊ûÿ׊ýÿ׊ÿ׊ÿ׊ ÿ׊ÿ׊ÿ׊ÿìŠÿìŠÿ׊ÿì‹�ÿ®‹�ÿ®‹Îÿ׋Õÿ׋òÿ׋ÿ®‹ ÿ®‹sÿ׋Ïÿ׋ÿ׋ÿ׌Ÿÿ׌¸ÿ׌»ÿ׌¾ÿ׌áÿ׌lÿ׌~ÿ׌„ÿ׌†ÿ׌ˆÿ׌Šÿ׌Œÿ׌±ÿ׌³ÿ׌Àÿ׌Âÿ׌Åÿ׌Çÿ׌Õÿ׌ïÿ׌ñÿ׌óÿ׌þÿ׌ ÿ׌ ÿ׌ÿ׌ÿ׌ÿו£�á•ê�)•ÿוÿ×–�ÿì–� ÿì–ÿì– ÿì—�ÿ®—� ÿ®—ÿ×—¦ÿ×—¼ÿ®—Áÿ®—Äÿ×—Üÿ×—äÿ×—ÿ®— ÿ®—|ÿ®—€ÿׂÿשÿ×—ªÿ×—µÿ®—¶ÿ×—·ÿ×¹ÿ×½ÿ×—¾ÿ×—¿ÿ®—Áÿ®—Ôÿ®—ýÿ®— ÿš—ÿš—ÿ×—ÿט�ÿ…˜� ÿ…˜ÐÿטÜÿš˜ÝÿØßÿטáÿ®˜äÿš˜öÿØÿ…˜ ÿ…˜mÿטÿטƒÿט‹ÿט ÿטªÿš˜¶ÿš˜¸ÿغÿؼÿט¾ÿš˜Àÿ®˜Âÿ®˜ÆÿטÈÿטËÿטÕÿ®˜æÿטêÿטøÿØúÿØüÿØþÿ®˜ÿטÿטÿš˜ÿš˜ÿš™�þö™�þö™¤ÿ…™ªÿš™®ÿ…™°ÿ×™µÿ…™¿ÿ×™Îÿš™Õÿš™òÿš™þö™ þö™rÿš™sÿš™vÿ왟ÿ×™»ÿ×™Êÿ×™Îÿ…™Ïÿš™Ùÿš™Ûÿš™Ýÿš™åÿ×™ÿ×™ÿ×™ ÿ®™ ÿ®™ÿ…™ÿš™ÿ…™ÿšš�ÿìš� ÿìšÐÿךÜÿìšÝÿìšßÿךáÿìšäÿìšöÿìšÿìš ÿìš ÿךªÿìš¶ÿìš¼ÿך¾ÿìšÀÿìšÂÿìšËÿךÕÿìšæÿךøÿìšúÿìšüÿìšþÿìšÿךÿךÿìšÿìšÿì›�ÿš›�ÿ×›�ÿš›�)›Ÿÿ×›¤ÿ®›¦�)›ªÿ…›®ÿ®›µÿ®›¸ÿ×›»ÿ×›¼�)›¾ÿÛÄ�)›ÌÿÛÍÿÛÎÿš›Ïÿ®›Ðÿ×›Ñÿ×›ÒÿÛÓÿÛÔÿÛÕÿš›ÖÿÛ×ÿÛØÿ®›ÙÿÛÚÿÛÛÿ®›Þÿ®›ßÿ×›àÿÛáÿš›âÿÛãÿÛåÿÛæÿÛçÿ×›èÿÛêÿ®›ë�)›ìÿÛíÿ®›îÿÛòÿš›óÿÛô�)›õÿÛ÷ÿÛùÿÛÿ×›ÿ×›ÿ×›ÿš› ÿš›jÿ®›kÿÛlÿ×›qÿÛrÿ…›sÿš›uÿÛwÿ×›yÿÛ}ÿÛ~ÿ×›ÿ®›„ÿ×›…ÿ®›†ÿ×›‡ÿ®›ˆÿ×›‰ÿ®›Šÿ×›Œÿ×›ÿ®›–ÿÛ˜�)›šÿÛžÿÛ ÿ×›¢ÿ×›¤ÿÛ¦ÿÛ¨�)›©�)›¬ÿÛ®ÿÛ°ÿÛ±ÿ×›²ÿ®›³ÿ×›´ÿ®›µ�)›¼ÿ×›½�)›Àÿš›Âÿš›ÄÿÛÅÿ×›ÆÿÛÇÿ×›ÈÿÛËÿ×›ÍÿÛÎÿ®›Ïÿš›ÑÿÛÓÿÛÕÿš›×ÿÛÙÿ…›Ûÿ…›Ýÿ…›àÿ®›æÿ×›èÿ×›ìÿÛîÿÛïÿ×›ðÿ®›ñÿ×›òÿ®›óÿ×›ôÿ®›öÿ×›þÿš›�ÿÛÿÛÿ×›ÿ×› ÿš› ÿ®› ÿš› ÿ®›ÿ×›ÿ×›ÿ®›ÿš›ÿÛÿ×›ÿ®›�)›ÿ®›ÿ®›ÿšœ�ÿÜ�ÿÜÎÿÜÏÿלÕÿÜØÿלÛÿלÞÿלêÿלíÿלòÿÜÿÜ ÿÜjÿלsÿÜÿל…ÿל‡ÿל‰ÿלÿל²ÿל´ÿלÏÿÜàÿלðÿלòÿלôÿל ÿל ÿלÿÜÿלÿלÿÃ�ÿÃ� ÿÃÿã�f¦ÿüÿÃÁÿ®ÄÿÃÜÿ×áÿ×äÿ×ÿà ÿÃ|ÿ®€ÿÂÿéÿêÿ×µÿöÿ×·ÿ×¹ÿ×½ÿþÿ׿ÿ®Àÿ×Áÿ®Âÿ×Ôÿ®Õÿ×ýÿ®þÿ× ÿ×ÿÃÿ×ÿÃÿÃÿמ�ÿÞ� ÿÞÿÞ ÿÞÿמÿןŸÿן£�៸ÿן»ÿן¾ÿßÜÿןáÿ®Ÿäÿןlÿן{�=Ÿ}ÿìŸ~ÿן„ÿן†ÿןˆÿןŠÿןŒÿןªÿן±ÿן³ÿן¶ÿן¾ÿןÀÿ®ŸÂÿ®ŸÅÿ߯ÿןÇÿßÈÿןÕÿ®Ÿïÿןñÿןóÿןþÿ®Ÿÿןÿןÿןÿ× Ïÿì Øÿì Ûÿì Þÿì áÿì êÿì íÿì jÿì ÿì …ÿì ‡ÿì ‰ÿì ÿì ²ÿì ´ÿì Àÿì Âÿì Õÿì àÿì ðÿì òÿì ôÿì þÿì  ÿì  ÿì ÿ× ÿ× ÿì ÿì¡�ÿ®¡�ÿ®¡ÿ®¡ ÿ®¡€ÿì¡‚ÿì¡·ÿ졹ÿì¡ ÿסÿ×¢é�)£Ÿÿ×££�᣸ÿ×£»ÿ×£¾ÿãÜÿ×£áÿ®£äÿ×£lÿ×£{�=£}ÿì£~ÿ×£„ÿ×£†ÿ×£ˆÿ×£Šÿ×£Œÿ×£ªÿ×£±ÿ×£³ÿ×£¶ÿ×£¾ÿ×£Àÿ®£Âÿ®£ÅÿãÆÿ×£ÇÿãÈÿ×£Õÿ®£ïÿ×£ñÿ×£óÿ×£þÿ®£ÿ×£ÿ×£ÿ×£ÿפÏÿì¤Øÿì¤Ûÿì¤Þÿì¤áÿì¤êÿì¤íÿì¤jÿì¤ÿ줅ÿ줇ÿ줉ÿì¤ÿ줲ÿ줴ÿì¤Àÿì¤Âÿì¤Õÿì¤àÿì¤ðÿì¤òÿì¤ôÿì¤þÿì¤ ÿì¤ ÿì¤ÿפÿפÿì¤ÿ쥟ÿ×¥¸ÿ×¥»ÿ×¥¾ÿ×¥Áÿ×¥áÿ×¥lÿ×¥|ÿ×¥~ÿ×¥„ÿ×¥†ÿ×¥ˆÿ×¥Šÿ×¥Œÿ×¥±ÿ×¥³ÿ×¥¿ÿ×¥Àÿ×¥Áÿ×¥Âÿ×¥Åÿš¥Çÿš¥Ôÿ×¥Õÿ×¥ïÿ×¥ñÿ×¥óÿ×¥ýÿ×¥þÿ×¥ ÿ×¥ ÿ×¥ÿ×¥ÿ×¥ÿ×¥ÿì¦ÏÿצØÿצÛÿצÞÿצáÿצêÿצíÿצjÿצÿצ…ÿצ‡ÿצ‰ÿצÿצ²ÿצ´ÿצÀÿצÂÿצÆÿצÈÿצÕÿצàÿצðÿצòÿצôÿצþÿצ ÿצ ÿצÿצÿ×§Ÿÿ×§¸ÿ×§»ÿ×§¾ÿ×§Áÿ×§áÿ×§lÿ×§|ÿ×§~ÿ×§„ÿ×§†ÿ×§ˆÿ×§Šÿ×§Œÿ×§±ÿ×§³ÿ×§¿ÿ×§Àÿ×§Áÿ×§Âÿ×§Åÿš§Çÿš§Ôÿ×§Õÿ×§ïÿ×§ñÿ×§óÿ×§ýÿ×§þÿ×§ ÿ×§ ÿ×§ÿ×§ÿ×§ÿ×§ÿì¨ÏÿרØÿרÛÿרÞÿרáÿרêÿרíÿרjÿרÿר…ÿר‡ÿר‰ÿרÿר²ÿר´ÿרÀÿרÂÿרÆÿרÈÿרÕÿרàÿרðÿרòÿרôÿרþÿר ÿר ÿרÿרÿשŸÿש¸ÿש»ÿש¾ÿשÁÿשáÿשlÿש|ÿש~ÿש„ÿש†ÿשˆÿשŠÿשŒÿש±ÿש³ÿש¿ÿשÀÿשÁÿשÂÿשÅÿš©Çÿš©ÔÿשÕÿשïÿשñÿשóÿשýÿשþÿש ÿש ÿשÿשÿשÿשÿìªÏÿתØÿתÛÿתÞÿתáÿתêÿתíÿתjÿתÿת…ÿת‡ÿת‰ÿתÿת²ÿת´ÿתÀÿתÂÿתÆÿתÈÿתÕÿתàÿתðÿתòÿתôÿתþÿת ÿת ÿתÿתÿ׫£�á«ê�)«ÿ׫ÿ׬�ÿì¬� ÿì¬ÿì¬ ÿì­�ÿš­�ÿ×­�ÿš­�)­Ÿÿ×­¤ÿ®­¦�)­ªÿ…­®ÿ®­µÿ®­¸ÿ×­»ÿ×­¼�)­¾ÿíÄ�)­ÌÿíÍÿíÎÿš­Ïÿ®­Ðÿ×­Ñÿ×­ÒÿíÓÿíÔÿíÕÿš­Öÿí×ÿíØÿ®­ÙÿíÚÿíÛÿ®­Þÿ®­ßÿ×­àÿíáÿš­âÿíãÿíåÿíæÿíçÿ×­èÿíêÿ®­ë�)­ìÿííÿ®­îÿíòÿš­óÿíô�)­õÿí÷ÿíùÿíÿ×­ÿ×­ÿ×­ÿš­ ÿš­jÿ®­kÿílÿ×­qÿírÿ…­sÿš­uÿíwÿ×­yÿí}ÿí~ÿ×­ÿ®­„ÿ×­…ÿ®­†ÿ×­‡ÿ®­ˆÿ×­‰ÿ®­Šÿ×­Œÿ×­ÿ®­–ÿí˜�)­šÿížÿí ÿ×­¢ÿ×­¤ÿí¦ÿí¨�)­©�)­¬ÿí®ÿí°ÿí±ÿ×­²ÿ®­³ÿ×­´ÿ®­µ�)­¼ÿ×­½�)­Àÿš­Âÿš­ÄÿíÅÿ×­ÆÿíÇÿ×­ÈÿíËÿ×­ÍÿíÎÿ®­Ïÿš­ÑÿíÓÿíÕÿš­×ÿíÙÿ…­Ûÿ…­Ýÿ…­àÿ®­æÿ×­èÿ×­ìÿíîÿíïÿ×­ðÿ®­ñÿ×­òÿ®­óÿ×­ôÿ®­öÿ×­þÿš­�ÿíÿíÿ×­ÿ×­ ÿš­ ÿ®­ ÿš­ ÿ®­ÿ×­ÿ×­ÿ®­ÿš­ÿíÿ×­ÿ®­�)­ÿ®­ÿ®­ÿš®�ÿš®�ÿ×®�ÿš®ÎÿîÏÿì®ÕÿîØÿì®Ûÿì®Þÿì®êÿì®íÿì®òÿîÿ×®ÿ×®ÿ×®ÿš® ÿš®jÿì®sÿîÿì®…ÿ쮇ÿ쮉ÿì®ÿ쮲ÿì®´ÿì®Ïÿîàÿì®ðÿì®òÿì®ôÿì® ÿì® ÿì®ÿîÿì®ÿì®ÿï�ÿ\¯� ÿ\¯ÿš¯£�f¯¦ÿš¯¼ÿH¯Áÿ…¯Äÿš¯Üÿ®¯áÿׯäÿ®¯ÿ\¯ ÿ\¯|ÿ…¯€ÿq¯‚ÿq¯©ÿš¯ªÿ®¯µÿH¯¶ÿ®¯·ÿš¯¹ÿš¯½ÿš¯¾ÿ®¯¿ÿ…¯ÀÿׯÁÿ…¯ÂÿׯÅÿïÆÿׯÇÿïÈÿׯÔÿ…¯Õÿׯýÿ…¯þÿׯ ÿH¯ÿ®¯ÿH¯ÿ®¯ÿš¯ÿ®°�ÿq°� ÿq°Üÿš°áÿ×°äÿš°ÿq° ÿq°mÿ×°ÿ×°ƒÿ×°‹ÿ×°ªÿš°¶ÿš°¸ÿ×°ºÿ×°¾ÿš°Àÿ×°Âÿ×°Æÿ×°Èÿ×°Õÿ×°þÿ×°ÿq°ÿq°ÿš±ÿ×±¦ÿ×±¼ÿñÄÿ×±€ÿ챂ÿ챩ÿ×±µÿñ·ÿì±¹ÿì±½ÿ×± ÿ×±ÿ×±ÿײ�ÿì²� ÿì²ÐÿײÜÿì²Ýÿì²ßÿײáÿì²äÿì²öÿì²ÿì² ÿì² ÿײªÿì²¶ÿì²¼ÿײ¾ÿì²Àÿì²Âÿì²ËÿײÕÿì²æÿײøÿì²úÿì²üÿì²þÿì²ÿײÿײÿì²ÿì²ÿ쳟ÿ׳¸ÿ׳»ÿ׳¾ÿ׳áÿ׳lÿ׳~ÿ׳„ÿ׳†ÿ׳ˆÿ׳Šÿ׳Œÿ׳±ÿ׳³ÿ׳Àÿ׳Âÿ׳Åÿ׳Çÿ׳Õÿ׳ïÿ׳ñÿ׳óÿ׳þÿ׳ ÿ׳ ÿ׳ÿ׳ÿ׳ÿ×µ�ÿ…µ�ÿ®µ�ÿ…µŸÿ×µ¤ÿšµªÿqµ®ÿšµµÿšµ¸ÿ×µ»ÿ×µ¼�)µ¾ÿ®µÌÿšµÍÿšµÎÿ…µÏÿqµÐÿ×µÑÿ×µÒÿšµÓÿšµÔÿšµÕÿ…µÖÿšµ×ÿšµØÿqµÙÿšµÚÿšµÛÿqµÜÿ®µÝÿ®µÞÿqµßÿ×µàÿšµáÿšµâÿšµãÿšµäÿ®µåÿšµæÿšµçÿ×µèÿšµéÿõêÿqµìÿšµíÿqµîÿ…µòÿ…µóÿšµõÿšµöÿ®µ÷ÿšµùÿšµÿ®µÿ®µÿ®µÿ…µ ÿ…µjÿqµkÿšµlÿ×µmÿ×µqÿšµrÿqµsÿ…µuÿšµwÿšµyÿšµ}ÿšµ~ÿ×µÿqµÿ×µƒÿ×µ„ÿ×µ…ÿqµ†ÿ×µ‡ÿqµˆÿ×µ‰ÿqµŠÿ×µ‹ÿ×µŒÿ×µÿqµ–ÿšµšÿšµžÿšµ ÿ×µ¢ÿ×µ¤ÿšµ¦ÿšµªÿ®µ¬ÿšµ®ÿšµ°ÿšµ±ÿ×µ²ÿqµ³ÿ×µ´ÿqµµ�)µ¶ÿ®µ¸ÿ®µºÿ®µ¼ÿ×µ¾ÿ®µÀÿšµÂÿšµÄÿšµÅÿšµÆÿqµÇÿšµÈÿqµËÿ×µÍÿšµÎÿšµÏÿ…µÑÿšµÓÿšµÕÿšµ×ÿšµÙÿqµÛÿqµÝÿqµàÿqµæÿ×µèÿ×µêÿõìÿšµîÿšµïÿ×µðÿqµñÿ×µòÿqµóÿ×µôÿqµöÿ×µøÿ®µúÿ®µüÿ®µþÿšµ�ÿšµÿšµÿ×µÿ×µ ÿqµ ÿqµ ÿqµ ÿqµÿšµÿšµÿšµÿ…µÿšµÿ×µÿqµÿ®µÿqµÿšµÿ…¶�ÿš¶�ÿ×¶�ÿš¶ÎÿöÏÿì¶ÕÿöØÿì¶Ûÿì¶Þÿì¶êÿì¶íÿì¶òÿöÿ×¶ÿ×¶ÿ×¶ÿš¶ ÿš¶jÿì¶sÿöÿì¶…ÿ춇ÿ춉ÿì¶ÿì¶²ÿì¶´ÿì¶Ïÿöàÿì¶ðÿì¶òÿì¶ôÿì¶ ÿì¶ ÿì¶ÿöÿì¶ÿì¶ÿ÷�ÿ…·�ÿ…·Ÿÿ×·¤ÿ®·ªÿ…·®ÿ®·µÿ®·¸ÿ×·»ÿ×·¾ÿ÷Êÿ®·Ìÿ÷Íÿ÷Îÿš·Ïÿš·Òÿ÷Óÿ÷Ôÿ÷Õÿš·Öÿ÷×ÿ÷Øÿš·Ùÿ÷Úÿ÷Ûÿš·Þÿš·àÿ÷áÿ®·âÿ÷ãÿ÷åÿ÷æÿ÷èÿ÷éÿ×·êÿš·ë�)·ìÿ÷íÿš·îÿ®·òÿš·óÿ÷ô�)·õÿ÷÷ÿ÷ùÿ÷ÿ…· ÿ…·jÿš·kÿ÷lÿ×·qÿ÷rÿ…·sÿš·uÿ÷wÿ×·yÿ÷}ÿ×·~ÿ×·ÿš·„ÿ×·…ÿš·†ÿ×·‡ÿš·ˆÿ×·‰ÿš·Šÿ×·Œÿ×·ÿš·–ÿ÷˜�)·šÿ÷žÿ÷¤ÿ÷¦ÿ÷¨�)·¬ÿ÷®ÿ÷°ÿ÷±ÿ×·²ÿš·³ÿ×·´ÿš·Àÿ®·Âÿ®·Äÿ÷Æÿ®·Èÿ®·Íÿ÷Îÿ®·Ïÿš·Ñÿ÷Óÿ÷Õÿ®·×ÿ÷Ùÿ…·Úÿ®·Ûÿ…·Üÿ®·Ýÿ…·Þÿ®·àÿš·áÿì·âÿ®·ãÿì·äÿ®·ìÿ÷îÿ÷ïÿ×·ðÿš·ñÿ×·òÿš·óÿ×·ôÿš·þÿ®·�ÿ÷ÿ÷ ÿ®· ÿš· ÿ®· ÿš·ÿ×·ÿ×·ÿ®·ÿš·ÿ÷ÿ×·ÿš·ÿì·ÿš·ÿ®·ÿš¸�ÿ®¸�ÿ®¸Îÿì¸Õÿì¸òÿì¸ÿ®¸ ÿ®¸sÿì¸Ïÿì¸ÿì¸ÿì¹�ÿ…¹�ÿ…¹Ÿÿ×¹¤ÿ®¹ªÿ…¹®ÿ®¹µÿ®¹¸ÿ×¹»ÿ×¹¾ÿùÊÿ®¹ÌÿùÍÿùÎÿš¹Ïÿš¹ÒÿùÓÿùÔÿùÕÿš¹Öÿù×ÿùØÿš¹ÙÿùÚÿùÛÿš¹Þÿš¹àÿùáÿ®¹âÿùãÿùåÿùæÿùèÿùéÿ×¹êÿš¹ë�)¹ìÿùíÿš¹îÿ®¹òÿš¹óÿùô�)¹õÿù÷ÿùùÿùÿ…¹ ÿ…¹jÿš¹kÿùlÿ×¹qÿùrÿ…¹sÿš¹uÿùwÿ×¹yÿù}ÿ×¹~ÿ×¹ÿš¹„ÿ×¹…ÿš¹†ÿ×¹‡ÿš¹ˆÿ×¹‰ÿš¹Šÿ×¹Œÿ×¹ÿš¹–ÿù˜�)¹šÿùžÿù¤ÿù¦ÿù¨�)¹¬ÿù®ÿù°ÿù±ÿ×¹²ÿš¹³ÿ×¹´ÿš¹Àÿ®¹Âÿ®¹ÄÿùÆÿ®¹Èÿ®¹ÍÿùÎÿ®¹Ïÿš¹ÑÿùÓÿùÕÿ®¹×ÿùÙÿ…¹Úÿ®¹Ûÿ…¹Üÿ®¹Ýÿ…¹Þÿ®¹àÿš¹áÿì¹âÿ®¹ãÿì¹äÿ®¹ìÿùîÿùïÿ×¹ðÿš¹ñÿ×¹òÿš¹óÿ×¹ôÿš¹þÿ®¹�ÿùÿù ÿ®¹ ÿš¹ ÿ®¹ ÿš¹ÿ×¹ÿ×¹ÿ®¹ÿš¹ÿùÿ×¹ÿš¹ÿì¹ÿš¹ÿ®¹ÿšº�ÿ®º�ÿ®ºÎÿìºÕÿìºòÿìºÿ®º ÿ®ºsÿìºÏÿìºÿìºÿ컟ÿ×»£�Ỹÿ×»»ÿ×»¾ÿûÜÿ×»áÿ®»äÿ×»lÿ×»{�=»}ÿì»~ÿ×»„ÿ×»†ÿ×»ˆÿ×»Šÿ×»Œÿ×»ªÿ×»±ÿ×»³ÿ×»¶ÿ×»¾ÿ×»Àÿ®»Âÿ®»ÅÿûÆÿ×»ÇÿûÈÿ×»Õÿ®»ïÿ×»ñÿ×»óÿ×»þÿ®»ÿ×»ÿ×»ÿ×»ÿ×¼Ïÿì¼Øÿì¼Ûÿì¼Þÿì¼áÿì¼êÿì¼íÿì¼jÿì¼ÿì¼…ÿ켇ÿ켉ÿì¼ÿì¼²ÿì¼´ÿì¼Àÿì¼Âÿì¼Õÿì¼àÿì¼ðÿì¼òÿì¼ôÿì¼þÿì¼ ÿì¼ ÿì¼ÿ×¼ÿ×¼ÿì¼ÿì½£�á½ê�)½ÿ×½ÿ×¾�ÿì¾� ÿì¾ÿì¾ ÿì¿£�á¿ê�)¿ÿ׿ÿ×À�ÿìÀ� ÿìÀÿìÀ ÿìÃ�ÿÃÃ� ÿÃÃÿ׿ÿ×üÿ…ÃÁÿ®ÃÄÿ×ÃÜÿ×ÃÝÿìÃáÿìÃäÿ×ÃöÿìÃÿÃà ÿÃÃ|ÿ®Ã€ÿÃÂÿÃéÿ×êÿ×õÿ…öÿ×÷ÿšÃ¹ÿšÃ½ÿ×þÿ×ÿÿ®ÃÀÿìÃÁÿ®ÃÂÿìÃÔÿ®ÃÕÿìÃøÿìÃúÿìÃüÿìÃýÿ®Ãþÿìà ÿ®Ãÿ×Ãÿ®Ãÿ×Ãÿ×Ãÿ×Ä�ÿšÄ� ÿšÄÜÿ×ÄÝÿ×Ääÿ×Äöÿ×ÄÿšÄ ÿšÄªÿ×Ķÿ×ĸÿ×ĺÿ׾ÿ×Äøÿ×Äúÿ×Äüÿ×Äÿ®Äÿ®Äÿ׿ÿ×Å€ÿìÅ‚ÿìŵÿ×Å·ÿìŹÿìÅ ÿìÅÿìÆ�ÿìÆ� ÿìÆÿìÆ ÿìǼÿ×Ç€ÿìÇ‚ÿìǵÿ×Ç·ÿìǹÿìÇ ÿìÇÿìÈ�ÿìÈ� ÿìÈÿìÈ ÿìÊŸÿ×ʸÿ×Ê»ÿ×ʾÿ×ÊÁÿ×Êáÿ×Êlÿ×Ê|ÿ×Ê~ÿ×Ê„ÿ×ʆÿ×ʈÿ×ÊŠÿ×ÊŒÿ×ʱÿ×ʳÿ×Ê¿ÿ×ÊÀÿ×ÊÁÿ×ÊÂÿ×ÊÅÿšÊÇÿšÊÔÿ×ÊÕÿ×Êïÿ×Êñÿ×Êóÿ×Êýÿ×Êþÿ×Ê ÿ×Ê ÿ×Êÿ×Êÿ×Êÿ×ÊÿìËÏÿ×ËØÿ×ËÛÿ×ËÞÿ×Ëáÿ×Ëêÿ×Ëíÿ×Ëjÿ×Ëÿ×Ë…ÿסÿ×ˉÿ×Ëÿ×˲ÿ×Ë´ÿ×ËÀÿ×ËÂÿ×ËÆÿ×ËÈÿ×ËÕÿ×Ëàÿ×Ëðÿ×Ëòÿ×Ëôÿ×Ëþÿ×Ë ÿ×Ë ÿ×Ëÿ×Ëÿ×Ì�ÿÃÌ� ÿÃÌ£�f̼ÿ×̾ÿ×ÌÁÿ®ÌÜÿÃÌáÿ×ÌäÿÃÌÿÃÌ ÿÃÌmÿìÌ|ÿ®Ì€ÿ×ÌÿìÌ‚ÿ×̃ÿìÌ‹ÿì̪ÿÃ̵ÿ×̶ÿÃÌ·ÿ×̸ÿì̹ÿ×̺ÿì̾ÿÃÌ¿ÿ®ÌÀÿ×ÌÁÿ®ÌÂÿ×ÌÅÿÃÌÆÿ×ÌÇÿÃÌÈÿ×ÌÔÿ®ÌÕÿ×Ìýÿ®Ìþÿ×Ì ÿ×ÌÿÃÌÿ×ÌÿÃÌÿÃÍáÿ×ÍÀÿ×ÍÂÿ×ÍÕÿ×Íþÿ×Σ�áÎê�)Îÿ×Îÿ×Ï�ÿìÏ� ÿìÏÿìÏ ÿìÒ£�áÒê�)Òÿ×Òÿ×Ó�ÿìÓ� ÿìÓÿìÓ ÿìÖ£�áÖê�)Öÿ×Öÿ××�ÿì×� ÿì×ÿì× ÿìÙ�ÿqÙ� ÿqÙÿšÙ¦ÿšÙ¼ÿqÙ¾ÿ×ÙÁÿšÙÄÿšÙÜÿ×Ùáÿ×Ùäÿ×ÙÿqÙ ÿqÙnÿ×Ù|ÿšÙ€ÿ®Ù‚ÿ®Ù—ÿ×Ù›ÿ×Ù§ÿ×Ù©ÿšÙªÿ×ÙµÿqÙ¶ÿ×Ù·ÿ…Ù¹ÿ…Ù½ÿšÙ¾ÿ×Ù¿ÿšÙÀÿ×ÙÁÿšÙÂÿ×ÙÅÿšÙÇÿšÙÔÿšÙÕÿ×Ùáÿ×Ùãÿ×ÙýÿšÙþÿ×Ùÿ×Ù ÿqÙÿ×ÙÿqÙÿ×ÙÿšÙÿ×Ú�ÿìÚ� ÿìÚÿìÚ ÿìÛ�ÿqÛ� ÿqÛÿšÛ¦ÿšÛ¼ÿqÛ¾ÿ×ÛÁÿšÛÄÿšÛÜÿ×Ûáÿ×Ûäÿ×ÛÿqÛ ÿqÛnÿ×Û|ÿšÛ€ÿ®Û‚ÿ®Û—ÿ×Û›ÿ×Û§ÿ×Û©ÿšÛªÿ×ÛµÿqÛ¶ÿ×Û·ÿ…Û¹ÿ…Û½ÿšÛ¾ÿ×Û¿ÿšÛÀÿ×ÛÁÿšÛÂÿ×ÛÅÿšÛÇÿšÛÔÿšÛÕÿ×Ûáÿ×Ûãÿ×ÛýÿšÛþÿ×Ûÿ×Û ÿqÛÿ×ÛÿqÛÿ×ÛÿšÛÿ×Ü�ÿìÜ� ÿìÜÿìÜ ÿìÞ�ÿìÞ� ÿìÞÿìÞ ÿìà�ÿìà� ÿìàÿìà ÿìá�ÿ®á�ÿ®áÿìá¤ÿ×á¦ÿìá¨ÿ×áªÿ×á®ÿ×á°ÿ×á±ÿìáµÿ×á¼ÿÃá½ÿ×á¿ÿ×áÁÿ×áÄÿìáÇÿìáÎÿìáÕÿìáòÿìáÿ®á ÿ®árÿ×ásÿìázÿìá|ÿ×á€ÿìá‚ÿìáŸÿ×á¡ÿìá©ÿìáµÿÃá·ÿìá¹ÿìá»ÿ×á½ÿìá¿ÿ×áÁÿ×áÊÿ×áÎÿ×áÏÿìáÔÿ×áÙÿ×áÛÿ×áÝÿ×áåÿ×áçÿìáõÿìá÷ÿ×áùÿ×áûÿ×áýÿ×áÿ×áÿ×á ÿ×áÿ×áÿ×áÿìáÿìáÿ×áÿìâ�ÿìâ� ÿìâÐÿ×âÜÿìâÝÿìâßÿ×âáÿìâäÿìâöÿìâÿìâ ÿìâ ÿ×âªÿìâ¶ÿìâ¼ÿ×â¾ÿìâÀÿìâÂÿìâËÿ×âÕÿìâæÿ×âøÿìâúÿìâüÿìâþÿìâÿ×âÿ×âÿìâÿìâÿìã�ÿ®ã�ÿ®ãÿìã¤ÿ×ã¦ÿìã¨ÿ×ãªÿ×ã®ÿ×ã°ÿ×ã±ÿìãµÿ×ã¼ÿÃã½ÿ×ã¿ÿ×ãÁÿ×ãÄÿìãÇÿìãÎÿìãÕÿìãòÿìãÿ®ã ÿ®ãrÿ×ãsÿìãzÿìã|ÿ×ã€ÿìã‚ÿìãŸÿ×ã¡ÿìã©ÿìãµÿÃã·ÿìã¹ÿìã»ÿ×ã½ÿìã¿ÿ×ãÁÿ×ãÊÿ×ãÎÿ×ãÏÿìãÔÿ×ãÙÿ×ãÛÿ×ãÝÿ×ãåÿ×ãçÿìãõÿìã÷ÿ×ãùÿ×ãûÿ×ãýÿ×ãÿ×ãÿ×ã ÿ×ãÿ×ãÿ×ãÿìãÿìãÿ×ãÿìä�ÿìä� ÿìäÐÿ×äÜÿìäÝÿìäßÿ×äáÿìääÿìäöÿìäÿìä ÿìä ÿ×äªÿìä¶ÿìä¼ÿ×ä¾ÿìäÀÿìäÂÿìäËÿ×äÕÿìäæÿ×äøÿìäúÿìäüÿìäþÿìäÿ×äÿ×äÿìäÿìäÿìåŸÿ×å¸ÿ×å»ÿ×å¾ÿ×åÁÿ×åáÿ×ålÿ×å|ÿ×å~ÿ×å„ÿ×å†ÿ×åˆÿ×åŠÿ×åŒÿ×å±ÿ×å³ÿ×å¿ÿ×åÀÿ×åÁÿ×åÂÿ×åÅÿšåÇÿšåÔÿ×åÕÿ×åïÿ×åñÿ×åóÿ×åýÿ×åþÿ×å ÿ×å ÿ×åÿ×åÿ×åÿ×åÿìæÏÿ׿Øÿ׿Ûÿ׿Þÿ׿áÿ׿êÿ׿íÿ׿jÿ׿ÿ׿…ÿ׿‡ÿ׿‰ÿ׿ÿ׿²ÿ׿´ÿ׿Àÿ׿Âÿ׿Æÿ׿Èÿ׿Õÿ׿àÿ׿ðÿ׿òÿ׿ôÿ׿þÿ׿ ÿ׿ ÿ׿ÿ׿ÿ×ç�ÿ®ç�ÿ®çÿ®ç ÿ®ç€ÿìç‚ÿìç·ÿìç¹ÿìç ÿ×çÿ×èé�)é�ÿìé� ÿìéÿìé ÿìéÿ×éÿ×ï�ÿ®ï�ÿ®ïÿìï¤ÿ×ï¦ÿìï¨ÿ×ïªÿ×ï®ÿ×ï°ÿ×ï±ÿìïµÿ×ï¼ÿÃï½ÿ×ï¿ÿ×ïÁÿ×ïÄÿìïÇÿìïÎÿìïÕÿìïòÿìïÿ®ï ÿ®ïrÿ×ïsÿìïzÿìï|ÿ×ï€ÿìï‚ÿìïŸÿ×ï¡ÿìï©ÿìïµÿÃï·ÿìï¹ÿìï»ÿ×ï½ÿìï¿ÿ×ïÁÿ×ïÊÿ×ïÎÿ×ïÏÿìïÔÿ×ïÙÿ×ïÛÿ×ïÝÿ×ïåÿ×ïçÿìïõÿìï÷ÿ×ïùÿ×ïûÿ×ïýÿ×ïÿ×ïÿ×ï ÿ×ïÿ×ïÿ×ïÿìïÿìïÿ×ïÿìð�ÿìð� ÿìðÐÿ×ðÜÿìðÝÿìðßÿ×ðáÿìðäÿìðöÿìðÿìð ÿìð ÿ×ðªÿìð¶ÿìð¼ÿ×ð¾ÿìðÀÿìðÂÿìðËÿ×ðÕÿìðæÿ×ðøÿìðúÿìðüÿìðþÿìðÿ×ðÿ×ðÿìðÿìðÿìñ�ÿ®ñ�ÿ®ñÿìñ¤ÿ×ñ¦ÿìñ¨ÿ×ñªÿ×ñ®ÿ×ñ°ÿ×ñ±ÿìñµÿ×ñ¼ÿÃñ½ÿ×ñ¿ÿ×ñÁÿ×ñÄÿìñÇÿìñÎÿìñÕÿìñòÿìñÿ®ñ ÿ®ñrÿ×ñsÿìñzÿìñ|ÿ×ñ€ÿìñ‚ÿìñŸÿ×ñ¡ÿìñ©ÿìñµÿÃñ·ÿìñ¹ÿìñ»ÿ×ñ½ÿìñ¿ÿ×ñÁÿ×ñÊÿ×ñÎÿ×ñÏÿìñÔÿ×ñÙÿ×ñÛÿ×ñÝÿ×ñåÿ×ñçÿìñõÿìñ÷ÿ×ñùÿ×ñûÿ×ñýÿ×ñÿ×ñÿ×ñ ÿ×ñÿ×ñÿ×ñÿìñÿìñÿ×ñÿìò�ÿìò� ÿìòÐÿ×òÜÿìòÝÿìòßÿ×òáÿìòäÿìòöÿìòÿìò ÿìò ÿ×òªÿìò¶ÿìò¼ÿ×ò¾ÿìòÀÿìòÂÿìòËÿ×òÕÿìòæÿ×òøÿìòúÿìòüÿìòþÿìòÿ×òÿ×òÿìòÿìòÿìó�ÿ®ó�ÿ®óÿìó¤ÿ×ó¦ÿìó¨ÿ×óªÿ×ó®ÿ×ó°ÿ×ó±ÿìóµÿ×ó¼ÿÃó½ÿ×ó¿ÿ×óÁÿ×óÄÿìóÇÿìóÎÿìóÕÿìóòÿìóÿ®ó ÿ®órÿ×ósÿìózÿìó|ÿ×ó€ÿìó‚ÿìóŸÿ×ó¡ÿìó©ÿìóµÿÃó·ÿìó¹ÿìó»ÿ×ó½ÿìó¿ÿ×óÁÿ×óÊÿ×óÎÿ×óÏÿìóÔÿ×óÙÿ×óÛÿ×óÝÿ×óåÿ×óçÿìóõÿìó÷ÿ×óùÿ×óûÿ×óýÿ×óÿ×óÿ×ó ÿ×óÿ×óÿ×óÿìóÿìóÿ×óÿìô�ÿìô� ÿìôÐÿ×ôÜÿìôÝÿìôßÿ×ôáÿìôäÿìôöÿìôÿìô ÿìô ÿ×ôªÿìô¶ÿìô¼ÿ×ô¾ÿìôÀÿìôÂÿìôËÿ×ôÕÿìôæÿ×ôøÿìôúÿìôüÿìôþÿìôÿ×ôÿ×ôÿìôÿìôÿìõ�ÿ®õ�ÿ®õÿìõ¤ÿ×õ¦ÿìõ¨ÿ×õªÿ×õ®ÿ×õ°ÿ×õ±ÿìõµÿ×õ¼ÿÃõ½ÿ×õ¿ÿ×õÁÿ×õÄÿìõÇÿìõÎÿìõÕÿìõòÿìõÿ®õ ÿ®õrÿ×õsÿìõzÿìõ|ÿ×õ€ÿìõ‚ÿìõŸÿ×õ¡ÿìõ©ÿìõµÿÃõ·ÿìõ¹ÿìõ»ÿ×õ½ÿìõ¿ÿ×õÁÿ×õÊÿ×õÎÿ×õÏÿìõÔÿ×õÙÿ×õÛÿ×õÝÿ×õåÿ×õçÿìõõÿìõ÷ÿ×õùÿ×õûÿ×õýÿ×õÿ×õÿ×õ ÿ×õÿ×õÿ×õÿìõÿìõÿ×õÿìö�ÿìö� ÿìöÐÿ×öÜÿìöÝÿìößÿ×öáÿìöäÿìööÿìöÿìö ÿìö ÿ×öªÿìö¶ÿìö¼ÿ×ö¾ÿìöÀÿìöÂÿìöËÿ×öÕÿìöæÿ×öøÿìöúÿìöüÿìöþÿìöÿ×öÿ×öÿìöÿìöÿì÷�ÿ…÷�ÿ…÷Ÿÿì÷¤ÿš÷ªÿq÷®ÿš÷µÿš÷¸ÿì÷»ÿì÷¾ÿÃ÷Éÿì÷Îÿ®÷Ïÿ×÷Õÿ®÷Øÿ×÷Ûÿ×÷Þÿ×÷áÿ×÷êÿ×÷ë�f÷íÿ×÷îÿì÷òÿ®÷ô�f÷ÿ…÷ ÿ…÷jÿ×÷lÿì÷rÿq÷sÿ®÷~ÿì÷ÿ×÷„ÿì÷…ÿ×÷†ÿì÷‡ÿ×÷ˆÿì÷‰ÿ×÷Šÿì÷Œÿì÷ÿ×÷˜�f÷¨�f÷±ÿì÷²ÿ×÷³ÿì÷´ÿ×÷Àÿ×÷Âÿ×÷Åÿ×÷ÆÿÃ÷Çÿ×÷ÈÿÃ÷Îÿš÷Ïÿ®÷Õÿ×÷Ùÿq÷Ûÿq÷Ýÿq÷àÿ×÷ïÿì÷ðÿ×÷ñÿì÷òÿ×÷óÿì÷ôÿ×÷þÿ×÷ ÿq÷ ÿ×÷ ÿq÷ ÿ×÷ÿš÷ÿ®÷ÿì÷ÿ×÷ÿ×÷ÿš÷ÿ®ø�ÿ®ø�ÿ®øÎÿ×øÕÿ×øòÿ×øÿ®ø ÿ®øsÿ×øÏÿ×øÿ×øÿ×ù�ÿ…ù�ÿ…ùŸÿìù¤ÿšùªÿqù®ÿšùµÿšù¸ÿìù»ÿìù¾ÿÃùÉÿìùÎÿ®ùÏÿ×ùÕÿ®ùØÿ×ùÛÿ×ùÞÿ×ùáÿ×ùêÿ×ùë�fùíÿ×ùîÿìùòÿ®ùô�fùÿ…ù ÿ…ùjÿ×ùlÿìùrÿqùsÿ®ù~ÿìùÿ×ù„ÿìù…ÿ×ù†ÿìù‡ÿ×ùˆÿìù‰ÿ×ùŠÿìùŒÿìùÿ×ù˜�fù¨�fù±ÿìù²ÿ×ù³ÿìù´ÿ×ùÀÿ×ùÂÿ×ùÅÿ×ùÆÿÃùÇÿ×ùÈÿÃùÎÿšùÏÿ®ùÕÿ×ùÙÿqùÛÿqùÝÿqùàÿ×ùïÿìùðÿ×ùñÿìùòÿ×ùóÿìùôÿ×ùþÿ×ù ÿqù ÿ×ù ÿqù ÿ×ùÿšùÿ®ùÿìùÿ×ùÿ×ùÿšùÿ®ú�ÿ®ú�ÿ®úÎÿ×úÕÿ×úòÿ×úÿ®ú ÿ®úsÿ×úÏÿ×úÿ×úÿ×û�ÿ…û�ÿ…ûŸÿìû¤ÿšûªÿqû®ÿšûµÿšû¸ÿìû»ÿìû¾ÿÃûÉÿìûÎÿ®ûÏÿ×ûÕÿ®ûØÿ×ûÛÿ×ûÞÿ×ûáÿ×ûêÿ×ûë�fûíÿ×ûîÿìûòÿ®ûô�fûÿ…û ÿ…ûjÿ×ûlÿìûrÿqûsÿ®û~ÿìûÿ×û„ÿìû…ÿ×û†ÿìû‡ÿ×ûˆÿìû‰ÿ×ûŠÿìûŒÿìûÿ×û˜�fû¨�fû±ÿìû²ÿ×û³ÿìû´ÿ×ûÀÿ×ûÂÿ×ûÅÿ×ûÆÿÃûÇÿ×ûÈÿÃûÎÿšûÏÿ®ûÕÿ×ûÙÿqûÛÿqûÝÿqûàÿ×ûïÿìûðÿ×ûñÿìûòÿ×ûóÿìûôÿ×ûþÿ×û ÿqû ÿ×û ÿqû ÿ×ûÿšûÿ®ûÿìûÿ×ûÿ×ûÿšûÿ®ü�ÿ®ü�ÿ®üÎÿ×üÕÿ×üòÿ×üÿ®ü ÿ®üsÿ×üÏÿ×üÿ×üÿ×ÿ�ÿ…ÿ�ÿ®ÿ�ÿ…ÿŸÿ×ÿ¤ÿšÿªÿqÿ®ÿšÿµÿšÿ¸ÿ×ÿ»ÿ×ÿ¼�)ÿ¾ÿ®ÿÌÿšÿÍÿšÿÎÿ…ÿÏÿqÿÐÿ×ÿÑÿ×ÿÒÿšÿÓÿšÿÔÿšÿÕÿ…ÿÖÿšÿ×ÿšÿØÿqÿÙÿšÿÚÿšÿÛÿqÿÜÿ®ÿÝÿ®ÿÞÿqÿßÿ×ÿàÿšÿáÿšÿâÿšÿãÿšÿäÿ®ÿåÿšÿæÿšÿçÿ×ÿèÿšÿéÿÃÿêÿqÿìÿšÿíÿqÿîÿ…ÿòÿ…ÿóÿšÿõÿšÿöÿ®ÿ÷ÿšÿùÿšÿÿ®ÿÿ®ÿÿ®ÿÿ…ÿ ÿ…ÿjÿqÿkÿšÿlÿ×ÿmÿ×ÿqÿšÿrÿqÿsÿ…ÿuÿšÿwÿšÿyÿšÿ}ÿšÿ~ÿ×ÿÿqÿÿ×ÿƒÿ×ÿ„ÿ×ÿ…ÿqÿ†ÿ×ÿ‡ÿqÿˆÿ×ÿ‰ÿqÿŠÿ×ÿ‹ÿ×ÿŒÿ×ÿÿqÿ–ÿšÿšÿšÿžÿšÿ ÿ×ÿ¢ÿ×ÿ¤ÿšÿ¦ÿšÿªÿ®ÿ¬ÿšÿ®ÿšÿ°ÿšÿ±ÿ×ÿ²ÿqÿ³ÿ×ÿ´ÿqÿµ�)ÿ¶ÿ®ÿ¸ÿ®ÿºÿ®ÿ¼ÿ×ÿ¾ÿ®ÿÀÿšÿÂÿšÿÄÿšÿÅÿšÿÆÿqÿÇÿšÿÈÿqÿËÿ×ÿÍÿšÿÎÿšÿÏÿ…ÿÑÿšÿÓÿšÿÕÿšÿ×ÿšÿÙÿqÿÛÿqÿÝÿqÿàÿqÿæÿ×ÿèÿ×ÿêÿÃÿìÿšÿîÿšÿïÿ×ÿðÿqÿñÿ×ÿòÿqÿóÿ×ÿôÿqÿöÿ×ÿøÿ®ÿúÿ®ÿüÿ®ÿþÿšÿ�ÿšÿÿšÿÿ×ÿÿ×ÿ ÿqÿ ÿqÿ ÿqÿ ÿqÿÿšÿÿšÿÿšÿÿ…ÿÿšÿÿ×ÿÿqÿÿ®ÿÿqÿÿšÿÿ…��ÿš��ÿ×��ÿš�ÎÿÃ�Ïÿì�ÕÿÃ�Øÿì�Ûÿì�Þÿì�êÿì�íÿì�òÿÃ�ÿ×�ÿ×�ÿ×�ÿš� ÿš�jÿì�sÿÃ�ÿì�…ÿì�‡ÿì�‰ÿì�ÿì�²ÿì�´ÿì�ÏÿÃ�àÿì�ðÿì�òÿì�ôÿì� ÿì� ÿì�ÿÃ�ÿì�ÿì�ÿÃ�ÿš�ÿ×�ÿš�)Ÿÿפÿ®¦�)ªÿ…®ÿ®µÿ®¸ÿ×»ÿ×¼�)¾ÿÃÄ�)ÌÿÃÍÿÃÎÿšÏÿ®Ðÿ×Ñÿ×ÒÿÃÓÿÃÔÿÃÕÿšÖÿÃ×ÿÃØÿ®ÙÿÃÚÿÃÛÿ®Þÿ®ßÿ×àÿÃáÿšâÿÃãÿÃåÿÃæÿÃçÿ×èÿÃêÿ®ë�)ìÿÃíÿ®îÿÃòÿšóÿÃô�)õÿÃ÷ÿÃùÿÃÿ×ÿ×ÿ×ÿš ÿšjÿ®kÿÃlÿ×qÿÃrÿ…sÿšuÿÃwÿ×yÿÃ}ÿÃ~ÿ×ÿ®„ÿ×…ÿ®†ÿׇÿ®ˆÿ׉ÿ®Šÿ׌ÿ×ÿ®–ÿØ�)šÿÞÿàÿ×¢ÿפÿæÿè�)©�)¬ÿîÿðÿñÿײÿ®³ÿ×´ÿ®µ�)¼ÿ×½�)ÀÿšÂÿšÄÿÃÅÿׯÿÃÇÿ×ÈÿÃËÿ×ÍÿÃÎÿ®ÏÿšÑÿÃÓÿÃÕÿš×ÿÃÙÿ…Ûÿ…Ýÿ…àÿ®æÿ×èÿ×ìÿÃîÿÃïÿ×ðÿ®ñÿ×òÿ®óÿ×ôÿ®öÿ×þÿš�ÿÃÿÃÿ×ÿ× ÿš ÿ® ÿš ÿ®ÿ×ÿ×ÿ®ÿšÿÃÿ×ÿ®�)ÿ®ÿ®ÿš�ÿÃ�ÿÃÎÿÃÏÿ×ÕÿÃØÿ×Ûÿ×Þÿ×êÿ×íÿ×òÿÃÿà ÿÃjÿ×sÿÃÿ×…ÿׇÿ׉ÿ×ÿײÿ×´ÿ×ÏÿÃàÿ×ðÿ×òÿ×ôÿ× ÿ× ÿ×ÿÃÿ×ÿ×ÿßÿ×£�á¸ÿ×»ÿ×¾ÿÃÜÿ×áÿ®äÿ×lÿ×{�=}ÿì~ÿׄÿ׆ÿ׈ÿ׊ÿ׌ÿתÿ×±ÿ׳ÿ×¶ÿ×¾ÿ×Àÿ®Âÿ®ÅÿÃÆÿ×ÇÿÃÈÿ×Õÿ®ïÿ×ñÿ×óÿ×þÿ®ÿ×ÿ×ÿ×ÿ×ÏÿìØÿìÛÿìÞÿìáÿìêÿìíÿìjÿìÿì…ÿì‡ÿì‰ÿìÿì²ÿì´ÿìÀÿìÂÿìÕÿìàÿìðÿìòÿìôÿìþÿì ÿì ÿìÿ×ÿ×ÿìÿìŸÿ׸ÿ×»ÿ×¾ÿ×Áÿ×áÿ×lÿ×|ÿ×~ÿׄÿ׆ÿ׈ÿ׊ÿ׌ÿ×±ÿ׳ÿ׿ÿ×Àÿ×Áÿ×Âÿ×ÅÿšÇÿšÔÿ×Õÿ×ïÿ×ñÿ×óÿ×ýÿ×þÿ× ÿ× ÿ×ÿ×ÿ×ÿ×ÿìÏÿìØÿìÛÿìÞÿìáÿìêÿìíÿìjÿìÿì…ÿì‡ÿì‰ÿìÿì²ÿì´ÿìÀÿìÂÿìÕÿìàÿìðÿìòÿìôÿìþÿì ÿì ÿìÿ×ÿ×ÿìÿì �ÿš � ÿš ÿ® ¦ÿ® ¨ÿà ªÿà °ÿà ¼ÿq ½ÿà ¿ÿà Áÿà Äÿ® Ðÿ× Üÿà ßÿ× áÿ× äÿà ÿš  ÿš rÿà vÿ× |ÿà €ÿà ‚ÿà Ÿÿà  ÿ× ©ÿ® ªÿà µÿq ¶ÿà ·ÿà ¹ÿà »ÿà ¼ÿ× ½ÿ® ¾ÿà ¿ÿà Àÿ× Áÿà Âÿ× Êÿà Ëÿ× Ôÿà Õÿ× Ùÿà Ûÿà Ýÿà åÿà æÿ× ÷ÿà ùÿà ûÿà ýÿà þÿ× ÿà ÿ× ÿà ÿ×  ÿ× ÿ× ÿ× ÿ× ÿ® ÿà �ÿš � ÿš Ðÿ× Üÿà Ýÿ× ßÿ× áÿ× äÿà öÿ× ÿš  ÿš  ÿ× ªÿà ¶ÿà ¼ÿ× ¾ÿà Àÿ× Âÿ× Ëÿ× Õÿ× æÿ× øÿ× úÿ× üÿ× þÿ× ÿ× ÿ× ÿš ÿš ÿà �ÿš � ÿš ÿ® ¦ÿ® ¨ÿà ªÿà °ÿà ¼ÿq ½ÿà ¿ÿà Áÿà Äÿ® Ðÿ× Üÿà ßÿ× áÿ× äÿà ÿš  ÿš rÿà vÿ× |ÿà €ÿà ‚ÿà Ÿÿà  ÿ× ©ÿ® ªÿà µÿq ¶ÿà ·ÿà ¹ÿà »ÿà ¼ÿ× ½ÿ® ¾ÿà ¿ÿà Àÿ× Áÿà Âÿ× Êÿà Ëÿ× Ôÿà Õÿ× Ùÿà Ûÿà Ýÿà åÿà æÿ× ÷ÿà ùÿà ûÿà ýÿà þÿ× ÿà ÿ× ÿà ÿ×  ÿ× ÿ× ÿ× ÿ× ÿ® ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿã�áê�)ÿ×ÿ×�ÿì� ÿìÿì ÿì�ÿš� ÿšÿ®¦ÿ®¨ÿêÿðÿüÿq½ÿÿÿÃÁÿÃÄÿ®Ðÿ×ÜÿÃßÿ×áÿ×äÿÃÿš ÿšrÿÃvÿ×|ÿÀÿÂÿßÿàÿשÿ®ªÿõÿq¶ÿ÷ÿùÿûÿüÿ×½ÿ®¾ÿÿÿÃÀÿ×ÁÿÃÂÿ×ÊÿÃËÿ×ÔÿÃÕÿ×ÙÿÃÛÿÃÝÿÃåÿÃæÿ×÷ÿÃùÿÃûÿÃýÿÃþÿ×ÿÃÿ×ÿÃÿ× ÿ×ÿ×ÿ×ÿ×ÿ®ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃ�ÿš� ÿšÿ®¦ÿ®¨ÿêÿðÿüÿq½ÿÿÿÃÁÿÃÄÿ®Ðÿ×ÜÿÃßÿ×áÿ×äÿÃÿš ÿšrÿÃvÿ×|ÿÀÿÂÿßÿàÿשÿ®ªÿõÿq¶ÿ÷ÿùÿûÿüÿ×½ÿ®¾ÿÿÿÃÀÿ×ÁÿÃÂÿ×ÊÿÃËÿ×ÔÿÃÕÿ×ÙÿÃÛÿÃÝÿÃåÿÃæÿ×÷ÿÃùÿÃûÿÃýÿÃþÿ×ÿÃÿ×ÿÃÿ× ÿ×ÿ×ÿ×ÿ×ÿ®ÿÃ�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃ�ÿ®�ÿ®ªÿì°ÿ×¼ÿ׿ÿ×ÿ® ÿ®rÿì€ÿì‚ÿìŸÿ×µÿ×·ÿì¹ÿì»ÿ×Êÿ×ÙÿìÛÿìÝÿìåÿ×ÿ×ÿ×�ÿ×� ÿ×ÐÿìÝÿìßÿìöÿìÿ× ÿ× ÿì¼ÿìËÿìæÿìøÿìúÿìüÿìÿìÿìÿ×ÿ×�ÿ®� ÿ®ÿæÿêÿ×°ÿ×¼ÿÿÿ×Áÿ×ÄÿÃÜÿ×äÿ×ÿ® ÿ®rÿ×|ÿ×€ÿׂÿןÿשÿêÿ×µÿöÿ×·ÿ×¹ÿ×»ÿ×½ÿþÿ׿ÿ×Áÿ×Êÿ×Ôÿ×Ùÿ×Ûÿ×Ýÿ×åÿ×ýÿ×ÿ×ÿ× ÿ×ÿ×ÿÃÿ×�ÿš� ÿšÐÿ×ÜÿÃÝÿ×ßÿ×áÿ×äÿÃöÿ×ÿš ÿš ÿתÿöÿüÿ×¾ÿÃÀÿ×Âÿ×Ëÿ×Õÿ׿ÿ×øÿ×úÿ×üÿ×þÿ×ÿ×ÿ×ÿšÿšÿÃáÿ×Àÿ×Âÿ×Õÿ×þÿ×£�áê�)ÿ×ÿ×�ÿì� ÿìÿì ÿì�ÿq� ÿq�&ÿ×�*ÿ×�- �2ÿ×�4ÿ×�7ÿq�9ÿ®�:ÿ®�<ÿ…�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�Ÿÿ…�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿq&ÿq6ÿ®8ÿ…:ÿ…Gÿ×úÿ®üÿ®þÿ®�ÿ…ÿq ÿq_ÿ×Iÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×oÿ…qÿ…sÿ…ÿq�ÿì� ÿìÿì ÿì�ÿq� ÿq�&ÿ×�*ÿ×�- �2ÿ×�4ÿ×�7ÿq�9ÿ®�:ÿ®�<ÿ…�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�Ÿÿ…�Èÿ×�Êÿ×�Ìÿ×�Îÿ×�Þÿ×�àÿ×�âÿ×�äÿ×ÿ×ÿ×ÿ×ÿ×$ÿq&ÿq6ÿ®8ÿ…:ÿ…Gÿ×úÿ®üÿ®þÿ®�ÿ…ÿq ÿq_ÿ×Iÿ×Kÿ×Mÿ×Oÿ×Qÿ×Sÿ×Uÿ×Wÿ×Yÿ×[ÿ×]ÿ×_ÿ×oÿ…qÿ…sÿ…ÿq �ÿì � ÿì ÿì  ÿì!�ÿq!� ÿq!�&ÿ×!�*ÿ×!�- !�2ÿ×!�4ÿ×!�7ÿq!�9ÿ®!�:ÿ®!�<ÿ…!�‰ÿ×!�”ÿ×!�•ÿ×!�–ÿ×!�—ÿ×!�˜ÿ×!�šÿ×!�Ÿÿ…!�Èÿ×!�Êÿ×!�Ìÿ×!�Îÿ×!�Þÿ×!�àÿ×!�âÿ×!�äÿ×!ÿ×!ÿ×!ÿ×!ÿ×!$ÿq!&ÿq!6ÿ®!8ÿ…!:ÿ…!Gÿ×!úÿ®!üÿ®!þÿ®!�ÿ…!ÿq! ÿq!_ÿ×!Iÿ×!Kÿ×!Mÿ×!Oÿ×!Qÿ×!Sÿ×!Uÿ×!Wÿ×!Yÿ×![ÿ×!]ÿ×!_ÿ×!oÿ…!qÿ…!sÿ…!ÿq"�ÿì"� ÿì"ÿì" ÿì#�ÿq#� ÿq#�&ÿ×#�*ÿ×#�- #�2ÿ×#�4ÿ×#�7ÿq#�9ÿ®#�:ÿ®#�<ÿ…#�‰ÿ×#�”ÿ×#�•ÿ×#�–ÿ×#�—ÿ×#�˜ÿ×#�šÿ×#�Ÿÿ…#�Èÿ×#�Êÿ×#�Ìÿ×#�Îÿ×#�Þÿ×#�àÿ×#�âÿ×#�äÿ×#ÿ×#ÿ×#ÿ×#ÿ×#$ÿq#&ÿq#6ÿ®#8ÿ…#:ÿ…#Gÿ×#úÿ®#üÿ®#þÿ®#�ÿ…#ÿq# ÿq#_ÿ×#Iÿ×#Kÿ×#Mÿ×#Oÿ×#Qÿ×#Sÿ×#Uÿ×#Wÿ×#Yÿ×#[ÿ×#]ÿ×#_ÿ×#oÿ…#qÿ…#sÿ…#ÿq$�ÿì$� ÿì$ÿì$ ÿì%�ÿq%� ÿq%�&ÿ×%�*ÿ×%�- %�2ÿ×%�4ÿ×%�7ÿq%�9ÿ®%�:ÿ®%�<ÿ…%�‰ÿ×%�”ÿ×%�•ÿ×%�–ÿ×%�—ÿ×%�˜ÿ×%�šÿ×%�Ÿÿ…%�Èÿ×%�Êÿ×%�Ìÿ×%�Îÿ×%�Þÿ×%�àÿ×%�âÿ×%�äÿ×%ÿ×%ÿ×%ÿ×%ÿ×%$ÿq%&ÿq%6ÿ®%8ÿ…%:ÿ…%Gÿ×%úÿ®%üÿ®%þÿ®%�ÿ…%ÿq% ÿq%_ÿ×%Iÿ×%Kÿ×%Mÿ×%Oÿ×%Qÿ×%Sÿ×%Uÿ×%Wÿ×%Yÿ×%[ÿ×%]ÿ×%_ÿ×%oÿ…%qÿ…%sÿ…%ÿq&�ÿì&� ÿì&ÿì& ÿì'�ÿq'� ÿq'�&ÿ×'�*ÿ×'�- '�2ÿ×'�4ÿ×'�7ÿq'�9ÿ®'�:ÿ®'�<ÿ…'�‰ÿ×'�”ÿ×'�•ÿ×'�–ÿ×'�—ÿ×'�˜ÿ×'�šÿ×'�Ÿÿ…'�Èÿ×'�Êÿ×'�Ìÿ×'�Îÿ×'�Þÿ×'�àÿ×'�âÿ×'�äÿ×'ÿ×'ÿ×'ÿ×'ÿ×'$ÿq'&ÿq'6ÿ®'8ÿ…':ÿ…'Gÿ×'úÿ®'üÿ®'þÿ®'�ÿ…'ÿq' ÿq'_ÿ×'Iÿ×'Kÿ×'Mÿ×'Oÿ×'Qÿ×'Sÿ×'Uÿ×'Wÿ×'Yÿ×'[ÿ×']ÿ×'_ÿ×'oÿ…'qÿ…'sÿ…'ÿq(�ÿì(� ÿì(ÿì( ÿì)�ÿq)� ÿq)�&ÿ×)�*ÿ×)�- )�2ÿ×)�4ÿ×)�7ÿq)�9ÿ®)�:ÿ®)�<ÿ…)�‰ÿ×)�”ÿ×)�•ÿ×)�–ÿ×)�—ÿ×)�˜ÿ×)�šÿ×)�Ÿÿ…)�Èÿ×)�Êÿ×)�Ìÿ×)�Îÿ×)�Þÿ×)�àÿ×)�âÿ×)�äÿ×)ÿ×)ÿ×)ÿ×)ÿ×)$ÿq)&ÿq)6ÿ®)8ÿ…):ÿ…)Gÿ×)úÿ®)üÿ®)þÿ®)�ÿ…)ÿq) ÿq)_ÿ×)Iÿ×)Kÿ×)Mÿ×)Oÿ×)Qÿ×)Sÿ×)Uÿ×)Wÿ×)Yÿ×)[ÿ×)]ÿ×)_ÿ×)oÿ…)qÿ…)sÿ…)ÿq*�ÿì*� ÿì*ÿì* ÿì+�ÿq+� ÿq+�&ÿ×+�*ÿ×+�- +�2ÿ×+�4ÿ×+�7ÿq+�9ÿ®+�:ÿ®+�<ÿ…+�‰ÿ×+�”ÿ×+�•ÿ×+�–ÿ×+�—ÿ×+�˜ÿ×+�šÿ×+�Ÿÿ…+�Èÿ×+�Êÿ×+�Ìÿ×+�Îÿ×+�Þÿ×+�àÿ×+�âÿ×+�äÿ×+ÿ×+ÿ×+ÿ×+ÿ×+$ÿq+&ÿq+6ÿ®+8ÿ…+:ÿ…+Gÿ×+úÿ®+üÿ®+þÿ®+�ÿ…+ÿq+ ÿq+_ÿ×+Iÿ×+Kÿ×+Mÿ×+Oÿ×+Qÿ×+Sÿ×+Uÿ×+Wÿ×+Yÿ×+[ÿ×+]ÿ×+_ÿ×+oÿ…+qÿ…+sÿ…+ÿq,�ÿì,� ÿì,ÿì, ÿì-�ÿq-� ÿq-�&ÿ×-�*ÿ×-�- -�2ÿ×-�4ÿ×-�7ÿq-�9ÿ®-�:ÿ®-�<ÿ…-�‰ÿ×-�”ÿ×-�•ÿ×-�–ÿ×-�—ÿ×-�˜ÿ×-�šÿ×-�Ÿÿ…-�Èÿ×-�Êÿ×-�Ìÿ×-�Îÿ×-�Þÿ×-�àÿ×-�âÿ×-�äÿ×-ÿ×-ÿ×-ÿ×-ÿ×-$ÿq-&ÿq-6ÿ®-8ÿ…-:ÿ…-Gÿ×-úÿ®-üÿ®-þÿ®-�ÿ…-ÿq- ÿq-_ÿ×-Iÿ×-Kÿ×-Mÿ×-Oÿ×-Qÿ×-Sÿ×-Uÿ×-Wÿ×-Yÿ×-[ÿ×-]ÿ×-_ÿ×-oÿ…-qÿ…-sÿ…-ÿq.�ÿì.� ÿì.ÿì. ÿì/�ÿq/� ÿq/�&ÿ×/�*ÿ×/�- /�2ÿ×/�4ÿ×/�7ÿq/�9ÿ®/�:ÿ®/�<ÿ…/�‰ÿ×/�”ÿ×/�•ÿ×/�–ÿ×/�—ÿ×/�˜ÿ×/�šÿ×/�Ÿÿ…/�Èÿ×/�Êÿ×/�Ìÿ×/�Îÿ×/�Þÿ×/�àÿ×/�âÿ×/�äÿ×/ÿ×/ÿ×/ÿ×/ÿ×/$ÿq/&ÿq/6ÿ®/8ÿ…/:ÿ…/Gÿ×/úÿ®/üÿ®/þÿ®/�ÿ…/ÿq/ ÿq/_ÿ×/Iÿ×/Kÿ×/Mÿ×/Oÿ×/Qÿ×/Sÿ×/Uÿ×/Wÿ×/Yÿ×/[ÿ×/]ÿ×/_ÿ×/oÿ…/qÿ…/sÿ…/ÿq0�ÿì0� ÿì0ÿì0 ÿì1�ÿq1� ÿq1�&ÿ×1�*ÿ×1�- 1�2ÿ×1�4ÿ×1�7ÿq1�9ÿ®1�:ÿ®1�<ÿ…1�‰ÿ×1�”ÿ×1�•ÿ×1�–ÿ×1�—ÿ×1�˜ÿ×1�šÿ×1�Ÿÿ…1�Èÿ×1�Êÿ×1�Ìÿ×1�Îÿ×1�Þÿ×1�àÿ×1�âÿ×1�äÿ×1ÿ×1ÿ×1ÿ×1ÿ×1$ÿq1&ÿq16ÿ®18ÿ…1:ÿ…1Gÿ×1úÿ®1üÿ®1þÿ®1�ÿ…1ÿq1 ÿq1_ÿ×1Iÿ×1Kÿ×1Mÿ×1Oÿ×1Qÿ×1Sÿ×1Uÿ×1Wÿ×1Yÿ×1[ÿ×1]ÿ×1_ÿ×1oÿ…1qÿ…1sÿ…1ÿq2�ÿì2� ÿì2ÿì2 ÿì3�ÿq3� ÿq3�&ÿ×3�*ÿ×3�- 3�2ÿ×3�4ÿ×3�7ÿq3�9ÿ®3�:ÿ®3�<ÿ…3�‰ÿ×3�”ÿ×3�•ÿ×3�–ÿ×3�—ÿ×3�˜ÿ×3�šÿ×3�Ÿÿ…3�Èÿ×3�Êÿ×3�Ìÿ×3�Îÿ×3�Þÿ×3�àÿ×3�âÿ×3�äÿ×3ÿ×3ÿ×3ÿ×3ÿ×3$ÿq3&ÿq36ÿ®38ÿ…3:ÿ…3Gÿ×3úÿ®3üÿ®3þÿ®3�ÿ…3ÿq3 ÿq3_ÿ×3Iÿ×3Kÿ×3Mÿ×3Oÿ×3Qÿ×3Sÿ×3Uÿ×3Wÿ×3Yÿ×3[ÿ×3]ÿ×3_ÿ×3oÿ…3qÿ…3sÿ…3ÿq4�ÿì4� ÿì4ÿì4 ÿì5�-�{6�ÿì6� ÿì6�Yÿ×6�Zÿ×6�[ÿ×6�\ÿ×6�]ÿì6�¿ÿ×67ÿ×6<ÿì6>ÿì6@ÿì6ûÿ×6ýÿ×6ÿì6 ÿì6pÿ×7�-�{8�ÿì8� ÿì8�Yÿ×8�Zÿ×8�[ÿ×8�\ÿ×8�]ÿì8�¿ÿ×87ÿ×8<ÿì8>ÿì8@ÿì8ûÿ×8ýÿ×8ÿì8 ÿì8pÿ×9�-�{:�ÿì:� ÿì:�Yÿ×:�Zÿ×:�[ÿ×:�\ÿ×:�]ÿì:�¿ÿ×:7ÿ×:<ÿì:>ÿì:@ÿì:ûÿ×:ýÿ×:ÿì: ÿì:pÿ×;�-�{<�ÿì<� ÿì<�Yÿ×<�Zÿ×<�[ÿ×<�\ÿ×<�]ÿì<�¿ÿ×<7ÿ×<<ÿì<>ÿì<@ÿì<ûÿ×<ýÿ×<ÿì< ÿì<pÿ×=�-�{>�ÿì>� ÿì>�Yÿ×>�Zÿ×>�[ÿ×>�\ÿ×>�]ÿì>�¿ÿ×>7ÿ×><ÿì>>ÿì>@ÿì>ûÿ×>ýÿ×>ÿì> ÿì>pÿ×?�-�{@�ÿì@� ÿì@�Yÿ×@�Zÿ×@�[ÿ×@�\ÿ×@�]ÿì@�¿ÿ×@7ÿ×@<ÿì@>ÿì@@ÿì@ûÿ×@ýÿ×@ÿì@ ÿì@pÿ×A�-�{B�ÿìB� ÿìB�Yÿ×B�Zÿ×B�[ÿ×B�\ÿ×B�]ÿìB�¿ÿ×B7ÿ×B<ÿìB>ÿìB@ÿìBûÿ×Býÿ×BÿìB ÿìBpÿ×C�-�{D�ÿìD� ÿìD�Yÿ×D�Zÿ×D�[ÿ×D�\ÿ×D�]ÿìD�¿ÿ×D7ÿ×D<ÿìD>ÿìD@ÿìDûÿ×Dýÿ×DÿìD ÿìDpÿ×I�ÿ®I�ÿ®I�$ÿ×I�7ÿÃI�9ÿìI�:ÿìI�;ÿ×I�<ÿìI�=ÿìI�‚ÿ×I�ƒÿ×I�„ÿ×I�…ÿ×I�†ÿ×I�‡ÿ×I�ŸÿìI�Âÿ×I�Äÿ×I�Æÿ×I$ÿÃI&ÿÃI6ÿìI8ÿìI:ÿìI;ÿìI=ÿìI?ÿìICÿ×I ÿìIúÿìIüÿìIþÿìI�ÿìIÿ®I ÿ®IXÿ×Iÿ×Iÿ×I!ÿ×I#ÿ×I%ÿ×I'ÿ×I)ÿ×I+ÿ×I-ÿ×I/ÿ×I1ÿ×I3ÿ×IoÿìIqÿìIsÿìIÿÃJ�ÿìJ� ÿìJ�Yÿ×J�Zÿ×J�[ÿ×J�\ÿ×J�]ÿìJ�¿ÿ×J7ÿ×J<ÿìJ>ÿìJ@ÿìJûÿ×Jýÿ×JÿìJ ÿìJpÿ×K�ÿ®K�ÿ®K�$ÿ×K�7ÿÃK�9ÿìK�:ÿìK�;ÿ×K�<ÿìK�=ÿìK�‚ÿ×K�ƒÿ×K�„ÿ×K�…ÿ×K�†ÿ×K�‡ÿ×K�ŸÿìK�Âÿ×K�Äÿ×K�Æÿ×K$ÿÃK&ÿÃK6ÿìK8ÿìK:ÿìK;ÿìK=ÿìK?ÿìKCÿ×K ÿìKúÿìKüÿìKþÿìK�ÿìKÿ®K ÿ®KXÿ×Kÿ×Kÿ×K!ÿ×K#ÿ×K%ÿ×K'ÿ×K)ÿ×K+ÿ×K-ÿ×K/ÿ×K1ÿ×K3ÿ×KoÿìKqÿìKsÿìKÿÃL�ÿìL� ÿìL�Yÿ×L�Zÿ×L�[ÿ×L�\ÿ×L�]ÿìL�¿ÿ×L7ÿ×L<ÿìL>ÿìL@ÿìLûÿ×Lýÿ×LÿìL ÿìLpÿ×M�ÿ®M�ÿ®M�$ÿ×M�7ÿÃM�9ÿìM�:ÿìM�;ÿ×M�<ÿìM�=ÿìM�‚ÿ×M�ƒÿ×M�„ÿ×M�…ÿ×M�†ÿ×M�‡ÿ×M�ŸÿìM�Âÿ×M�Äÿ×M�Æÿ×M$ÿÃM&ÿÃM6ÿìM8ÿìM:ÿìM;ÿìM=ÿìM?ÿìMCÿ×M ÿìMúÿìMüÿìMþÿìM�ÿìMÿ®M ÿ®MXÿ×Mÿ×Mÿ×M!ÿ×M#ÿ×M%ÿ×M'ÿ×M)ÿ×M+ÿ×M-ÿ×M/ÿ×M1ÿ×M3ÿ×MoÿìMqÿìMsÿìMÿÃO�ÿ®O�ÿ®O�$ÿ×O�7ÿÃO�9ÿìO�:ÿìO�;ÿ×O�<ÿìO�=ÿìO�‚ÿ×O�ƒÿ×O�„ÿ×O�…ÿ×O�†ÿ×O�‡ÿ×O�ŸÿìO�Âÿ×O�Äÿ×O�Æÿ×O$ÿÃO&ÿÃO6ÿìO8ÿìO:ÿìO;ÿìO=ÿìO?ÿìOCÿ×O ÿìOúÿìOüÿìOþÿìO�ÿìOÿ®O ÿ®OXÿ×Oÿ×Oÿ×O!ÿ×O#ÿ×O%ÿ×O'ÿ×O)ÿ×O+ÿ×O-ÿ×O/ÿ×O1ÿ×O3ÿ×OoÿìOqÿìOsÿìOÿÃQ�ÿ®Q�ÿ®Q�$ÿ×Q�7ÿÃQ�9ÿìQ�:ÿìQ�;ÿ×Q�<ÿìQ�=ÿìQ�‚ÿ×Q�ƒÿ×Q�„ÿ×Q�…ÿ×Q�†ÿ×Q�‡ÿ×Q�ŸÿìQ�Âÿ×Q�Äÿ×Q�Æÿ×Q$ÿÃQ&ÿÃQ6ÿìQ8ÿìQ:ÿìQ;ÿìQ=ÿìQ?ÿìQCÿ×Q ÿìQúÿìQüÿìQþÿìQ�ÿìQÿ®Q ÿ®QXÿ×Qÿ×Qÿ×Q!ÿ×Q#ÿ×Q%ÿ×Q'ÿ×Q)ÿ×Q+ÿ×Q-ÿ×Q/ÿ×Q1ÿ×Q3ÿ×QoÿìQqÿìQsÿìQÿÃS�ÿ®S�ÿ®S�$ÿ×S�7ÿÃS�9ÿìS�:ÿìS�;ÿ×S�<ÿìS�=ÿìS�‚ÿ×S�ƒÿ×S�„ÿ×S�…ÿ×S�†ÿ×S�‡ÿ×S�ŸÿìS�Âÿ×S�Äÿ×S�Æÿ×S$ÿÃS&ÿÃS6ÿìS8ÿìS:ÿìS;ÿìS=ÿìS?ÿìSCÿ×S ÿìSúÿìSüÿìSþÿìS�ÿìSÿ®S ÿ®SXÿ×Sÿ×Sÿ×S!ÿ×S#ÿ×S%ÿ×S'ÿ×S)ÿ×S+ÿ×S-ÿ×S/ÿ×S1ÿ×S3ÿ×SoÿìSqÿìSsÿìSÿÃU�ÿ®U�ÿ®U�$ÿ×U�7ÿÃU�9ÿìU�:ÿìU�;ÿ×U�<ÿìU�=ÿìU�‚ÿ×U�ƒÿ×U�„ÿ×U�…ÿ×U�†ÿ×U�‡ÿ×U�ŸÿìU�Âÿ×U�Äÿ×U�Æÿ×U$ÿÃU&ÿÃU6ÿìU8ÿìU:ÿìU;ÿìU=ÿìU?ÿìUCÿ×U ÿìUúÿìUüÿìUþÿìU�ÿìUÿ®U ÿ®UXÿ×Uÿ×Uÿ×U!ÿ×U#ÿ×U%ÿ×U'ÿ×U)ÿ×U+ÿ×U-ÿ×U/ÿ×U1ÿ×U3ÿ×UoÿìUqÿìUsÿìUÿÃX�I�RX�W�RX�Y�fX�Z�fX�[�fX�\�fX�¿�fX%�RX'�RX7�fXû�fXý�fX4�RX5�RX]�RX^�RXp�fX�RX�RZ�I�RZ�W�RZ�Y�fZ�Z�fZ�[�fZ�\�fZ�¿�fZ%�RZ'�RZ7�fZû�fZý�fZ4�RZ5�RZ]�RZ^�RZp�fZ�RZ�R\�I�R\�W�R\�Y�f\�Z�f\�[�f\�\�f\�¿�f\%�R\'�R\7�f\û�f\ý�f\4�R\5�R\]�R\^�R\p�f\�R\�R^�I�R^�W�R^�Y�f^�Z�f^�[�f^�\�f^�¿�f^%�R^'�R^7�f^û�f^ý�f^4�R^5�R^]�R^^�R^p�f^�R^�R`�I�R`�W�R`�Y�f`�Z�f`�[�f`�\�f`�¿�f`%�R`'�R`7�f`û�f`ý�f`4�R`5�R`]�R`^�R`p�f`�R`�Ra�ÿ×a�ÿ×a�$ÿìa�‚ÿìa�ƒÿìa�„ÿìa�…ÿìa�†ÿìa�‡ÿìa�Âÿìa�Äÿìa�ÆÿìaCÿìaÿ×a ÿ×aXÿìaÿìaÿìa!ÿìa#ÿìa%ÿìa'ÿìa)ÿìa+ÿìa-ÿìa/ÿìa1ÿìa3ÿìf�I�ff�W�ff�Y�ff�Z�ff�[�ff�\�ff�¿�ff%�ff'�ff7�ffû�ffý�ff4�ff5�ff]�ff^�ffp�ff�ff�fh�I�fh�W�fh�Y�fh�Z�fh�[�fh�\�fh�¿�fh%�fh'�fh7�fhû�fhý�fh4�fh5�fh]�fh^�fhp�fh�fh�fj�I�fj�W�fj�Y�fj�Z�fj�[�fj�\�fj�¿�fj%�fj'�fj7�fjû�fjý�fj4�fj5�fj]�fj^�fjp�fj�fj�fl�I�fl�W�fl�Y�fl�Z�fl�[�fl�\�fl�¿�fl%�fl'�fl7�flû�flý�fl4�fl5�fl]�fl^�flp�fl�fl�fn�I�fn�W�fn�Y�fn�Z�fn�[�fn�\�fn�¿�fn%�fn'�fn7�fnû�fný�fn4�fn5�fn]�fn^�fnp�fn�fn�fo�ÿ…o�ÿ…o�"�)o�$ÿ…o�&ÿ×o�*ÿ×o�2ÿ×o�4ÿ×o�Dÿšo�Fÿšo�Gÿšo�Hÿšo�Jÿ×o�PÿÃo�QÿÃo�Rÿšo�SÿÃo�Tÿšo�UÿÃo�Vÿ®o�XÿÃo�]ÿ×o�‚ÿ…o�ƒÿ…o�„ÿ…o�…ÿ…o�†ÿ…o�‡ÿ…o�‰ÿ×o�”ÿ×o�•ÿ×o�–ÿ×o�—ÿ×o�˜ÿ×o�šÿ×o�¢ÿšo�£ÿšo�¤ÿšo�¥ÿšo�¦ÿšo�§ÿšo�¨ÿšo�©ÿšo�ªÿšo�«ÿšo�¬ÿšo�­ÿšo�´ÿšo�µÿšo�¶ÿšo�·ÿšo�¸ÿšo�ºÿšo�»ÿÃo�¼ÿÃo�½ÿÃo�¾ÿÃo�Âÿ…o�Ãÿšo�Äÿ…o�Åÿšo�Æÿ…o�Çÿšo�Èÿ×o�Éÿšo�Êÿ×o�Ëÿšo�Ìÿ×o�Íÿšo�Îÿ×o�Ïÿšo�Ñÿšo�Óÿšo�Õÿšo�×ÿšo�Ùÿšo�Ûÿšo�Ýÿšo�Þÿ×o�ßÿ×o�àÿ×o�áÿ×o�âÿ×o�ãÿ×o�äÿ×o�åÿ×o�úÿÃoÿÃoÿÃo ÿÃoÿ×oÿšoÿ×oÿšoÿ×oÿšoÿ×oÿšoÿÃoÿÃoÿ®o!ÿ®o+ÿÃo-ÿÃo/ÿÃo1ÿÃo3ÿÃo5ÿÃo<ÿ×o>ÿ×o@ÿ×oCÿ…oDÿšoFÿšoGÿ×oHÿšoJÿ®oÿ…o ÿ…oWÿÃoXÿ…oYÿšo_ÿ×o`ÿšobÿÃoÿ…oÿšoÿ…o ÿšo!ÿ…o"ÿšo#ÿ…o%ÿ…o&ÿšo'ÿ…o(ÿšo)ÿ…o*ÿšo+ÿ…o,ÿšo-ÿ…o.ÿšo/ÿ…o0ÿšo1ÿ…o2ÿšo3ÿ…o4ÿšo6ÿšo8ÿšo:ÿšo<ÿšo@ÿšoBÿšoDÿšoIÿ×oJÿšoKÿ×oLÿšoMÿ×oNÿšoOÿ×oQÿ×oRÿšoSÿ×oTÿšoUÿ×oVÿšoWÿ×oXÿšoYÿ×oZÿšo[ÿ×o\ÿšo]ÿ×o^ÿšo_ÿ×o`ÿšobÿÃodÿÃofÿÃohÿÃojÿÃolÿÃonÿÃp��Rp� �Rp�ÿ®p�ÿ®p�"�)p�Rpÿ®p �Rp ÿ®q�ÿ…q�ÿ…q�"�)q�$ÿ…q�&ÿ×q�*ÿ×q�2ÿ×q�4ÿ×q�Dÿšq�Fÿšq�Gÿšq�Hÿšq�Jÿ×q�PÿÃq�QÿÃq�Rÿšq�SÿÃq�Tÿšq�UÿÃq�Vÿ®q�XÿÃq�]ÿ×q�‚ÿ…q�ƒÿ…q�„ÿ…q�…ÿ…q�†ÿ…q�‡ÿ…q�‰ÿ×q�”ÿ×q�•ÿ×q�–ÿ×q�—ÿ×q�˜ÿ×q�šÿ×q�¢ÿšq�£ÿšq�¤ÿšq�¥ÿšq�¦ÿšq�§ÿšq�¨ÿšq�©ÿšq�ªÿšq�«ÿšq�¬ÿšq�­ÿšq�´ÿšq�µÿšq�¶ÿšq�·ÿšq�¸ÿšq�ºÿšq�»ÿÃq�¼ÿÃq�½ÿÃq�¾ÿÃq�Âÿ…q�Ãÿšq�Äÿ…q�Åÿšq�Æÿ…q�Çÿšq�Èÿ×q�Éÿšq�Êÿ×q�Ëÿšq�Ìÿ×q�Íÿšq�Îÿ×q�Ïÿšq�Ñÿšq�Óÿšq�Õÿšq�×ÿšq�Ùÿšq�Ûÿšq�Ýÿšq�Þÿ×q�ßÿ×q�àÿ×q�áÿ×q�âÿ×q�ãÿ×q�äÿ×q�åÿ×q�úÿÃqÿÃqÿÃq ÿÃqÿ×qÿšqÿ×qÿšqÿ×qÿšqÿ×qÿšqÿÃqÿÃqÿ®q!ÿ®q+ÿÃq-ÿÃq/ÿÃq1ÿÃq3ÿÃq5ÿÃq<ÿ×q>ÿ×q@ÿ×qCÿ…qDÿšqFÿšqGÿ×qHÿšqJÿ®qÿ…q ÿ…qWÿÃqXÿ…qYÿšq_ÿ×q`ÿšqbÿÃqÿ…qÿšqÿ…q ÿšq!ÿ…q"ÿšq#ÿ…q%ÿ…q&ÿšq'ÿ…q(ÿšq)ÿ…q*ÿšq+ÿ…q,ÿšq-ÿ…q.ÿšq/ÿ…q0ÿšq1ÿ…q2ÿšq3ÿ…q4ÿšq6ÿšq8ÿšq:ÿšq<ÿšq@ÿšqBÿšqDÿšqIÿ×qJÿšqKÿ×qLÿšqMÿ×qNÿšqOÿ×qQÿ×qRÿšqSÿ×qTÿšqUÿ×qVÿšqWÿ×qXÿšqYÿ×qZÿšq[ÿ×q\ÿšq]ÿ×q^ÿšq_ÿ×q`ÿšqbÿÃqdÿÃqfÿÃqhÿÃqjÿÃqlÿÃqnÿÃr��Rr� �Rr�ÿ®r�ÿ®r�"�)r�Rrÿ®r �Rr ÿ®s�ÿ…s�ÿ…s�"�)s�$ÿ…s�&ÿ×s�*ÿ×s�2ÿ×s�4ÿ×s�Dÿšs�Fÿšs�Gÿšs�Hÿšs�Jÿ×s�PÿÃs�QÿÃs�Rÿšs�SÿÃs�Tÿšs�UÿÃs�Vÿ®s�XÿÃs�]ÿ×s�‚ÿ…s�ƒÿ…s�„ÿ…s�…ÿ…s�†ÿ…s�‡ÿ…s�‰ÿ×s�”ÿ×s�•ÿ×s�–ÿ×s�—ÿ×s�˜ÿ×s�šÿ×s�¢ÿšs�£ÿšs�¤ÿšs�¥ÿšs�¦ÿšs�§ÿšs�¨ÿšs�©ÿšs�ªÿšs�«ÿšs�¬ÿšs�­ÿšs�´ÿšs�µÿšs�¶ÿšs�·ÿšs�¸ÿšs�ºÿšs�»ÿÃs�¼ÿÃs�½ÿÃs�¾ÿÃs�Âÿ…s�Ãÿšs�Äÿ…s�Åÿšs�Æÿ…s�Çÿšs�Èÿ×s�Éÿšs�Êÿ×s�Ëÿšs�Ìÿ×s�Íÿšs�Îÿ×s�Ïÿšs�Ñÿšs�Óÿšs�Õÿšs�×ÿšs�Ùÿšs�Ûÿšs�Ýÿšs�Þÿ×s�ßÿ×s�àÿ×s�áÿ×s�âÿ×s�ãÿ×s�äÿ×s�åÿ×s�úÿÃsÿÃsÿÃs ÿÃsÿ×sÿšsÿ×sÿšsÿ×sÿšsÿ×sÿšsÿÃsÿÃsÿ®s!ÿ®s+ÿÃs-ÿÃs/ÿÃs1ÿÃs3ÿÃs5ÿÃs<ÿ×s>ÿ×s@ÿ×sCÿ…sDÿšsFÿšsGÿ×sHÿšsJÿ®sÿ…s ÿ…sWÿÃsXÿ…sYÿšs_ÿ×s`ÿšsbÿÃsÿ…sÿšsÿ…s ÿšs!ÿ…s"ÿšs#ÿ…s%ÿ…s&ÿšs'ÿ…s(ÿšs)ÿ…s*ÿšs+ÿ…s,ÿšs-ÿ…s.ÿšs/ÿ…s0ÿšs1ÿ…s2ÿšs3ÿ…s4ÿšs6ÿšs8ÿšs:ÿšs<ÿšs@ÿšsBÿšsDÿšsIÿ×sJÿšsKÿ×sLÿšsMÿ×sNÿšsOÿ×sQÿ×sRÿšsSÿ×sTÿšsUÿ×sVÿšsWÿ×sXÿšsYÿ×sZÿšs[ÿ×s\ÿšs]ÿ×s^ÿšs_ÿ×s`ÿšsbÿÃsdÿÃsfÿÃshÿÃsjÿÃslÿÃsnÿÃt��Rt� �Rt�ÿ®t�ÿ®t�"�)t�Rtÿ®t �Rt ÿ®��{� �{�{ �{�ÿ…�ÿ®�ÿ…�"�)�$ÿq�&ÿ×�*ÿ×�2ÿ×�4ÿ×�7�)�Dÿ\�Fÿq�Gÿq�Hÿq�Jÿq�Pÿš�Qÿš�Rÿq�Sÿš�Tÿq�Uÿš�Vÿ…�Xÿš�Yÿ×�Zÿ×�[ÿ×�\ÿ×�]ÿ®�‚ÿq�ƒÿq�„ÿq�…ÿq�†ÿq�‡ÿq�‰ÿ×�”ÿ×�•ÿ×�–ÿ×�—ÿ×�˜ÿ×�šÿ×�¢ÿq�£ÿ\�¤ÿ\�¥ÿ\�¦ÿ\�§ÿ\�¨ÿ\�©ÿq�ªÿq�«ÿq�¬ÿq�­ÿq�´ÿq�µÿq�¶ÿq�·ÿq�¸ÿq�ºÿq�»ÿš�¼ÿš�½ÿš�¾ÿš�¿ÿ×�Âÿq�Ãÿ\�Äÿq�Åÿ\�Æÿq�Çÿ\�Èÿ×�Éÿq�Êÿ×�Ëÿq�Ìÿ×�Íÿq�Îÿ×�Ïÿq�Ñÿq�Óÿq�Õÿq�×ÿq�Ùÿq�Ûÿq�Ýÿq�Þÿ×�ßÿq�àÿ×�áÿq�âÿ×�ãÿq�äÿ×�åÿq�úÿšÿšÿš ÿšÿ×ÿqÿ×ÿqÿ×ÿqÿ×ÿqÿšÿšÿ…!ÿ…$�)&�)+ÿš-ÿš/ÿš1ÿš3ÿš5ÿš7ÿ×<ÿ®>ÿ®@ÿ®CÿqDÿ\Fÿ\Gÿ×HÿqJÿ…ûÿ×ýÿ×ÿ®ÿ®ÿ®ÿ… ÿ…WÿšXÿqYÿ\_ÿ×`ÿqbÿšÿqÿ\ÿq ÿ\!ÿq"ÿ\#ÿq%ÿq&ÿ\'ÿq(ÿ\)ÿq*ÿ\+ÿq,ÿ\-ÿq.ÿ\/ÿq0ÿ\1ÿq2ÿ\3ÿq4ÿ\6ÿq8ÿq:ÿq<ÿq@ÿqBÿqDÿqIÿ×JÿqKÿ×LÿqMÿ×NÿqOÿ×Qÿ×RÿqSÿ×TÿqUÿ×VÿqWÿ×XÿqYÿ×Zÿq[ÿ×\ÿq]ÿ×^ÿq_ÿ×`ÿqbÿšdÿšfÿšhÿšjÿšlÿšnÿšpÿ×�)��)� �)�) �)�����>��������9��������� �9��������B�������#�F��������Q������� �i������� �u�������R�‚��������Ô������ ��è������ �.������ �.2�������*`�� ���rŠ�� ��ü�� ���� ��F�� ��,�� ��\�� ��t�� ��¤Ž�� ��(2�� � �8Z�� � �\’�� � �\î�� ��TJDigitized data copyright © 2010-2011, Google Corporation.Open SansBoldAscender - Open Sans Bold Build 100Version 1.10OpenSans-BoldOpen Sans is a trademark of Google and may be registered in certain jurisdictions.Ascender Corporationhttp://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0�D�i�g�i�t�i�z�e�d� �d�a�t�a� �c�o�p�y�r�i�g�h�t� �©� �2�0�1�0�-�2�0�1�1�,� �G�o�o�g�l�e� �C�o�r�p�o�r�a�t�i�o�n�.�O�p�e�n� �S�a�n�s�B�o�l�d�A�s�c�e�n�d�e�r� �-� �O�p�e�n� �S�a�n�s� �B�o�l�d� �B�u�i�l�d� �1�0�0�V�e�r�s�i�o�n� �1�.�1�0�O�p�e�n�S�a�n�s�-�B�o�l�d�O�p�e�n� �S�a�n�s� �i�s� �a� �t�r�a�d�e�m�a�r�k� �o�f� �G�o�o�g�l�e� �a�n�d� �m�a�y� �b�e� �r�e�g�i�s�t�e�r�e�d� �i�n� �c�e�r�t�a�i�n� �j�u�r�i�s�d�i�c�t�i�o�n�s�.�A�s�c�e�n�d�e�r� �C�o�r�p�o�r�a�t�i�o�n�h�t�t�p�:�/�/�w�w�w�.�a�s�c�e�n�d�e�r�c�o�r�p�.�c�o�m�/�h�t�t�p�:�/�/�w�w�w�.�a�s�c�e�n�d�e�r�c�o�r�p�.�c�o�m�/�t�y�p�e�d�e�s�i�g�n�e�r�s�.�h�t�m�l�L�i�c�e�n�s�e�d� �u�n�d�e�r� �t�h�e� �A�p�a�c�h�e� �L�i�c�e�n�s�e�,� �V�e�r�s�i�o�n� �2�.�0�h�t�t�p�:�/�/�w�w�w�.�a�p�a�c�h�e�.�o�r�g�/�l�i�c�e�n�s�e�s�/�L�I�C�E�N�S�E�-�2�.�0�������ÿf�f��������������������ª      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ�      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«.notdefnullnonmarkingreturnspaceexclamquotedbl numbersigndollarpercent ampersand quotesingle parenleft parenrightasteriskpluscommahyphenperiodslashzeroonetwothreefourfivesixseveneightninecolon semicolonlessequalgreaterquestionatABCDEFGHI.altJKLMNOPQRSTUVWXYZ bracketleft backslash bracketright asciicircum underscoregraveabcdefghijklmnopqrstuvwxyz braceleftbar braceright asciitildenonbreakingspace exclamdowncentsterlingcurrencyyen brokenbarsectiondieresis copyright ordfeminine guillemotleft logicalnotuni00AD registered overscoredegree plusminus twosuperior threesuperioracutemu paragraphperiodcenteredcedilla onesuperior ordmasculineguillemotright onequarteronehalf threequarters questiondownAgraveAacute AcircumflexAtilde AdieresisAringAECcedillaEgraveEacute Ecircumflex Edieresis Igrave.alt Iacute.altIcircumflex.alt Idieresis.altEthNtildeOgraveOacute OcircumflexOtilde OdieresismultiplyOslashUgraveUacute Ucircumflex UdieresisYacuteThorn germandblsagraveaacute acircumflexatilde adieresisaringaeccedillaegraveeacute ecircumflex edieresisigraveiacute icircumflex idieresisethntildeograveoacute ocircumflexotilde odieresisdivideoslashugraveuacute ucircumflex udieresisyacutethorn ydieresisAmacronamacronAbreveabreveAogonekaogonekCacutecacute Ccircumflex ccircumflexCdotcdotCcaronccaronDcarondcaronDcroatdcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGbrevegbreveGdotgdot Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbar Itilde.altitilde Imacron.altimacron Ibreve.altibreve Iogonek.altiogonekIdotaccent.altdotlessiIJ.altij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotLslashlslashNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautOEoeRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflexScedillascedillaScaronscaron Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflex YdieresisZacutezacute Zdotaccent zdotaccentZcaronzcaronlongsflorin Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute Scommaaccent scommaaccent circumflexcaronmacronbreve dotaccentringogonektilde hungarumlauttonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos.alt Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIota.altKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9Iotadieresis.altUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronpirhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055.alt afii10056.alt afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygraveendashemdash afii00208 underscoredbl quoteleft quoterightquotesinglbase quotereversed quotedblleft quotedblright quotedblbasedagger daggerdblbulletellipsis perthousandminutesecond guilsinglleftguilsinglright exclamdblfraction nsuperiorfranc afii08941pesetaEuro afii61248 afii61289 afii61352 trademarkOmega estimated oneeighth threeeighths fiveeighths seveneighths partialdiffDeltaproduct summationminusradicalinfinityintegral approxequalnotequal lessequal greaterequallozengeuniFB01uniFB02 cyrillicbrevedotlessjcaroncommaaccent commaaccentcommaaccentrotate zerosuperior foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperioruni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni1F4Duni02F3 dasiaoxiauniFB03uniFB04OhornohornUhornuhornuni0300uni0301uni0303hookdotbelowuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BF uni04C0.altuni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CE uni04CF.altuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7 uni1EC8.altuni1EC9 uni1ECA.altuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABuni030Fcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCcyrillicbighookLCone.pnumzero.osone.ostwo.osthree.osfour.osfive.ossix.osseven.oseight.osnine.osffuni2120Tcedillatcedillag.altgcircumflex.alt gbreve.altgdot.altgcommaaccent.altIIgraveIacute Icircumflex IdieresisItildeImacronIbreveIogonek IdotaccentIJ IotatonosIota Iotadieresis afii10055 afii10056uni04C0uni04CFuni1EC8uni1ECA����� ÿÿ����� ���������©������������� �4�6�latn���MOL �ROM ���ÿÿ����ÿÿ����ÿÿ���������� �nä�latn���MOL �(ROM �B��ÿÿ� ��� ���������ÿÿ� ��� � ��������ÿÿ� ��� � �������liga�°liga�¶liga�¼lnum�Âlnum�Èlnum�Îlocl�Ôlocl�Úonum�àonum�èonum�ðpnum�øpnum�þpnumsalt saltsaltss01"ss01*ss012ss02:ss02@ss02Fss03Lss03Rss03Xtnum^tnumftnumn���� ���� ���� ������������������������������������������������������������������������������������������������������������������������������ ���&�.�6�>�F�N�V�^�����P�����z�����ª�����Æ�����î�����ô������������2����H���‘’“”•���J�J���ß�ß��á�á��ã�ã��å�å���.��,�Ž���‘�ê�ì�î�ð�ò�ôZgw¡¢ÉØEG��–©����� ƒ„…†‡ˆ‰Š‹Œ��������� ƒ…†‡ˆ‰Š‹Œ„���������‚‚� ���‚������ ������������ƒŒ��������‚��� �‚����������ƒŒ����� !��$%��IJ���6���� ���"�(^��I�O]��I�L��I5��O4��L���I������������^���������V0‚R *†H†÷  ‚C0‚?1 0 +�0a +‚7 S0Q0, +‚7¢€�<�<�<�O�b�s�o�l�e�t�e�>�>�>0!0 +�iV“žÅ˱ÅHÜê,ñ[¦ð¯  ‚]0‚z0‚b 8%×úøa¯žôç&µÖZÕ0  *†H†÷ �0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0 070615000000Z 120614235959Z0\1 0 UUS10U VeriSign, Inc.1402U+VeriSign Time Stamping Services Signer - G20Ÿ0  *†H†÷ ��0‰�ĵòR¼ˆ†`)J[/K‘k‡‘ó5TX5êÑ6^bMRQ4qÂ{f‰ÈÝ*Äj ö7Ù˜t‘ö’®°µv–ñ©JcEG.k ’NK+ŒîXJ‹Ôä,ø‚ªXÙÍBó-ÀuÞ«ÇŽšlL•ÞÛïgárÂIž`<áâ¾£cxi{­-�£Ä0Á04+(0&0$+0†http://ocsp.verisign.com0 Uÿ0�03U,0*0( & $†"http://crl.verisign.com/tss-ca.crl0U%ÿ 0 +0UÿÀ0U0¤010 UTSA1-20  *†H†÷ �‚�PÅKÈ$€ßä $ÂÞ±¡¡¦‚- ƒ7 ‚,°ZaµØþˆÛñ‘‘³V@¦ë’¾89°u6t:˜Oä7º™‰Ê•B°¹Ç WàúÕdB5NÑ3¢ÈMª'Çòá†L8MƒxÆüSàëà‡Ý¤–ž^ ˜â¥¾¿‚…Ã`áß­(ØÇ¥KdÚÇ[½¬9Õ8"¡3‹/Ššë¼!?DA µe$¼HÓD€ë¡ÏÉ´ÏTÇ£€\ùy>]r}ˆž,C¢ÊSÎ}=ö*:¸O”�¥m ƒ]ù^Sô³WpÃûõ­• ÞÄ€`É+n†ñëôx'ÑÅî4[^¹I2ò30‚Ä0‚- G¿•ßRFC÷ÛmH 1¤0  *†H†÷ �0‹1 0 UZA10U Western Cape10U Durbanville10 U Thawte10U Thawte Certification10UThawte Timestamping CA0 031204000000Z 131203235959Z0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0‚"0  *†H†÷ �‚�0‚ ‚�©Ê²¤ÌÍ ¯ }‰¬‡uð´NñßÁ¿ga½£dÚ»ùÊ3«„0‰X~ŒÛkÝ6ž¿Ñìxòw¦~o<¿“¯ ºhôl”ʽR-«H=õ¶Õ]_Ÿú/k¤÷£š¦ÈáLRã`ì@~¹ Þ?Ǵ߇½_zj1.™¨G Î1s W-Íx43•™¹Þh/ªæãŠŒ*Ë!‡f½ƒXWou¿<ª&‡]Ê<Ÿ„êTÁ nÄþÅJݹ—"|Û>'ÑxìŸ1Éñæ"ÛijGCš_ ä^õî|ñ}«bõM ÞÐ"V¨•Í®ˆv®îº óäMÙ ûh ®;³‡Á»�£Û0Ø04+(0&0$+0†http://ocsp.verisign.com0Uÿ0ÿ�0AU:0806 4 2†0http://crl.verisign.com/ThawteTimestampingCA.crl0U% 0 +0Uÿ0$U0¤010U TSA2048-1-530  *†H†÷ ��JkùêXÂD1‰y™+–¿‚¬ÖLͰŠXnß)£^ÈÊ“çR ïG'/�8°äÉ“NšÔ"b÷?7!Op1€ñ‹8‡³èè—�þÏU–N$Ò©'Nz®·aAó*ÎçÉÙ^Ý»+…>µµÙáWÿ¾´Å~õÏ žð—þ+Ó;R8'÷?J0‚ü0‚e eR&á².áY)…¬"ç\0  *†H†÷ �0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority0 090521000000Z 190520235959Z0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0‚"0  *†H†÷ �‚�0‚ ‚�¾g´`ªIoV|fÉ^† Õñ¬§qƒŽ‹‰øˆ‰º-„!•äÑœPLûÒ"½Úò²5;à ûü.Z¿‰|=;%öóX{œôµÆ ¸€Î¾'tag'MjåìaXy£à'°áM4+G D¹Þf$fŠÍOºÅ8ÈTáröfuj¹IhÏ8y ª0¨Û,`Hž×ª©ƒ×8‘09–:|@T¶­à/ƒÜ¨R>³×+ý!¶§\£ ©¦P4.M§ÎÉ^%ÔŒ¼ón|)¼]ü1‡ZÕŒ…gXˆ ¿5ðê+£!çöƒå¨í`x^{`ƒýW ]A cT`ÖC!ï�£‚Û0‚×0Uÿ0ÿ�0pU i0g0e `†H†øE0V0(+https://www.verisign.com/cps0*+0https://www.verisign.com/rpa0Uÿ0m+ a0_¡] [0Y0W0U image/gif0!00+åÓ†¬ŽkÃÏ€jÔH,{.0%#http://logo.verisign.com/vslogo.gif0U%0++04+(0&0$+0†http://ocsp.verisign.com01U*0(0& $ "† http://crl.verisign.com/pca3.crl0)U"0 ¤010UClass3CA2048-1-550U—Ðk¨&pÈ¡?”-Ä5›¤¡ò0  *†H†÷ ��‹ÀÝ”ØA¢ai°¨xÇ0Æ<~B÷$¶äƒsœ¡âú/ëÀÊDçràP¶U ƒn–’äšQj´71Ü¥-ëŒ�ÇOçM2º…øN¾úgUeðj¾zÊd8xEv1ó†z`³]ö‹fv‚Yáƒå½I¥8VåÞAwX0‚0‚û fãðgyÊmPSoˆƒ0  *†H†÷ �0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CA0 100729000000Z 120808235959Z0Ð1 0 UUS10U Massachusetts10 UWoburn10U Monotype Imaging Inc.1>0<U 5Digital ID Class 3 - Microsoft Software Validation v210U Type Operations10UMonotype Imaging Inc.0Ÿ0  *†H†÷ ��0‰�”D •i|U ÐÛ25ŠL3«^ ¡L×*‡8ט¥@ðI "SOÂC¦Ê‹©VïnH¨9c;$¹˜ÏÊ5}rãGWýyËŠJç@p-5c®€ÏįØû÷Éü‰Ø×¤ Û ò¢ò{ïÍuÁ÷ePd"½}¼­¸KÌXEMÑYLM�£‚ƒ0‚0 U0�0Uÿ€0DU=0;09 7 5†3http://csc3-2009-2-crl.verisign.com/CSC3-2009-2.crl0DU =0;09 `†H†øE0*0(+https://www.verisign.com/rpa0U% 0 +0u+i0g0$+0†http://ocsp.verisign.com0?+0†3http://csc3-2009-2-aia.verisign.com/CSC3-2009-2.cer0U#0€—Ðk¨&pÈ¡?”-Ä5›¤¡ò0 `†H†øB0 +‚70�ÿ0  *†H†÷ �‚�Næ"‡ßgAâÒî~ΙÖc½ðµ“åjrbáõÒ<8î¨=_ºG‚_[KIô ú“ ÐVD¢ˆóû®÷ 5Þ< ¬D”`E*›þ›oL;±4gp†ÿZ9\Zãl‚«5|eKý˜mµ”Iœˆp¾=±b•´Û´ÔÚèA~þ}¹¤’ënò"ŠÆw6MŠZ S1Ó+(¯RázkµwD½ ­ô]%,ãÍŠ0>KœyʦN® ÂÌ$ Á”‚öñº¶›šØ\<ñê'M<‰o3ŠÓ†ÞéX3u=ë“iâDoN�lÏÕ…ÚV¦š¦?ËL!hò`ºáè]9!2í1‚g0‚c0Ë0¶1 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)09100.U'VeriSign Class 3 Code Signing 2009-2 CAfãðgyÊmPSoˆƒ0 +� p0 +‚7 10�0 *†H†÷  1  +‚70 +‚7 10  +‚70# *†H†÷  1ù79±:Sz3ÇkH9 ”0ÎL0  *†H†÷ �€}4ñn[Ax0\b25[Ñv ¦KÎHÛ5zk“ëé&%°b3ž•´U}/žÌ‘NÈ‹vÿ‰j楹‹Ÿ£øi‡¢†ê7ϱV Œ(~ê‡ö†DäTuÏš/gªOý õì!®ñ©Ü´O:ؼö>U,¾:ÿ&lDO¶™w( ¡‚0‚{ *†H†÷  1‚l0‚h0g0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA8%×úøa¯žôç&µÖZÕ0 +� ]0 *†H†÷  1  *†H†÷ 0 *†H†÷  1 110505165508Z0# *†H†÷  1ñ¤Ÿ“á ÓÚA{l˨í‘ßó`0  *†H†÷ �€‘‹b=èD•¬™Îªi›Æ?=ºÕ¤ÖZ ¤Þ'W‚×BÊ…„½‹šm“o*õs^szê×-Aå•ç×È&FxµÕÓ‚¸kõVôGû¦}öÖ|¶ò•`¸Aª_-ðªù`E4÷QVÈÄÁàÀ 2ÁågSgu§Ÿ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fonts/_win32.py�������������������������������������������������������������0000644�0001750�0001751�00000010642�14507104216�017234� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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, orig_face=None): 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"' % (orig_face or 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 break fname = fname or find_font('', bold, italic, face) # fall back to default 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.14.1/vispy/util/fonts/_vispy_fonts.py�������������������������������������������������������0000644�0001750�0001751�00000001333�14507104216�020652� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import os.path as op # List the vispy fonts made available online _vispy_fonts = ('OpenSans',) 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 op.join(op.dirname(__file__), 'data', name) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fonts/_triage.py������������������������������������������������������������0000644�0001750�0001751�00000002144�14507104216�017543� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/util/fonts/_quartz.py������������������������������������������������������������0000644�0001750�0001751�00000020004�14507104216�017611� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/util/fonts/_freetype.py����������������������������������������������������������0000644�0001750�0001751�00000005114�14507104216�020113� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 freetype import Face, FT_FACE_FLAG_SCALABLE 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) if (FT_FACE_FLAG_SCALABLE & font.face_flags) == 0: raise RuntimeError('Font %s is not scalable, so cannot be loaded' % face) _font_dict[key] = font return font def _load_glyph(f, char, glyphs_dict): """Load glyph from font into dict""" from 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.14.1/vispy/util/fonts/__init__.py�����������������������������������������������������������0000644�0001750�0001751�00000001072�14507104216�017667� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/util/filter.py�������������������������������������������������������������������0000644�0001750�0001751�00000002565�14507104216�016274� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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[tuple(sl)] return filtered + baseline �������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/fetching.py�����������������������������������������������������������������0000644�0001750�0001751�00000024100�14507104216�016563� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 import urllib.request 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://raw.githubusercontent.com/vispy/demo-data/main/' 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, str): 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.14.1/vispy/util/event.py��������������������������������������������������������������������0000644�0001750�0001751�00000071033�14507104216�016124� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 from collections import OrderedDict import inspect import traceback import weakref from .logs import logger, _handle_exception 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 = 0 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 callback = self._normalize_cb(callback) if callback in callbacks: return # 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, str): 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: callback = self._normalize_cb(callback) if callback in self._callbacks: idx = self._callbacks.index(callback) self._callbacks.pop(idx) self._callback_refs.pop(idx) def _normalize_cb(self, callback): # dereference methods into a (self, method_name) pair so that we can # make the connection without making a strong reference to the # instance. if inspect.ismethod(callback): callback = (callback.__self__, callback.__name__) # always use a weak ref if (isinstance(callback, tuple) and not isinstance(callback[0], weakref.ref)): callback = (weakref.ref(callback[0]),) + callback[1:] return callback 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 # 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 += 1 try: if blocked.get(None, 0) > 0: # this is the same as self.blocked() return event rem = [] for cb in self._callbacks[:]: if isinstance(cb, tuple): obj = cb[0]() if obj is None: rem.append(cb) continue cb = getattr(obj, cb[1], None) if cb is None: continue if blocked.get(cb, 0) > 0: continue if self._emitting > 1: raise RuntimeError('EventEmitter loop detected!') self._invoke_callback(cb, event) if event.blocked: break # remove callbacks to dead objects for cb in rem: self.disconnect(cb) finally: self._emitting -= 1 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. Examples -------- 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.14.1/vispy/util/eq.py�����������������������������������������������������������������������0000644�0001750�0001751�00000002114�14507104216�015402� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- 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.14.1/vispy/util/dpi/������������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�015200� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/dpi/tests/������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�016342� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/dpi/tests/test_dpi.py�������������������������������������������������������0000644�0001750�0001751�00000000574�14507104216�020536� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/dpi/tests/__init__.py�������������������������������������������������������0000644�0001750�0001751�00000000000�14507104216�020442� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/dpi/_win32.py���������������������������������������������������������������0000644�0001750�0001751�00000002146�14507104216�016657� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/util/dpi/_quartz.py��������������������������������������������������������������0000644�0001750�0001751�00000001475�14507104216�017247� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/util/dpi/_linux.py���������������������������������������������������������������0000644�0001750�0001751�00000003766�14507104216�017065� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import os 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 _xrandr_calc(x_px, y_px, x_mm, y_mm): if x_mm == 0 or y_mm == 0: logger.warning("'xrandr' output has screen dimension of 0mm, " + "can't compute proper DPI") return 96. return 25.4 * (x_px / x_mm + y_px / y_mm) / 2 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. """ # If we are running without an X server (e.g. OSMesa), use a fixed DPI if 'DISPLAY' not in os.environ: return 96. 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', _xrandr_calc) 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.14.1/vispy/util/dpi/__init__.py�������������������������������������������������������������0000644�0001750�0001751�00000001317�14507104216�017314� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/util/config.py�������������������������������������������������������������������0000644�0001750�0001751�00000036141�14507104216�016251� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Vispy configuration functions.""" import io import os from os import path as op import inspect import json import sys import platform import getopt import traceback import tempfile import atexit from shutil import rmtree import numpy as np from .event import EmitterGroup, EventEmitter, Event from .logs import logger, set_log_level, use_log_level file_types = [io.TextIOWrapper, io.BufferedRandom] try: file_types += [tempfile._TemporaryFileWrapper] # Py3k Windows this happens except Exception: pass file_types = tuple(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': (str,), 'default_backend': (str,), 'gl_backend': (str,), 'gl_debug': (bool,), 'glir_file': (str,) + file_types, 'include_path': list, 'logging_level': (str,), 'qt_lib': (str,), 'dpi': (int, type(None)), 'profile': (str, type(None),), 'audit_tests': (bool,), 'test_data_path': (str, 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': 'warning', '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(), str(err))) set_log_level(config['logging_level']) _parse_command_line_arguments() ############################################################################### # Command line flag parsing VISPY_HELP = """ VisPy command line arguments: --vispy-backend=(qt|pyqt4|pyqt5|pyqt6|pyside|pyside2|pyside6|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 = 'Platform: %s\n' % platform.platform() out += 'Python: %s\n' % str(sys.version).replace('\n', ' ') out += 'NumPy: %s\n' % (np.__version__,) 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 .check_environment import has_backend # get default app with use_log_level('warning'): app = use_app(call_reuse=False) # suppress messages 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 += 'App info-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() if hasattr(inspect, 'signature'): # py35 def _get_args(function, varargs=False): params = inspect.signature(function).parameters args = [key for key, param in params.items() if param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD)] if varargs: varargs = [param.name for param in params.values() if param.kind == param.VAR_POSITIONAL] if len(varargs) == 0: varargs = None return args, varargs else: return args else: def _get_args(function, varargs=False): out = inspect.getargspec(function) # args, varargs, keywords, defaults if varargs: return out[:2] else: return out[0] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/check_environment.py��������������������������������������������������������0000644�0001750�0001751�00000003332�14507104216�020501� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import os from packaging.version import Version from vispy.util import use_log_level def has_matplotlib(version='1.2'): """Determine if mpl is a usable version""" try: import matplotlib except Exception: has_mpl = False else: if Version(matplotlib.__version__) >= Version(version): has_mpl = True else: has_mpl = False return has_mpl def has_skimage(version='0.11'): """Determine if scikit-image is a usable version""" try: import skimage except ImportError: return False sk_version = Version(skimage.__version__) return sk_version >= Version(version) 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 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/util/bunch.py��������������������������������������������������������������������0000644�0001750�0001751�00000000656�14507104216�016105� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/util/__init__.py�����������������������������������������������������������������0000644�0001750�0001751�00000001275�14507104216�016543� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 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, _get_args) # noqa from .fetching import load_data_file # noqa from .frozen import Frozen # noqa from . import fonts # noqa from . import transforms # noqa from .wrappers import use, run_subprocess # noqa from .bunch import SimpleBunch # noqa �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/testing/�������������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�015124� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/testing/tests/�������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�016266� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/testing/tests/test_testing.py����������������������������������������������������0000644�0001750�0001751�00000001421�14507104216�021353� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/testing/tests/__init__.py��������������������������������������������������������0000644�0001750�0001751�00000000000�14507104216�020366� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/testing/rendered_array_tester.py�������������������������������������������������0000644�0001750�0001751�00000005322�14507104216�022055� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """Test utilities for comparing rendered arrays with expected array results.""" from typing import Optional, Any import numpy as np import pytest try: from numpy.typing import ArrayLike, DtypeLike except ImportError: ArrayLike = np.ndarray DtypeLike = Any def compare_render( orig_data: ArrayLike, rendered_data: ArrayLike, previous_render: Optional[ArrayLike] = None, atol: Optional[float] = 1.0 ): """Compare an expected original array with the rendered result. Parameters ---------- orig_data Expected output result array. This will be converted to an RGBA array to be compared against the rendered data. rendered_data Actual rendered result as an RGBA 8-bit unsigned array. previous_render Previous instance of a render that the current render should not be equal to. atol Absolute tolerance to be passed to :func:`numpy.testing.assert_allclose`. """ predicted = make_rgba(orig_data) np.testing.assert_allclose(rendered_data.astype(float), predicted.astype(float), atol=atol) if previous_render is not None: # assert not allclose pytest.raises(AssertionError, np.testing.assert_allclose, rendered_data, previous_render, atol=10) def max_for_dtype(input_dtype: DtypeLike): """Get maximum value an image array should have for a specific dtype. This is max int for each integer type or 1.0 for floating point types. """ if np.issubdtype(input_dtype, np.integer): max_val = np.iinfo(input_dtype).max else: max_val = 1.0 return max_val def make_rgba(data_in: ArrayLike) -> ArrayLike: """Convert any array to an RGBA array. RGBA arrays have 3 dimensions where the last represents the channels. If an Alpha channel needs to be added it will be made completely opaque. Returns ------- 3D RGBA unsigned 8-bit array """ max_val = max_for_dtype(data_in.dtype) if data_in.ndim == 3 and data_in.shape[-1] == 1: data_in = data_in.squeeze() if data_in.ndim == 2: out = np.stack([data_in] * 4, axis=2) out[:, :, 3] = max_val elif data_in.shape[-1] == 3: out = np.concatenate((data_in, np.ones((*data_in.shape[:2], 1)) * max_val), axis=2) else: out = data_in return np.round((out.astype(np.float32) * 255 / max_val)).astype(np.uint8) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/testing/image_tester.py����������������������������������������������������������0000644�0001750�0001751�00000042006�14507104216�020151� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """Test utilities for comparing rendered results with expected image files. 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. The config file is located at `vispy/vispy/util/config.py` 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 main 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. Finally, update the tag name in ``get_test_data_repo`` to the new name. """ import time import os import sys import inspect import base64 from subprocess import check_call, CalledProcessError import numpy as np from http.client import HTTPConnection from urllib.parse import urlencode from .. import scene, config from ..io import read_png, write_png from ..gloo.util import _screenshot from ..util import run_subprocess from . import IS_CI 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. Run the test with python --vispy-audit-tests to bring up the auditing GUI. 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 isinstance(image, str) and 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 IS_CI: _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 = HTTPConnection(host) req = 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): self.grid = None self.views = None self.console = None self.last_key = None 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) # unfreeze it to set the image and label on the view # this is slightly hacky, but it is simpler than # creating another class/storing as a dict or a tuple v.unfreeze() v.image = scene.Image(parent=v.scene) v.label = scene.Text(label_text[i], parent=v, color='yellow', anchor_x='left', anchor_y='top') v.freeze() 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() 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-10' data_path = config['test_data_path'] git_path = 'http://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 IS_CI: # 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', 'main', '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.14.1/vispy/testing/_testing.py��������������������������������������������������������������0000644�0001750�0001751�00000030016�14507104216�017313� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 import gc import pytest import functools from packaging.version import Version from ..util.check_environment import has_backend skipif = pytest.mark.skipif IS_TRAVIS_CI = "true" in os.getenv("TRAVIS", "") IS_GITHUB_ACTIONS = "true" in os.getenv("GITHUB_ACTIONS", "") IS_CI = IS_TRAVIS_CI or IS_GITHUB_ACTIONS def SkipTest(*args, **kwargs): """Backport for raising SkipTest that gives a better traceback.""" __tracebackhide__ = True import pytest return pytest.skip(*args, **kwargs) 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)) class raises(object): """Helper class to test exception raising""" def __init__(self, exc): self.exc = exc def __enter__(self): return self def __exit__(self, exc_typ, exc, tb): if isinstance(exc, self.exc): return True elif exc is None: raise AssertionError("Expected %s (no exception raised)" % self.exc.__name__) else: raise AssertionError("Expected %s, got %s instead (%s)" % (self.exc.__name__, type(exc).__name__, exc)) ############################################################################### # GL stuff def has_pyopengl(): try: from OpenGL import GL # noqa, analysis:ignore except ImportError: return False else: return True def requires_pyopengl(): skip = not has_pyopengl() return skipif(skip, reason='Requires PyOpenGL') def requires_ssl(): bad = os.getenv('CIBW_BUILDING', 'false') == 'true' return skipif(bad, reason='Requires proper SSL support') ############################################################################### # App stuff 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 garbage_collect(f): # Pytest expects things like the name of the functions not to change # Therefore, we must use the functools.wraps decorator on our deco @functools.wraps(f) def deco(*args, **kwargs): gc.collect() try: return f(*args, **kwargs) finally: gc.collect() return deco def requires_application(backend=None, has=(), capable=(), force_gc=True): """Return a decorator for tests that require an application""" good, msg = has_application(backend, has, capable) dec_backend = skipif(not good, reason="Skipping test: %s" % msg) try: import pytest except Exception: return dec_backend dec_app = pytest.mark.vispy_app_test funcs = [dec_app, dec_backend] if force_gc: funcs.append(garbage_collect) return composed(*funcs) 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 skipif(not has_img_lib, reason='imageio or PIL required') def has_ipython(version='3.0'): """Function that checks the presence of IPython""" # typecast version to a string, in case an integer is given version = str(version) try: import IPython # noqa except Exception: return False, "IPython library not found" else: if Version(IPython.__version__) >= Version(version): return True, "IPython present" else: message = ( "current IPython version: (%s) is " "older than expected version: (%s)") % \ (IPython.__version__, version) return False, message def requires_ipython(version='3.0'): ipython_present, message = has_ipython(version) return skipif(not ipython_present, reason=message) def requires_numpydoc(): try: import numpydoc # noqa except Exception: present = False else: present = True return skipif(not present, reason='numpydoc is required') ############################################################################### # Visuals stuff def _has_scipy(min_version): try: assert isinstance(min_version, str) import scipy # noqa, analysis:ignore this_version = Version(scipy.__version__) if this_version < min_version: return False except Exception: return False else: return True def requires_scipy(min_version='0.13'): return skipif(not _has_scipy(min_version), reason='Requires Scipy version >= %s' % min_version) def _bad_glfw_decorate(app): return app.backend_name == 'Glfw' and \ app.backend_module.glfw.__version__ == (3, 3, 1) @nottest def TestingCanvas(bgcolor='black', size=(100, 100), dpi=None, decorate=None, **kwargs): """Avoid importing scene until necessary.""" # On Windows decorations can force windows to be an incorrect size # (e.g., instead of 100x100 they will be 100x248), having no # decorations works around this from ..scene import SceneCanvas class TestingCanvas(SceneCanvas): def __init__(self, bgcolor, size, dpi, decorate, **kwargs): self._entered = False self._wanted_vp = None if decorate is None: # deal with GLFW's problems from vispy.app import use_app app = use_app() if _bad_glfw_decorate(app): decorate = True else: decorate = False SceneCanvas.__init__(self, bgcolor=bgcolor, size=size, dpi=dpi, decorate=decorate, **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): if not self._entered: return SceneCanvas.draw_visual(self, visual, event) self.context.finish() return TestingCanvas(bgcolor, size, dpi, decorate, **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.14.1/vispy/testing/_runners.py��������������������������������������������������������������0000644�0001750�0001751�00000037037�14507104216�017344� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # vispy: testskip # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Test running functions.""" from __future__ import print_function import sys import os import warnings 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 has_application, nottest, IS_CI, IS_TRAVIS_CI _line_sep = '-' * 70 class VispySkipSuite(Exception): """Class we use to internally signal skipping a test suite.""" def __init__(self, msg=''): if msg: print(msg) super(VispySkipSuite, self).__init__(msg) def _get_import_dir(): import_dir = op.abspath(op.join(op.dirname(__file__), '..')) up_dir = op.join(import_dir, '..') if (op.isfile(op.join(up_dir, 'setup.py')) and op.isdir(op.join(up_dir, 'vispy')) and op.isdir(op.join(up_dir, 'examples'))): dev = True else: dev = False return import_dir, dev def _unit(mode, extra_arg_string='', coverage=False): """Run unit tests using a particular mode""" if isinstance(extra_arg_string, str): if len(extra_arg_string): extra_args = extra_arg_string.split(' ') else: extra_args = () else: extra_args = extra_arg_string del extra_arg_string assert isinstance(extra_args, (list, tuple)) assert all(isinstance(e, str) for e in extra_args) import_dir = _get_import_dir()[0] cwd = op.abspath(op.join(import_dir, '..')) extra_args = list(extra_args) try: import pytest # noqa, analysis:ignore except ImportError: raise VispySkipSuite('Skipping unit tests, pytest not installed') if mode == 'nobackend': msg = 'Running tests with no backend' extra_args += ['-m', 'not vispy_app_test'] else: # check to make sure we actually have the backend of interest stdout, stderr, invalid = run_subprocess( [sys.executable, '-c', 'import vispy.app; vispy.app.use_app("%s"); exit(0)' % mode], return_code=True) if invalid: stdout = stdout + '\n' + stderr stdout = '\n'.join(' ' + x for x in stdout.split('\n')) raise VispySkipSuite( '\n%s\n%s\n%s' % (_line_sep, 'Skipping backend %s, not ' 'installed or working properly:\n%s' % (mode, stdout), _line_sep)) msg = 'Running tests with %s backend' % mode extra_args += ['-m', 'vispy_app_test'] if coverage: # Don't actually print the coverage because it's way too long extra_args += ['--cov', 'vispy', '--cov-report='] if not any(e.startswith('-r') for e in extra_args): extra_args.append('-ra') # make a call to "python" so that it inherits whatever the system # thinks is "python" (e.g., virtualenvs) extra_args += [import_dir] # positional argument cmd = [sys.executable, '-m', 'pytest'] + extra_args 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, VISPY_IGNORE_OLD_VERSION='true')) env_str = '_VISPY_TESTING_APP=%s ' % mode if len(msg) > 0: cmd_string = ' '.join(cmd) msg = ('%s\n%s:\n%s%s' % (_line_sep, 'msg', env_str, cmd_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) if coverage: # Running a py.test with coverage will wipe out any files that # exist as .coverage or .coverage.*. It should work to pass # COVERAGE_FILE env var when doing run_subprocess above, but # it does not. Therefore we instead use our own naming scheme, # and in Travis when we combine them, use COVERAGE_FILE with the # `coverage combine` command. out_name = op.join(cwd, '.vispy-coverage.%s' % mode) if op.isfile(out_name): os.remove(out_name) os.rename(op.join(cwd, '.coverage'), out_name) def _docs(): """Test docstring parameters using vispy/utils/tests/test_docstring_parameters.py """ dev = _get_import_dir()[1] if not dev: warnings.warn("Docstring test imports Vispy from" " Vispy's installation. It is" " recommended to setup Vispy using" " 'python setup.py develop'" " so that the latest sources are used automatically") try: # this should always be importable from ..util.tests import test_docstring_parameters print("Running docstring test...") test_docstring_parameters.test_docstring_parameters() except AssertionError as docstring_violations: # the test harness expects runtime errors, # not AssertionError. So wrap the AssertionError # that is thrown by test_docstring_parameters() # with a RuntimeError raise RuntimeError(docstring_violations) def _flake(): """Test flake8""" orig_dir = os.getcwd() import_dir, dev = _get_import_dir() os.chdir(op.join(import_dir, '..')) if dev: sys.argv[1:] = ['vispy', 'examples', 'make'] else: sys.argv[1:] = [op.basename(import_dir)] try: try: from flake8.main import main except ImportError: from flake8.main.cli 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 = [] import_dir, dev = _get_import_dir() for dirpath, dirnames, filenames in os.walk(import_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, import_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 import os try: import faulthandler faulthandler.enable() except Exception: pass from vispy.util.gallery_scraper import get_canvaslike_from_globals, FrameGrabber os.environ['VISPY_IGNORE_OLD_VERSION'] = 'true' os.environ['_VISPY_RUNNING_GALLERY_EXAMPLES'] = 'true' import {0} canvas_or_widget = get_canvaslike_from_globals({0}.__dict__) if canvas_or_widget is None: raise RuntimeError('Bad example formatting: fix or add `# vispy: testskip`' ' to the top of the file.') frame_grabber = FrameGrabber(canvas_or_widget, [1, 2, 3, 4, 5]) frame_grabber.collect_frames() """ bad_examples = [] if IS_TRAVIS_CI and sys.platform == 'darwin': # example scripts that contain non-ascii text # seem to fail on Travis OSX bad_examples = [ 'examples/basics/plotting/colorbar.py', 'examples/basics/plotting/plot.py', 'examples/demo/gloo/high_frequency.py', 'examples/basics/scene/shared_context.py', ] elif IS_CI and 'linux' in sys.platform: # example scripts that contain non-ascii text # seem to fail on Travis OSX bad_examples = [ 'examples/basics/scene/shared_context.py', ] if IS_CI: # OpenGL >2.0 that fail on Travis bad_examples += [ 'examples/basics/gloo/geometry_shader.py', 'examples/gloo/geometry_shader.py', ] def _skip_example(fname): for bad_ex in bad_examples: if fname.endswith(bad_ex): return True return False 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. """ import_dir, dev = _get_import_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: raise VispySkipSuite('Skipping example test: %s' % reason) # 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: examples_dir = '' fnames = fnames_str.split(' ') else: examples_dir = op.join(import_dir, '..', 'examples') fnames = [op.join(d[0], fname) for d in os.walk(examples_dir) for fname in d[2] if fname.endswith('.py')] fnames = sorted(fnames, key=lambda x: x.lower()) print(_line_sep + '\nRunning examples using %s backend' % (backend,)) op.join('tutorial', 'app', 'shared_context.py'), # non-standard fails = [] n_ran = n_skipped = 0 t0 = time() for fi, fname in enumerate(fnames): n_ran += 1 root_name = fname[-len(fname) + len(examples_dir):] good = True with open(fname, 'rb') as fid: for _ in range(10): # just check the first 10 lines line = fid.readline().decode('utf-8') if line == '': break elif line.startswith('# vispy: ') and 'testskip' in line: good = False break if _skip_example(fname): print("Skipping example that fails on Travis CI: {}".format(fname)) good = False if not good: n_ran -= 1 n_skipped += 1 continue line_str = ('[%3d/%3d] %s' % (fi + 1, len(fnames), root_name)) print(line_str.ljust(len(_line_sep) - 1), end='') 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') else: ext = '\n' + _line_sep + '\n' fails.append('X%sExample %s failed (%s):%s%s%s' % (ext, root_name, retcode, ext, stderr, ext)) print(fails[-1]) else: print('✓') 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='', coverage=False): """Test vispy software Parameters ---------- label : str Can be one of 'full', 'unit', 'nobackend', 'extra', 'lineendings', 'flake', 'docs', or any backend name (e.g., 'qt'). extra_arg_string : str | list of str Extra arguments to sent to ``pytest``. Can also be a list of str to more explicitly provide the arguments. coverage : bool If True, collect coverage data. """ if label == 'osmesa': # Special case for OSMesa, we have to modify the VISPY_GL_LIB envvar # before the vispy.gloo package gets imported from ..util.osmesa_gl import fix_osmesa_gl_lib fix_osmesa_gl_lib() from ..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', 'docs', '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)) # remove troublesome backends # see https://github.com/vispy/vispy/issues/2009 backend_names.remove('tkinter') # 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, coverage), backend]) elif label in backend_names: runs.append([partial(_unit, label, extra_arg_string, coverage), label]) if label in ('full', 'unit', 'nobackend'): runs.append([partial(_unit, 'nobackend', extra_arg_string, coverage), 'nobackend']) 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', 'extra', 'lineendings'): runs.append([_check_line_endings, 'lineendings']) if label in ('full', 'extra', 'flake'): runs.append([_flake, 'flake']) if label in ('extra', 'docs'): runs.append([_docs, 'docs']) t0 = time() fail = [] skip = [] for run in runs: try: run[0]() except RuntimeError as exp: print('Failed: %s' % str(exp)) fail += [run[1]] except VispySkipSuite: 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') sys.stdout.flush() 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.14.1/vispy/testing/__init__.py��������������������������������������������������������������0000644�0001750�0001751�00000004605�14507104216�017243� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 different 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 GitHub Actions. There are multiple testing modes that use e.g. full dependencies, minimal dependencies, etc. See ``.github/workflows/main.yml`` to determine what automatic tests are run. """ from ._testing import (SkipTest, requires_application, requires_ipython, # noqa requires_img_lib, # noqa requires_pyopengl, # noqa requires_scipy, # noqa save_testing_image, TestingCanvas, has_pyopengl, # noqa run_tests_if_main, requires_ssl, # noqa assert_is, assert_in, assert_not_in, assert_equal, assert_not_equal, assert_raises, assert_true, # noqa raises, requires_numpydoc, IS_TRAVIS_CI, IS_CI) # noqa from ._runners import test # noqa ���������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/���������������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�014544� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/�������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�016212� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/widget.py����������������������������������������������������������0000644�0001750�0001751�00000033047�14507104216�020057� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 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 from ..visuals import Compound from ...visuals.mesh import MeshVisual from ...visuals.transforms import STTransform from ...visuals.filters import Clipper from ...util.event import Event from ...geometry import Rect from ...color import Color class Widget(Compound): """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. border_width : float The width of the border line in pixels. bgcolor : color The background color. 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, border_width=1, bgcolor=None, padding=0, margin=0, **kwargs): # For drawing border. # A mesh is required because GL lines cannot be drawn with predictable # shape across all platforms. self._mesh = MeshVisual(color=border_color, mode='triangles') self._mesh.set_gl_state('translucent', depth_test=False, cull_face=False) self._picking_mesh = MeshVisual(mode='triangle_fan') self._picking_mesh.set_gl_state(cull_face=False, depth_test=False) self._picking_mesh.visible = False # reserved space inside border self._padding = padding self._border_width = border_width # reserved space outside border self._margin = margin self._size = 100, 100 # layout interaction self._width_limits = [0, None] self._height_limits = [0, None] self._stretch = [None, None] # used by the constraint solver # in Grid - these are Cassowary variables self._var_w = self._var_h = None self._var_x = self._var_y = None self._widgets = [] self._border_color = Color(border_color) self._bgcolor = Color(bgcolor) self._face_colors = None # Flag to allow rect setter to know if pos or size changed. self._pos_or_size_changed = False Compound.__init__(self, [self._mesh, self._picking_mesh], **kwargs) self.transform = STTransform() self.events.add(resize=Event) self.pos = pos self._update_colors() self.size = size @property def pos(self): return tuple(self.transform.translate[:2]) @pos.setter def pos(self, p): assert isinstance(p, tuple) assert len(p) == 2 # Handle floating point discrepancies if abs(p[0] - self.pos[0]) < 1e-4 and \ abs(p[1] - self.pos[1]) < 1e-4: return self._pos_or_size_changed = True self.transform.translate = p[0], p[1], 0, 0 self._update_line() @property def size(self): """The size (w, h) of this widget. If the widget is a child of another widget, then its size is assigned automatically by its parent. """ return self._size @size.setter def size(self, s): assert isinstance(s, tuple) assert len(s) == 2 # Handle floating point discrepancies if abs(s[0] - self._size[0]) < 1e-4 and \ abs(s[1] - self._size[1]) < 1e-4: return self._pos_or_size_changed = True self._size = s self._update_line() self._update_child_widgets() self._update_clipper() self.events.resize() @property def width(self): """The actual width of this widget""" return self._size[0] @property def width_min(self): """The minimum width the widget can have""" return self._width_limits[0] @width_min.setter def width_min(self, width_min): """Set the minimum height of the widget Parameters ---------- height_min: float the minimum height of the widget """ if width_min is None: self._width_limits[0] = 0 return width_min = float(width_min) assert(0 <= width_min) self._width_limits[0] = width_min self._update_layout() @property def width_max(self): """The maximum width the widget can have""" return self._width_limits[1] @width_max.setter def width_max(self, width_max): """Set the maximum width of the widget. Parameters ---------- width_max: None | float the maximum width of the widget. if None, maximum width is unbounded """ if width_max is None: self._width_limits[1] = None return width_max = float(width_max) assert(self.width_min <= width_max) self._width_limits[1] = width_max self._update_layout() @property def height(self): """The actual height of the widget""" return self._size[1] @property def height_min(self): """The minimum height of the widget""" return self._height_limits[0] @height_min.setter def height_min(self, height_min): """Set the minimum height of the widget Parameters ---------- height_min: float the minimum height of the widget """ if height_min is None: self._height_limits[0] = 0 return height_min = float(height_min) assert(height_min >= 0) self._height_limits[0] = height_min self._update_layout() @property def height_max(self): """The maximum height of the widget""" return self._height_limits[1] @height_max.setter def height_max(self, height_max): """Set the maximum height of the widget. Parameters ---------- height_max: None | float the maximum height of the widget. if None, maximum height is unbounded """ if height_max is None: self._height_limits[1] = None return height_max = float(height_max) assert(0 <= self.height_min <= height_max) self._height_limits[1] = height_max self._update_layout() @property def rect(self): return Rect((0, 0), self.size) @rect.setter def rect(self, r): self._pos_or_size_changed = False with self.events.resize.blocker(): self.pos = r.pos self.size = r.size if self._pos_or_size_changed: 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 sub-widgets outside this rectangle. """ m = self.margin + self._border_width + 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 stretch(self): """Stretch factors (w, h) used when determining how much space to allocate to this widget in a layout. If either stretch factor is None, then it will be assigned when the widget is added to a layout based on the number of columns or rows it occupies. """ return self._stretch @stretch.setter def stretch(self, s): self._stretch = [float(s[0]), float(s[1])] if self._stretch[0] == 0: raise RuntimeError("received 0 as stretch parameter: %s", s) if self._stretch[1] == 0: raise RuntimeError("received 0 as stretch parameter: %s", s) self._update_layout() def _update_layout(self): if isinstance(self.parent, Widget): self.parent._update_child_widgets() def _update_clipper(self): """Called whenever the clipper for this widget may need to be updated.""" if self.clip_children and self._clipper is None: self._clipper = Clipper() elif not self.clip_children: self._clipper = None if self._clipper is None: return self._clipper.rect = self.inner_rect self._clipper.transform = self.get_transform('framebuffer', 'visual') @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_child_widgets() self._update_line() self.update() self.events.resize() @property def padding(self): return self._padding @padding.setter def padding(self, p): self._padding = p self._update_child_widgets() self.update() def _update_line(self): """Update border line to match new shape""" w = self._border_width m = 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.. # ........ # ........ # left = bot = m right = self.size[0] - m top = self.size[1] - m pos = np.array([ [left, bot], [left+w, bot+w], [right, bot], [right-w, bot+w], [right, top], [right-w, top-w], [left, top], [left+w, top-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._mesh.set_data(vertices=pos, faces=faces[start:stop], face_colors=face_colors) # picking mesh covers the entire area self._picking_mesh.set_data(vertices=pos[::2]) 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) self._update_visibility() @property def picking(self): return self._picking @picking.setter def picking(self, p): Compound.picking.fset(self, p) self._update_visibility() def _update_visibility(self): blank = self.border_color.is_blank and self.bgcolor.is_blank picking = self.picking self._picking_mesh.visible = picking and self.interactive self._mesh.visible = not picking and not blank 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.parent = None self._update_child_widgets() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/viewbox.py���������������������������������������������������������0000644�0001750�0001751�00000015022�14507104216�020250� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from typing import Union import numpy as np from .widget import Widget from ..subscene import SubScene from ..cameras import make_camera, BaseCamera from ...visuals.filters import Clipper 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 : instance of Camera | str | None 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`. **kwargs : dict Extra keyword arguments to pass to `Widget`. """ def __init__(self, camera=None, **kwargs): self._camera = None self._scene = None Widget.__init__(self, **kwargs) self.interactive = True # Each viewbox has an internal scene node, which has a transform that # represents the transformation imposed by camera. if self.name is not None: name = str(self.name) + "_Scene" else: name = None self._scene = SubScene(name=name, parent=self) self._scene._clipper = Clipper() self._scene.clip_children = True self.transforms.changed.connect(self._update_scene_clipper) # Camera is a helper object that handles scene transformation # and user interaction. if camera is None: camera = 'base' if isinstance(camera, str): 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) -> BaseCamera: """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: Union[str, BaseCamera]): if isinstance(cam, str): # 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.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. """ return self.scene.is_child(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 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(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.parent = viewbox.scene` Parameters ---------- node : instance of Node The node to add. """ node.parent = self.scene def on_resize(self, event): """Resize event handler Parameters ---------- event : instance of Event The event. """ if self._scene is None: # happens during init return self._update_scene_clipper() def _update_scene_clipper(self, event=None): tr = self.get_transform('visual', 'framebuffer') self._scene._clipper.bounds = tr.map(self.inner_rect) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/tests/�������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�017354� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/tests/test_colorbar.py���������������������������������������������0000644�0001750�0001751�00000002522�14507104216�022572� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- """Tests for ColorbarVWidget. All images are of size (100,100) to keep a small file size """ from vispy.scene.widgets import ColorBarWidget from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved def create_colorbar(pos, orientation, label='label string here'): colorbar = ColorBarWidget(pos=pos, orientation=orientation, label=label, cmap='autumn', border_color='white', border_width=2) colorbar.label.color = 'white' colorbar.label.font_size = 5 colorbar.ticks[0].color = 'white' colorbar.ticks[0].font_size = 5 colorbar.ticks[1].color = 'white' colorbar.ticks[1].font_size = 5 return colorbar @requires_application() def test_colorbar_widget(): with TestingCanvas() as c: colorbar_top = create_colorbar(pos=(50, 50), label="my label", orientation='top') c.draw_visual(colorbar_top) assert_image_approved(c.render(), 'visuals/colorbar/top.png') assert colorbar_top.label.text == "my label" run_tests_if_main() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/tests/__init__.py��������������������������������������������������0000644�0001750�0001751�00000000000�14507104216�021454� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/label.py�����������������������������������������������������������0000644�0001750�0001751�00000002434�14507104216�017647� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from .widget import Widget from ...visuals import TextVisual class Label(Widget): """Label widget Parameters ---------- text : str The label. rotation : float The rotation of the label. **kwargs : dict Keyword arguments to pass to TextVisual. """ def __init__(self, text, rotation=0., **kwargs): self._text_visual = TextVisual(text=text, rotation=rotation, **kwargs) self.rotation = rotation Widget.__init__(self) self.add_subvisual(self._text_visual) self._set_pos() def on_resize(self, event): """Resize event handler Parameters ---------- event : instance of Event The event. """ self._set_pos() def _set_pos(self): self._text_visual.pos = self.rect.center @property def text(self): return self._text_visual.text @text.setter def text(self, t): self._text_visual.text = t ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/grid.py������������������������������������������������������������0000644�0001750�0001751�00000044340�14507104216�017517� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Grid widget for providing a gridded layout to child widgets.""" from __future__ import division import numpy as np from vispy.geometry import Rect from .widget import Widget from .viewbox import ViewBox from kiwisolver import Solver, Variable, UnsatisfiableConstraint class Grid(Widget): """Widget for proportionally dividing its internal area into a grid. This widget will automatically set the position and size of child widgets according to provided constraints. Parameters ---------- spacing : int Spacing between widgets. **kwargs : dict Keyword arguments to pass to `Widget`. """ def __init__(self, spacing=6, **kwargs): """Create solver and basic grid parameters.""" 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 self._solver = Solver() self._need_solver_recreate = True # width and height of the Rect used to place child widgets self._var_w = Variable("w_rect") self._var_h = Variable("h_rect") self._width_grid = None self._height_grid = None # self._height_stay = None # self._width_stay = None 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(0, 1, 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, **kwargs): """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. Can be used to replace a widget as well. Parameters ---------- widget : Widget | None The Widget to add. New widget is constructed if widget is None. 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 parameters sent to the new Widget that is constructed if widget is None 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(**kwargs) else: if kwargs: raise ValueError("cannot send kwargs if widget is given") _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] widget._var_w = Variable("w-(row: %s | col: %s)" % (row, col)) widget._var_h = Variable("h-(row: %s | col: %s)" % (row, col)) # update stretch based on colspan/rowspan # usually, if you make something consume more grids or columns, # you also want it to actually *take it up*, ratio wise. # otherwise, it will never *use* the extra rows and columns, # thereby collapsing the extras to 0. stretch = list(widget.stretch) stretch[0] = col_span if stretch[0] is None else stretch[0] stretch[1] = row_span if stretch[1] is None else stretch[1] widget.stretch = stretch self._need_solver_recreate = True return widget def remove_widget(self, widget): """Remove a widget from this grid. Parameters ---------- widget : Widget The Widget to remove """ self._grid_widgets = dict((key, val) for (key, val) in self._grid_widgets.items() if val[-1] != widget) self._need_solver_recreate = True def resize_widget(self, widget, row_span, col_span): """Resize a widget in the grid to new dimensions. Parameters ---------- widget : Widget The widget to resize row_span : int The number of rows to be occupied by this widget. col_span : int The number of columns to be occupied by this widget. """ row = None col = None for (r, c, _rspan, _cspan, w) in self._grid_widgets.values(): if w == widget: row = r col = c break if row is None or col is None: raise ValueError("%s not found in grid" % widget) self.remove_widget(widget) self.add_widget(widget, row, col, row_span, col_span) self._need_solver_recreate = True def _prepare_draw(self, view): self._update_child_widget_dim() 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`. """ 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 (('') @staticmethod def _add_total_width_constraints(solver, width_grid, _var_w): for ws in width_grid: width_expr = ws[0] for w in ws[1:]: width_expr += w solver.addConstraint(width_expr == _var_w) @staticmethod def _add_total_height_constraints(solver, height_grid, _var_h): for hs in height_grid: height_expr = hs[0] for h in hs[1:]: height_expr += h solver.addConstraint(height_expr == _var_h) @staticmethod def _add_gridding_width_constraints(solver, width_grid): # access widths of one "y", different x for ws in width_grid.T: for w in ws[1:]: solver.addConstraint(ws[0] == w) @staticmethod def _add_gridding_height_constraints(solver, height_grid): # access heights of one "y" for hs in height_grid.T: for h in hs[1:]: solver.addConstraint(hs[0] == h) @staticmethod def _add_stretch_constraints(solver, width_grid, height_grid, grid_widgets, widget_grid): xmax = len(height_grid) ymax = len(width_grid) stretch_widths = [[] for _ in range(0, ymax)] stretch_heights = [[] for _ in range(0, xmax)] for (y, x, ys, xs, widget) in grid_widgets.values(): for ws in width_grid[y:y+ys]: total_w = np.sum(ws[x:x+xs]) for sw in stretch_widths[y:y+ys]: sw.append((total_w, widget.stretch[0])) for hs in height_grid[x:x+xs]: total_h = np.sum(hs[y:y+ys]) for sh in stretch_heights[x:x+xs]: sh.append((total_h, widget.stretch[1])) for (x, xs) in enumerate(widget_grid): for(y, widget) in enumerate(xs): if widget is None: stretch_widths[y].append((width_grid[y][x], 1)) stretch_heights[x].append((height_grid[x][y], 1)) for sws in stretch_widths: if len(sws) <= 1: continue comparator = sws[0][0] / sws[0][1] for (stretch_term, stretch_val) in sws[1:]: solver.addConstraint((comparator == stretch_term/stretch_val) | 'weak') for sws in stretch_heights: if len(sws) <= 1: continue comparator = sws[0][0] / sws[0][1] for (stretch_term, stretch_val) in sws[1:]: solver.addConstraint((comparator == stretch_term/stretch_val) | 'weak') @staticmethod def _add_widget_dim_constraints(solver, width_grid, height_grid, total_var_w, total_var_h, grid_widgets): assert(total_var_w is not None) assert(total_var_h is not None) for ws in width_grid: for w in ws: solver.addConstraint(w >= 0,) for hs in height_grid: for h in hs: solver.addConstraint(h >= 0) for (_, val) in grid_widgets.items(): (y, x, ys, xs, widget) = val for ws in width_grid[y:y+ys]: total_w = np.sum(ws[x:x+xs]) # assert(total_w is not None) solver.addConstraint(total_w >= widget.width_min) if widget.width_max is not None: solver.addConstraint(total_w <= widget.width_max) else: solver.addConstraint(total_w <= total_var_w) for hs in height_grid[x:x+xs]: total_h = np.sum(hs[y:y+ys]) solver.addConstraint(total_h >= widget.height_min) if widget.height_max is not None: solver.addConstraint(total_h <= widget.height_max) else: solver.addConstraint(total_h <= total_var_h) def _recreate_solver(self): self._solver.reset() self._var_w = Variable("w_rect") self._var_h = Variable("h_rect") self._solver.addEditVariable(self._var_w, 'strong') self._solver.addEditVariable(self._var_h, 'strong') rect = self.rect.padded(self.padding + self.margin) ymax, xmax = self.grid_size self._solver.suggestValue(self._var_w, rect.width) self._solver.suggestValue(self._var_h, rect.height) self._solver.addConstraint(self._var_w >= 0) self._solver.addConstraint(self._var_h >= 0) # self._height_stay = None # self._width_stay = None # add widths self._width_grid = np.array([[Variable("width(x: %s, y: %s)" % (x, y)) for x in range(0, xmax)] for y in range(0, ymax)]) # add heights self._height_grid = np.array([[Variable("height(x: %s, y: %s" % (x, y)) for y in range(0, ymax)] for x in range(0, xmax)]) # setup stretch stretch_grid = np.zeros(shape=(xmax, ymax, 2), dtype=float) stretch_grid.fill(1) for (_, val) in self._grid_widgets.items(): (y, x, ys, xs, widget) = val stretch_grid[x:x+xs, y:y+ys] = widget.stretch # even though these are REQUIRED, these should never fail # since they're added first, and thus the slack will "simply work". Grid._add_total_width_constraints(self._solver, self._width_grid, self._var_w) Grid._add_total_height_constraints(self._solver, self._height_grid, self._var_h) try: # these are REQUIRED constraints for width and height. # These are the constraints which can fail if # the corresponding dimension of the widget cannot be fit in the # grid. Grid._add_gridding_width_constraints(self._solver, self._width_grid) Grid._add_gridding_height_constraints(self._solver, self._height_grid) except UnsatisfiableConstraint: self._need_solver_recreate = True # these are WEAK constraints, so these constraints will never fail # with a RequiredFailure. Grid._add_stretch_constraints(self._solver, self._width_grid, self._height_grid, self._grid_widgets, self._widget_grid) Grid._add_widget_dim_constraints(self._solver, self._width_grid, self._height_grid, self._var_w, self._var_h, self._grid_widgets) self._solver.updateVariables() def _update_child_widget_dim(self): # think in terms of (x, y). (row, col) makes code harder to read ymax, xmax = self.grid_size if ymax <= 0 or xmax <= 0: return rect = self.rect # .padded(self.padding + self.margin) if rect.width <= 0 or rect.height <= 0: return if self._need_solver_recreate: self._need_solver_recreate = False self._recreate_solver() # we only need to remove and add the height and width constraints of # the solver if they are not the same as the current value h_changed = abs(rect.height - self._var_h.value()) > 1e-4 w_changed = abs(rect.width - self._var_w.value()) > 1e-4 if h_changed: self._solver.suggestValue(self._var_h, rect.height) if w_changed: self._solver.suggestValue(self._var_w, rect.width) if h_changed or w_changed: self._solver.updateVariables() value_vectorized = np.vectorize(lambda x: x.value()) for (_, val) in self._grid_widgets.items(): (row, col, rspan, cspan, widget) = val width = np.sum(value_vectorized( self._width_grid[row][col:col+cspan])) height = np.sum(value_vectorized( self._height_grid[col][row:row+rspan])) if col == 0: x = 0 else: x = np.sum(value_vectorized(self._width_grid[row][0:col])) if row == 0: y = 0 else: y = np.sum(value_vectorized(self._height_grid[col][0:row])) if isinstance(widget, ViewBox): widget.rect = Rect(x, y, width, height) else: widget.size = (width, height) widget.pos = (x, y) @property def _widget_grid(self): ymax, xmax = self.grid_size widget_grid = np.array([[None for _ in range(0, ymax)] for _ in range(0, xmax)]) for (_, val) in self._grid_widgets.items(): (y, x, ys, xs, widget) = val widget_grid[x:x+xs, y:y+ys] = widget return widget_grid ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/console.py���������������������������������������������������������0000644�0001750�0001751�00000033275�14507104216�020241� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 .widget import Widget from ...visuals import Visual from ...gloo import VertexBuffer from ...color import Color # 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 = """ #version 120 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): self._visual = ConsoleVisual(text_color, font_size) Widget.__init__(self, **kwargs) self.add_subvisual(self._visual) def on_resize(self, event): """Resize event handler Parameters ---------- event : instance of Event The event. """ self._visual.size = self.size def clear(self): """Clear the console""" self._visual.clear() 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. """ self._visual.write(text) @property def text_color(self): """The color of the text""" return self._visual._text_color @text_color.setter def text_color(self, color): self._visual._text_color = Color(color) @property def font_size(self): """The font size (in points) of the text""" return self._visual._font_size @font_size.setter def font_size(self, font_size): self._visual._font_size = float(font_size) class ConsoleVisual(Visual): def __init__(self, text_color, font_size, **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._pending_writes = [] self._text_lines = [] self._col = 0 self._current_sizes = (-1,) * 3 self._size = (100, 100) Visual.__init__(self, VERTEX_SHADER, FRAGMENT_SHADER) self._draw_mode = 'points' self.set_gl_state(depth_test=False, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) @property def size(self): return self._size @size.setter def size(self, s): self._size = s @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 _prepare_draw(self, view): xform = view.get_transform() tr = view.get_transform('document', 'render') logical_scale = np.diff(tr.map(([0, 1], [1, 0])), axis=0)[0, :2] tr = view.get_transform('document', '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['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) def _prepare_transforms(self, view): pass 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, str): 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 ord_chars = np.array([ord(c) - 32 for c in line[:self._n_cols]]) ord_chars = np.clip(ord_chars, 0, len(__font_6x8__)-1) if len(ord_chars) > 0: b = __font_6x8__[ord_chars] self._bytes_012[idx, :len(ord_chars)] = b[:, :3] self._bytes_345[idx, :len(ord_chars)] = b[:, 3:] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/colorbar.py��������������������������������������������������������0000644�0001750�0001751�00000014016�14507104216�020372� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from .widget import Widget from ...visuals import ColorBarVisual class ColorBarWidget(Widget): """Widget containing a ColorBar Parameters ---------- cmap : str | vispy.color.ColorMap Either the name of the ColorMap to be used from the standard set of names (refer to `vispy.color.get_colormap`), or a custom ColorMap object. The ColorMap is used to apply a gradient on the colorbar. orientation : {'left', 'right', 'top', 'bottom'} The orientation of the colorbar, used for rendering. The orientation can be thought of as the position of the label relative to the color bar. When the orientation is 'left' or 'right', the colorbar is vertically placed. When it is 'top' or 'bottom', the colorbar is horizontally placed. The colorbar automatically resizes when its container's dimension changes. * 'top': the colorbar is horizontal. Color is applied from left to right. Minimum corresponds to left and maximum to right. Label is to the top of the colorbar * 'bottom': Same as top, except that label is to the bottom of the colorbar * 'left': the colorbar is vertical. Color is applied from bottom to top. Minimum corresponds to bottom and maximum to top. Label is to the left of the colorbar * 'right': Same as left, except that the label is placed to the right of the colorbar label : str The label that is to be drawn with the colorbar that provides information about the colorbar. label_color : str | vispy.color.Color The color of labels. This can either be a str as the color's name or an actual instace of a vipy.color.Color clim : tuple (min, max) the minimum and maximum values of the data that is given to the colorbar. This is used to draw the scale on the side of the colorbar. border_width : float (in px) The width of the border the colormap should have. This measurement is given in pixels border_color : str | vispy.color.Color The color of the border of the colormap. This can either be a str as the color's name or an actual instace of a vipy.color.Color padding : tuple (major_axis, minor_axis) [0, 1] padding with respect to the major and minor axis axis_ratio : float ratio of minor axis to major axis """ def __init__(self, cmap, orientation, label="", label_color='black', clim=("", ""), border_width=0.0, border_color="black", padding=(0.2, 0.2), axis_ratio=0.05, **kwargs): dummy_size = (1, 1) self._major_axis_padding = padding[0] self._minor_axis_padding = padding[1] self._minor_axis_ratio = axis_ratio self._colorbar = ColorBarVisual(size=dummy_size, cmap=cmap, orientation=orientation, label=label, clim=clim, label_color=label_color, border_width=border_width, border_color=border_color, **kwargs) Widget.__init__(self) self.add_subvisual(self._colorbar) self._update_colorbar() def on_resize(self, event): """Resize event handler Parameters ---------- event : instance of Event The event. """ self._update_colorbar() def _update_colorbar(self): self._colorbar.pos = self.rect.center self._colorbar.size = self._calc_size() def _calc_size(self): """Calculate a size""" (total_halfx, total_halfy) = (self.rect.right, self.rect.top) if self._colorbar.orientation in ["bottom", "top"]: (total_major_axis, total_minor_axis) = (total_halfx, total_halfy) else: (total_major_axis, total_minor_axis) = (total_halfy, total_halfx) major_axis = total_major_axis * (1.0 - self._major_axis_padding) minor_axis = major_axis * self._minor_axis_ratio # if the minor axis is "leaking" from the padding, then clamp minor_axis = np.minimum(minor_axis, total_minor_axis * (1.0 - self._minor_axis_padding)) return (major_axis, minor_axis) @property def cmap(self): return self._colorbar.cmap @cmap.setter def cmap(self, cmap): self._colorbar.cmap = cmap @property def label(self): return self._colorbar.label @label.setter def label(self, label): self._colorbar.label = label @property def ticks(self): return self._colorbar.ticks @ticks.setter def ticks(self, ticks): self._colorbar.ticks = ticks @property def clim(self): return self._colorbar.clim @clim.setter def clim(self, clim): self._colorbar.clim = clim @property def border_color(self): """The color of the border around the ColorBar in pixels""" return self._colorbar.border_color @border_color.setter def border_color(self, border_color): self._colorbar.border_color = border_color @property def border_width(self): """The width of the border around the ColorBar in pixels""" return self._colorbar.border_width @border_width.setter def border_width(self, border_width): self._colorbar.border_width = border_width @property def orientation(self): return self._colorbar.orientation ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/axis.py������������������������������������������������������������0000644�0001750�0001751�00000005612�14507104216�017535� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np from .widget import Widget from ...visuals import AxisVisual class AxisWidget(Widget): """Widget containing an axis Parameters ---------- orientation : str Orientation of the axis, 'left' or 'bottom'. **kwargs : dict Keyword arguments to pass to AxisVisual. """ def __init__(self, orientation='left', **kwargs): if 'tick_direction' not in kwargs: tickdir = {'left': (-1, 0), 'right': (1, 0), 'bottom': (0, 1), 'top': (0, -1)}[orientation] kwargs['tick_direction'] = tickdir self.axis = AxisVisual(**kwargs) self.orientation = orientation self._linked_view = None Widget.__init__(self) self.add_subvisual(self.axis) def on_resize(self, event): """Resize event handler Parameters ---------- event : instance of Event The event. """ self._update_axis() def _update_axis(self): self.axis.pos = self._axis_ends() def _axis_ends(self): r = self.rect if self.orientation == 'left': return np.array([[r.right, r.top], [r.right, r.bottom]]) elif self.orientation == 'bottom': return np.array([[r.left, r.bottom], [r.right, r.bottom]]) elif self.orientation == 'right': return np.array([[r.left, r.top], [r.left, r.bottom]]) elif self.orientation == 'top': return np.array([[r.left, r.top], [r.right, r.top]]) else: raise RuntimeError( 'Orientation %s not supported.' % self.orientation) def link_view(self, view): """Link this axis to a ViewBox This makes it so that the axis's domain always matches the visible range in the ViewBox. Parameters ---------- view : instance of ViewBox The ViewBox to link. """ if view is self._linked_view: return if self._linked_view is not None: self._linked_view.scene.transform.changed.disconnect( self._view_changed) self._linked_view = view view.scene.transform.changed.connect(self._view_changed) self._view_changed() def _view_changed(self, event=None): """Linked view transform has changed; update ticks.""" tr = self.node_transform(self._linked_view.scene) p1, p2 = tr.map(self._axis_ends()) if self.orientation in ('left', 'right'): self.axis.domain = (p1[1], p2[1]) else: self.axis.domain = (p1[0], p2[0]) ����������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/widgets/anchor.py����������������������������������������������������������0000644�0001750�0001751�00000001552�14507104216�020042� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������from ..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.14.1/vispy/scene/widgets/__init__.py��������������������������������������������������������0000644�0001750�0001751�00000001253�14507104216�020325� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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__ = ['AxisWidget', 'Console', 'ColorBarWidget', 'Grid', 'Label', 'ViewBox', 'Widget'] from .console import Console # noqa from .grid import Grid # noqa from .viewbox import ViewBox # noqa from .widget import Widget # noqa from .axis import AxisWidget # noqa from .colorbar import ColorBarWidget # noqa from .label import Label # noqa �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/visuals.py�����������������������������������������������������������������0000644�0001750�0001751�00000023320�14507104216�016605� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 import weakref from .. import visuals from .node import Node from ..visuals.filters import Alpha, PickingFilter from typing import TypeVar _T = TypeVar("_T") class VisualNode(Node): _next_id = 1 _visual_ids = weakref.WeakValueDictionary() def __init__(self, parent=None, name=None): Node.__init__(self, parent=parent, name=name, transforms=self.transforms) self.interactive = False self._opacity_filter = Alpha() self.attach(self._opacity_filter) self._id = VisualNode._next_id VisualNode._visual_ids[self._id] = self VisualNode._next_id += 1 self._picking_filter = PickingFilter(id_=self._id) self.attach(self._picking_filter) def _update_opacity(self): self._opacity_filter.alpha = self._opacity self.update() def _set_clipper(self, node, clipper): """Assign a clipper that is inherited from a parent node. If *clipper* is None, then remove any clippers for *node*. """ if node in self._clippers: self.detach(self._clippers.pop(node)) if clipper is not None: self.attach(clipper) self._clippers[node] = clipper @property def picking(self): """Boolean that determines whether this node (and its children) are drawn in picking mode. """ return self._picking @picking.setter def picking(self, p): for c in self.children: c.picking = p if self._picking == p: return self._picking = p self._picking_filter.enabled = p if p: self.push_gl_state_update(blend=False) else: self.pop_gl_state() def _update_trsys(self, event): """Transform object(s) have changed for this Node; assign these to the visual's TransformSystem. """ doc = self.document_node scene = self.scene_node root = self.root_node self.transforms.visual_transform = self.node_transform(scene) self.transforms.scene_transform = scene.node_transform(doc) self.transforms.document_transform = doc.node_transform(root) Node._update_trsys(self, event) @property def interactive(self): """Whether this widget should be allowed to accept mouse and touch events. """ return self._interactive @interactive.setter def interactive(self, i): self._interactive = i def draw(self): if self.picking and not self.interactive: return self._visual_superclass.draw(self) def create_visual_node(subclass: _T) -> _T: # Create a new subclass of Node. # Decide on new class name clsname = subclass.__name__ if not (clsname.endswith('Visual') and issubclass(subclass, visuals.BaseVisual)): raise RuntimeError('Class "%s" must end with Visual, and must ' 'subclass BaseVisual' % clsname) 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__ self._visual_superclass = subclass subclass.__init__(self, *args, **kwargs) self.unfreeze() VisualNode.__init__(self, parent=parent, name=name) self.freeze() # Create new class cls = type(clsname, (VisualNode, subclass), {'__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 # This is _not_ automated to help with auto-completion of IDEs, # python REPL and IPython. # Explicitly initializing these members allow IDEs to lookup # and provide auto-completion. One problem is the fact that # Docstrings are _not_ looked up correctly by IDEs, since they # are attached programatically in the create_visual_node call. # However, help(vispy.scene.FooVisual) still works Arrow = create_visual_node(visuals.ArrowVisual) Axis = create_visual_node(visuals.AxisVisual) Box = create_visual_node(visuals.BoxVisual) ColorBar = create_visual_node(visuals.ColorBarVisual) Compound = create_visual_node(visuals.CompoundVisual) Cube = create_visual_node(visuals.CubeVisual) Ellipse = create_visual_node(visuals.EllipseVisual) Graph = create_visual_node(visuals.GraphVisual) GridLines = create_visual_node(visuals.GridLinesVisual) GridMesh = create_visual_node(visuals.GridMeshVisual) Histogram = create_visual_node(visuals.HistogramVisual) Image = create_visual_node(visuals.ImageVisual) ComplexImage = create_visual_node(visuals.ComplexImageVisual) InfiniteLine = create_visual_node(visuals.InfiniteLineVisual) InstancedMesh = create_visual_node(visuals.InstancedMeshVisual) Isocurve = create_visual_node(visuals.IsocurveVisual) Isoline = create_visual_node(visuals.IsolineVisual) Isosurface = create_visual_node(visuals.IsosurfaceVisual) Line = create_visual_node(visuals.LineVisual) LinearRegion = create_visual_node(visuals.LinearRegionVisual) LinePlot = create_visual_node(visuals.LinePlotVisual) Markers = create_visual_node(visuals.MarkersVisual) Mesh = create_visual_node(visuals.MeshVisual) MeshNormals = create_visual_node(visuals.MeshNormalsVisual) Plane = create_visual_node(visuals.PlaneVisual) Polygon = create_visual_node(visuals.PolygonVisual) Rectangle = create_visual_node(visuals.RectangleVisual) RegularPolygon = create_visual_node(visuals.RegularPolygonVisual) ScrollingLines = create_visual_node(visuals.ScrollingLinesVisual) Spectrogram = create_visual_node(visuals.SpectrogramVisual) Sphere = create_visual_node(visuals.SphereVisual) SurfacePlot = create_visual_node(visuals.SurfacePlotVisual) Text = create_visual_node(visuals.TextVisual) Tube = create_visual_node(visuals.TubeVisual) # Visual = create_visual_node(visuals.Visual) # Should not be created Volume = create_visual_node(visuals.VolumeVisual) Windbarb = create_visual_node(visuals.WindbarbVisual) XYZAxis = create_visual_node(visuals.XYZAxisVisual) __all__ = [name for (name, obj) in globals().items() if isinstance(obj, type) and issubclass(obj, VisualNode)] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/tests/���������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�015706� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/tests/test_visuals.py������������������������������������������������������0000644�0001750�0001751�00000010415�14507104216�021007� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������import pytest from vispy.scene import visuals, Node from vispy.scene.visuals import VisualNode 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 or obj is VisualNode: 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) def test_push_gl_state(): node = vispy.visuals.MeshVisual() og_gl_state = node._vshare.gl_state.copy() node.push_gl_state(blend=False, depth_test=False) assert node._vshare.gl_state != og_gl_state # preset is always set, unset kwargs should be absent assert node._vshare.gl_state == { "preset": None, "blend": False, "depth_test": False, } node.pop_gl_state() assert node._vshare.gl_state == og_gl_state def test_push_gl_state_update(): node = vispy.visuals.MeshVisual() og_gl_state = node._vshare.gl_state.copy() assert "blend" not in og_gl_state assert node._vshare.gl_state["depth_test"] node.push_gl_state_update(blend=False, depth_test=False) assert node._vshare.gl_state != og_gl_state assert not node._vshare.gl_state["blend"] assert not node._vshare.gl_state["depth_test"] node.pop_gl_state() assert node._vshare.gl_state == og_gl_state def test_pop_empty_gl_state(): node = vispy.visuals.MeshVisual() assert node._prev_gl_state == [] og_gl_state = node._vshare.gl_state.copy() node.pop_gl_state() assert node._vshare.gl_state == og_gl_state def test_update_gl_state(): node = vispy.visuals.MeshVisual() og_gl_state = node._vshare.gl_state.copy() assert og_gl_state og_gl_state["blend"] = False node.update_gl_state(blend=True) # check that the state was updated assert node._vshare.gl_state.pop("blend") != og_gl_state.pop("blend") # the rest of the state should be unchanged assert node._vshare.gl_state == og_gl_state def test_update_gl_state_context_manager(): node = vispy.visuals.MeshVisual() node.set_gl_state(blend=False) og_gl_state = node._vshare.gl_state.copy() with node.update_gl_state(blend=True): # check that the state was updated assert node._vshare.gl_state == {**og_gl_state, "blend": True} # the update should be reverted once out of the context assert node._vshare.gl_state == og_gl_state def test_set_gl_state(): node = vispy.visuals.MeshVisual() node.set_gl_state(blend=False, depth_test=False) # preset is always set, unset kwargs should be absent assert node._vshare.gl_state == { "preset": None, "blend": False, "depth_test": False, } node.set_gl_state(blend=False) # preset is always set, unset kwargs should be absent assert node._vshare.gl_state == {"preset": None, "blend": False} def test_set_gl_state_context_manager(): node = vispy.visuals.MeshVisual() node.set_gl_state(blend=False) og_gl_state = node._vshare.gl_state.copy() with node.set_gl_state(blend=True): # preset is always set, unset kwargs should be absent assert node._vshare.gl_state == {"preset": None, "blend": True} # the update should be reverted once out of the context assert node._vshare.gl_state == og_gl_state @pytest.mark.parametrize("enable_picking", [True, False]) def test_picking_context(enable_picking): mesh = visuals.Mesh() mesh.picking = not enable_picking assert mesh.picking != enable_picking with mesh.set_picking(picking=enable_picking) as p: assert p == enable_picking assert mesh.picking == enable_picking assert mesh.picking != enable_picking ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/tests/test_node.py���������������������������������������������������������0000644�0001750�0001751�00000011510�14507104216�020243� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- from vispy.scene.node import Node from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main, raises) from vispy.visuals.transforms import STTransform import numpy as np class EventCheck(object): def __init__(self, emitter): self._events = [] self.emitter = emitter emitter.connect(self.callback) def callback(self, event): self._events.append(event) @property def events(self): ev = self._events self._events = [] return ev @requires_application() def test_topology(): c = TestingCanvas() assert c.scene.canvas is c with raises(AttributeError): c.foo = 'bar' w = c.central_widget assert w.parent is c.scene assert w.scene_node is c.scene assert w.document_node is c.scene g = w.add_grid() with raises(AttributeError): g.foo = 'bar' grid_check = EventCheck(g.events.children_change) v1 = g.add_view(row=0, col=0) assert v1.parent is g assert v1.scene_node is c.scene assert len(grid_check.events) == 1 v2 = g.add_view(row=1, col=0) assert v2.parent is g assert v2.scene_node is c.scene assert v2.document_node is c.scene assert len(grid_check.events) == 1 n1 = Node() n1_parent_check = EventCheck(n1.events.parent_change) n1_child_check = EventCheck(n1.events.children_change) v1.add(n1) assert len(n1_parent_check.events) == 1 assert n1.parent is v1.scene assert n1.scene_node is v1.scene assert n1.document_node is c.scene n2 = Node(parent=n1) n2_parent_check = EventCheck(n2.events.parent_change) assert n2.parent is n1 assert n2.scene_node is v1.scene assert n2.document_node is c.scene assert len(n1_child_check.events) == 1 assert len(grid_check.events) == 2 v2.add(n1) assert len(grid_check.events) == 2 assert len(n1_parent_check.events) == 1 assert len(n2_parent_check.events) == 1 assert n1.parent is v2.scene assert n2.scene_node is v2.scene assert n2.document_node is c.scene def test_transforms(): # test transform mapping between nodes root = Node() n1 = Node(parent=root) n2 = Node(parent=n1) n3 = Node(parent=root) n4 = Node(parent=n3) n1.transform = STTransform(scale=(0.1, 0.1), translate=(7, 6)) n2.transform = STTransform(scale=(0.2, 0.3), translate=(5, 4)) n3.transform = STTransform(scale=(0.4, 0.5), translate=(3, 2)) n4.transform = STTransform(scale=(0.6, 0.7), translate=(1, 0)) assert np.allclose(n1.transform.map((0, 0))[:2], (7, 6)) assert np.allclose(n1.node_transform(root).map((0, 0))[:2], (7, 6)) assert np.allclose(n2.transform.map((0, 0))[:2], (5, 4)) assert np.allclose(n2.node_transform(root).map((0, 0))[:2], (5*0.1+7, 4*0.1+6)) assert np.allclose(root.node_transform(n1).map((0, 0))[:2], (-7/0.1, -6/0.1)) assert np.allclose(root.node_transform(n2).map((0, 0))[:2], ((-7/0.1-5)/0.2, (-6/0.1-4)/0.3)) # just check that we can assemble transforms correctly mapping across the # scenegraph assert n2.node_path(n4) == ([n2, n1, root], [n3, n4]) assert n4.node_path(n2) == ([n4, n3, root], [n1, n2]) assert n2.node_path(root) == ([n2, n1, root], []) assert root.node_path(n4) == ([root], [n3, n4]) assert n2.node_path_transforms(n4) == [n4.transform.inverse, n3.transform.inverse, n1.transform, n2.transform] assert n4.node_path_transforms(n2) == [n2.transform.inverse, n1.transform.inverse, n3.transform, n4.transform] pts = np.array([[0, 0], [1, 1], [-56.3, 800.2]]) assert np.all(n2.node_transform(n1).map(pts) == n2.transform.map(pts)) assert np.all(n2.node_transform(root).map(pts) == n1.transform.map(n2.transform.map(pts))) assert np.all(n1.node_transform(n3).map(pts) == n3.transform.inverse.map(n1.transform.map(pts))) assert np.all(n2.node_transform(n3).map(pts) == n3.transform.inverse.map( n1.transform.map(n2.transform.map(pts)))) assert np.all(n2.node_transform(n4).map(pts) == n4.transform.inverse.map(n3.transform.inverse.map( n1.transform.map(n2.transform.map(pts))))) # test transforms still work after reparenting n3.parent = n1 assert np.all(n2.node_transform(n4).map(pts) == n4.transform.inverse.map( n3.transform.inverse.map(n2.transform.map(pts)))) # test transform simplification assert np.all(n2.node_transform(n4).map(pts) == n2.node_transform(n4).simplified.map(pts)) run_tests_if_main() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/tests/test_canvas.py�������������������������������������������������������0000644�0001750�0001751�00000010076�14507104216�020577� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy import gloo, scene from vispy.testing import requires_application, TestingCanvas from vispy.visuals.transforms import STTransform import numpy as np import pytest @requires_application() @pytest.mark.parametrize( 'blend_func', [ ('src_alpha', 'one_minus_src_alpha', 'one', 'one_minus_src_alpha'), ('src_alpha', 'one_minus_src_alpha'), None, ]) def test_canvas_render(blend_func): """Test rendering a canvas to an array. Different blending functions are used to test what various Visuals may produce without actually using different types of Visuals. """ with TestingCanvas(size=(125, 125), show=True, title='run') as c: view = c.central_widget.add_view() im1 = np.zeros((100, 100, 4)).astype(np.float32) im1[:, :, 0] = 1 im1[:, :, 3] = 1 im2 = np.zeros((50, 50, 4)).astype(np.float32) im2[:, :, 1] = 1 im2[:, :, 3] = 0.4 # Create the image image1 = scene.visuals.Image(im1, parent=view.scene) image1.transform = STTransform(translate=(20, 20, 0)) image2 = scene.visuals.Image(im2, parent=view.scene) image2.transform = STTransform(translate=(0, 0, -1)) if blend_func: image1.set_gl_state(preset='translucent', blend_func=blend_func) image2.set_gl_state(preset='translucent', blend_func=blend_func) rgba_result = c.render() rgb_result = c.render(alpha=False) # the results should be the same except for alpha np.testing.assert_allclose(rgba_result[..., :3], rgb_result) # the image should have something drawn in it assert not np.allclose(rgba_result[..., :3], 0) # the alpha should not be completely transparent assert not np.allclose(rgba_result[..., 3], 0) if blend_func is None or 'one' in blend_func: # no transparency np.testing.assert_allclose(rgba_result[..., 3], 255) else: # the alpha should have some transparency assert (rgba_result[..., 3] != 255).any() @requires_application() def test_picking_basic(): """Test basic picking behavior. Based on https://github.com/vispy/vispy/issues/2107. """ with TestingCanvas(size=(125, 125), show=True, title='run') as c: view = c.central_widget.add_view() view.margin = 5 # add empty space where there are no visuals view.camera = 'panzoom' x = np.linspace(0, 400, 100) y = np.linspace(0, 200, 100) line = scene.Line(np.array((x, y)).T.astype(np.float32)) line.interactive = True view.add(line) view.camera.set_range() c.render() # initial basic draw for _ in range(2): # run picking twice to make sure it is repeatable # get Visuals on a Canvas point that Line is drawn on picked_visuals = c.visuals_at((100, 25)) assert len(picked_visuals) == 2 assert any(isinstance(vis, scene.ViewBox) for vis in picked_visuals) assert any(isinstance(vis, scene.Line) for vis in picked_visuals) @requires_application() @pytest.mark.parametrize( 'preset', [ 'opaque', 'additive', 'translucent', ]) def test_blend_presets(preset): """Test blending presets render a canvas to an array. Different blending presets are used to test that they properly set blend equations. """ with TestingCanvas(size=(125, 125), show=True, title='run') as c: view = c.central_widget.add_view() im1 = np.zeros((100, 100, 4)).astype(np.float32) im1[:, :, 1] = 1 im1[:, :, 3] = .4 # Create the image image1 = scene.visuals.Image(im1, parent=view.scene) image1.transform = STTransform(translate=(20, 20, -1)) gloo.set_state(blend_equation='min') image1.set_gl_state(preset) rgba_result = c.render() assert not np.allclose(rgba_result[..., :3], 0) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/tests/__init__.py����������������������������������������������������������0000644�0001750�0001751�00000000000�14507104216�020006� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/subscene.py����������������������������������������������������������������0000644�0001750�0001751�00000001064�14507104216�016727� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from .node import Node class SubScene(Node): """A Node subclass that serves as a marker and parent node for certain branches of the scenegraph. SubScene nodes are used as the top-level node for the internal scenes of a canvas and a view box. """ def __init__(self, **kwargs): Node.__init__(self, **kwargs) self.document = self ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/node.py��������������������������������������������������������������������0000644�0001750�0001751�00000046324�14507104216�016055� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import weakref from contextlib import contextmanager from ..util.event import Event, EmitterGroup from ..visuals.transforms import (NullTransform, BaseTransform, ChainTransform, create_transform, TransformSystem) 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. 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 thought 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. transforms : instance of TransformSystem | None The associated transforms. """ # Needed to allow subclasses to repr() themselves before Node.__init__() _name = None def __init__(self, parent=None, name=None, transforms=None): self.name = name self._visible = True self._canvas = None self._document_node = None self._scene_node = None self._opacity = 1.0 self._order = 0 self._picking = False # clippers inherited from parents self._clippers = weakref.WeakKeyDictionary() # {node: clipper} # whether this widget should clip its children self._clip_children = False self._clipper = None self.transforms = (TransformSystem() if transforms is None else transforms) # Add some events to the emitter groups: events = ['canvas_change', 'parent_change', 'children_change', 'transform_change', 'mouse_press', 'mouse_move', 'mouse_release', 'mouse_wheel', 'key_press', 'key_release', 'gesture_zoom', 'gesture_rotate'] # 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])) self._children = [] self._transform = NullTransform() self._parent = None if parent is not None: self.parent = parent self._document = None @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 opacity(self): return self._opacity @opacity.setter def opacity(self, o): self._opacity = o self._update_opacity() def _update_opacity(self): pass def _set_clipper(self, node, clipper): """Assign a clipper that is inherited from a parent node. If *clipper* is None, then remove any clippers for *node*. """ pass @property def clip_children(self): """Boolean indicating whether children of this node will inherit its clipper. """ return self._clip_children @clip_children.setter def clip_children(self, clip): if self._clip_children == clip: return self._clip_children = clip for ch in self.children: ch._set_clipper(self, self.clipper) @property def clipper(self): """A visual filter that can be used to clip visuals to the boundaries of this node. """ return self._clipper @property def order(self): """A value used to determine the order in which nodes are drawn. Greater values are drawn later. Children are always drawn after their parent. """ return self._order @order.setter def order(self, o): self._order = o self.update() @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): """The parent of this node in the scenegraph. Nodes inherit coordinate transformations and some filters (opacity and clipping by default) from their parents. Setting this property assigns a new parent, changing the topology of the scenegraph. May be set to None to remove this node (and its children) from a scenegraph. """ if self._parent is None: return None else: return self._parent() @parent.setter def parent(self, parent): if not isinstance(parent, (Node, type(None))): raise ValueError('Parent must be Node instance or None (got %s).' % parent.__class__.__name__) prev = self.parent if parent is prev: return if prev is not None: prev._remove_child(self) # remove all clippers inherited from parents for k in list(self._clippers): self._set_clipper(k, None) if parent is None: self._set_canvas(None) self._parent = None else: self._set_canvas(parent.canvas) self._parent = weakref.ref(parent) parent._add_child(self) # inherit clippers from parents p = parent while p is not None: if p.clip_children: self._set_clipper(p, p.clipper) p = p.parent self.events.parent_change(new=parent, old=prev) self._update_trsys(None) self.update() def _add_child(self, node): self._children.append(node) self.events.children_change(added=node) node.events.children_change.connect(self.events.children_change) self.events.parent_change.connect(node.events.parent_change) def _remove_child(self, node): self._children.remove(node) self.events.children_change(removed=node) node.events.children_change.disconnect(self.events.children_change) self.events.parent_change.disconnect(node.events.parent_change) def on_parent_change(self, event): """Parent change event handler Parameters ---------- event : instance of Event The event. """ self._scene_node = None def is_child(self, node): """Check if a node is a child of the current node Parameters ---------- node : instance of Node The potential child. Returns ------- child : bool Whether or not the node is a child. """ if node in self.children: return True for c in self.children: if c.is_child(node): return True return False @property def canvas(self): """The canvas in which this node's scenegraph is being drawn.""" if self._canvas is None: return None else: return self._canvas() @property def document_node(self): """The node to be used as the document coordinate system. By default, the document node is `self.root_node`. """ if self._document_node is None: return self.root_node return self._document_node @document_node.setter def document_node(self, doc): self._document_node = doc self._update_transform() @property def scene_node(self): """The first ancestor of this node that is a SubScene instance, or self if no such node exists. """ if self._scene_node is None: from .subscene import SubScene p = self.parent while True: if isinstance(p, SubScene) or p is None: self._scene_node = p and weakref.ref(p) break p = p.parent if self._scene_node is None: self._scene_node = weakref.ref(self) return self._scene_node() @property def root_node(self): node = self while True: p = node.parent if p is None: return node node = p def _set_canvas(self, c): old = self.canvas if old is c: return # Use canvas/framebuffer transforms from canvas self.transforms.canvas = c if c is None: self._canvas = None else: self._canvas = weakref.ref(c) tr = c.transforms self.transforms.canvas_transform = tr.canvas_transform self.transforms.framebuffer_transform = tr.framebuffer_transform # update all children for ch in self.children: ch._set_canvas(c) self.events.canvas_change(old=old, new=c) def update(self): """ Emit an event to inform listeners that properties of this Node have changed. Also request a canvas update. """ self.events.update() c = getattr(self, 'canvas', None) if c is not None: c.update(node=self) @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): # Other nodes might be interested in this information, but turning it # on by default is too expensive. assert isinstance(tr, BaseTransform) if tr is not self._transform: self._transform = tr self._update_trsys(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 _update_trsys(self, event): """Called when has changed. This allows the node and its children to react (notably, VisualNode uses this to update its TransformSystem). Note that this method is only called when one transform is replaced by another; it is not called if an existing transform internally changes its state. """ for ch in self.children: ch._update_trsys(event) self.events.transform_change() self.update() def parent_chain(self): """ Return the list of parents starting from this node. The chain ends at the first node with no 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. 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 If *node* is not a (grand)child of this node, then raise RuntimeError. 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 child.parent is not None: child = child.parent path1.append(child) # Early exit if child is self: return list(reversed(path1)) # Verify that we're not cut off if path1[-1].parent is None: 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]) """ 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 ------- transforms : list A list of Transform instances. """ a, b = self.node_path(node) return ([n.transform for n in a[:-1]] + [n.transform.inverse for n in b])[::-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)) @property def picking(self): """Boolean that determines whether this node (and its children) are drawn in picking mode. """ return self._picking @picking.setter def picking(self, p): for c in self.children: c.picking = p self._picking = p @contextmanager def set_picking(self, *, picking=True): """Context manager to temporarily set picking for this node and its children. Note that this function will not alter the picking mode unless/until the context manager is entered (using the `with` statement). Use :py:attr:`~.picking` for setting the picking mode directly. """ old_picking = self.picking try: self.picking = picking yield self.picking finally: self.picking = old_picking ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/events.py������������������������������������������������������������������0000644�0001750�0001751�00000004637�14507104216�016435� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from ..util.event import Event class SceneMouseEvent(Event): """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, visual): self.mouse_event = event self.visual = visual Event.__init__(self, type=event.type) @property def visual(self): return self._visual @visual.setter def visual(self, v): self._visual = v self._pos = None @property def pos(self): """The position of this event in the local coordinate system of the visual. """ if self._pos is None: tr = self.visual.get_transform('canvas', 'visual') self._pos = tr.map(self.mouse_event.pos) return self._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 @property def scale(self): """The scale of a gesture_zoom event""" try: return self.mouse_event.scale except AttributeError: errmsg = f"SceneMouseEvent type '{self.type}' has no scale" raise TypeError(errmsg) def copy(self): ev = self.__class__(self.mouse_event, self.visual) return ev �������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/canvas.py������������������������������������������������������������������0000644�0001750�0001751�00000054360�14507104216�016402� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import weakref import numpy as np from .. import gloo from .. import app from .visuals import VisualNode from ..visuals.transforms import TransformSystem from ..color import Color from ..util import logger, Frozen from ..util.profiler import Profiler from .subscene import SubScene from .events import SceneMouseEvent from .widgets import Widget class SceneCanvas(app.Canvas, Frozen): """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._scene = None # A default widget that follows the shape of the canvas self._central_widget = None self._draw_order = weakref.WeakKeyDictionary() self._drawing = False self._update_pending = False self._fb_stack = [] self._vp_stack = [] self._mouse_handler = None self.transforms = TransformSystem(canvas=self) self._bgcolor = Color(bgcolor).rgba # Set to True to enable sending mouse events even when no button is # pressed. Disabled by default because it is very expensive. Also # private for now because this behavior / API needs more thought. self._send_hover_events = False 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) self.events.touch.connect(self._process_mouse_event) self.scene = SubScene() self.freeze() @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, node): oldscene = self._scene self._scene = node if oldscene is not None: oldscene._set_canvas(None) oldscene.events.children_change.disconnect(self._update_scenegraph) if node is not None: node._set_canvas(self) node.events.children_change.connect(self._update_scenegraph) @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 @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() def update(self, node=None): """Update the scene Parameters ---------- node : instance of Node Not used. """ # TODO: use node bounds to keep track of minimum drawable area if self._drawing: return # Keep things civil in the node update system. Once an update # has been scheduled, there is no need to flood the event queue # of the backend with additional updates. if not self._update_pending: self._update_pending = True super(SceneCanvas, 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') # Now that a draw event is going to be handled, open up the # scheduling of further updates self._update_pending = False self._draw_scene() def render(self, region=None, size=None, bgcolor=None, crop=None, alpha=True): """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, multiplied by the pixel scaling factor of the canvas (see `pixel_scale`). This argument allows the scene to be rendered at resolutions different from the native canvas resolution. bgcolor : instance of Color | None The background color to use. crop : array-like | None If specified it determines the pixels read from the framebuffer. In the format (x, y, w, h), relative to the region being rendered. alpha : bool If True (default) produce an RGBA array (h, w, 4). If False, remove the Alpha channel and return the RGB array (h, w, 3). This may be useful if blending of various elements requires a solid background to produce the expected visualization. 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. If ``alpha`` is ``False``, then only 3 channels will be returned (RGB). """ self.set_current() # 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:] s = self.pixel_scale size = tuple([int(x * s) for x in 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(bgcolor=bgcolor) result = fbo.read(crop=crop) finally: self.pop_fbo() if not alpha: result = result[..., :3] return result def _draw_scene(self, bgcolor=None): if bgcolor is None: bgcolor = self._bgcolor self.context.clear(color=bgcolor, depth=True) self.draw_visual(self.scene) def draw_visual(self, visual, event=None): """Draw a visual and its children 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. """ prof = Profiler() # make sure this canvas's context is active self.set_current() try: self._drawing = True # get order to draw visuals if visual not in self._draw_order: self._draw_order[visual] = self._generate_draw_order() order = self._draw_order[visual] # draw (while avoiding branches with visible=False) stack = [] invisible_node = None for node, start in order: if start: stack.append(node) if invisible_node is None: if not node.visible: # disable drawing until we exit this node's subtree invisible_node = node else: if hasattr(node, 'draw'): node.draw() prof.mark(str(node)) else: if node is invisible_node: invisible_node = None stack.pop() finally: self._drawing = False def _generate_draw_order(self, node=None): """Return a list giving the order to draw visuals. Each node appears twice in the list--(node, True) appears before the node's children are drawn, and (node, False) appears after. """ if node is None: node = self._scene order = [(node, True)] children = node.children children.sort(key=lambda ch: ch.order) for ch in children: order.extend(self._generate_draw_order(ch)) order.append((node, False)) return order def _update_scenegraph(self, event): """Called when topology of scenegraph has changed.""" self._draw_order.clear() self.update() def _process_mouse_event(self, event): prof = Profiler() # noqa deliver_types = [ 'mouse_press', 'mouse_wheel', 'gesture_zoom', 'gesture_rotate', ] if self._send_hover_events: deliver_types += ['mouse_move'] picked = self._mouse_handler if picked is None: if event.type in deliver_types: picked = self.visual_at(event.pos) # No visual to handle this event; bail out now if picked is None: return # Create an event to pass to the picked visual scene_event = SceneMouseEvent(event=event, visual=picked) # Deliver the event if picked == self._mouse_handler: # If we already have a mouse handler, then no other node may # receive the event if event.type == 'mouse_release': self._mouse_handler = None getattr(picked.events, event.type)(scene_event) else: # If we don't have a mouse handler, then pass the event through # the chain of parents until a node accepts the event. while picked is not None: getattr(picked.events, event.type)(scene_event) if scene_event.handled: if event.type == 'mouse_press': self._mouse_handler = picked break if event.type in deliver_types: # events that are not handled get passed to parent picked = picked.parent scene_event.visual = picked else: picked = None # If something in the scene handled the scene_event, then we mark # the original event accordingly. event.handled = scene_event.handled def visual_at(self, pos): """Return the visual at a given position Parameters ---------- pos : tuple The position in logical coordinates to query. Returns ------- visual : instance of Visual | None The visual at the position, if it exists. """ tr = self.transforms.get_transform('canvas', 'framebuffer') fbpos = tr.map(pos)[:2] try: id_ = self._render_picking((fbpos[0], fbpos[1], 1, 1)) vis = VisualNode._visual_ids.get(id_[0, 0], None) except RuntimeError: # Don't have read_pixels() support for IPython. Fall back to # bounds checking. return self._visual_bounds_at(pos) return vis def _visual_bounds_at(self, pos, node=None): """Find a visual whose bounding rect encompasses *pos*.""" if node is None: node = self.scene for ch in node.children: hit = self._visual_bounds_at(pos, ch) if hit is not None: return hit if (not isinstance(node, VisualNode) or not node.visible or not node.interactive): return None # let nodes know we are picking to handle any special cases (picking meshes) # we can't do this before this or child nodes may be considered visible # which would cause the above 'if' statement to pass when it shouldn't node.picking = True bounds = [node.bounds(axis=i) for i in range(2)] node.picking = False if None in bounds: return None tr = self.scene.node_transform(node).inverse corners = np.array([ [bounds[0][0], bounds[1][0]], [bounds[0][0], bounds[1][1]], [bounds[0][1], bounds[1][0]], [bounds[0][1], bounds[1][1]]]) bounds = tr.map(corners) xhit = bounds[:, 0].min() < pos[0] < bounds[:, 0].max() yhit = bounds[:, 1].min() < pos[1] < bounds[:, 1].max() if xhit and yhit: return node def visuals_at(self, pos, radius=10): """Return a list of visuals within *radius* pixels of *pos*. Visuals are sorted by their proximity to *pos*. Parameters ---------- pos : tuple (x, y) position at which to find visuals. radius : int Distance away from *pos* to search for visuals. """ tr = self.transforms.get_transform('canvas', 'framebuffer') pos = tr.map(pos)[:2] id = self._render_picking((pos[0]-radius, pos[1]-radius, radius * 2 + 1, radius * 2 + 1)) ids = [] seen = set() for i in range(radius): subr = id[radius-i:radius+i+1, radius-i:radius+i+1] subr_ids = set(list(np.unique(subr))) ids.extend(list(subr_ids - seen)) seen |= subr_ids visuals = [VisualNode._visual_ids.get(x, None) for x in ids] return [v for v in visuals if v is not None] def _render_picking(self, crop): """Render the scene in picking mode, returning a 2D array of visual IDs in the area specified by crop. Parameters ---------- crop : array-like The crop (x, y, w, h) of the framebuffer to read. For picking the full canvas is rendered and cropped on read as it is much faster than triggering transform updates across the scene with every click. """ with self._scene.set_picking(): img = self.render(bgcolor=(0, 0, 0, 0), crop=crop) img = img.astype('int32') * [2**0, 2**8, 2**16, 2**24] id_ = img.sum(axis=2).astype('int32') return id_ def on_resize(self, event): """Resize handler Parameters ---------- event : instance of Event The resize event. """ self._update_transforms() if self._central_widget is not None: self._central_widget.size = self.size if len(self._vp_stack) == 0: self.context.set_viewport(0, 0, *self.physical_size) def on_close(self, event): """Close event handler Parameters ---------- event : instance of Event The event. """ self.events.mouse_press.disconnect(self._process_mouse_event) self.events.mouse_move.disconnect(self._process_mouse_event) self.events.mouse_release.disconnect(self._process_mouse_event) self.events.mouse_wheel.disconnect(self._process_mouse_event) self.events.touch.disconnect(self._process_mouse_event) # -------------------------------------------------- transform handling --- def push_viewport(self, viewport): """Push a viewport (x, y, w, h) on the stack. Values must be integers relative to the active framebuffer. 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) try: self.context.set_viewport(*vp) except Exception: self._vp_stack.pop() raise self._update_transforms() def pop_viewport(self): """Pop a viewport from the stack.""" vp = self._vp_stack.pop() # Activate latest if len(self._vp_stack) > 0: self.context.set_viewport(*self._vp_stack[-1]) else: self.context.set_viewport(0, 0, *self.physical_size) self._update_transforms() return vp def push_fbo(self, fbo, offset, csize): """Push an FBO on the stack. This activates the framebuffer and causes subsequent rendering to be written to the framebuffer rather than the canvas's back buffer. This will also set the canvas viewport to cover the boundaries of the framebuffer. Parameters ---------- fbo : instance of FrameBuffer The framebuffer object . offset : tuple The location of the fbo origin relative to the canvas's framebuffer origin. csize : tuple The size of the region in the canvas's framebuffer that should be covered by this framebuffer object. """ self._fb_stack.append((fbo, offset, csize)) try: fbo.activate() h, w = fbo.color_buffer.shape[:2] self.push_viewport((0, 0, w, h)) except Exception: self._fb_stack.pop() raise self._update_transforms() 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._update_transforms() 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] def _update_transforms(self): """Update the canvas's TransformSystem to correct for the current canvas size, framebuffer, and viewport. """ if len(self._fb_stack) == 0: fb_size = fb_rect = None else: fb, origin, fb_size = self._fb_stack[-1] fb_rect = origin + fb_size if len(self._vp_stack) == 0: viewport = None else: viewport = self._vp_stack[-1] self.transforms.configure(viewport=viewport, fbo_size=fb_size, fbo_rect=fb_rect) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/�������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�016157� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/turntable.py�������������������������������������������������������0000644�0001750�0001751�00000013113�14507104216�020531� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 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 from ...util import transforms from .perspective import Base3DRotationCamera 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. When ``elevation`` and ``azimuth`` are set to 0, the camera points along the +y axis. Parameters ---------- fov : float Field of view. 0.0 means orthographic projection, default is 45.0 (some perspective) elevation : float Elevation angle in degrees. The elevation angle represents a rotation of the camera around the current scene x-axis. The camera points along the x-y plane when the angle is 0. azimuth : float Azimuth angle in degrees. The azimuth angle represents a rotation of the camera around the scene z-axis according to the right-hand screw rule. The camera points along the y-z plane when the angle is 0. roll : float Roll angle in degrees. The roll angle represents a rotation of the camera around the current scene y-axis. 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. translate_speed : float Scale factor on translation speed when moving the camera center point. **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=45.0, elevation=30.0, azimuth=30.0, roll=0.0, distance=None, translate_speed=1.0, **kwargs ): super(TurntableCamera, self).__init__(fov=fov, **kwargs) # Set camera attributes self.azimuth = azimuth self.elevation = elevation self.roll = roll self.distance = distance # None means auto-distance self.translate_speed = translate_speed @property def elevation(self): """Get the camera elevation angle in degrees. The camera points along the x-y plane when the angle is 0. """ 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): """Get the camera azimuth angle in degrees. The camera points along the y-z plane when the angle is 0. """ 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): """Get the camera roll angle in degrees.""" 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 _get_rotation_tr(self): """Return a rotation matrix based on camera parameters""" up, forward, right = self._get_dim_vectors() matrix = ( transforms.rotate(self.elevation, -right) .dot(transforms.rotate(self.azimuth, up)) .dot(transforms.rotate(self.roll, forward)) ) return matrix 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) d0, d1 = dist[0], dist[1] dx = (+ d0 * (cro * caz + sro * sel * saz) + d1 * (sro * caz - cro * sel * saz)) * self.translate_speed dy = (+ d0 * (cro * saz - sro * sel * caz) + d1 * (sro * saz + cro * sel * caz)) * self.translate_speed dz = (- d0 * sro * cel + d1 * cro * cel) * self.translate_speed return dx, dy, dz �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/tests/�������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�017321� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/tests/test_perspective.py������������������������������������������0000644�0001750�0001751�00000010351�14507104216�023264� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import sys import numpy as np import pytest from vispy import scene, io from vispy.testing import (requires_application, TestingCanvas, run_tests_if_main) from vispy.testing.image_tester import assert_image_approved @pytest.mark.xfail('darwin' in sys.platform, reason="Differences in OSX rendering") @requires_application() def test_perspective_render(): with TestingCanvas(size=(120, 200)) as canvas: grid = canvas.central_widget.add_grid() imdata = io.load_crate().astype('float32') / 255 views = [] images = [] for i, imethod in enumerate(['impostor', 'subdivide']): v = grid.add_view(row=i, col=0, border_color='white') v.camera = 'turntable' v.camera.fov = 50 v.camera.distance = 30 views.append(v) image = scene.visuals.Image(imdata, method=imethod, grid=(4, 4)) image.transform = scene.STTransform(translate=(-12.8, -12.8), scale=(0.1, 0.1)) v.add(image) images.append(image) image = canvas.render() canvas.close() # Allow many pixels to differ by a small amount--texture sampling and # exact triangle position will differ across platforms. However a # change in perspective or in the widget borders should trigger a # failure. assert_image_approved(image, 'scene/cameras/perspective_test.png', 'perspective test 1: 2 identical views with ' 'correct perspective', px_threshold=20, px_count=60, max_px_diff=200) @requires_application() def test_panzoom_center(): with TestingCanvas(size=(120, 200)) as canvas: grid = canvas.central_widget.add_grid() imdata = io.load_crate().astype('float32') / 255 v = grid.add_view(row=0, col=0) v.camera = 'panzoom' image = scene.visuals.Image(imdata) image.transform = scene.STTransform(translate=(-12.8, -12.8), scale=(0.1, 0.1)) v.add(image) result1 = canvas.render()[..., :3] assert v.camera.center == (0.5, 0.5, 0) v.camera.center = (-12.8, -12.8, 0) result2 = canvas.render()[..., :3] assert not np.allclose(result1, result2) # we moved to the lower-left corner of the image that means only the # upper-right quadrant should have data, the rest is black background np.testing.assert_allclose(result2[100:, :], 0) np.testing.assert_allclose(result2[:, :60], 0) assert not np.allclose(result2[:100, 60:], 0) assert v.camera.center == (-12.8, -12.8, 0) @requires_application() def test_panzoom_gesture_zoom(): with TestingCanvas(size=(120, 200)) as canvas: view = canvas.central_widget.add_view() imdata = io.load_crate().astype('float32') / 255 scene.visuals.Image(imdata, parent=view.scene) view.camera = scene.PanZoomCamera(aspect=1) assert view.camera.rect.size == (1, 1) canvas.events.touch( type="gesture_zoom", pos=(60, 100), scale=-1.0, ) assert view.camera.rect.size == (2, 2) @requires_application() def test_turntable_gesture_zoom(): with TestingCanvas(size=(120, 200)) as canvas: view = canvas.central_widget.add_view() imdata = io.load_crate().astype('float32') / 255 scene.visuals.Image(imdata, parent=view.scene) view.camera = scene.TurntableCamera() initial_scale_factor = view.camera.scale_factor canvas.events.touch( type="gesture_zoom", pos=(60, 100), scale=-1.0, ) assert view.camera.scale_factor == 2 * initial_scale_factor run_tests_if_main() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/tests/test_link.py�������������������������������������������������0000644�0001750�0001751�00000003114�14507104216�021667� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from vispy.scene.widgets import ViewBox from vispy.testing import run_tests_if_main def test_turntable_camera_link(): vbs = [ViewBox(camera='turntable') for _ in range(3)] cams = [vb.camera for vb in vbs] for cam in cams: cam.elevation = 45.0 cam.azimuth = 120.0 cam.scale_factor = 4.0 cams[0].link(cams[1]) cams[0].link(cams[2], props=['azimuth', 'elevation']) cams[1].elevation = 30.0 cams[1].azimuth = 90.0 cams[1].scale_factor = 2.0 assert cams[0].elevation == 30.0 assert cams[0].azimuth == 90.0 assert cams[0].scale_factor == 2.0 assert cams[2].elevation == 30.0 assert cams[2].azimuth == 90.0 assert cams[2].scale_factor == 4.0 def test_panzoom_link(): vbs = [ViewBox(camera='panzoom') for _ in range(4)] cams = [vb.camera for vb in vbs] for cam in cams: cam.rect = (0, 0, 100, 100) cams[0].link(cams[1]) cams[0].link(cams[2], axis='x') cams[0].link(cams[3], axis='y') cams[1].rect = (-20, -20, 130, 130) assert cams[0].rect.pos == (-20, -20) and cams[0].rect.size == (130, 130) assert cams[2].rect.pos == (-20, 0) and cams[2].rect.size == (130, 100) assert cams[3].rect.pos == (0, -20) and cams[3].rect.size == (100, 130) run_tests_if_main() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/tests/test_cameras.py����������������������������������������������0000644�0001750�0001751�00000002054�14507104216�022347� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import numpy as np import pytest from vispy.scene.cameras import TurntableCamera from vispy.testing import run_tests_if_main @pytest.mark.parametrize( "elevation, azimuth, roll, expected", [ [0, 0, 0, np.eye(4)], [90, 0, 0, [[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]], [0, 90, 0, [[0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]], [0, 0, 90, [[0, 0, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]], ], ) def test_turntable_camera_transform(elevation, azimuth, roll, expected): camera = TurntableCamera(elevation=elevation, azimuth=azimuth, roll=roll) matrix = camera._get_rotation_tr() np.testing.assert_allclose(matrix, expected, atol=1e-5) run_tests_if_main() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/tests/__init__.py��������������������������������������������������0000644�0001750�0001751�00000000000�14507104216�021421� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/perspective.py�����������������������������������������������������0000644�0001750�0001751�00000027077�14507104216�021100� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division import math import numpy as np from .base_camera import BaseCamera from ...util import keys, transforms from ...visuals.transforms import MatrixTransform 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 = MatrixTransform() # 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() elif event.type == 'gesture_zoom': s = 1 - event.scale 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) if (w == 0) or (h == 0): return # 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 == 0) or (h == 0): return 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.depth_value 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_press': event.handled = True elif event.type == 'mouse_move': if event.press_event is None: return if 1 in event.buttons and 2 in event.buttons: 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 or len(self._event_value) == 2: 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): 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]) pos = -self._actual_distance * forward scale = [1.0/a for a in self._flip_factors] self.transform.matrix = np.linalg.multi_dot(( transforms.affine_map(pp1, pp2).T, transforms.translate(pos), self._get_rotation_tr(), transforms.scale(scale), transforms.translate(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.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 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/panzoom.py���������������������������������������������������������0000644�0001750�0001751�00000025326�14507104216�020225� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 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 from .base_camera import BaseCamera from ...geometry import Rect from ...visuals.transforms import STTransform, MatrixTransform DEFAULT_RECT_TUPLE = (0, 0, 1, 1) 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=DEFAULT_RECT_TUPLE, aspect=None, **kwargs): super(PanZoomCamera, self).__init__(**kwargs) self.transform = STTransform() self.tf_mat = MatrixTransform() # Set camera attributes self.aspect = aspect self._rect = 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. """ # Init some variables center = center if (center is not None) else self.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] # Make a new object (copy), so that allocation will # trigger view_changed: rect = 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. This is expressed in the coordinate system of the scene. See :class:`~vispy.geometry.rect.Rect` for different ways this can be specified. 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): rect = Rect(*args) else: rect = Rect(args) if self._rect != rect: self._rect = rect self.view_changed() @property def center(self): rect = self._rect return (*rect.center, 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 = Rect(self.rect) or Rect(*DEFAULT_RECT_TUPLE) rect.center = center[:2] 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) event.handled = True elif event.type == 'gesture_zoom': center = self._scene_transform.imap(event.pos) self.zoom(1 - event.scale, center) event.handled = True 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) event.handled = True 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) event.handled = True else: event.handled = False elif event.type == 'mouse_press': # accept the event if it is button 1 or 2. # This is required in order to receive future events event.handled = event.button in [1, 2] else: event.handled = False 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.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, update=False) # 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': self.tf_mat.matrix = self.transform.as_matrix().matrix else: rr = self._real_rect 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 self.tf_mat.set_mapping(np.array(pp2), np.array(pp1)) # Set on viewbox self._set_scene_transform(self.tf_mat) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/magnify.py���������������������������������������������������������0000644�0001750�0001751�00000012413�14507104216�020165� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 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 from .panzoom 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): """The 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): """The 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.14.1/vispy/scene/cameras/fly.py�������������������������������������������������������������0000644�0001750�0001751�00000037727�14507104216�017344� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 .perspective import PerspectiveCamera 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. """ # Using _rotation1 and _rotation2 for camera states instead of _rotation _state_props = PerspectiveCamera._state_props + ('rotation1', 'rotation2') def __init__(self, fov=60, rotation=None, **kwargs): # Motion speed vector self._speed = np.zeros((6,), 'float64') self._distance = None # 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.rotation1 = rotation.normalize() 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): print("rotation.setter called, use rotation1.setter instead") @property def rotation1(self): """rotation1 records confirmed camera rotation""" return self._rotation1 @rotation1.setter def rotation1(self, value): assert isinstance(value, Quaternion) self._rotation1 = value.normalize() @property def rotation2(self): """rotation2 records on going camera rotation.""" return self._rotation2 @rotation2.setter def rotation2(self, value): assert isinstance(value, Quaternion) self._rotation2 = value.normalize() @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): """The 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 event.handled = True def viewbox_mouse_event(self, event): """The 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_press': event.handled = True if event.type == 'mouse_release': # Reset self._event_value = None # Apply rotation self._rotation1 = (self._rotation2 * self._rotation1).normalize() self._rotation2 = Quaternion() event.handled = True 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() event.handled = True 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)) event.handled = True # 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) �����������������������������������������vispy-0.14.1/vispy/scene/cameras/base_camera.py�����������������������������������������������������0000644�0001750�0001751�00000044725�14507104216�020770� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division from ...util import keys from ..node import Node from ...visuals.transforms import (STTransform, MatrixTransform, NullTransform, TransformCache) def nested_getattr(obj, names): for name in names: obj = getattr(obj, name) return obj def nested_setattr(obj, names, val): target = nested_getattr(obj, names[:-1]) setattr(target, names[-1], val) 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 = MatrixTransform() 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 self._depth_value = 1e6 # bit+depth >= 24, otherwise should do 3e3 # 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 @property def depth_value(self): """The depth value to use in orthographic and perspective projection For orthographic projections, ``depth_value`` is the distance between the near and far clipping planes. For perspective projections, it is the ratio between the near and far clipping plane distances. GL has a fixed amount of precision in the depth buffer, and a fixed constant will not work for both a very large range and very high precision. This property provides the user a way to override the default value if necessary. """ return self._depth_value @depth_value.setter def depth_value(self, value): value = float(value) if value <= 0: raise ValueError('depth value must be positive') self._depth_value = value self.view_changed() def _depth_to_z(self, depth): """Get the z-coord, given the depth value.""" val = self.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.gesture_zoom.connect(self.viewbox_mouse_event) viewbox.events.gesture_rotate.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.gesture_zoom.disconnect(self.viewbox_mouse_event) viewbox.events.gesture_rotate.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 0 <= fov <= 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, props=None): """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. Parameters ---------- props : list of strings | None List of properties to include in the returned dict. If None, all of camera state is returned. """ if props is None: props = self._state_props state = {} for key in props: # We support tuple keys to accomodate camera linking. if isinstance(key, tuple): state[key] = nested_getattr(self, key) else: state[key] = getattr(self, key) return state 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. """ state = state or {} state.update(kwargs) # In first pass, process tuple keys which select subproperties. This # is an undocumented feature used for selective linking of camera state. # # Subproperties are handled by first copying old value of the root # property, then setting the subproperty on this copy, and finally # assigning the copied object back to the camera property. There needs # to be an assignment of the root property so setters are called and # update is triggered. for key in list(state.keys()): if isinstance(key, tuple): key1 = key[0] if key1 not in state: root_prop = getattr(self, key1) # We make copies by passing the old object to the type's # constructor. This needs to be supported as is the case in # e.g. the geometry.Rect class. state[key1] = root_prop.__class__(root_prop) nested_setattr(state[key1], key[1:], state[key]) # In second pass, assign the new root properties. for key, val in state.items(): if isinstance(key, tuple): continue 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, props=None, axis=None): """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. props : list of strings | tuple of strings | None List of camera state properties to keep in sync between the two cameras. If None, all of camera state is kept in sync. axis : "x" | "y" | None An axis to link between two PanZoomCamera instances. If not None, view limits in the selected axis only will be kept in sync between the cameras. """ if axis is not None: props = props or [] if axis == "x": props += [("rect", "left"), ("rect", "right")] elif axis == "y": props += [("rect", "bottom"), ("rect", "top")] else: raise ValueError("Axis can be 'x' or 'y', not %r" % axis) if props is None: props = self._state_props cam1, cam2 = self, camera while cam1 in cam2._linked_cameras: del cam2._linked_cameras[cam1] while cam2 in cam1._linked_cameras: del cam1._linked_cameras[cam2] # Link both ways cam1._linked_cameras[cam2] = props cam2._linked_cameras[cam1] = props # 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. """ pass def on_canvas_change(self, event): """Canvas change event handler Parameters ---------- event : instance of Event The event. """ # Connect key events from canvas to camera. # TODO: canvas should keep track of a single node with keyboard focus. if event.old is not None: event.old.events.key_press.disconnect(self.viewbox_key_event) event.old.events.key_release.disconnect(self.viewbox_key_event) if event.new is not None: event.new.events.key_press.connect(self.viewbox_key_event) event.new.events.key_release.connect(self.viewbox_key_event) def viewbox_key_event(self, event): """The 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]) # Mark the transform dynamic so that it will not be collapsed with # others self._scene_transform.dynamic = True # 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 linked_props = self._linked_cameras[cam] cam.set_state(self.get_state(linked_props)) finally: cam._linked_cameras_no_update = None �������������������������������������������vispy-0.14.1/vispy/scene/cameras/arcball.py���������������������������������������������������������0000644�0001750�0001751�00000007026�14507104216�020137� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 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 from ...util import transforms from ...util.quaternion import Quaternion from ...visuals.transforms import MatrixTransform from .perspective import Base3DRotationCamera 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. translate_speed : float Scale factor on translation speed when moving the camera center point. **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 + ('_quaternion',) def __init__(self, fov=45.0, distance=None, translate_speed=1.0, **kwargs): super(ArcballCamera, self).__init__(fov=fov, **kwargs) # Set camera attributes self._quaternion = Quaternion() self.distance = distance # None means auto-distance self.translate_speed = translate_speed 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 _get_rotation_tr(self): """Return a rotation matrix based on camera parameters""" rot, x, y, z = self._quaternion.get_axis_angle() return transforms.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 = MatrixTransform() tr.rotate(180 * rot / np.pi, (x, y, z)) dx, dz, dy = np.dot(tr.matrix[:3, :3], (dist[0], dist[1], 0.)) * self.translate_speed 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)) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/_base.py�����������������������������������������������������������0000644�0001750�0001751�00000002375�14507104216�017612� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from .base_camera import BaseCamera from .perspective import PerspectiveCamera from .panzoom import PanZoomCamera from .arcball import ArcballCamera from .turntable import TurntableCamera from .fly import FlyCamera 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())) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/cameras/__init__.py��������������������������������������������������������0000644�0001750�0001751�00000002334�14507104216�020273� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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. """ __all__ = ['ArcballCamera', 'BaseCamera', 'FlyCamera', 'MagnifyCamera', 'Magnify1DCamera', 'PanZoomCamera', 'TurntableCamera'] from ._base import make_camera # noqa from .base_camera import BaseCamera # noqa from .panzoom import PanZoomCamera # noqa from .arcball import ArcballCamera # noqa from .turntable import TurntableCamera # noqa from .fly import FlyCamera # noqa from .magnify import MagnifyCamera, Magnify1DCamera # noqa ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/scene/__init__.py����������������������������������������������������������������0000644�0001750�0001751�00000002717�14507104216�016665� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 """ 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 ..visuals import filters # noqa from . import widgets # noqa from . import cameras # noqa from .node import Node # noqa �������������������������������������������������vispy-0.14.1/vispy/plot/����������������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�014425� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/plot/tests/����������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�015567� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/plot/tests/test_plot.py����������������������������������������������������������0000644�0001750�0001751�00000003262�14507104216�020162� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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) from vispy.visuals.axis import AxisVisual from unittest import mock @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)) @requires_application() def test_plot_widget_axes(): """Test that the axes domains are updated correctly when a figure is first drawn""" fig = vp.Fig(size=(800, 800), show=False) point = (0, 100) fig[0, 0].plot((point, point)) # mocking the AxisVisual domain.setter domain_setter = mock.Mock(wraps=AxisVisual.domain.fset) mock_property = AxisVisual.domain.setter(domain_setter) with mock.patch.object(AxisVisual, "domain", mock_property): # note: fig.show() must be called for this test to work... otherwise # Grid._update_child_widget_dim is not triggered and the axes aren't updated fig.show(run=False) # currently, the AxisWidget adds a buffer of 5% of the # full range to either end of the axis domain buffer = (point[1] - point[0]) * 0.05 expectation = [point[0] - buffer, point[1] + buffer] for call in domain_setter.call_args_list: assert [round(x, 2) for x in call[0][1]] == expectation run_tests_if_main() ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/plot/tests/__init__.py�����������������������������������������������������������0000644�0001750�0001751�00000000000�14507104216�017667� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/plot/plotwidget.py���������������������������������������������������������������0000644�0001750�0001751�00000044243�14507104216�017171� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from ..import scene from ..io import read_mesh from ..geometry import MeshData __all__ = ['PlotWidget'] class PlotWidget(scene.Widget): """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]``. """ def __init__(self, *args, **kwargs): self._fg = kwargs.pop('fg_color', 'k') self.grid = None self.camera = None self.title = None self.title_widget = None self.yaxis = None self.xaxis = None self.xlabel = None self.ylabel = None self._configured = False self.visuals = [] self.section_y_x = None self.cbar_top = None self.cbar_bottom = None self.cbar_left = None self.cbar_right = None super(PlotWidget, self).__init__(*args, **kwargs) self.grid = self.add_grid(spacing=0, margin=10) self.title = scene.Label("", font_size=16, color="#ff0000") def _configure_2d(self, fg_color=None): if self._configured: return if fg_color is None: fg = self._fg else: fg = fg_color # c0 c1 c2 c3 c4 c5 c6 # r0 +---------+-------+-------+-------+-------+---------+---------+ # | | | title | | | # r1 | +-----------------------+-------+---------+ | # | | | cbar | | | # r2 | +-------+-------+-------+-------+---------+ | # | | cbar | ylabel| yaxis | view | cbar | padding | # r3 | padding +-------+-------+-------+-------+---------+ | # | | | xaxis | | | # r4 | +-----------------------+-------+---------+ | # | | | xlabel| | | # r5 | +-----------------------+-------+---------+ | # | | | cbar | | | # r6 |---------+-----------------------+-------+---------+---------| # | padding | # +---------+-----------------------+-------+---------+---------+ # padding left padding_left = self.grid.add_widget(None, row=0, row_span=5, col=0) padding_left.width_min = 30 padding_left.width_max = 60 # padding right padding_right = self.grid.add_widget(None, row=0, row_span=5, col=6) padding_right.width_min = 30 padding_right.width_max = 60 # padding right padding_bottom = self.grid.add_widget(None, row=6, col=0, col_span=6) padding_bottom.height_min = 20 padding_bottom.height_max = 40 # row 0 # title - column 4 to 5 self.title_widget = self.grid.add_widget(self.title, row=0, col=4) self.title_widget.height_min = self.title_widget.height_max = 40 # row 1 # colorbar - column 4 to 5 self.cbar_top = self.grid.add_widget(None, row=1, col=4) self.cbar_top.height_max = 1 # row 2 # colorbar_left - column 1 # ylabel - column 2 # yaxis - column 3 # view - column 4 # colorbar_right - column 5 self.cbar_left = self.grid.add_widget(None, row=2, col=1) self.cbar_left.width_max = 1 self.ylabel = scene.Label("", rotation=-90) ylabel_widget = self.grid.add_widget(self.ylabel, row=2, col=2) ylabel_widget.width_max = 1 self.yaxis = scene.AxisWidget(orientation='left', text_color=fg, axis_color=fg, tick_color=fg) yaxis_widget = self.grid.add_widget(self.yaxis, row=2, col=3) yaxis_widget.width_max = 40 # row 3 # xaxis - column 4 self.xaxis = scene.AxisWidget(orientation='bottom', text_color=fg, axis_color=fg, tick_color=fg) xaxis_widget = self.grid.add_widget(self.xaxis, row=3, col=4) xaxis_widget.height_max = 40 self.cbar_right = self.grid.add_widget(None, row=2, col=5) self.cbar_right.width_max = 1 # row 4 # xlabel - column 4 self.xlabel = scene.Label("") xlabel_widget = self.grid.add_widget(self.xlabel, row=4, col=4) xlabel_widget.height_max = 40 # row 5 self.cbar_bottom = self.grid.add_widget(None, row=5, col=4) self.cbar_bottom.height_max = 1 # This needs to be added to the grid last (to fix #1742) self.view = self.grid.add_view(row=2, col=4, border_color='grey', bgcolor="#efefef") self.view.camera = 'panzoom' self.camera = self.view.camera self._configured = True self.xaxis.link_view(self.view) self.yaxis.link_view(self.view) def _configure_3d(self): if self._configured: return self.view = self.grid.add_view(row=0, col=0, border_color='grey', bgcolor="#efefef") self.view.camera = 'turntable' self.camera = self.view.camera self._configured = True 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. """ self._configure_2d() hist = scene.Histogram(data, bins, color, orientation) self.view.add(hist) self.view.camera.set_range() return hist def image(self, data, cmap='cubehelix', clim='auto', fg_color=None, **kwargs): """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. fg_color : Color or None Sets the plot foreground color if specified. kwargs : keyword arguments. More args to pass to :class:`~vispy.visuals.image.Image`. Returns ------- image : instance of Image The image. Notes ----- The colormap is only used if the image pixels are scalars. """ self._configure_2d(fg_color) image = scene.Image(data, cmap=cmap, clim=clim, **kwargs) self.view.add(image) self.view.camera.aspect = 1 self.view.camera.set_range() 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, shading='auto'): """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. shading : str Shading to use, can be None, 'smooth', 'flat', or 'auto'. Default ('auto') will use None if face_colors is set, and 'smooth' otherwise. Returns ------- mesh : instance of Mesh The mesh. """ self._configure_3d() if shading == 'auto': shading = 'smooth' if face_colors is not None: shading = None 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, vertex_colors=vertex_colors, face_colors=face_colors) mesh = scene.Mesh(meshdata=meshdata, vertex_colors=vertex_colors, face_colors=face_colors, color=color, shading=shading) self.view.add(mesh) self.view.camera.set_range() return mesh def plot(self, data, color='k', symbol=None, line_kind='-', width=1., marker_size=10., edge_color='k', face_color='b', edge_width=1., title=None, xlabel=None, ylabel=None, connect='strip'): """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. title : str | None The title string to be displayed above the plot xlabel : str | None The label to display along the bottom axis ylabel : str | None The label to display along the left axis. connect : str | array Determines which vertices are connected by lines. Returns ------- line : instance of LinePlot The line plot. See also -------- LinePlot """ self._configure_2d() line = scene.LinePlot(data, connect=connect, 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.view.add(line) self.view.camera.set_range() self.visuals.append(line) if title is not None: self.title.text = title if xlabel is not None: self.xlabel.text = xlabel if ylabel is not None: self.ylabel.text = ylabel return line def spectrogram(self, x, n_fft=256, step=None, fs=1., window='hann', normalize=False, 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. normalize : bool Normalization of spectrogram values across frequencies. 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 """ self._configure_2d() # XXX once we have axes, we should use "fft_freqs", too spec = scene.Spectrogram(x, n_fft, step, fs, window, normalize, color_scale, cmap, clim) self.view.add(spec) self.view.camera.set_range() return spec def volume(self, vol, clim=None, method='mip', threshold=None, cmap='grays', **kwargs): """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. kwargs : keyword arguments. More args to pass to :class:`~vispy.visuals.volume.Volume`. Returns ------- volume : instance of Volume The volume visualization. See also -------- Volume """ self._configure_3d() volume = scene.Volume(vol, clim, method, threshold, cmap=cmap, **kwargs) self.view.add(volume) self.view.camera.set_range() return volume def surface(self, zdata, **kwargs): """Show a 3D surface plot. Extra keyword arguments are passed to `SurfacePlot()`. Parameters ---------- zdata : array-like A 2D array of the surface Z values. """ self._configure_3d() surf = scene.SurfacePlot(z=zdata, **kwargs) self.view.add(surf) self.view.camera.set_range() return surf def colorbar(self, cmap, position="right", label="", clim=("", ""), border_width=0.0, border_color="black", **kwargs): """Show a ColorBar Parameters ---------- cmap : str | vispy.color.ColorMap Either the name of the ColorMap to be used from the standard set of names (refer to `vispy.color.get_colormap`), or a custom ColorMap object. The ColorMap is used to apply a gradient on the colorbar. position : {'left', 'right', 'top', 'bottom'} The position of the colorbar with respect to the plot. 'top' and 'bottom' are placed horizontally, while 'left' and 'right' are placed vertically label : str The label that is to be drawn with the colorbar that provides information about the colorbar. clim : tuple (min, max) the minimum and maximum values of the data that is given to the colorbar. This is used to draw the scale on the side of the colorbar. border_width : float (in px) The width of the border the colormap should have. This measurement is given in pixels border_color : str | vispy.color.Color The color of the border of the colormap. This can either be a str as the color's name or an actual instace of a vipy.color.Color Returns ------- colorbar : instance of ColorBarWidget See also -------- ColorBarWidget """ self._configure_2d() cbar = scene.ColorBarWidget(orientation=position, label=label, cmap=cmap, clim=clim, border_width=border_width, border_color=border_color, **kwargs) CBAR_LONG_DIM = 50 if cbar.orientation == "bottom": self.grid.remove_widget(self.cbar_bottom) self.cbar_bottom = self.grid.add_widget(cbar, row=5, col=4) self.cbar_bottom.height_max = \ self.cbar_bottom.height_max = CBAR_LONG_DIM elif cbar.orientation == "top": self.grid.remove_widget(self.cbar_top) self.cbar_top = self.grid.add_widget(cbar, row=1, col=4) self.cbar_top.height_max = self.cbar_top.height_max = CBAR_LONG_DIM elif cbar.orientation == "left": self.grid.remove_widget(self.cbar_left) self.cbar_left = self.grid.add_widget(cbar, row=2, col=1) self.cbar_left.width_max = self.cbar_left.width_min = CBAR_LONG_DIM else: # cbar.orientation == "right" self.grid.remove_widget(self.cbar_right) self.cbar_right = self.grid.add_widget(cbar, row=2, col=5) self.cbar_right.width_max = \ self.cbar_right.width_min = CBAR_LONG_DIM return cbar �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/plot/fig.py����������������������������������������������������������������������0000644�0001750�0001751�00000003305�14507104216�015546� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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. **kwargs : dict Keywoard arguments to pass to `SceneCanvas` base class. 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], [1, 0]]) 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, keys='interactive', **kwargs): self._plot_widgets = [] self._grid = None # initialize before the freeze occurs super(Fig, self).__init__(bgcolor=bgcolor, keys=keys, show=show, size=size, **kwargs) self._grid = self.central_widget.add_grid() self._grid._default_class = PlotWidget @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.14.1/vispy/plot/__init__.py�����������������������������������������������������������������0000644�0001750�0001751�00000001753�14507104216�016545� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 # noqa from .plotwidget import PlotWidget # noqa from ..scene import * # noqa ���������������������vispy-0.14.1/vispy/io/������������������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�014056� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/io/wavefront.py������������������������������������������������������������������0000644�0001750�0001751�00000027775�14507104216�016466� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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. See `wikipedia `_ for more details. 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 gzip import GzipFile from os import path as op 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): """Entry point for reading OBJ files. Parameters ---------- fname : str The name of the file to read. """ # Open file fmt = op.splitext(fname)[1].lower() assert fmt in ('.obj', '.gz') opener = open if fmt == '.obj' else GzipFile 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='', reshape_faces=True): """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') reshape_faces : bool Reshape the `faces` array to (Nf, 3). Set to `False` if you need to write a mesh with non triangular faces. """ # 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 GzipFile f = opener(fname, 'wb') try: writer = WavefrontWriter(f) writer.writeMesh(vertices, faces, normals, texcoords, name, reshape_faces=reshape_faces) 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='', reshape_faces=True): """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 = True if reshape_faces: Nfaces = faces.size // 3 faces = faces.reshape((Nfaces, 3)) else: is_triangular = np.array([len(f) == 3 for f in faces]) if not(np.all(is_triangular)): logger.warning('''Faces doesn't appear to be triangular, be advised the file cannot be read back in vispy''') # 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.14.1/vispy/io/tests/������������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�015220� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/io/tests/test_io.py��������������������������������������������������������������0000644�0001750�0001751�00000010177�14507104216�017247� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 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 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, requires_ssl) temp_dir = _TempDir() @requires_ssl() 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 test_wavefront_non_triangular(): """Test wavefront writing with non-triangular faces""" vertices = np.array([[0.5, 1.375, 0.], [0.5, 0.625, 0.], [3.25, 1., 0.], [1., 0.375, 0.], [2., 0.375, 0.], [1.5, 0.625, 0.], [1.5, 1.375, 0.], [1., 1.625, 0.], [2., 1.625, 0.]]) faces = np.array([[1, 0, 7, 6, 5, 3], [4, 5, 6, 8, 2]], dtype=object) fname_out = op.join(temp_dir, 'temp.obj') write_mesh(fname_out, vertices=vertices, faces=faces, normals=None, texcoords=None, overwrite=True, reshape_faces=False) assert_raises(RuntimeError, read_mesh, fname_out) with open(fname_out, 'r+') as out_file: lines = out_file.readlines() assert lines[-1].startswith('f 5 6 7 9 3') assert lines[-2].startswith('f 2 1 8 7 6 4') def test_meshio(): """Test meshio i/o""" vertices = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.], [-.0, 1.0, 0.], [1.0, 1.0, 0.]]) faces = np.array([[0, 1, 3], [1, 2, 3]]) fname_out = op.join(temp_dir, 'temp.vtk') write_mesh(fname_out, vertices=vertices, faces=faces, normals=None, texcoords=None, overwrite=True, reshape_faces=False) out_vertices, out_faces, _, _ = read_mesh(fname_out) assert np.all(np.abs(out_vertices - vertices) < 1.0e-14) assert np.all(out_faces == faces) 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.14.1/vispy/io/tests/test_image.py�����������������������������������������������������������0000644�0001750�0001751�00000003025�14507104216�017714� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/io/tests/__init__.py�������������������������������������������������������������0000644�0001750�0001751�00000000000�14507104216�017320� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/io/stl.py������������������������������������������������������������������������0000644�0001750�0001751�00000013455�14507104216�015243� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 2015 Michael Dawson-Haggerty # Distributed under the MIT License. # Copied from the trimesh project. # See https://github.com/mikedh/trimesh for more information. # See https://github.com/mikedh/trimesh/blob/master/LICENSE.md for # the license. import numpy as np class HeaderError(Exception): # the exception raised if an STL file object doesn't match its header pass # define a numpy datatype for the data section of a binary STL file _stl_dtype = np.dtype([('normals', np.float32, (3)), ('vertices', np.float32, (3, 3)), ('attributes', np.uint16)]) # define a numpy datatype for the header of a binary STL file _stl_dtype_header = np.dtype([('header', np.void, 80), ('face_count', np.int32)]) def load_stl(file_obj, file_type=None): """ Load an STL file from a file object. Parameters ---------- file_obj: open file- like object file_type: not used Returns ------- loaded: kwargs for a Trimesh constructor with keys: vertices: (n,3) float, vertices faces: (m,3) int, indexes of vertices face_normals: (m,3) float, normal vector of each face """ # save start of file obj file_pos = file_obj.tell() try: # check the file for a header which matches the file length # if that is true, it is almost certainly a binary STL file # if the header doesn't match the file length a HeaderError will be # raised return load_stl_binary(file_obj) except HeaderError: # move the file back to where it was initially file_obj.seek(file_pos) # try to load the file as an ASCII STL # if the header doesn't match the file length a HeaderError will be # raised return load_stl_ascii(file_obj) def load_stl_binary(file_obj): """ Load a binary STL file from a file object. Parameters ---------- file_obj: open file- like object Returns ------- loaded: kwargs for a Trimesh constructor with keys: vertices: (n,3) float, vertices faces: (m,3) int, indexes of vertices face_normals: (m,3) float, normal vector of each face """ # the header is always 84 bytes long, we just reference the dtype.itemsize # to be explicit about where that magical number comes from header_length = _stl_dtype_header.itemsize header_data = file_obj.read(header_length) if len(header_data) < header_length: raise HeaderError('Binary STL file not long enough to contain header!') header = np.fromstring(header_data, dtype=_stl_dtype_header) # now we check the length from the header versus the length of the file # data_start should always be position 84, but hard coding that felt ugly data_start = file_obj.tell() # this seeks to the end of the file # position 0, relative to the end of the file 'whence=2' file_obj.seek(0, 2) # we save the location of the end of the file and seek back to where we # started from data_end = file_obj.tell() file_obj.seek(data_start) # the binary format has a rigidly defined structure, and if the length # of the file doesn't match the header, the loaded version is almost # certainly going to be garbage. len_data = data_end - data_start len_expected = header['face_count'] * _stl_dtype.itemsize # this check is to see if this really is a binary STL file. # if we don't do this and try to load a file that isn't structured properly # we will be producing garbage or crashing hard # so it's much better to raise an exception here. if len_data != len_expected: raise HeaderError('Binary STL has incorrect length in header!') # all of our vertices will be loaded in order due to the STL format, # so faces are just sequential indices reshaped. faces = np.arange(header['face_count'] * 3).reshape((-1, 3)) blob = np.fromstring(file_obj.read(), dtype=_stl_dtype) result = {'vertices': blob['vertices'].reshape((-1, 3)), 'face_normals': blob['normals'].reshape((-1, 3)), 'faces': faces} return result def load_stl_ascii(file_obj): """ Load an ASCII STL file from a file object. Parameters ---------- file_obj: open file- like object Returns ------- loaded: kwargs for a Trimesh constructor with keys: vertices: (n,3) float, vertices faces: (m,3) int, indexes of vertices face_normals: (m,3) float, normal vector of each face """ # header (not used by this function) file_obj.readline() text = file_obj.read() if hasattr(text, 'decode'): text = text.decode('utf-8') text = text.lower().split('endsolid')[0] blob = np.array(text.split()) # there are 21 'words' in each face face_len = 21 face_count = len(blob) / face_len if (len(blob) % face_len) != 0: raise HeaderError('Incorrect number of values in STL file!') face_count = int(face_count) # this offset is to be added to a fixed set of indices that is tiled offset = face_len * np.arange(face_count).reshape((-1, 1)) normal_index = np.tile([2, 3, 4], (face_count, 1)) + offset vertex_index = np.tile( [8, 9, 10, 12, 13, 14, 16, 17, 18], (face_count, 1)) + offset # faces are groups of three sequential vertices, as vertices are not # references faces = np.arange(face_count * 3).reshape((-1, 3)) face_normals = blob[normal_index].astype(np.float64) vertices = blob[vertex_index.reshape((-1, 3))].astype(np.float64) return {'vertices': vertices, 'faces': faces, 'face_normals': face_normals} _stl_loaders = {'stl': load_stl, 'stl_ascii': load_stl} �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/io/mesh.py�����������������������������������������������������������������������0000644�0001750�0001751�00000006747�14507104216�015403� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Reading and writing of data like images and meshes.""" import os from os import path as op from .wavefront import WavefrontReader, WavefrontWriter from .stl import load_stl 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 fmt in ('.stl'): file_obj = open(fname, mode='rb') mesh = load_stl(file_obj) vertices = mesh['vertices'] faces = mesh['faces'] normals = mesh['face_normals'] texcoords = None return vertices, faces, normals, texcoords else: try: import meshio except ImportError: raise ValueError('read_mesh does not understand format %s.' % fmt) try: mesh = meshio.read(fname) except meshio.ReadError: raise ValueError('read_mesh does not understand format %s.' % fmt) triangles = mesh.get_cells_type("triangle") if len(triangles) == 0: raise ValueError('mesh file does not contain triangles.') return mesh.points, triangles, None, None def write_mesh(fname, vertices, faces, normals, texcoords, name='', format=None, overwrite=False, reshape_faces=True): """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. reshape_faces : bool Reshape the `faces` array to (Nf, 3). Set to `False` if you need to write a mesh with non triangular faces. """ # Check file if op.isfile(fname) and not overwrite: raise IOError('file "%s" exists, use overwrite=True' % fname) if format is None: format = os.path.splitext(fname)[1][1:] # Check format if format == 'obj': WavefrontWriter.write(fname, vertices, faces, normals, texcoords, name, reshape_faces) return try: import meshio except ImportError: raise ValueError('write_mesh does not understand format %s.' % format) cell_data = {} if normals is not None: cell_data["normals"] = [normals] if texcoords is not None: cell_data["texcoords"] = [texcoords] mesh = meshio.Mesh(vertices, [("triangle", faces)], cell_data=cell_data) try: mesh.write(fname, file_format=format) except meshio.WriteError: raise ValueError('write_mesh does not understand format %s.' % format) �������������������������vispy-0.14.1/vispy/io/image.py����������������������������������������������������������������������0000644�0001750�0001751�00000014043�14507104216�015515� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 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 specialized 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').tobytes() 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').tobytes() 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 Requires Pillow. Parameters ---------- filename : str File to read. Returns ------- data : array Image data. See also -------- write_png, imread, imsave """ try: from PIL import Image x = Image.open(filename) try: y = np.asarray(x) y = np.array([yy for yy in y], np.uint8) finally: x.close() return y except ImportError: raise RuntimeError("read_png requires the Pillow package.") 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.14.1/vispy/io/datasets.py�������������������������������������������������������������������0000644�0001750�0001751�00000004665�14507104216�016254� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- # Copyright (c) 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 ..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'] def pack_unit(value): """Packs float values between [0,1] into 4 unsigned int8 Returns ------- pack: array packed interpolation kernel """ pack = np.zeros(value.shape + (4,), dtype=np.ubyte) for i in range(4): value, pack[..., i] = np.modf(value * 256.) return pack def pack_ieee(value): """Packs float ieee binary representation into 4 unsigned int8 Returns ------- pack: array packed interpolation kernel """ return np.fromstring(value.tobytes(), np.ubyte).reshape((value.shape + (4,))) def load_spatial_filters(packed=True): """Load spatial-filters kernel Parameters ---------- packed : bool Whether or not the data should be in "packed" representation for use in GLSL code. Returns ------- kernel : array 16x1024x4 (packed float in rgba) or 16x1024 (unpacked float) 16 interpolation kernel with length 1024 each. names : tuple of strings Respective interpolation names, plus "Nearest" which does not require a filter but can still be used """ names = ("Linear", "Hanning", "Hamming", "Hermite", "Kaiser", "Quadric", "Cubic", "CatRom", "Mitchell", "Spline16", "Spline36", "Gaussian", "Bessel", "Sinc", "Lanczos", "Blackman", "Nearest") kernel = np.load(op.join(DATA_DIR, 'spatial-filters.npy')) if packed: # convert the kernel to a packed representation kernel = pack_unit(kernel) return kernel, names ���������������������������������������������������������������������������vispy-0.14.1/vispy/io/_data/������������������������������������������������������������������������0000755�0001750�0001751�00000000000�14507104251�015126� 5����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������vispy-0.14.1/vispy/io/_data/spatial-filters.npy�����������������������������������������������������0000644�0001750�0001751�00000200200�14507104216�020754� 0����������������������������������������������������������������������������������������������������ustar �nilesh��������������������������nilesh�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������“NUMPY�v�{'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>•5U?˜U?XU?ÔéT? ÐT?¶T?¯›T?T?>fT?KT?´/T?T?øS?ÕÛS?Q¿S?…¢S?q…S?hS?nJS?,S?FS?ÄïR?öÐR?ß±R?|’R?ÎrR?ÔRR?Ž2R?ûR?ñQ?ðÏQ?w®Q?°ŒQ?šjQ?7HQ?…%Q?„Q?3ßP?“»P?£—P?csP?ÒNP?ñ)P?¾P?:ßO?d¹O?<“O?ÁlO?ôEO?ÔO?a÷N?›ÏN?€§N?N?OVN?7-N?ËN? ÚM?ò¯M?……M?ÃZM?ª/M?:M?tØL?W¬L?ãL?SL?ô%L?yøK?¦ÊK?zœK?ömK??K?ãK?TàJ?l°J?*€J?ŽOJ?˜J?HíI?ž»I?™‰I?9WI?$I?iñH?ù½H?-ŠH?VH?‚!H?£ìG?i·G?ÒG?ßKG?G?åÞF?ݧF?xpF?·8F?™F?ÈE?FE?VE?€E?‘âD?D¨D?›mD?”2D?0÷C?o»C?PC?ÔBC?ûC?ÄÈB?0‹B?>MB?ïB?CÐA?9‘A?ÒQA? A?ìÑ@?m‘@?‘P@?X@?ÃÍ??Ћ??€I??Ô??ËÃ>?f€>?¥<>?‡ø=? ´=?7o=?*=?xä«:?€b:?h:?÷Ï9?-†9? <9?ñ8?¹¦8?Œ[8?8?+Ä7?øw7?m+7?ŒÞ6?U‘6?ÈC6?åõ5?­§5? Y5?> 5?»4?~k4?¡4?qË3?îz3?*3?óØ2?{‡2?²52?˜ã1?/‘1?v>1?në0?˜0?rD0?€ð/?@œ/?´G/?Ûò.?·.?IH.?ò-?Œœ-??F-?©ï,?˘,?¥A,?9ê+?…’+?Œ:+?Nâ*?ʉ*?1*?ø×)?«~)?%)?JË(?8q(?å(?T¼'?ƒa'?t'?(«&?žO&?Ùó%?Ù—%?;%?(ß$?z‚$?“%$?tÈ#?k#?“ #?Ò¯"?ÜQ"?²ó!?U•!?Æ6!?Ø ?y ?ò ?¡º?![?uû?››?•;?eÛ? {?†?Ù¹?Y?ø?ç–? 5?5Ô?¦r?õ?"¯?.M?ë?èˆ?—&?(Ä?a?÷þ?6œ?[9?hÖ?\s?:?­?³I?Qæ?Û‚?S?º»?X?Uô?Œ?¶,?ÒÈ?âd?ç?âœ?Ô8?½Ô?Ÿp?z ?O¨ ? D ?îß ?¸{ ? ?H³ ?O ?Øê ?¢† ?n" ??¾ ?Z ?îõ?Α?¶-?¦É?Ÿe?¢?°?Ê9?ðÕ?#r?f?·ª?G?Œã?€?¨?S¹?V?èò?Ô?Ö,?ñÉ?%g?r?´Cÿ>º~þ>ù¹ý>rõü>&1ü>mû>I©ú>»åù>n"ù>f_ø>£œ÷>'Úö>óö> Võ>l”ô>Óó>ó>hQò>‘ñ>üÐð>Dð>âQï>Ø’î>'Ôí>Ñí>ÖWì>9šë>úÜê> ê>Ÿcé>…§è>Ïëç>0ç>•uæ>»å>üå>OGä> Žã>9Õâ>Ôâ>Þdá>Y­à>Föß>¦?ß>{‰Þ>ÆÓÝ>‡Ý>ÀiÜ>rµÛ>žÛ>ENÚ>i›Ù> éØ>(7Ø>Ç…×>æÔÖ>†$Ö>¨tÕ>NÅÔ>xÔ>'hÓ>\ºÒ> Ò>]`Ñ>*´Ð>€Ð>b]Ï>βÎ>ÇÎ>L_Í>`¶Ì>Ì>3fË>ô¾Ê>FÊ>)rÉ>ŸÌÈ>¨'È>DƒÇ>t߯>9<Æ>”™Å>…÷Ä> VÄ>+µÃ>áÃ>0uÂ>ÖÁ>š7Á>µ™À>kü¿>¼_¿>©Ã¾>1(¾>V½>ó¼>wY¼>sÀ»> (»>Fº>ù¹>•b¹>«Ì¸>a7¸>·¢·>®·>E{¶>~èµ>WVµ>ÒÄ´>ï3´>­£³>³>…²>¶ö±>ÿh±>êÛ°>xO°>©Ã¯>~8¯>ö­®>$®>К­>3­>9Ь>ã¬>1|«>#öª>¹pª>òë©>Ðg©>Rä¨>wa¨>@ß§>­]§>¾Ü¦>r\¦>ÊÜ¥>Æ]¥>eߤ>¨a¤>Žä£>h£>Bì¢>q¢>ƒö¡>—|¡>N¡>¦Š >¡ >>›Ÿ>|$Ÿ>\®ž>Þ8ž>Ä>ÃO>&Üœ>*iœ>Îö›>…›>õ›>x£š>š3š>ZÄ™>¹U™>µç˜>Pz˜>ˆ ˜>]¡—>Ï5—>ÝÊ–>ˆ`–>Îö•>°•>-%•>D½”>öU”>Bï“>'‰“>¥#“>¼¾’>lZ’>³ö‘>’“‘>1‘>Ï>¸m>ñ >¿¬>"M>îŽ>¦Ž>Å1Ž>xÔ>½w>”>þ¿Œ>ødŒ>ƒ Œ>Ÿ°‹>JW‹>…þŠ>O¦Š>§NŠ>÷‰>§¹‰>†”‰>^o‰>.J‰>ø$‰>»ÿˆ>vÚˆ>+µˆ>Úˆ>jˆ>"Eˆ>½ˆ>Qú‡>àÔ‡>h¯‡>ꉇ>fd‡>Ý>‡>N‡>¹ó†>Ά>¨†>Û‚†>1]†>‚7†>φ>ì…>YÆ…>˜ …>Òz…>U…>:/…>h …>’ã„>¸½„>Û—„>úq„>L„>/&„>E„>XÚƒ>h´ƒ>uŽƒ>€hƒ>‰Bƒ>ƒ>”ö‚>—Ђ>˜ª‚>—„‚>•^‚>‘8‚>‚>‡ì>Æ>z >sz>kT>c.>\>Tâ€>M¼€>F–€>@p€>;J€>7$€>hü>e°>ed>i>qÌ~>}€~>4~>£è}>¾œ}>ßP}>}>3¹|>gm|>¢!|>åÕ{>0Š{>„>{>àòz>F§z>µ[z>/z>²Äy>Ayy>Û-y>€âx>2—x>ðKx>»x>”µw>zjw>nw>qÔv>ƒ‰v>¥>v>Öóu>©u>j^u>Îu>DÉt>Ë~t>e4t>ês>ÓŸs>¨Us>‘ s>ŽÁr>¡wr>Ê-r> äq>_šq>ËPq>Oq>ì½p> tp>n+p>Uâo>V™o>rPo>¨o>ú¾n>gvn>ñ-n>—åm>[m>< m>ZÅl>—}l>ô5l>qîk>§k>Í_k>­k>¯Ñj>ÔŠj>Dj>‡ýi>·i>Épi>¢*i> äh>Þh> Yh>~h>Îg>׈g>¿Cg>Ðþf> ºf>nuf>ý0f>¶ìe>š¨e>ªde>æ e>NÝd>ä™d>§Vd>˜d>·Ðc>Žc>„Kc>1 c>Çb>…b>_Cb>Ñb>uÀa>Ka>U>a>’ý`>½`>ª|`>„<`>”ü_>Ú¼_>V}_>>_>òþ^>À^>l^>ýB^>È^>ËÆ]>‰]>K]>0]>Ñ\>D”\>§W\>F\>"ß[>;£[>g[>$,[>õðZ>¶Z>S{Z>à@Z>­Z>ºÌY>“Y>”YY>b Y>rçX>îX>VvX>+>X>CX>ÎW>;—W>`W>B)W>«òV>Y¼V>K†V>‚PV>ÿV>ÂåU>ʰU>|U>­GU>ˆU>ªßT>¬T>ÄxT>½ET>ýT>…àS>V®S>o|S>ÑJS>|S>pèR>®·R>5‡R>WR> 'R>…÷Q>4ÈQ>-™Q>qjQ>Ú Q>þßP>n²P>)…P>/XP>+P>ÿO>ÓO>=§O>¾{O>‹PO>¤%O> ûN>ºÐN>¸¦N>}N>˜SN>z*N>ªN>%ÙM>í°M>‰M>caM>:M> M>SìL>æÅL>ÆŸL>òyL>kTL>1/L>C L>¡åK>KÁK>BK>…yK>VK>ï2K>K>‰íJ>GËJ>R©J>¨‡J>IfJ>6EJ>n$J>ñJ>¾ãI>×ÃI>;¤I>é„I>áeI>$GI>°(I>‡ I>§ìH>ÏH>ıH>À”H>xH>“[H>j?H>ˆ#H>ïH>žìG>”ÑG>Ò¶G>WœG>#‚G>6hG>NG>.5G>G>?G>¯êF>dÒF>_ºF>ž¢F>!‹F>èsF>ó\F>AFF>Ó/F>§F>¾F>îE>±ØE>ÃE>«®E> šE>¨…E>‡qE>¦]E>JE>¢6E>#E>šE>óýD>ŠëD>^ÙD>oÇD>½µD>H¤D>“D>‚D>MqD>Ä`D>wPD>c@D>‰0D>è D>€D>PD>YóC>™äC>ÖC>¿ÇC>¤¹C>¿«C>žC>•C>PƒC>?vC>biC>¸\C>BPC>ÿCC>î7C>,C>a C>äC>˜ C>|þB>óB>ÔèB>FÞB>çÓB>¶ÉB>²¿B>ܵB>2¬B>µ¢B>d™B>>B>D‡B>t~B>ÎuB>RmB>ÿdB>Õ\B>ÔTB>úLB>IEB>¾=B>Z6B>/B>(B>!B>EB>œB> B>·B>yB>^úA>fôA>îA>ÛèA>GãA>ÔÝA>‚ØA>OÓA>;ÎA>GÉA>qÄA>º¿A> »A>£¶A>D²A>®A>Ú©A>Ï¥A>ß¡A> žA>OšA>®–A>'“A>¹A>eŒA>(‰A>†A>÷‚A>€A>#}A>ZzA>¨wA> uA>„rA>pA>´mA>kkA>5iA>gA>eA>cA>aA>B_A>z]A>Ä[A>ZA>‰XA>WA>UA>)TA>ÒRA>‰QA>OPA>#OA>NA>òLA>îKA>öJA> JA>*IA>UHA>ŒGA>ÍFA>FA>pEA>ÐDA>9DA>¬CA>(CA>­BA>9BA>ÎAA>kAA>AA>¹@A>k@A>#@A>á?A>¦?A>o?A>??A>?A>ë>A>É>A>ª>A>>A>y>A>e>A>T>A>F>A>;>A>2>A>+>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.14.1/vispy/io/__init__.py0000644000175000017510000000143414507104216016172 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 load_spatial_filters) # 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_spatial_filters', 'load_data_file', 'read_mesh', 'read_png', 'write_mesh', 'write_png'] vispy-0.14.1/vispy/glsl/0000755000175000017510000000000014507104251014410 5ustar nileshnileshvispy-0.14.1/vispy/glsl/transforms/0000755000175000017510000000000014507104251016606 5ustar nileshnileshvispy-0.14.1/vispy/glsl/transforms/z.glsl0000644000175000017510000000055014507104216017743 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/y.glsl0000644000175000017510000000062314507104216017743 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/x.glsl0000644000175000017510000000067314507104216017747 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/viewport.glsl0000644000175000017510000000341614507104216021355 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/viewport-transform.glsl0000644000175000017510000000134414507104216023364 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/viewport-clipping.glsl0000644000175000017510000000116114507104216023153 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/transverse_mercator.glsl0000644000175000017510000000260014507104216023560 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/translate.glsl0000644000175000017510000000177414507104216021500 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/trackball.glsl0000644000175000017510000000077514507104216021442 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/rotate.glsl0000644000175000017510000000216314507104216020772 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/pvm.glsl0000644000175000017510000000063314507104216020276 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/projection.glsl0000644000175000017510000000017014507104216021644 0ustar nileshnilesh// Simple matrix projection uniform mat4 projection; vec4 transform(vec4 position) { return projection*position; } vispy-0.14.1/vispy/glsl/transforms/power-scale.glsl0000644000175000017510000000751414507104216021722 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/position.glsl0000644000175000017510000000140414507104216021335 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/polar.glsl0000644000175000017510000000320714507104216020611 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/panzoom.glsl0000644000175000017510000000067114507104216021161 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/mercator-transverse-inverse.glsl0000644000175000017510000000220414507104216025147 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/mercator-transverse-forward.glsl0000644000175000017510000000213314507104216025141 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/log-scale.glsl0000644000175000017510000000627514507104216021352 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/linear-scale.glsl0000644000175000017510000000650214507104216022034 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/identity_inverse.glsl0000644000175000017510000000120314507104216023052 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/identity_forward.glsl0000644000175000017510000000120214507104216023042 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/identity.glsl0000644000175000017510000000054414507104216021326 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/hammer.glsl0000644000175000017510000000313314507104216020743 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/azimuthal-equidistant.glsl0000644000175000017510000000240614507104216024022 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/transforms/azimuthal-equal-area.glsl0000644000175000017510000000232014507104216023500 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/misc/0000755000175000017510000000000014507104251015343 5ustar nileshnileshvispy-0.14.1/vispy/glsl/misc/viewport-NDC.glsl0000644000175000017510000000117214507104216020511 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/misc/spatial-filters.frag0000644000175000017510000022271314507104216021317 0ustar nileshnilesh// ------------------------------------ // Automatically generated, do not edit // ------------------------------------ const float kernel_bias = -0.23437733388100893; const float kernel_scale = 1.2419743353536359; const float kernel_size = 1024; const vec4 bits = vec4(1, 0.00390625, 1.52587890625e-05, 5.960464477539063e-08); uniform sampler2D u_kernel; float unpack_unit(vec4 rgba) { // return rgba.r; // uncomment this for r32f debugging return dot(rgba, bits); } float unpack_ieee(vec4 rgba) { // return rgba.r; // uncomment this for r32f debugging rgba.rgba = rgba.abgr * 255; float sign = 1 - step(128 , rgba[0]) * 2; float exponent = 2 * mod(rgba[0] , 128) + step(128 , rgba[1]) - 127; float mantissa = mod(rgba[1] , 128) * 65536 + rgba[2] * 256 + rgba[3] + float(0x800000); return sign * exp2(exponent) * (mantissa * exp2(-23.)); } float unpack_interpolate(sampler2D kernel, vec2 uv) { // return texture2D(kernel, uv).r; //uncomment this for r32f debug without interpolation float kpixel = 1. / kernel_size; float u = uv.x / kpixel; float v = uv.y; float uf = fract(u); u = (u - uf) * kpixel; float d0 = unpack_unit(texture2D(kernel, vec2(u, v))); float d1 = unpack_unit(texture2D(kernel, vec2(u + 1. * kpixel, v))); return mix(d0, d1, uf); } vec4 filter1D_radius1(sampler2D kernel, float index, float x, vec4 c0, vec4 c1) { float w, w_sum = 0; vec4 r = vec4(0); w = unpack_interpolate(kernel, vec2(0.0 + (x / 1), index)); w = w * kernel_scale + kernel_bias; r += c0 * w; w = unpack_interpolate(kernel, vec2(1.0 - (x / 1), index)); 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.5); vec2 f = fract(texel); texel = (texel - fract(texel) + vec2(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 filter3D_radius1(sampler3D texture, sampler2D kernel, float index, vec3 uv, vec3 pixel) { vec3 texel = uv / pixel - vec3(0.5); vec3 f = fract(texel); texel = (texel - fract(texel) + vec3(0.001)) * pixel; vec4 t00 = filter1D_radius1(kernel, index, f.x, texture3D(texture, texel + vec3(0, 0, 0) * pixel), texture3D(texture, texel + vec3(1, 0, 0) * pixel)); vec4 t01 = filter1D_radius1(kernel, index, f.x, texture3D(texture, texel + vec3(0, 1, 0) * pixel), texture3D(texture, texel + vec3(1, 1, 0) * pixel)); vec4 t10 = filter1D_radius1(kernel, index, f.x, texture3D(texture, texel + vec3(0, 0, 1) * pixel), texture3D(texture, texel + vec3(1, 0, 1) * pixel)); vec4 t11 = filter1D_radius1(kernel, index, f.x, texture3D(texture, texel + vec3(0, 1, 1) * pixel), texture3D(texture, texel + vec3(1, 1, 1) * pixel)); vec4 t0 = filter1D_radius1(kernel, index, f.y, t00, t01); vec4 t1 = filter1D_radius1(kernel, index, f.y, t10, t11); return filter1D_radius1(kernel, index, f.z, t0, t1); } vec4 filter1D_radius2(sampler2D kernel, float index, float x, vec4 c0, vec4 c1, vec4 c2, vec4 c3) { float w, w_sum = 0; vec4 r = vec4(0); w = unpack_interpolate(kernel, vec2(0.5 + (x / 2), index)); w = w * kernel_scale + kernel_bias; r += c0 * w; w = unpack_interpolate(kernel, vec2(0.5 - (x / 2), index)); w = w * kernel_scale + kernel_bias; r += c2 * w; w = unpack_interpolate(kernel, vec2(0.0 + (x / 2), index)); w = w * kernel_scale + kernel_bias; r += c1 * w; w = unpack_interpolate(kernel, vec2(1.0 - (x / 2), index)); 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.5); vec2 f = fract(texel); texel = (texel - fract(texel) + vec2(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 filter3D_radius2(sampler3D texture, sampler2D kernel, float index, vec3 uv, vec3 pixel) { vec3 texel = uv / pixel - vec3(0.5); vec3 f = fract(texel); texel = (texel - fract(texel) + vec3(0.001)) * pixel; vec4 t00 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, -1, -1) * pixel), texture3D(texture, texel + vec3(0, -1, -1) * pixel), texture3D(texture, texel + vec3(1, -1, -1) * pixel), texture3D(texture, texel + vec3(2, -1, -1) * pixel)); vec4 t01 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 0, -1) * pixel), texture3D(texture, texel + vec3(0, 0, -1) * pixel), texture3D(texture, texel + vec3(1, 0, -1) * pixel), texture3D(texture, texel + vec3(2, 0, -1) * pixel)); vec4 t02 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 1, -1) * pixel), texture3D(texture, texel + vec3(0, 1, -1) * pixel), texture3D(texture, texel + vec3(1, 1, -1) * pixel), texture3D(texture, texel + vec3(2, 1, -1) * pixel)); vec4 t03 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 2, -1) * pixel), texture3D(texture, texel + vec3(0, 2, -1) * pixel), texture3D(texture, texel + vec3(1, 2, -1) * pixel), texture3D(texture, texel + vec3(2, 2, -1) * pixel)); vec4 t10 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, -1, 0) * pixel), texture3D(texture, texel + vec3(0, -1, 0) * pixel), texture3D(texture, texel + vec3(1, -1, 0) * pixel), texture3D(texture, texel + vec3(2, -1, 0) * pixel)); vec4 t11 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 0, 0) * pixel), texture3D(texture, texel + vec3(0, 0, 0) * pixel), texture3D(texture, texel + vec3(1, 0, 0) * pixel), texture3D(texture, texel + vec3(2, 0, 0) * pixel)); vec4 t12 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 1, 0) * pixel), texture3D(texture, texel + vec3(0, 1, 0) * pixel), texture3D(texture, texel + vec3(1, 1, 0) * pixel), texture3D(texture, texel + vec3(2, 1, 0) * pixel)); vec4 t13 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 2, 0) * pixel), texture3D(texture, texel + vec3(0, 2, 0) * pixel), texture3D(texture, texel + vec3(1, 2, 0) * pixel), texture3D(texture, texel + vec3(2, 2, 0) * pixel)); vec4 t20 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, -1, 1) * pixel), texture3D(texture, texel + vec3(0, -1, 1) * pixel), texture3D(texture, texel + vec3(1, -1, 1) * pixel), texture3D(texture, texel + vec3(2, -1, 1) * pixel)); vec4 t21 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 0, 1) * pixel), texture3D(texture, texel + vec3(0, 0, 1) * pixel), texture3D(texture, texel + vec3(1, 0, 1) * pixel), texture3D(texture, texel + vec3(2, 0, 1) * pixel)); vec4 t22 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 1, 1) * pixel), texture3D(texture, texel + vec3(0, 1, 1) * pixel), texture3D(texture, texel + vec3(1, 1, 1) * pixel), texture3D(texture, texel + vec3(2, 1, 1) * pixel)); vec4 t23 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 2, 1) * pixel), texture3D(texture, texel + vec3(0, 2, 1) * pixel), texture3D(texture, texel + vec3(1, 2, 1) * pixel), texture3D(texture, texel + vec3(2, 2, 1) * pixel)); vec4 t30 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, -1, 2) * pixel), texture3D(texture, texel + vec3(0, -1, 2) * pixel), texture3D(texture, texel + vec3(1, -1, 2) * pixel), texture3D(texture, texel + vec3(2, -1, 2) * pixel)); vec4 t31 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 0, 2) * pixel), texture3D(texture, texel + vec3(0, 0, 2) * pixel), texture3D(texture, texel + vec3(1, 0, 2) * pixel), texture3D(texture, texel + vec3(2, 0, 2) * pixel)); vec4 t32 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 1, 2) * pixel), texture3D(texture, texel + vec3(0, 1, 2) * pixel), texture3D(texture, texel + vec3(1, 1, 2) * pixel), texture3D(texture, texel + vec3(2, 1, 2) * pixel)); vec4 t33 = filter1D_radius2(kernel, index, f.x, texture3D(texture, texel + vec3(-1, 2, 2) * pixel), texture3D(texture, texel + vec3(0, 2, 2) * pixel), texture3D(texture, texel + vec3(1, 2, 2) * pixel), texture3D(texture, texel + vec3(2, 2, 2) * pixel)); vec4 t0 = filter1D_radius2(kernel, index, f.y, t00, t01, t02, t03); vec4 t1 = filter1D_radius2(kernel, index, f.y, t10, t11, t12, t13); vec4 t2 = filter1D_radius2(kernel, index, f.y, t20, t21, t22, t23); vec4 t3 = filter1D_radius2(kernel, index, f.y, t30, t31, t32, t33); return filter1D_radius2(kernel, index, f.z, 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; vec4 r = vec4(0); w = unpack_interpolate(kernel, vec2(0.6666666666666667 + (x / 3), index)); w = w * kernel_scale + kernel_bias; r += c0 * w; w = unpack_interpolate(kernel, vec2(0.3333333333333333 - (x / 3), index)); w = w * kernel_scale + kernel_bias; r += c3 * w; w = unpack_interpolate(kernel, vec2(0.33333333333333337 + (x / 3), index)); w = w * kernel_scale + kernel_bias; r += c1 * w; w = unpack_interpolate(kernel, vec2(0.6666666666666666 - (x / 3), index)); w = w * kernel_scale + kernel_bias; r += c4 * w; w = unpack_interpolate(kernel, vec2(0.0 + (x / 3), index)); w = w * kernel_scale + kernel_bias; r += c2 * w; w = unpack_interpolate(kernel, vec2(1.0 - (x / 3), index)); 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.5); vec2 f = fract(texel); texel = (texel - fract(texel) + vec2(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 filter3D_radius3(sampler3D texture, sampler2D kernel, float index, vec3 uv, vec3 pixel) { vec3 texel = uv / pixel - vec3(0.5); vec3 f = fract(texel); texel = (texel - fract(texel) + vec3(0.001)) * pixel; vec4 t00 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -2, -2) * pixel), texture3D(texture, texel + vec3(-1, -2, -2) * pixel), texture3D(texture, texel + vec3(0, -2, -2) * pixel), texture3D(texture, texel + vec3(1, -2, -2) * pixel), texture3D(texture, texel + vec3(2, -2, -2) * pixel), texture3D(texture, texel + vec3(3, -2, -2) * pixel)); vec4 t01 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -1, -2) * pixel), texture3D(texture, texel + vec3(-1, -1, -2) * pixel), texture3D(texture, texel + vec3(0, -1, -2) * pixel), texture3D(texture, texel + vec3(1, -1, -2) * pixel), texture3D(texture, texel + vec3(2, -1, -2) * pixel), texture3D(texture, texel + vec3(3, -1, -2) * pixel)); vec4 t02 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 0, -2) * pixel), texture3D(texture, texel + vec3(-1, 0, -2) * pixel), texture3D(texture, texel + vec3(0, 0, -2) * pixel), texture3D(texture, texel + vec3(1, 0, -2) * pixel), texture3D(texture, texel + vec3(2, 0, -2) * pixel), texture3D(texture, texel + vec3(3, 0, -2) * pixel)); vec4 t03 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 1, -2) * pixel), texture3D(texture, texel + vec3(-1, 1, -2) * pixel), texture3D(texture, texel + vec3(0, 1, -2) * pixel), texture3D(texture, texel + vec3(1, 1, -2) * pixel), texture3D(texture, texel + vec3(2, 1, -2) * pixel), texture3D(texture, texel + vec3(3, 1, -2) * pixel)); vec4 t04 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 2, -2) * pixel), texture3D(texture, texel + vec3(-1, 2, -2) * pixel), texture3D(texture, texel + vec3(0, 2, -2) * pixel), texture3D(texture, texel + vec3(1, 2, -2) * pixel), texture3D(texture, texel + vec3(2, 2, -2) * pixel), texture3D(texture, texel + vec3(3, 2, -2) * pixel)); vec4 t05 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 3, -2) * pixel), texture3D(texture, texel + vec3(-1, 3, -2) * pixel), texture3D(texture, texel + vec3(0, 3, -2) * pixel), texture3D(texture, texel + vec3(1, 3, -2) * pixel), texture3D(texture, texel + vec3(2, 3, -2) * pixel), texture3D(texture, texel + vec3(3, 3, -2) * pixel)); vec4 t10 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -2, -1) * pixel), texture3D(texture, texel + vec3(-1, -2, -1) * pixel), texture3D(texture, texel + vec3(0, -2, -1) * pixel), texture3D(texture, texel + vec3(1, -2, -1) * pixel), texture3D(texture, texel + vec3(2, -2, -1) * pixel), texture3D(texture, texel + vec3(3, -2, -1) * pixel)); vec4 t11 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -1, -1) * pixel), texture3D(texture, texel + vec3(-1, -1, -1) * pixel), texture3D(texture, texel + vec3(0, -1, -1) * pixel), texture3D(texture, texel + vec3(1, -1, -1) * pixel), texture3D(texture, texel + vec3(2, -1, -1) * pixel), texture3D(texture, texel + vec3(3, -1, -1) * pixel)); vec4 t12 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 0, -1) * pixel), texture3D(texture, texel + vec3(-1, 0, -1) * pixel), texture3D(texture, texel + vec3(0, 0, -1) * pixel), texture3D(texture, texel + vec3(1, 0, -1) * pixel), texture3D(texture, texel + vec3(2, 0, -1) * pixel), texture3D(texture, texel + vec3(3, 0, -1) * pixel)); vec4 t13 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 1, -1) * pixel), texture3D(texture, texel + vec3(-1, 1, -1) * pixel), texture3D(texture, texel + vec3(0, 1, -1) * pixel), texture3D(texture, texel + vec3(1, 1, -1) * pixel), texture3D(texture, texel + vec3(2, 1, -1) * pixel), texture3D(texture, texel + vec3(3, 1, -1) * pixel)); vec4 t14 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 2, -1) * pixel), texture3D(texture, texel + vec3(-1, 2, -1) * pixel), texture3D(texture, texel + vec3(0, 2, -1) * pixel), texture3D(texture, texel + vec3(1, 2, -1) * pixel), texture3D(texture, texel + vec3(2, 2, -1) * pixel), texture3D(texture, texel + vec3(3, 2, -1) * pixel)); vec4 t15 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 3, -1) * pixel), texture3D(texture, texel + vec3(-1, 3, -1) * pixel), texture3D(texture, texel + vec3(0, 3, -1) * pixel), texture3D(texture, texel + vec3(1, 3, -1) * pixel), texture3D(texture, texel + vec3(2, 3, -1) * pixel), texture3D(texture, texel + vec3(3, 3, -1) * pixel)); vec4 t20 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -2, 0) * pixel), texture3D(texture, texel + vec3(-1, -2, 0) * pixel), texture3D(texture, texel + vec3(0, -2, 0) * pixel), texture3D(texture, texel + vec3(1, -2, 0) * pixel), texture3D(texture, texel + vec3(2, -2, 0) * pixel), texture3D(texture, texel + vec3(3, -2, 0) * pixel)); vec4 t21 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -1, 0) * pixel), texture3D(texture, texel + vec3(-1, -1, 0) * pixel), texture3D(texture, texel + vec3(0, -1, 0) * pixel), texture3D(texture, texel + vec3(1, -1, 0) * pixel), texture3D(texture, texel + vec3(2, -1, 0) * pixel), texture3D(texture, texel + vec3(3, -1, 0) * pixel)); vec4 t22 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 0, 0) * pixel), texture3D(texture, texel + vec3(-1, 0, 0) * pixel), texture3D(texture, texel + vec3(0, 0, 0) * pixel), texture3D(texture, texel + vec3(1, 0, 0) * pixel), texture3D(texture, texel + vec3(2, 0, 0) * pixel), texture3D(texture, texel + vec3(3, 0, 0) * pixel)); vec4 t23 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 1, 0) * pixel), texture3D(texture, texel + vec3(-1, 1, 0) * pixel), texture3D(texture, texel + vec3(0, 1, 0) * pixel), texture3D(texture, texel + vec3(1, 1, 0) * pixel), texture3D(texture, texel + vec3(2, 1, 0) * pixel), texture3D(texture, texel + vec3(3, 1, 0) * pixel)); vec4 t24 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 2, 0) * pixel), texture3D(texture, texel + vec3(-1, 2, 0) * pixel), texture3D(texture, texel + vec3(0, 2, 0) * pixel), texture3D(texture, texel + vec3(1, 2, 0) * pixel), texture3D(texture, texel + vec3(2, 2, 0) * pixel), texture3D(texture, texel + vec3(3, 2, 0) * pixel)); vec4 t25 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 3, 0) * pixel), texture3D(texture, texel + vec3(-1, 3, 0) * pixel), texture3D(texture, texel + vec3(0, 3, 0) * pixel), texture3D(texture, texel + vec3(1, 3, 0) * pixel), texture3D(texture, texel + vec3(2, 3, 0) * pixel), texture3D(texture, texel + vec3(3, 3, 0) * pixel)); vec4 t30 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -2, 1) * pixel), texture3D(texture, texel + vec3(-1, -2, 1) * pixel), texture3D(texture, texel + vec3(0, -2, 1) * pixel), texture3D(texture, texel + vec3(1, -2, 1) * pixel), texture3D(texture, texel + vec3(2, -2, 1) * pixel), texture3D(texture, texel + vec3(3, -2, 1) * pixel)); vec4 t31 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -1, 1) * pixel), texture3D(texture, texel + vec3(-1, -1, 1) * pixel), texture3D(texture, texel + vec3(0, -1, 1) * pixel), texture3D(texture, texel + vec3(1, -1, 1) * pixel), texture3D(texture, texel + vec3(2, -1, 1) * pixel), texture3D(texture, texel + vec3(3, -1, 1) * pixel)); vec4 t32 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 0, 1) * pixel), texture3D(texture, texel + vec3(-1, 0, 1) * pixel), texture3D(texture, texel + vec3(0, 0, 1) * pixel), texture3D(texture, texel + vec3(1, 0, 1) * pixel), texture3D(texture, texel + vec3(2, 0, 1) * pixel), texture3D(texture, texel + vec3(3, 0, 1) * pixel)); vec4 t33 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 1, 1) * pixel), texture3D(texture, texel + vec3(-1, 1, 1) * pixel), texture3D(texture, texel + vec3(0, 1, 1) * pixel), texture3D(texture, texel + vec3(1, 1, 1) * pixel), texture3D(texture, texel + vec3(2, 1, 1) * pixel), texture3D(texture, texel + vec3(3, 1, 1) * pixel)); vec4 t34 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 2, 1) * pixel), texture3D(texture, texel + vec3(-1, 2, 1) * pixel), texture3D(texture, texel + vec3(0, 2, 1) * pixel), texture3D(texture, texel + vec3(1, 2, 1) * pixel), texture3D(texture, texel + vec3(2, 2, 1) * pixel), texture3D(texture, texel + vec3(3, 2, 1) * pixel)); vec4 t35 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 3, 1) * pixel), texture3D(texture, texel + vec3(-1, 3, 1) * pixel), texture3D(texture, texel + vec3(0, 3, 1) * pixel), texture3D(texture, texel + vec3(1, 3, 1) * pixel), texture3D(texture, texel + vec3(2, 3, 1) * pixel), texture3D(texture, texel + vec3(3, 3, 1) * pixel)); vec4 t40 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -2, 2) * pixel), texture3D(texture, texel + vec3(-1, -2, 2) * pixel), texture3D(texture, texel + vec3(0, -2, 2) * pixel), texture3D(texture, texel + vec3(1, -2, 2) * pixel), texture3D(texture, texel + vec3(2, -2, 2) * pixel), texture3D(texture, texel + vec3(3, -2, 2) * pixel)); vec4 t41 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -1, 2) * pixel), texture3D(texture, texel + vec3(-1, -1, 2) * pixel), texture3D(texture, texel + vec3(0, -1, 2) * pixel), texture3D(texture, texel + vec3(1, -1, 2) * pixel), texture3D(texture, texel + vec3(2, -1, 2) * pixel), texture3D(texture, texel + vec3(3, -1, 2) * pixel)); vec4 t42 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 0, 2) * pixel), texture3D(texture, texel + vec3(-1, 0, 2) * pixel), texture3D(texture, texel + vec3(0, 0, 2) * pixel), texture3D(texture, texel + vec3(1, 0, 2) * pixel), texture3D(texture, texel + vec3(2, 0, 2) * pixel), texture3D(texture, texel + vec3(3, 0, 2) * pixel)); vec4 t43 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 1, 2) * pixel), texture3D(texture, texel + vec3(-1, 1, 2) * pixel), texture3D(texture, texel + vec3(0, 1, 2) * pixel), texture3D(texture, texel + vec3(1, 1, 2) * pixel), texture3D(texture, texel + vec3(2, 1, 2) * pixel), texture3D(texture, texel + vec3(3, 1, 2) * pixel)); vec4 t44 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 2, 2) * pixel), texture3D(texture, texel + vec3(-1, 2, 2) * pixel), texture3D(texture, texel + vec3(0, 2, 2) * pixel), texture3D(texture, texel + vec3(1, 2, 2) * pixel), texture3D(texture, texel + vec3(2, 2, 2) * pixel), texture3D(texture, texel + vec3(3, 2, 2) * pixel)); vec4 t45 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 3, 2) * pixel), texture3D(texture, texel + vec3(-1, 3, 2) * pixel), texture3D(texture, texel + vec3(0, 3, 2) * pixel), texture3D(texture, texel + vec3(1, 3, 2) * pixel), texture3D(texture, texel + vec3(2, 3, 2) * pixel), texture3D(texture, texel + vec3(3, 3, 2) * pixel)); vec4 t50 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -2, 3) * pixel), texture3D(texture, texel + vec3(-1, -2, 3) * pixel), texture3D(texture, texel + vec3(0, -2, 3) * pixel), texture3D(texture, texel + vec3(1, -2, 3) * pixel), texture3D(texture, texel + vec3(2, -2, 3) * pixel), texture3D(texture, texel + vec3(3, -2, 3) * pixel)); vec4 t51 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, -1, 3) * pixel), texture3D(texture, texel + vec3(-1, -1, 3) * pixel), texture3D(texture, texel + vec3(0, -1, 3) * pixel), texture3D(texture, texel + vec3(1, -1, 3) * pixel), texture3D(texture, texel + vec3(2, -1, 3) * pixel), texture3D(texture, texel + vec3(3, -1, 3) * pixel)); vec4 t52 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 0, 3) * pixel), texture3D(texture, texel + vec3(-1, 0, 3) * pixel), texture3D(texture, texel + vec3(0, 0, 3) * pixel), texture3D(texture, texel + vec3(1, 0, 3) * pixel), texture3D(texture, texel + vec3(2, 0, 3) * pixel), texture3D(texture, texel + vec3(3, 0, 3) * pixel)); vec4 t53 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 1, 3) * pixel), texture3D(texture, texel + vec3(-1, 1, 3) * pixel), texture3D(texture, texel + vec3(0, 1, 3) * pixel), texture3D(texture, texel + vec3(1, 1, 3) * pixel), texture3D(texture, texel + vec3(2, 1, 3) * pixel), texture3D(texture, texel + vec3(3, 1, 3) * pixel)); vec4 t54 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 2, 3) * pixel), texture3D(texture, texel + vec3(-1, 2, 3) * pixel), texture3D(texture, texel + vec3(0, 2, 3) * pixel), texture3D(texture, texel + vec3(1, 2, 3) * pixel), texture3D(texture, texel + vec3(2, 2, 3) * pixel), texture3D(texture, texel + vec3(3, 2, 3) * pixel)); vec4 t55 = filter1D_radius3(kernel, index, f.x, texture3D(texture, texel + vec3(-2, 3, 3) * pixel), texture3D(texture, texel + vec3(-1, 3, 3) * pixel), texture3D(texture, texel + vec3(0, 3, 3) * pixel), texture3D(texture, texel + vec3(1, 3, 3) * pixel), texture3D(texture, texel + vec3(2, 3, 3) * pixel), texture3D(texture, texel + vec3(3, 3, 3) * pixel)); vec4 t0 = filter1D_radius3(kernel, index, f.y, t00, t01, t02, t03, t04, t05); vec4 t1 = filter1D_radius3(kernel, index, f.y, t10, t11, t12, t13, t14, t15); vec4 t2 = filter1D_radius3(kernel, index, f.y, t20, t21, t22, t23, t24, t25); vec4 t3 = filter1D_radius3(kernel, index, f.y, t30, t31, t32, t33, t34, t35); vec4 t4 = filter1D_radius3(kernel, index, f.y, t40, t41, t42, t43, t44, t45); vec4 t5 = filter1D_radius3(kernel, index, f.y, t50, t51, t52, t53, t54, t55); return filter1D_radius3(kernel, index, f.z, 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; vec4 r = vec4(0); w = unpack_interpolate(kernel, vec2(0.75 + (x / 4), index)); w = w * kernel_scale + kernel_bias; r += c0 * w; w = unpack_interpolate(kernel, vec2(0.25 - (x / 4), index)); w = w * kernel_scale + kernel_bias; r += c4 * w; w = unpack_interpolate(kernel, vec2(0.5 + (x / 4), index)); w = w * kernel_scale + kernel_bias; r += c1 * w; w = unpack_interpolate(kernel, vec2(0.5 - (x / 4), index)); w = w * kernel_scale + kernel_bias; r += c5 * w; w = unpack_interpolate(kernel, vec2(0.25 + (x / 4), index)); w = w * kernel_scale + kernel_bias; r += c2 * w; w = unpack_interpolate(kernel, vec2(0.75 - (x / 4), index)); w = w * kernel_scale + kernel_bias; r += c6 * w; w = unpack_interpolate(kernel, vec2(0.0 + (x / 4), index)); w = w * kernel_scale + kernel_bias; r += c3 * w; w = unpack_interpolate(kernel, vec2(1.0 - (x / 4), index)); 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.5); vec2 f = fract(texel); texel = (texel - fract(texel) + vec2(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 filter3D_radius4(sampler3D texture, sampler2D kernel, float index, vec3 uv, vec3 pixel) { vec3 texel = uv / pixel - vec3(0.5); vec3 f = fract(texel); texel = (texel - fract(texel) + vec3(0.001)) * pixel; vec4 t00 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -3, -3) * pixel), texture3D(texture, texel + vec3(-2, -3, -3) * pixel), texture3D(texture, texel + vec3(-1, -3, -3) * pixel), texture3D(texture, texel + vec3(0, -3, -3) * pixel), texture3D(texture, texel + vec3(1, -3, -3) * pixel), texture3D(texture, texel + vec3(2, -3, -3) * pixel), texture3D(texture, texel + vec3(3, -3, -3) * pixel), texture3D(texture, texel + vec3(4, -3, -3) * pixel)); vec4 t01 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -2, -3) * pixel), texture3D(texture, texel + vec3(-2, -2, -3) * pixel), texture3D(texture, texel + vec3(-1, -2, -3) * pixel), texture3D(texture, texel + vec3(0, -2, -3) * pixel), texture3D(texture, texel + vec3(1, -2, -3) * pixel), texture3D(texture, texel + vec3(2, -2, -3) * pixel), texture3D(texture, texel + vec3(3, -2, -3) * pixel), texture3D(texture, texel + vec3(4, -2, -3) * pixel)); vec4 t02 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -1, -3) * pixel), texture3D(texture, texel + vec3(-2, -1, -3) * pixel), texture3D(texture, texel + vec3(-1, -1, -3) * pixel), texture3D(texture, texel + vec3(0, -1, -3) * pixel), texture3D(texture, texel + vec3(1, -1, -3) * pixel), texture3D(texture, texel + vec3(2, -1, -3) * pixel), texture3D(texture, texel + vec3(3, -1, -3) * pixel), texture3D(texture, texel + vec3(4, -1, -3) * pixel)); vec4 t03 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 0, -3) * pixel), texture3D(texture, texel + vec3(-2, 0, -3) * pixel), texture3D(texture, texel + vec3(-1, 0, -3) * pixel), texture3D(texture, texel + vec3(0, 0, -3) * pixel), texture3D(texture, texel + vec3(1, 0, -3) * pixel), texture3D(texture, texel + vec3(2, 0, -3) * pixel), texture3D(texture, texel + vec3(3, 0, -3) * pixel), texture3D(texture, texel + vec3(4, 0, -3) * pixel)); vec4 t04 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 1, -3) * pixel), texture3D(texture, texel + vec3(-2, 1, -3) * pixel), texture3D(texture, texel + vec3(-1, 1, -3) * pixel), texture3D(texture, texel + vec3(0, 1, -3) * pixel), texture3D(texture, texel + vec3(1, 1, -3) * pixel), texture3D(texture, texel + vec3(2, 1, -3) * pixel), texture3D(texture, texel + vec3(3, 1, -3) * pixel), texture3D(texture, texel + vec3(4, 1, -3) * pixel)); vec4 t05 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 2, -3) * pixel), texture3D(texture, texel + vec3(-2, 2, -3) * pixel), texture3D(texture, texel + vec3(-1, 2, -3) * pixel), texture3D(texture, texel + vec3(0, 2, -3) * pixel), texture3D(texture, texel + vec3(1, 2, -3) * pixel), texture3D(texture, texel + vec3(2, 2, -3) * pixel), texture3D(texture, texel + vec3(3, 2, -3) * pixel), texture3D(texture, texel + vec3(4, 2, -3) * pixel)); vec4 t06 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 3, -3) * pixel), texture3D(texture, texel + vec3(-2, 3, -3) * pixel), texture3D(texture, texel + vec3(-1, 3, -3) * pixel), texture3D(texture, texel + vec3(0, 3, -3) * pixel), texture3D(texture, texel + vec3(1, 3, -3) * pixel), texture3D(texture, texel + vec3(2, 3, -3) * pixel), texture3D(texture, texel + vec3(3, 3, -3) * pixel), texture3D(texture, texel + vec3(4, 3, -3) * pixel)); vec4 t07 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 4, -3) * pixel), texture3D(texture, texel + vec3(-2, 4, -3) * pixel), texture3D(texture, texel + vec3(-1, 4, -3) * pixel), texture3D(texture, texel + vec3(0, 4, -3) * pixel), texture3D(texture, texel + vec3(1, 4, -3) * pixel), texture3D(texture, texel + vec3(2, 4, -3) * pixel), texture3D(texture, texel + vec3(3, 4, -3) * pixel), texture3D(texture, texel + vec3(4, 4, -3) * pixel)); vec4 t10 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -3, -2) * pixel), texture3D(texture, texel + vec3(-2, -3, -2) * pixel), texture3D(texture, texel + vec3(-1, -3, -2) * pixel), texture3D(texture, texel + vec3(0, -3, -2) * pixel), texture3D(texture, texel + vec3(1, -3, -2) * pixel), texture3D(texture, texel + vec3(2, -3, -2) * pixel), texture3D(texture, texel + vec3(3, -3, -2) * pixel), texture3D(texture, texel + vec3(4, -3, -2) * pixel)); vec4 t11 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -2, -2) * pixel), texture3D(texture, texel + vec3(-2, -2, -2) * pixel), texture3D(texture, texel + vec3(-1, -2, -2) * pixel), texture3D(texture, texel + vec3(0, -2, -2) * pixel), texture3D(texture, texel + vec3(1, -2, -2) * pixel), texture3D(texture, texel + vec3(2, -2, -2) * pixel), texture3D(texture, texel + vec3(3, -2, -2) * pixel), texture3D(texture, texel + vec3(4, -2, -2) * pixel)); vec4 t12 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -1, -2) * pixel), texture3D(texture, texel + vec3(-2, -1, -2) * pixel), texture3D(texture, texel + vec3(-1, -1, -2) * pixel), texture3D(texture, texel + vec3(0, -1, -2) * pixel), texture3D(texture, texel + vec3(1, -1, -2) * pixel), texture3D(texture, texel + vec3(2, -1, -2) * pixel), texture3D(texture, texel + vec3(3, -1, -2) * pixel), texture3D(texture, texel + vec3(4, -1, -2) * pixel)); vec4 t13 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 0, -2) * pixel), texture3D(texture, texel + vec3(-2, 0, -2) * pixel), texture3D(texture, texel + vec3(-1, 0, -2) * pixel), texture3D(texture, texel + vec3(0, 0, -2) * pixel), texture3D(texture, texel + vec3(1, 0, -2) * pixel), texture3D(texture, texel + vec3(2, 0, -2) * pixel), texture3D(texture, texel + vec3(3, 0, -2) * pixel), texture3D(texture, texel + vec3(4, 0, -2) * pixel)); vec4 t14 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 1, -2) * pixel), texture3D(texture, texel + vec3(-2, 1, -2) * pixel), texture3D(texture, texel + vec3(-1, 1, -2) * pixel), texture3D(texture, texel + vec3(0, 1, -2) * pixel), texture3D(texture, texel + vec3(1, 1, -2) * pixel), texture3D(texture, texel + vec3(2, 1, -2) * pixel), texture3D(texture, texel + vec3(3, 1, -2) * pixel), texture3D(texture, texel + vec3(4, 1, -2) * pixel)); vec4 t15 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 2, -2) * pixel), texture3D(texture, texel + vec3(-2, 2, -2) * pixel), texture3D(texture, texel + vec3(-1, 2, -2) * pixel), texture3D(texture, texel + vec3(0, 2, -2) * pixel), texture3D(texture, texel + vec3(1, 2, -2) * pixel), texture3D(texture, texel + vec3(2, 2, -2) * pixel), texture3D(texture, texel + vec3(3, 2, -2) * pixel), texture3D(texture, texel + vec3(4, 2, -2) * pixel)); vec4 t16 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 3, -2) * pixel), texture3D(texture, texel + vec3(-2, 3, -2) * pixel), texture3D(texture, texel + vec3(-1, 3, -2) * pixel), texture3D(texture, texel + vec3(0, 3, -2) * pixel), texture3D(texture, texel + vec3(1, 3, -2) * pixel), texture3D(texture, texel + vec3(2, 3, -2) * pixel), texture3D(texture, texel + vec3(3, 3, -2) * pixel), texture3D(texture, texel + vec3(4, 3, -2) * pixel)); vec4 t17 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 4, -2) * pixel), texture3D(texture, texel + vec3(-2, 4, -2) * pixel), texture3D(texture, texel + vec3(-1, 4, -2) * pixel), texture3D(texture, texel + vec3(0, 4, -2) * pixel), texture3D(texture, texel + vec3(1, 4, -2) * pixel), texture3D(texture, texel + vec3(2, 4, -2) * pixel), texture3D(texture, texel + vec3(3, 4, -2) * pixel), texture3D(texture, texel + vec3(4, 4, -2) * pixel)); vec4 t20 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -3, -1) * pixel), texture3D(texture, texel + vec3(-2, -3, -1) * pixel), texture3D(texture, texel + vec3(-1, -3, -1) * pixel), texture3D(texture, texel + vec3(0, -3, -1) * pixel), texture3D(texture, texel + vec3(1, -3, -1) * pixel), texture3D(texture, texel + vec3(2, -3, -1) * pixel), texture3D(texture, texel + vec3(3, -3, -1) * pixel), texture3D(texture, texel + vec3(4, -3, -1) * pixel)); vec4 t21 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -2, -1) * pixel), texture3D(texture, texel + vec3(-2, -2, -1) * pixel), texture3D(texture, texel + vec3(-1, -2, -1) * pixel), texture3D(texture, texel + vec3(0, -2, -1) * pixel), texture3D(texture, texel + vec3(1, -2, -1) * pixel), texture3D(texture, texel + vec3(2, -2, -1) * pixel), texture3D(texture, texel + vec3(3, -2, -1) * pixel), texture3D(texture, texel + vec3(4, -2, -1) * pixel)); vec4 t22 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -1, -1) * pixel), texture3D(texture, texel + vec3(-2, -1, -1) * pixel), texture3D(texture, texel + vec3(-1, -1, -1) * pixel), texture3D(texture, texel + vec3(0, -1, -1) * pixel), texture3D(texture, texel + vec3(1, -1, -1) * pixel), texture3D(texture, texel + vec3(2, -1, -1) * pixel), texture3D(texture, texel + vec3(3, -1, -1) * pixel), texture3D(texture, texel + vec3(4, -1, -1) * pixel)); vec4 t23 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 0, -1) * pixel), texture3D(texture, texel + vec3(-2, 0, -1) * pixel), texture3D(texture, texel + vec3(-1, 0, -1) * pixel), texture3D(texture, texel + vec3(0, 0, -1) * pixel), texture3D(texture, texel + vec3(1, 0, -1) * pixel), texture3D(texture, texel + vec3(2, 0, -1) * pixel), texture3D(texture, texel + vec3(3, 0, -1) * pixel), texture3D(texture, texel + vec3(4, 0, -1) * pixel)); vec4 t24 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 1, -1) * pixel), texture3D(texture, texel + vec3(-2, 1, -1) * pixel), texture3D(texture, texel + vec3(-1, 1, -1) * pixel), texture3D(texture, texel + vec3(0, 1, -1) * pixel), texture3D(texture, texel + vec3(1, 1, -1) * pixel), texture3D(texture, texel + vec3(2, 1, -1) * pixel), texture3D(texture, texel + vec3(3, 1, -1) * pixel), texture3D(texture, texel + vec3(4, 1, -1) * pixel)); vec4 t25 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 2, -1) * pixel), texture3D(texture, texel + vec3(-2, 2, -1) * pixel), texture3D(texture, texel + vec3(-1, 2, -1) * pixel), texture3D(texture, texel + vec3(0, 2, -1) * pixel), texture3D(texture, texel + vec3(1, 2, -1) * pixel), texture3D(texture, texel + vec3(2, 2, -1) * pixel), texture3D(texture, texel + vec3(3, 2, -1) * pixel), texture3D(texture, texel + vec3(4, 2, -1) * pixel)); vec4 t26 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 3, -1) * pixel), texture3D(texture, texel + vec3(-2, 3, -1) * pixel), texture3D(texture, texel + vec3(-1, 3, -1) * pixel), texture3D(texture, texel + vec3(0, 3, -1) * pixel), texture3D(texture, texel + vec3(1, 3, -1) * pixel), texture3D(texture, texel + vec3(2, 3, -1) * pixel), texture3D(texture, texel + vec3(3, 3, -1) * pixel), texture3D(texture, texel + vec3(4, 3, -1) * pixel)); vec4 t27 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 4, -1) * pixel), texture3D(texture, texel + vec3(-2, 4, -1) * pixel), texture3D(texture, texel + vec3(-1, 4, -1) * pixel), texture3D(texture, texel + vec3(0, 4, -1) * pixel), texture3D(texture, texel + vec3(1, 4, -1) * pixel), texture3D(texture, texel + vec3(2, 4, -1) * pixel), texture3D(texture, texel + vec3(3, 4, -1) * pixel), texture3D(texture, texel + vec3(4, 4, -1) * pixel)); vec4 t30 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -3, 0) * pixel), texture3D(texture, texel + vec3(-2, -3, 0) * pixel), texture3D(texture, texel + vec3(-1, -3, 0) * pixel), texture3D(texture, texel + vec3(0, -3, 0) * pixel), texture3D(texture, texel + vec3(1, -3, 0) * pixel), texture3D(texture, texel + vec3(2, -3, 0) * pixel), texture3D(texture, texel + vec3(3, -3, 0) * pixel), texture3D(texture, texel + vec3(4, -3, 0) * pixel)); vec4 t31 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -2, 0) * pixel), texture3D(texture, texel + vec3(-2, -2, 0) * pixel), texture3D(texture, texel + vec3(-1, -2, 0) * pixel), texture3D(texture, texel + vec3(0, -2, 0) * pixel), texture3D(texture, texel + vec3(1, -2, 0) * pixel), texture3D(texture, texel + vec3(2, -2, 0) * pixel), texture3D(texture, texel + vec3(3, -2, 0) * pixel), texture3D(texture, texel + vec3(4, -2, 0) * pixel)); vec4 t32 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -1, 0) * pixel), texture3D(texture, texel + vec3(-2, -1, 0) * pixel), texture3D(texture, texel + vec3(-1, -1, 0) * pixel), texture3D(texture, texel + vec3(0, -1, 0) * pixel), texture3D(texture, texel + vec3(1, -1, 0) * pixel), texture3D(texture, texel + vec3(2, -1, 0) * pixel), texture3D(texture, texel + vec3(3, -1, 0) * pixel), texture3D(texture, texel + vec3(4, -1, 0) * pixel)); vec4 t33 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 0, 0) * pixel), texture3D(texture, texel + vec3(-2, 0, 0) * pixel), texture3D(texture, texel + vec3(-1, 0, 0) * pixel), texture3D(texture, texel + vec3(0, 0, 0) * pixel), texture3D(texture, texel + vec3(1, 0, 0) * pixel), texture3D(texture, texel + vec3(2, 0, 0) * pixel), texture3D(texture, texel + vec3(3, 0, 0) * pixel), texture3D(texture, texel + vec3(4, 0, 0) * pixel)); vec4 t34 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 1, 0) * pixel), texture3D(texture, texel + vec3(-2, 1, 0) * pixel), texture3D(texture, texel + vec3(-1, 1, 0) * pixel), texture3D(texture, texel + vec3(0, 1, 0) * pixel), texture3D(texture, texel + vec3(1, 1, 0) * pixel), texture3D(texture, texel + vec3(2, 1, 0) * pixel), texture3D(texture, texel + vec3(3, 1, 0) * pixel), texture3D(texture, texel + vec3(4, 1, 0) * pixel)); vec4 t35 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 2, 0) * pixel), texture3D(texture, texel + vec3(-2, 2, 0) * pixel), texture3D(texture, texel + vec3(-1, 2, 0) * pixel), texture3D(texture, texel + vec3(0, 2, 0) * pixel), texture3D(texture, texel + vec3(1, 2, 0) * pixel), texture3D(texture, texel + vec3(2, 2, 0) * pixel), texture3D(texture, texel + vec3(3, 2, 0) * pixel), texture3D(texture, texel + vec3(4, 2, 0) * pixel)); vec4 t36 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 3, 0) * pixel), texture3D(texture, texel + vec3(-2, 3, 0) * pixel), texture3D(texture, texel + vec3(-1, 3, 0) * pixel), texture3D(texture, texel + vec3(0, 3, 0) * pixel), texture3D(texture, texel + vec3(1, 3, 0) * pixel), texture3D(texture, texel + vec3(2, 3, 0) * pixel), texture3D(texture, texel + vec3(3, 3, 0) * pixel), texture3D(texture, texel + vec3(4, 3, 0) * pixel)); vec4 t37 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 4, 0) * pixel), texture3D(texture, texel + vec3(-2, 4, 0) * pixel), texture3D(texture, texel + vec3(-1, 4, 0) * pixel), texture3D(texture, texel + vec3(0, 4, 0) * pixel), texture3D(texture, texel + vec3(1, 4, 0) * pixel), texture3D(texture, texel + vec3(2, 4, 0) * pixel), texture3D(texture, texel + vec3(3, 4, 0) * pixel), texture3D(texture, texel + vec3(4, 4, 0) * pixel)); vec4 t40 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -3, 1) * pixel), texture3D(texture, texel + vec3(-2, -3, 1) * pixel), texture3D(texture, texel + vec3(-1, -3, 1) * pixel), texture3D(texture, texel + vec3(0, -3, 1) * pixel), texture3D(texture, texel + vec3(1, -3, 1) * pixel), texture3D(texture, texel + vec3(2, -3, 1) * pixel), texture3D(texture, texel + vec3(3, -3, 1) * pixel), texture3D(texture, texel + vec3(4, -3, 1) * pixel)); vec4 t41 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -2, 1) * pixel), texture3D(texture, texel + vec3(-2, -2, 1) * pixel), texture3D(texture, texel + vec3(-1, -2, 1) * pixel), texture3D(texture, texel + vec3(0, -2, 1) * pixel), texture3D(texture, texel + vec3(1, -2, 1) * pixel), texture3D(texture, texel + vec3(2, -2, 1) * pixel), texture3D(texture, texel + vec3(3, -2, 1) * pixel), texture3D(texture, texel + vec3(4, -2, 1) * pixel)); vec4 t42 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -1, 1) * pixel), texture3D(texture, texel + vec3(-2, -1, 1) * pixel), texture3D(texture, texel + vec3(-1, -1, 1) * pixel), texture3D(texture, texel + vec3(0, -1, 1) * pixel), texture3D(texture, texel + vec3(1, -1, 1) * pixel), texture3D(texture, texel + vec3(2, -1, 1) * pixel), texture3D(texture, texel + vec3(3, -1, 1) * pixel), texture3D(texture, texel + vec3(4, -1, 1) * pixel)); vec4 t43 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 0, 1) * pixel), texture3D(texture, texel + vec3(-2, 0, 1) * pixel), texture3D(texture, texel + vec3(-1, 0, 1) * pixel), texture3D(texture, texel + vec3(0, 0, 1) * pixel), texture3D(texture, texel + vec3(1, 0, 1) * pixel), texture3D(texture, texel + vec3(2, 0, 1) * pixel), texture3D(texture, texel + vec3(3, 0, 1) * pixel), texture3D(texture, texel + vec3(4, 0, 1) * pixel)); vec4 t44 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 1, 1) * pixel), texture3D(texture, texel + vec3(-2, 1, 1) * pixel), texture3D(texture, texel + vec3(-1, 1, 1) * pixel), texture3D(texture, texel + vec3(0, 1, 1) * pixel), texture3D(texture, texel + vec3(1, 1, 1) * pixel), texture3D(texture, texel + vec3(2, 1, 1) * pixel), texture3D(texture, texel + vec3(3, 1, 1) * pixel), texture3D(texture, texel + vec3(4, 1, 1) * pixel)); vec4 t45 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 2, 1) * pixel), texture3D(texture, texel + vec3(-2, 2, 1) * pixel), texture3D(texture, texel + vec3(-1, 2, 1) * pixel), texture3D(texture, texel + vec3(0, 2, 1) * pixel), texture3D(texture, texel + vec3(1, 2, 1) * pixel), texture3D(texture, texel + vec3(2, 2, 1) * pixel), texture3D(texture, texel + vec3(3, 2, 1) * pixel), texture3D(texture, texel + vec3(4, 2, 1) * pixel)); vec4 t46 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 3, 1) * pixel), texture3D(texture, texel + vec3(-2, 3, 1) * pixel), texture3D(texture, texel + vec3(-1, 3, 1) * pixel), texture3D(texture, texel + vec3(0, 3, 1) * pixel), texture3D(texture, texel + vec3(1, 3, 1) * pixel), texture3D(texture, texel + vec3(2, 3, 1) * pixel), texture3D(texture, texel + vec3(3, 3, 1) * pixel), texture3D(texture, texel + vec3(4, 3, 1) * pixel)); vec4 t47 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 4, 1) * pixel), texture3D(texture, texel + vec3(-2, 4, 1) * pixel), texture3D(texture, texel + vec3(-1, 4, 1) * pixel), texture3D(texture, texel + vec3(0, 4, 1) * pixel), texture3D(texture, texel + vec3(1, 4, 1) * pixel), texture3D(texture, texel + vec3(2, 4, 1) * pixel), texture3D(texture, texel + vec3(3, 4, 1) * pixel), texture3D(texture, texel + vec3(4, 4, 1) * pixel)); vec4 t50 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -3, 2) * pixel), texture3D(texture, texel + vec3(-2, -3, 2) * pixel), texture3D(texture, texel + vec3(-1, -3, 2) * pixel), texture3D(texture, texel + vec3(0, -3, 2) * pixel), texture3D(texture, texel + vec3(1, -3, 2) * pixel), texture3D(texture, texel + vec3(2, -3, 2) * pixel), texture3D(texture, texel + vec3(3, -3, 2) * pixel), texture3D(texture, texel + vec3(4, -3, 2) * pixel)); vec4 t51 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -2, 2) * pixel), texture3D(texture, texel + vec3(-2, -2, 2) * pixel), texture3D(texture, texel + vec3(-1, -2, 2) * pixel), texture3D(texture, texel + vec3(0, -2, 2) * pixel), texture3D(texture, texel + vec3(1, -2, 2) * pixel), texture3D(texture, texel + vec3(2, -2, 2) * pixel), texture3D(texture, texel + vec3(3, -2, 2) * pixel), texture3D(texture, texel + vec3(4, -2, 2) * pixel)); vec4 t52 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -1, 2) * pixel), texture3D(texture, texel + vec3(-2, -1, 2) * pixel), texture3D(texture, texel + vec3(-1, -1, 2) * pixel), texture3D(texture, texel + vec3(0, -1, 2) * pixel), texture3D(texture, texel + vec3(1, -1, 2) * pixel), texture3D(texture, texel + vec3(2, -1, 2) * pixel), texture3D(texture, texel + vec3(3, -1, 2) * pixel), texture3D(texture, texel + vec3(4, -1, 2) * pixel)); vec4 t53 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 0, 2) * pixel), texture3D(texture, texel + vec3(-2, 0, 2) * pixel), texture3D(texture, texel + vec3(-1, 0, 2) * pixel), texture3D(texture, texel + vec3(0, 0, 2) * pixel), texture3D(texture, texel + vec3(1, 0, 2) * pixel), texture3D(texture, texel + vec3(2, 0, 2) * pixel), texture3D(texture, texel + vec3(3, 0, 2) * pixel), texture3D(texture, texel + vec3(4, 0, 2) * pixel)); vec4 t54 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 1, 2) * pixel), texture3D(texture, texel + vec3(-2, 1, 2) * pixel), texture3D(texture, texel + vec3(-1, 1, 2) * pixel), texture3D(texture, texel + vec3(0, 1, 2) * pixel), texture3D(texture, texel + vec3(1, 1, 2) * pixel), texture3D(texture, texel + vec3(2, 1, 2) * pixel), texture3D(texture, texel + vec3(3, 1, 2) * pixel), texture3D(texture, texel + vec3(4, 1, 2) * pixel)); vec4 t55 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 2, 2) * pixel), texture3D(texture, texel + vec3(-2, 2, 2) * pixel), texture3D(texture, texel + vec3(-1, 2, 2) * pixel), texture3D(texture, texel + vec3(0, 2, 2) * pixel), texture3D(texture, texel + vec3(1, 2, 2) * pixel), texture3D(texture, texel + vec3(2, 2, 2) * pixel), texture3D(texture, texel + vec3(3, 2, 2) * pixel), texture3D(texture, texel + vec3(4, 2, 2) * pixel)); vec4 t56 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 3, 2) * pixel), texture3D(texture, texel + vec3(-2, 3, 2) * pixel), texture3D(texture, texel + vec3(-1, 3, 2) * pixel), texture3D(texture, texel + vec3(0, 3, 2) * pixel), texture3D(texture, texel + vec3(1, 3, 2) * pixel), texture3D(texture, texel + vec3(2, 3, 2) * pixel), texture3D(texture, texel + vec3(3, 3, 2) * pixel), texture3D(texture, texel + vec3(4, 3, 2) * pixel)); vec4 t57 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 4, 2) * pixel), texture3D(texture, texel + vec3(-2, 4, 2) * pixel), texture3D(texture, texel + vec3(-1, 4, 2) * pixel), texture3D(texture, texel + vec3(0, 4, 2) * pixel), texture3D(texture, texel + vec3(1, 4, 2) * pixel), texture3D(texture, texel + vec3(2, 4, 2) * pixel), texture3D(texture, texel + vec3(3, 4, 2) * pixel), texture3D(texture, texel + vec3(4, 4, 2) * pixel)); vec4 t60 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -3, 3) * pixel), texture3D(texture, texel + vec3(-2, -3, 3) * pixel), texture3D(texture, texel + vec3(-1, -3, 3) * pixel), texture3D(texture, texel + vec3(0, -3, 3) * pixel), texture3D(texture, texel + vec3(1, -3, 3) * pixel), texture3D(texture, texel + vec3(2, -3, 3) * pixel), texture3D(texture, texel + vec3(3, -3, 3) * pixel), texture3D(texture, texel + vec3(4, -3, 3) * pixel)); vec4 t61 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -2, 3) * pixel), texture3D(texture, texel + vec3(-2, -2, 3) * pixel), texture3D(texture, texel + vec3(-1, -2, 3) * pixel), texture3D(texture, texel + vec3(0, -2, 3) * pixel), texture3D(texture, texel + vec3(1, -2, 3) * pixel), texture3D(texture, texel + vec3(2, -2, 3) * pixel), texture3D(texture, texel + vec3(3, -2, 3) * pixel), texture3D(texture, texel + vec3(4, -2, 3) * pixel)); vec4 t62 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -1, 3) * pixel), texture3D(texture, texel + vec3(-2, -1, 3) * pixel), texture3D(texture, texel + vec3(-1, -1, 3) * pixel), texture3D(texture, texel + vec3(0, -1, 3) * pixel), texture3D(texture, texel + vec3(1, -1, 3) * pixel), texture3D(texture, texel + vec3(2, -1, 3) * pixel), texture3D(texture, texel + vec3(3, -1, 3) * pixel), texture3D(texture, texel + vec3(4, -1, 3) * pixel)); vec4 t63 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 0, 3) * pixel), texture3D(texture, texel + vec3(-2, 0, 3) * pixel), texture3D(texture, texel + vec3(-1, 0, 3) * pixel), texture3D(texture, texel + vec3(0, 0, 3) * pixel), texture3D(texture, texel + vec3(1, 0, 3) * pixel), texture3D(texture, texel + vec3(2, 0, 3) * pixel), texture3D(texture, texel + vec3(3, 0, 3) * pixel), texture3D(texture, texel + vec3(4, 0, 3) * pixel)); vec4 t64 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 1, 3) * pixel), texture3D(texture, texel + vec3(-2, 1, 3) * pixel), texture3D(texture, texel + vec3(-1, 1, 3) * pixel), texture3D(texture, texel + vec3(0, 1, 3) * pixel), texture3D(texture, texel + vec3(1, 1, 3) * pixel), texture3D(texture, texel + vec3(2, 1, 3) * pixel), texture3D(texture, texel + vec3(3, 1, 3) * pixel), texture3D(texture, texel + vec3(4, 1, 3) * pixel)); vec4 t65 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 2, 3) * pixel), texture3D(texture, texel + vec3(-2, 2, 3) * pixel), texture3D(texture, texel + vec3(-1, 2, 3) * pixel), texture3D(texture, texel + vec3(0, 2, 3) * pixel), texture3D(texture, texel + vec3(1, 2, 3) * pixel), texture3D(texture, texel + vec3(2, 2, 3) * pixel), texture3D(texture, texel + vec3(3, 2, 3) * pixel), texture3D(texture, texel + vec3(4, 2, 3) * pixel)); vec4 t66 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 3, 3) * pixel), texture3D(texture, texel + vec3(-2, 3, 3) * pixel), texture3D(texture, texel + vec3(-1, 3, 3) * pixel), texture3D(texture, texel + vec3(0, 3, 3) * pixel), texture3D(texture, texel + vec3(1, 3, 3) * pixel), texture3D(texture, texel + vec3(2, 3, 3) * pixel), texture3D(texture, texel + vec3(3, 3, 3) * pixel), texture3D(texture, texel + vec3(4, 3, 3) * pixel)); vec4 t67 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 4, 3) * pixel), texture3D(texture, texel + vec3(-2, 4, 3) * pixel), texture3D(texture, texel + vec3(-1, 4, 3) * pixel), texture3D(texture, texel + vec3(0, 4, 3) * pixel), texture3D(texture, texel + vec3(1, 4, 3) * pixel), texture3D(texture, texel + vec3(2, 4, 3) * pixel), texture3D(texture, texel + vec3(3, 4, 3) * pixel), texture3D(texture, texel + vec3(4, 4, 3) * pixel)); vec4 t70 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -3, 4) * pixel), texture3D(texture, texel + vec3(-2, -3, 4) * pixel), texture3D(texture, texel + vec3(-1, -3, 4) * pixel), texture3D(texture, texel + vec3(0, -3, 4) * pixel), texture3D(texture, texel + vec3(1, -3, 4) * pixel), texture3D(texture, texel + vec3(2, -3, 4) * pixel), texture3D(texture, texel + vec3(3, -3, 4) * pixel), texture3D(texture, texel + vec3(4, -3, 4) * pixel)); vec4 t71 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -2, 4) * pixel), texture3D(texture, texel + vec3(-2, -2, 4) * pixel), texture3D(texture, texel + vec3(-1, -2, 4) * pixel), texture3D(texture, texel + vec3(0, -2, 4) * pixel), texture3D(texture, texel + vec3(1, -2, 4) * pixel), texture3D(texture, texel + vec3(2, -2, 4) * pixel), texture3D(texture, texel + vec3(3, -2, 4) * pixel), texture3D(texture, texel + vec3(4, -2, 4) * pixel)); vec4 t72 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, -1, 4) * pixel), texture3D(texture, texel + vec3(-2, -1, 4) * pixel), texture3D(texture, texel + vec3(-1, -1, 4) * pixel), texture3D(texture, texel + vec3(0, -1, 4) * pixel), texture3D(texture, texel + vec3(1, -1, 4) * pixel), texture3D(texture, texel + vec3(2, -1, 4) * pixel), texture3D(texture, texel + vec3(3, -1, 4) * pixel), texture3D(texture, texel + vec3(4, -1, 4) * pixel)); vec4 t73 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 0, 4) * pixel), texture3D(texture, texel + vec3(-2, 0, 4) * pixel), texture3D(texture, texel + vec3(-1, 0, 4) * pixel), texture3D(texture, texel + vec3(0, 0, 4) * pixel), texture3D(texture, texel + vec3(1, 0, 4) * pixel), texture3D(texture, texel + vec3(2, 0, 4) * pixel), texture3D(texture, texel + vec3(3, 0, 4) * pixel), texture3D(texture, texel + vec3(4, 0, 4) * pixel)); vec4 t74 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 1, 4) * pixel), texture3D(texture, texel + vec3(-2, 1, 4) * pixel), texture3D(texture, texel + vec3(-1, 1, 4) * pixel), texture3D(texture, texel + vec3(0, 1, 4) * pixel), texture3D(texture, texel + vec3(1, 1, 4) * pixel), texture3D(texture, texel + vec3(2, 1, 4) * pixel), texture3D(texture, texel + vec3(3, 1, 4) * pixel), texture3D(texture, texel + vec3(4, 1, 4) * pixel)); vec4 t75 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 2, 4) * pixel), texture3D(texture, texel + vec3(-2, 2, 4) * pixel), texture3D(texture, texel + vec3(-1, 2, 4) * pixel), texture3D(texture, texel + vec3(0, 2, 4) * pixel), texture3D(texture, texel + vec3(1, 2, 4) * pixel), texture3D(texture, texel + vec3(2, 2, 4) * pixel), texture3D(texture, texel + vec3(3, 2, 4) * pixel), texture3D(texture, texel + vec3(4, 2, 4) * pixel)); vec4 t76 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 3, 4) * pixel), texture3D(texture, texel + vec3(-2, 3, 4) * pixel), texture3D(texture, texel + vec3(-1, 3, 4) * pixel), texture3D(texture, texel + vec3(0, 3, 4) * pixel), texture3D(texture, texel + vec3(1, 3, 4) * pixel), texture3D(texture, texel + vec3(2, 3, 4) * pixel), texture3D(texture, texel + vec3(3, 3, 4) * pixel), texture3D(texture, texel + vec3(4, 3, 4) * pixel)); vec4 t77 = filter1D_radius4(kernel, index, f.x, texture3D(texture, texel + vec3(-3, 4, 4) * pixel), texture3D(texture, texel + vec3(-2, 4, 4) * pixel), texture3D(texture, texel + vec3(-1, 4, 4) * pixel), texture3D(texture, texel + vec3(0, 4, 4) * pixel), texture3D(texture, texel + vec3(1, 4, 4) * pixel), texture3D(texture, texel + vec3(2, 4, 4) * pixel), texture3D(texture, texel + vec3(3, 4, 4) * pixel), texture3D(texture, texel + vec3(4, 4, 4) * pixel)); vec4 t0 = filter1D_radius4(kernel, index, f.y, t00, t01, t02, t03, t04, t05, t06, t07); vec4 t1 = filter1D_radius4(kernel, index, f.y, t10, t11, t12, t13, t14, t15, t16, t17); vec4 t2 = filter1D_radius4(kernel, index, f.y, t20, t21, t22, t23, t24, t25, t26, t27); vec4 t3 = filter1D_radius4(kernel, index, f.y, t30, t31, t32, t33, t34, t35, t36, t37); vec4 t4 = filter1D_radius4(kernel, index, f.y, t40, t41, t42, t43, t44, t45, t46, t47); vec4 t5 = filter1D_radius4(kernel, index, f.y, t50, t51, t52, t53, t54, t55, t56, t57); vec4 t6 = filter1D_radius4(kernel, index, f.y, t60, t61, t62, t63, t64, t65, t66, t67); vec4 t7 = filter1D_radius4(kernel, index, f.y, t70, t71, t72, t73, t74, t75, t76, t77); return filter1D_radius4(kernel, index, f.z, t0, t1, t2, t3, t4, t5, t6, t7); } vec4 Nearest2D(sampler2D texture, vec2 shape, vec2 uv) { return texture2D(texture, uv); } vec4 Nearest3D(sampler3D texture, vec3 shape, vec3 uv) { return texture3D(texture, uv); } vec4 Linear2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.03125, uv, 1 / shape); } vec4 Linear3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius1(texture, u_kernel, 0.03125, uv, 1 / shape); } vec4 Hanning2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.09375, uv, 1 / shape); } vec4 Hanning3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius1(texture, u_kernel, 0.09375, uv, 1 / shape); } vec4 Hamming2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.15625, uv, 1 / shape); } vec4 Hamming3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius1(texture, u_kernel, 0.15625, uv, 1 / shape); } vec4 Hermite2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.21875, uv, 1 / shape); } vec4 Hermite3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius1(texture, u_kernel, 0.21875, uv, 1 / shape); } vec4 Kaiser2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius1(texture, u_kernel, 0.28125, uv, 1 / shape); } vec4 Kaiser3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius1(texture, u_kernel, 0.28125, uv, 1 / shape); } vec4 Quadric2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.34375, uv, 1 / shape); } vec4 Quadric3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius2(texture, u_kernel, 0.34375, uv, 1 / shape); } vec4 Cubic2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.40625, uv, 1 / shape); } vec4 Cubic3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius2(texture, u_kernel, 0.40625, uv, 1 / shape); } vec4 CatRom2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.46875, uv, 1 / shape); } vec4 CatRom3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius2(texture, u_kernel, 0.46875, uv, 1 / shape); } vec4 Mitchell2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.53125, uv, 1 / shape); } vec4 Mitchell3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius2(texture, u_kernel, 0.53125, uv, 1 / shape); } vec4 Spline162D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.59375, uv, 1 / shape); } vec4 Spline163D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius2(texture, u_kernel, 0.59375, uv, 1 / shape); } vec4 Spline362D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius3(texture, u_kernel, 0.65625, uv, 1 / shape); } vec4 Spline363D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius3(texture, u_kernel, 0.65625, uv, 1 / shape); } vec4 Gaussian2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius2(texture, u_kernel, 0.71875, uv, 1 / shape); } vec4 Gaussian3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius2(texture, u_kernel, 0.71875, uv, 1 / shape); } vec4 Bessel2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius4(texture, u_kernel, 0.78125, uv, 1 / shape); } vec4 Bessel3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius4(texture, u_kernel, 0.78125, uv, 1 / shape); } vec4 Sinc2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius4(texture, u_kernel, 0.84375, uv, 1 / shape); } vec4 Sinc3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius4(texture, u_kernel, 0.84375, uv, 1 / shape); } vec4 Lanczos2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius4(texture, u_kernel, 0.90625, uv, 1 / shape); } vec4 Lanczos3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius4(texture, u_kernel, 0.90625, uv, 1 / shape); } vec4 Blackman2D(sampler2D texture, vec2 shape, vec2 uv) { return filter2D_radius4(texture, u_kernel, 0.96875, uv, 1 / shape); } vec4 Blackman3D(sampler3D texture, vec3 shape, vec3 uv) { return filter3D_radius4(texture, u_kernel, 0.96875, uv, 1 / shape); } vispy-0.14.1/vispy/glsl/misc/regular-grid.frag0000644000175000017510000001525414507104216020600 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/math/0000755000175000017510000000000014507104251015341 5ustar nileshnileshvispy-0.14.1/vispy/glsl/math/signed-segment-distance.glsl0000644000175000017510000000163414507104216022732 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/math/signed-line-distance.glsl0000644000175000017510000000145414507104216022217 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/math/point-to-line-projection.glsl0000644000175000017510000000160714507104216023101 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/math/point-to-line-distance.glsl0000644000175000017510000000162514507104216022517 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/math/functions.glsl0000644000175000017510000000124614507104216020240 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/math/double.glsl0000644000175000017510000000574514507104216017512 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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 https://blog.cyclemap.link/2011-06-09-glsl-part2-emu/. 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 result; } /* ------------------------------------------------------------------------- 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.14.1/vispy/glsl/math/constants.glsl0000644000175000017510000000322114507104216020237 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/math/circle-through-2-points.glsl0000644000175000017510000000166114507104216022621 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/0000755000175000017510000000000014507104251016054 5ustar nileshnileshvispy-0.14.1/vispy/glsl/markers/vbar.glsl0000644000175000017510000000056114507104216017674 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/triangle.glsl0000644000175000017510000000105314507104216020544 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/tag.glsl0000644000175000017510000000067714507104216017525 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/square.glsl0000644000175000017510000000061114507104216020236 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/spade.glsl0000644000175000017510000000202114507104216020027 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/ring.glsl0000644000175000017510000000063114507104216017677 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/pin.glsl0000644000175000017510000000120714507104216017526 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/markers.glsl0000644000175000017510000000152114507104216020403 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/marker.vert0000644000175000017510000000246214507104216020244 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/marker.frag0000644000175000017510000000263614507104216020206 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/marker-sdf.vert0000644000175000017510000000222614507104216021014 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.0*(a_linewidth + 1.5*v_antialias); } vispy-0.14.1/vispy/glsl/markers/marker-sdf.frag0000644000175000017510000000557014507104216020760 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/infinity.glsl0000644000175000017510000000115614507104216020574 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/heart.glsl0000644000175000017510000000121014507104216020035 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/hbar.glsl0000644000175000017510000000056114507104216017656 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/ellipse.glsl0000644000175000017510000000354514507104216020404 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/disc.glsl0000644000175000017510000000053114507104216017661 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/diamond.glsl0000644000175000017510000000073714507104216020362 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/cross.glsl0000644000175000017510000000120514507104216020067 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/club.glsl0000644000175000017510000000217014507104216017665 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/clover.glsl0000644000175000017510000000137714507104216020242 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/chevron.glsl0000644000175000017510000000113114507104216020400 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/asterisk.glsl0000644000175000017510000000126714507104216020573 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/markers/arrow.glsl0000644000175000017510000000100614507104216020067 0ustar nileshnilesh// ----------------------------------------------------------------------------- // 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.14.1/vispy/glsl/lines/0000755000175000017510000000000014507104251015522 5ustar nileshnileshvispy-0.14.1/vispy/glsl/lines/agg.vert0000644000175000017510000001670414507104216017173 0ustar nileshnileshconst 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); // 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; vec4 doc_pos = $transform(vec4(a_position,0.,1.)); //vec2 position = doc_pos.xy * u_scale; vec2 position = doc_pos.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, doc_pos.z, 1.0))); } vispy-0.14.1/vispy/glsl/lines/agg.frag0000644000175000017510000002323714507104216017131 0ustar nileshnilesh#include "math/constants.glsl" 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) < M_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) < M_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) < M_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) < M_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) > M_PI/2.0)) { d = abs(dy); } // Antialias at outer border dx = segment_start - dx; float angle = M_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) > M_PI/2.0) ) { d = abs(dy); } // Antialias at outer border dx = dx - segment_stop; float angle = M_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.14.1/vispy/glsl/colormaps/0000755000175000017510000000000014507104251016407 5ustar nileshnileshvispy-0.14.1/vispy/glsl/colormaps/winter.glsl0000644000175000017510000000122114507104216020577 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/wheel.glsl0000644000175000017510000000131714507104216020401 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/util.glsl0000644000175000017510000000174614507104216020260 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/user.glsl0000644000175000017510000000121414507104216020247 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/summer.glsl0000644000175000017510000000121314507104216020600 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/spring.glsl0000644000175000017510000000121314507104216020572 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/reds.glsl0000644000175000017510000000116514507104216020233 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/parse.py0000644000175000017510000000210614507104216020073 0ustar nileshnileshimport 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(r'\#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 '' if __name__ == "__main__": while re.search(re_include, code): code = re.sub(re_include, replace, code) print(code) vispy-0.14.1/vispy/glsl/colormaps/icefire.glsl0000644000175000017510000000143614507104216020705 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/ice.glsl0000644000175000017510000000113614507104216020034 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/hot.glsl0000644000175000017510000000132114507104216020062 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/greens.glsl0000644000175000017510000000117714507104216020564 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/gray.glsl0000644000175000017510000000113414507104216020234 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/fire.glsl0000644000175000017510000000126014507104216020217 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/cool.glsl0000644000175000017510000000120114507104216020221 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/colormaps.glsl0000644000175000017510000000141514507104216021273 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/color-space.glsl0000644000175000017510000000105114507104216021477 0ustar nileshnileshvec3 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.14.1/vispy/glsl/colormaps/blues.glsl0000644000175000017510000000117214507104216020406 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/colormaps/autumn.glsl0000644000175000017510000000121314507104216020601 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/0000755000175000017510000000000014507104251016726 5ustar nileshnileshvispy-0.14.1/vispy/glsl/collections/tick-labels.vert0000644000175000017510000000445014507104216022026 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/sdf-glyph.vert0000644000175000017510000000321214507104216021524 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/sdf-glyph.frag0000644000175000017510000000451014507104216021465 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/sdf-glyph-ticks.vert0000644000175000017510000000374614507104216022653 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/raw-triangle.vert0000644000175000017510000000122214507104216022222 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/raw-triangle.frag0000644000175000017510000000061014507104216022161 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/raw-segment.vert0000644000175000017510000000130514507104216022061 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/raw-segment.frag0000644000175000017510000000072514507104216022025 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/raw-point.vert0000644000175000017510000000133714507104216021555 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/raw-point.frag0000644000175000017510000000063714507104216021516 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/raw-path.vert0000644000175000017510000000125214507104216021354 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/raw-path.frag0000644000175000017510000000066514507104216021322 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/marker.vert0000644000175000017510000000250214507104216021111 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/marker.frag0000644000175000017510000000267114507104216021057 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-segment.vert0000644000175000017510000000403314507104216022027 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-segment.frag0000644000175000017510000000207314507104216021770 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-point.vert0000644000175000017510000000163514507104216021523 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-point.frag0000644000175000017510000000130314507104216021452 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-path.vert0000644000175000017510000001157314507104216021330 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-path.frag0000644000175000017510000000346614507104216021271 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-marker.vert0000644000175000017510000000245514507104216021654 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-marker.frag0000644000175000017510000000257614507104216021617 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-glyph.vert0000644000175000017510000000160714507104216021514 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-glyph.frag0000644000175000017510000000332414507104216021451 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-fast-path.vert0000644000175000017510000000457014507104216022262 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/collections/agg-fast-path.frag0000644000175000017510000000117614507104216022220 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/build_spatial_filters.py0000644000175000017510000004456414507104216021344 0ustar nileshnilesh#!/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) - Linear (radius 1) - Hanning (radius 1) - Hamming (radius 1) - Hermite (radius 1) - Kaiser (radius 1) - Quadric (radius 1.5) - Cubic (radius 2) - CatRom (radius 2) - Mitchell (radius 2) - Spline16 (radius 2) - Spline36 (radius 4) - Gaussian (radius 2) - Bessel (radius 3.2383) - Sinc (radius 4) - Lanczos (radius 4) - Blackman (radius 4) Note:: Weights code has been translated from the antigrain geometry library available at http://www.antigrain.com/ """ import math import numpy as np from inspect import cleandoc from itertools import product class SpatialFilter: def __init__(self, radius=1): self.radius = math.ceil(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 NotImplementedError def kernel(self, size=4 * 512): samples = int(size / self.radius) n = size # r*samples kernel = np.zeros(n) X = np.linspace(0, self.radius, n) for i in range(n): kernel[i] = self.weight(X[i]) N = np.zeros(samples) for i in range(self.radius): N += kernel[::+1][i * samples:(i + 1) * samples] N += kernel[::-1][i * samples:(i + 1) * samples] for i in range(self.radius): kernel[i * samples:(i + 1) * samples] /= N return kernel def call_code(self, index): code = cleandoc(f''' vec4 {self.__class__.__name__}2D(sampler2D texture, vec2 shape, vec2 uv) {{ return filter2D_radius{self.radius}(texture, u_kernel, {index}, uv, 1 / shape); }} vec4 {self.__class__.__name__}3D(sampler3D texture, vec3 shape, vec3 uv) {{ return filter3D_radius{self.radius}(texture, u_kernel, {index}, uv, 1 / shape); }} ''') return code class Linear(SpatialFilter): """ Linear filter (radius = 1). Weight function:: w(x) = 1 - x """ def weight(self, x): return 1 - x class Hanning(SpatialFilter): """ Hanning filter (radius = 1). Weight function:: w(x) = 0.5 + 0.5 * cos(pi * x) """ def weight(self, x): return 0.5 + 0.5 * math.cos(math.pi * x) class Hamming(SpatialFilter): """ Hamming filter (radius = 1). Weight function:: w(x) = 0.54 + 0.46 * cos(pi * x) """ def weight(self, x): return 0.54 + 0.46 * math.cos(math.pi * x) class Hermite(SpatialFilter): """Hermite filter (radius = 1). Weight function:: w(x) = (2*x-3)*x^2 + 1 """ def weight(self, x): return (2 * x - 3) * x**2 + 1 class Quadric(SpatialFilter): """ Quadric filter (radius = 1.5). Weight function:: | 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): super().__init__(radius=1.5) def weight(self, x): if x < 0.75: return 0.75 - x**2 elif x < 1.5: t = x - 1.5 return 0.5 * t**2 return 0 class Cubic(SpatialFilter): """ Cubic filter (radius = 2). Weight function:: w(x) = 1/6((x+2)^3 - 4*(x+1)^3 + 6*x^3 -4*(x-1)^3) """ def __init__(self): super().__init__(radius=2) def weight(self, x): return (1 / 6) * ( (x + 2)**3 - 4 * (x + 1)**3 + 6 * x**3 - 4 * (x - 1)**3 ) class Kaiser(SpatialFilter): """ Kaiser filter (radius = 1). 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 / self.bessel_i0(b) super().__init__(radius=1) def bessel_i0(self, x): s = 1 y = x**2 / 4 t = y i = 2 while t > self.epsilon: s += t t *= float(y) / i**2 i += 1 return s def weight(self, x): if x > 1: return 0 return self.bessel_i0(self.a * math.sqrt(1 - x**2)) * self.i0a class CatRom(SpatialFilter): """ Catmull-Rom filter (radius = 2). 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): super().__init__(radius=2) def weight(self, x): if x < 1: return 0.5 * (2 + x**2 * (-5 + x * 3)) elif x < 2: return 0.5 * (4 + x * (-8 + x * (5 - x))) else: return 0 class Mitchell(SpatialFilter): """ Mitchell-Netravali filter (radius = 2). 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/3, c=1/3): self.p0 = (6 - 2 * b) / 6 self.p2 = (-18 + 12 * b + 6 * c) / 6 self.p3 = (12 - 9 * b - 6 * c) / 6 self.q0 = (8 * b + 24 * c) / 6 self.q1 = (-12 * b - 48 * c) / 6 self.q2 = (6 * b + 30 * c) / 6 self.q3 = (-b - 6 * c) / 6 super().__init__(radius=2) def weight(self, x): if x < 1: return self.p0 + x**2 * (self.p2 + x * self.p3) elif x < 2: return self.q0 + x * (self.q1 + x * (self.q2 + x * self.q3)) else: return 0 class Spline16(SpatialFilter): """ Spline16 filter (radius = 2). 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): super().__init__(radius=2) def weight(self, x): if x < 1: return ((x - 9/5) * x - 1/5) * x + 1 else: return ((-1/3 * (x - 1) + 4/5) * (x - 1) - 7/15) * (x - 1) class Spline36(SpatialFilter): """ Spline36 filter (radius = 3). 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): super().__init__(radius=3) def weight(self, x): if x < 1: return ((13/11 * x - 453/209) * x - 3/209) * x + 1 elif x < 2: return ((-6/11 * (x - 1) + 270/209) * (x - 1) - 156 / 209) * (x - 1) else: return ((1/11 * (x - 2) - 45/209) * (x - 2) + 26/209) * (x - 2) class Gaussian(SpatialFilter): """ Gaussian filter (radius = 2). 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, 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): super().__init__(radius=2) def weight(self, x): return math.exp(-2 * x**2) * math.sqrt(2 / math.pi) class Bessel(SpatialFilter): """Bessel filter (radius = 3.2383).""" def __init__(self): super().__init__(radius=3.2383) def besj(self, x, n): """Function BESJ calculates Bessel function of first kind of order n. Parameters ---------- x: int value at which the Bessel function is required n : int an integer (>=0), the order Notes ----- 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 x = float(x) # force float type 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 current 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: return math.pi / 4 else: return self.besj(math.pi * x, 1) / (2 * x) class Sinc(SpatialFilter): """Sinc filter (radius = 4).""" def __init__(self): super().__init__(radius=4) def weight(self, x): if x == 0: return 1 x *= math.pi return (math.sin(x) / x) class Lanczos(SpatialFilter): """Lanczos filter (radius = 4).""" def __init__(self): super().__init__(radius=4) def weight(self, x): if x == 0: return 1 elif x > self.radius: return 0 x *= math.pi xr = x / self.radius return (math.sin(x) / x) * (math.sin(xr) / xr) class Blackman(SpatialFilter): """Blackman filter (radius = 4).""" def __init__(self): super().__init__(radius=4) def weight(self, x): if x == 0: return 1 elif x > self.radius: return 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)) def generate_filter_code(radius): n = int(math.ceil(radius)) nl = '\n' # cannot use backslash in fstring code = cleandoc(f''' vec4 filter1D_radius{n}(sampler2D kernel, float index, float x{''.join(f', vec4 c{i}' for i in range(n * 2))}) {{ float w, w_sum = 0; vec4 r = vec4(0); {''.join(f""" w = unpack_interpolate(kernel, vec2({1 - (i + 1) / n} + (x / {n}), index)); w = w * kernel_scale + kernel_bias; r += c{i} * w; w = unpack_interpolate(kernel, vec2({(i + 1) / n} - (x / {n}), index)); w = w * kernel_scale + kernel_bias; r += c{i + n} * w;""" for i in range(n))} return r; }} vec4 filter2D_radius{n}(sampler2D texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {{ vec2 texel = uv / pixel - vec2(0.5); vec2 f = fract(texel); texel = (texel - fract(texel) + vec2(0.001)) * pixel; {''.join(f""" vec4 t{i} = filter1D_radius{n}(kernel, index, f.x{f''.join( f',{nl} texture2D(texture, texel + vec2({-n + 1 + j}, {-n + 1 + i}) * pixel)' for j in range(n * 2))});""" for i in range(n * 2))} return filter1D_radius{n}(kernel, index, f.y{''.join(f', t{i}' for i in range(2*n))}); }} vec4 filter3D_radius{n}(sampler3D texture, sampler2D kernel, float index, vec3 uv, vec3 pixel) {{ vec3 texel = uv / pixel - vec3(0.5); vec3 f = fract(texel); texel = (texel - fract(texel) + vec3(0.001)) * pixel; {''.join(f""" vec4 t{i}{j} = filter1D_radius{n}(kernel, index, f.x{f''.join( f',{nl} texture3D(texture, texel + vec3({-n + 1 + k}, {-n + 1 + j}, {-n + 1 + i}) * pixel)' for k in range(n * 2))});""" for i, j in product(range(n * 2), range(n * 2)))} {f''.join(f""" vec4 t{i} = filter1D_radius{n}(kernel, index, f.y{"".join( f", t{i}{j}" for j in range(n * 2))});""" for i in range(n * 2))} return filter1D_radius{n}(kernel, index, f.z{''.join(f', t{i}' for i in range(2*n))}); }} ''') return code def main(): # Generate kernels texture (16 x 1024) filters = [Linear(), Hanning(), Hamming(), Hermite(), Kaiser(), Quadric(), Cubic(), CatRom(), Mitchell(), Spline16(), Spline36(), Gaussian(), Bessel(), Sinc(), Lanczos(), Blackman()] n = 1024 K = np.zeros((len(filters), 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)) code = cleandoc(f''' // ------------------------------------ // Automatically generated, do not edit // ------------------------------------ const float kernel_bias = {bias}; const float kernel_scale = {scale}; const float kernel_size = {n}; const vec4 bits = vec4(1, {1 / 256}, {1 / (256 * 256)}, {1 / (256 * 256 * 256)}); uniform sampler2D u_kernel; ''') # add basic unpack functions code += '\n\n' + cleandoc(''' float unpack_unit(vec4 rgba) { // return rgba.r; // uncomment this for r32f debugging return dot(rgba, bits); } float unpack_ieee(vec4 rgba) { // return rgba.r; // uncomment this for r32f debugging rgba.rgba = rgba.abgr * 255; float sign = 1 - step(128 , rgba[0]) * 2; float exponent = 2 * mod(rgba[0] , 128) + step(128 , rgba[1]) - 127; float mantissa = mod(rgba[1] , 128) * 65536 + rgba[2] * 256 + rgba[3] + float(0x800000); return sign * exp2(exponent) * (mantissa * exp2(-23.)); } float unpack_interpolate(sampler2D kernel, vec2 uv) { // return texture2D(kernel, uv).r; //uncomment this for r32f debug without interpolation float kpixel = 1. / kernel_size; float u = uv.x / kpixel; float v = uv.y; float uf = fract(u); u = (u - uf) * kpixel; float d0 = unpack_unit(texture2D(kernel, vec2(u, v))); float d1 = unpack_unit(texture2D(kernel, vec2(u + 1. * kpixel, v))); return mix(d0, d1, uf); } ''') # add 1d, 2d and 3d filter code for radius in range(4): code += '\n\n' + generate_filter_code(radius + 1) # add call functions for 2D and 3D filters # special case for nearest code += '\n\n' + cleandoc(''' vec4 Nearest2D(sampler2D texture, vec2 shape, vec2 uv) { return texture2D(texture, uv); } vec4 Nearest3D(sampler3D texture, vec3 shape, vec3 uv) { return texture3D(texture, uv); } ''') for i, f in enumerate(filters): code += '\n\n' + f.call_code((i + 0.5) / 16) print(code) if __name__ == "__main__": import sys sys.exit(main()) vispy-0.14.1/vispy/glsl/arrows/0000755000175000017510000000000014507104251015725 5ustar nileshnileshvispy-0.14.1/vispy/glsl/arrows/util.glsl0000644000175000017510000000625614507104216017577 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/triangle-90.glsl0000644000175000017510000000101214507104216020636 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/triangle-60.glsl0000644000175000017510000000101214507104216020633 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/triangle-30.glsl0000644000175000017510000000101314507104216020631 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/stealth.glsl0000644000175000017510000000334214507104216020257 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/curved.glsl0000644000175000017510000000404314507104216020102 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/common.glsl0000644000175000017510000001437514507104216020113 0ustar nileshnilesh/* ------------------------------------------------------------------------- * 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.14.1/vispy/glsl/arrows/arrows.glsl0000644000175000017510000000106214507104216020125 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/arrow.vert0000644000175000017510000000257014507104216017766 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/arrow.frag0000644000175000017510000000265014507104216017724 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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", // ---------------------------------------------------------------------------- #version 120 #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.14.1/vispy/glsl/arrows/angle-90.glsl0000644000175000017510000000077614507104216020137 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/angle-60.glsl0000644000175000017510000000077614507104216020134 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrows/angle-30.glsl0000644000175000017510000000077714507104216020132 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/arrowheads/0000755000175000017510000000000014507104251016547 5ustar nileshnileshvispy-0.14.1/vispy/glsl/arrowheads/util.glsl0000644000175000017510000000066114507104216020413 0ustar nileshnilesh/** * Copyright (c) Vispy Development Team * Distributed under the (new) BSD License. See LICENSE.txt for more info. * * This is a convencience include file with includes some math helper * functions. */ #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" vispy-0.14.1/vispy/glsl/arrowheads/triangle.glsl0000644000175000017510000000466114507104216021247 0ustar nileshnilesh/** * Copyright (c) Vispy Development Team * Distributed under the (new) BSD License. See LICENSE.txt for more info. * * This file contains the code for drawing complete triangles as arrow heads. * this includes triangles with a top corner of 30, 60 and 90 degrees. */ #include "arrowheads/util.glsl" /** * Computes the signed distance to a triangle arrow. This is a helper function, * and in general you'll use arrow_triangle_30, arrow_triangle_60, or * arrow_triangle_90. * * Parameters: * ----------- * * texcoord * Point to compute distance to * size * Size of the arrow head in pixels * linewidth * Width of the line * antialias * Anti alias width * height * Height of the head (pixel) * * See also * -------- * arrow_triangle_30, arrow_triangle_60, arrow_triangle_90 * * Return: * ------- * Signed distance to the arrow * */ float arrow_triangle(vec2 texcoord, float size, float linewidth, float antialias, float height) { vec2 start = -vec2(size/2.0, 0.0); vec2 end = +vec2(size/2.0, 0.0); // Head : 3 lines vec2 p1 = start + size*vec2(0.0, +height); vec2 p2 = start + size*vec2(0.0, -height); float d1 = line_distance(texcoord, end, p1); float d2 = line_distance(texcoord, p2, end); float d3 = start.x - texcoord.x; return max(max(d1, d2), d3); } /** * Returns the signed distance to an triangle arrow head with a tip corner * of 30 degrees. * * See also * -------- * arrow_triangle, arrow_triangle_60, arrow_triangle_90 */ float arrow_triangle_30(vec2 texcoord, float size, float linewidth, float antialias) { return arrow_triangle(texcoord, size, linewidth, antialias, 0.25); } /** * Returns the signed distance to an triangle arrow head with a tip corner * of 60 degrees. * * See also * -------- * arrow_triangle, arrow_triangle_30, arrow_triangle_90 */ float arrow_triangle_60(vec2 texcoord, float size, float linewidth, float antialias) { return arrow_triangle(texcoord, size, linewidth, antialias, 0.5); } /** * Returns the signed distance to an triangle arrow head with a tip corner * of 90 degrees. * * See also * -------- * arrow_triangle, arrow_triangle_30, arrow_triangle_60 */ float arrow_triangle_90(vec2 texcoord, float size, float linewidth, float antialias) { return arrow_triangle(texcoord, size, linewidth, antialias, 1.0); } vispy-0.14.1/vispy/glsl/arrowheads/stealth.glsl0000644000175000017510000000234614507104216021104 0ustar nileshnilesh/** * Copyright (c) Vispy Development Team * Distributed under the (new) BSD License. See LICENSE.txt for more info. * * This file contains the code for drawing "stealth" type arrow heads. */ #include "arrowheads/util.glsl" /** * Computes the signed distance to an stealth arrow * * Parameters: * ----------- * * texcoord * Point to compute distance to * size * Size of the arrow head in pixels * linewidth * Width of the line * antialias * Anti alias width * * Return: * ------- * Signed distance to the arrow */ float arrow_stealth(vec2 texcoord, float size, float linewidth, float antialias) { vec2 start = -vec2(size/2.0, 0.0); vec2 end = +vec2(size/2.0, 0.0); float height = 0.5; // Head : 4 lines float d1 = line_distance(texcoord, start + size*vec2(0.0, +height), end); float d2 = line_distance(texcoord, start + size*vec2(0.0, +height), start + size*vec2(0.3, 0.0)); float d3 = line_distance(texcoord, start + size*vec2(0.0, -height), end); float d4 = line_distance(texcoord, start + size*vec2(0.0, -height), start + size*vec2(0.25, 0.0)); return max( max(-d1, d3), - max(-d2,d4)); } vispy-0.14.1/vispy/glsl/arrowheads/inhibitor.glsl0000644000175000017510000000123114507104216021417 0ustar nileshnilesh/** * Copyright (c) Vispy Development Team * Distributed under the (new) BSD License. See LICENSE.txt for more info. * * This files contains the code for drawing curved arrow heads. */ #include "arrowheads/util.glsl" float arrow_inhibitor_round(vec2 texcoord, float size, float linewidth, float antialias) { vec2 c = vec2(size/2.0, 0.0); float radius = size/2.0; float radius_inner = radius - linewidth/6.0; float d1 = length(texcoord - c) - radius; float d2 = length(texcoord - c) - radius_inner; float d3 = texcoord.x - (size/2.5); return max(d3, max(d1, -d2)); //return max(d1, -d2); } vispy-0.14.1/vispy/glsl/arrowheads/curved.glsl0000644000175000017510000000231514507104216020724 0ustar nileshnilesh/** * Copyright (c) Vispy Development Team * Distributed under the (new) BSD License. See LICENSE.txt for more info. * * This files contains the code for drawing curved arrow heads. */ #include "arrowheads/util.glsl" /** * Computes the signed distance to an curved arrow * * Parameters: * ----------- * texcoord * Point to compute distance to * size * size of the arrow head in pixels * linewidth * Width of the line * antialias * Anti alias width * * Return: * ------- * Signed distance to the arrow * */ float arrow_curved(vec2 texcoord, float size, float linewidth, float antialias) { vec2 start = -vec2(size/2.0, 0.0); vec2 end = +vec2(size/2.0, 0.0); float height = 0.5; vec2 p1 = start + size*vec2(0.0, -height); vec2 p2 = start + size*vec2(0.0, +height); vec2 p3 = end; // Head : 3 circles vec2 c1 = circle_from_2_points(p1, p3, 6.0*size).zw; float d1 = length(texcoord - c1) - 6.0*size; vec2 c2 = circle_from_2_points(p2, p3, 6.0*size).xy; float d2 = length(texcoord - c2) - 6.0*size; vec2 c3 = circle_from_2_points(p1, p2, 3.0*size).xy; float d3 = length(texcoord - c3) - 3.0*size; return -min(d3, min(d1,d2)); } vispy-0.14.1/vispy/glsl/arrowheads/arrowheads.vert0000644000175000017510000000417014507104216021613 0ustar nileshnilesh/** * Copyright (c) Vispy Development Team * Distributed under the (new) BSD License. See LICENSE.txt for more info. * * This file contains the vertex shader template for arrow heads. * * Variables * --------- * $transform * Projection matrix of vertex to the screen * * Attributes * ---------- * v1 * The first vertex of the arrow body * v2 * The second vertex of the arrow body. This will also be the center * location of the arrow head. Using v1, we determine a direction vector * to automatically determine the orientation. * size * Size of the arrow head in pixels * color * The color of the arrow head * v_linewidth * The width for the stroke or outline of the shape. * * Varyings * -------- * v_size * The arrow head size in pixels * v_point_size * The actual size of the point used for drawing. This is larger than the * given arrow head size to make sure rotating goes well, and allows some * space for anti-aliasing. * v_color * The color for the arrow head * v_orientation * A direction vector for the orientation of the arrow head * v_antialias * Anti-alias width * v_linewidth * Width for the stroke or outline of the shape. */ #include "math/constants.glsl" // Uniforms // ------------------------------------ uniform float antialias; // Attributes // ------------------------------------ attribute vec4 v1; attribute vec4 v2; attribute float size; attribute vec4 color; attribute float linewidth; // Varyings // ------------------------------------ varying float v_size; varying float v_point_size; varying vec4 v_color; varying vec3 v_orientation; varying float v_antialias; varying float v_linewidth; // Main (hooked) // ------------------------------------ void main (void) { v_size = size; v_point_size = M_SQRT2 * size + 2.0 * (linewidth + 2.0*antialias); v_antialias = antialias; v_color = color; v_linewidth = linewidth; vec3 body = $transform(v2).xyz - $transform(v1).xyz; v_orientation = (body / length(body)); gl_Position = $transform(v2); gl_PointSize = v_point_size; } vispy-0.14.1/vispy/glsl/arrowheads/arrowheads.glsl0000644000175000017510000000073514507104216021577 0ustar nileshnilesh// ---------------------------------------------------------------------------- // Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. // Distributed under the (new) BSD License. // ---------------------------------------------------------------------------- #include "arrowheads/util.glsl" #include "arrowheads/curved.glsl" #include "arrowheads/stealth.glsl" #include "arrowheads/triangle.glsl" #include "arrowheads/angle.glsl" #include "arrowheads/inhibitor.glsl" vispy-0.14.1/vispy/glsl/arrowheads/arrowheads.frag0000644000175000017510000000343514507104216021555 0ustar nileshnilesh/** * Copyright (c) Vispy Development Team * Distributed under the (new) BSD License. See LICENSE.txt for more info. * * This file contains the fragment shader template for arrow heads. * * Variables * --------- * * $arrow_type * The type of arrow head. Examples incude: curved, stealth, triangle_30 * and more. * $fill_type * How to fill the arrow head. Possible values: "filled", "outline" or * "stroke". * * Varyings * -------- * v_size * The arrow head size in pixels * v_point_size * The actual size of the point used for drawing. This is larger than the * given arrow head size to make sure rotating goes well, and allows some * space for anti-aliasing. * v_color * The color for the arrow head * v_orientation * A direction vector for the orientation of the arrow head * v_antialias * Anti-alias width * v_linewidth * Width for the stroke or outline of the shape. */ #version 120 #include "math/constants.glsl" #include "arrowheads/arrowheads.glsl" #include "antialias/antialias.glsl" // Varyings // ------------------------------------ varying float v_size; varying float v_point_size; varying vec4 v_color; varying vec3 v_orientation; varying float v_antialias; varying float v_linewidth; void main() { // 1. Move the origin to the center of the point // 2. Rotate the canvas for drawing the arrow // 3. Scale the coordinates with v_point_size 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_point_size; float distance = arrow_$arrow_type(P, v_size, v_linewidth, v_antialias); gl_FragColor = $fill_type(distance, v_linewidth, v_antialias, v_color, v_color); } vispy-0.14.1/vispy/glsl/arrowheads/angle.glsl0000644000175000017510000000516214507104216020525 0ustar nileshnilesh/** * Copyright (c) Vispy Development Team * Distributed under the (new) BSD License. See LICENSE.txt for more info. * * This file contains the shader code for drawing "angle" arrow heads, which * are drawn as two lines moving away from the arrow tip under a certain * angle. */ #include "arrowheads/util.glsl" /** * Computes the signed distance to an angle arrow. This is a helper function, * and in general you'll use arrow_angle_30, arrow_angle_60 or arrow_angle_90. * * Parameters: * ----------- * texcoord * Point to compute distance to * size * Size of the arrow head in pixels * height * Height of the head (pixel) * * See also * -------- * arrow_angle_30, arrow_angle_60, arrow_angle_90 * * Return: * ------- * Signed distance to the arrow */ float arrow_angle(vec2 texcoord, float size, float linewidth, float antialias, float height) { float d; vec2 start = -vec2(size/2.0, 0.0); vec2 end = +vec2(size/2.0, 0.0); vec2 p1 = start + size*vec2(0.0, +height); vec2 p2 = start + size*vec2(0.0, -height); // Arrow tip (beyond segment end) if( texcoord.x > size/2.0) { // Head : 2 segments float d1 = line_distance(texcoord, end, p1); float d2 = line_distance(texcoord, p2, 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, p1, end); float d2 = segment_distance(texcoord, p2, end); // Body : 1 segment float d3 = segment_distance(texcoord, vec2(0.0, 0.0), end); d = min(min(d1,d2), d3); } return d; } /** * Returns the distance to an arrow with tip corner of 30 degrees * * See also * -------- * arro_angle, arrow_angle_60, arrow_angle_90 */ float arrow_angle_30(vec2 texcoord, float size, float linewidth, float antialias) { return arrow_angle(texcoord, size, linewidth, antialias, 0.25); } /** * Returns the distance to an arrow with tip corner of 60 degrees * * See also * -------- * arro_angle, arrow_angle_30, arrow_angle_90 */ float arrow_angle_60(vec2 texcoord, float size, float linewidth, float antialias) { return arrow_angle(texcoord, size, linewidth, antialias, 0.5); } /** * Returns the distance to an arrow with tip corner of 90 degrees * * See also * -------- * arro_angle, arrow_angle_30, arrow_angle_60 */ float arrow_angle_90(vec2 texcoord, float size, float linewidth, float antialias) { return arrow_angle(texcoord, size, linewidth, antialias, 1.0); } vispy-0.14.1/vispy/glsl/antialias/0000755000175000017510000000000014507104251016355 5ustar nileshnileshvispy-0.14.1/vispy/glsl/antialias/stroke.glsl0000644000175000017510000000246514507104216020557 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/antialias/outline.glsl0000644000175000017510000000244314507104216020723 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/antialias/filled.glsl0000644000175000017510000000233714507104216020505 0ustar nileshnilesh/** * Copyright (c) Vispy Development Team * Distributed under the (new) BSD License. See LICENSE.txt for more info. * * This file contains code for filling a shape with anti-alias. */ /** * 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) * bg_color * 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.14.1/vispy/glsl/antialias/caps.glsl0000644000175000017510000000325714507104216020176 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/antialias/cap.glsl0000644000175000017510000000325714507104216020013 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/antialias/cap-triangle-out.glsl0000644000175000017510000000174214507104216022420 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/antialias/cap-triangle-in.glsl0000644000175000017510000000175214507104216022220 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/antialias/cap-square.glsl0000644000175000017510000000171214507104216021303 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/antialias/cap-round.glsl0000644000175000017510000000163614507104216021137 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/antialias/cap-butt.glsl0000644000175000017510000000171214507104216020761 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/antialias/antialias.glsl0000644000175000017510000000056014507104216021207 0ustar nileshnilesh// ---------------------------------------------------------------------------- // 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.14.1/vispy/glsl/__init__.py0000644000175000017510000000225514507104216016526 0ustar nileshnilesh# -*- 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 .. 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.14.1/vispy/gloo/0000755000175000017510000000000014507104251014407 5ustar nileshnileshvispy-0.14.1/vispy/gloo/wrappers.py0000644000175000017510000006353414507104216016640 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 ..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', 'GL_PRESETS', '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_state will need to be updated to deal with it. #: Some OpenGL state presets for common use cases: 'opaque', 'translucent', #: 'additive'. #: #: To be used in :func:`.set_state`. 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', 'zero', 'one'), blend_equation='func_add'), 'additive': dict( depth_test=False, cull_face=False, blend=True, blend_func=('src_alpha', 'one'), blend_equation='func_add'), } 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, str)] 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 :data:`presets <.GL_PRESETS>`. Returns ------- presets : dict The dictionary of presets usable with :func:`.set_state`. """ return deepcopy(GL_PRESETS) def set_state(self, preset=None, **kwargs): """Set the OpenGL rendering state, optionally using a preset. Parameters ---------- preset : {'opaque', 'translucent', 'additive'}, optional A named state :data:`preset <.GL_PRESETS>` for 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 :func:`.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)) # Line width needs some special care ... if 'line_width' in kwargs: line_width = kwargs.pop('line_width') self.glir.command('FUNC', 'glLineWidth', line_width) if 'line_smooth' in kwargs: line_smooth = kwargs.pop('line_smooth') funcname = 'glEnable' if line_smooth else 'glDisable' line_smooth_enum_value = 2848 # int(GL.GL_LINE_SMOOTH) self.glir.command('FUNC', funcname, line_smooth_enum_value) # 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], str): 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, str) 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, mode='color', 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). This only effects the color mode. mode : str Type of buffer data to read. Can be one of 'colors', 'depth', or 'stencil'. See returns for more information. 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) for colors mode, with the top-left corner of the framebuffer at index [0, 0] in the returned array. If 'mode' is depth or stencil then the last dimension is 1. """ _check_valid('mode', mode, ['color', 'depth', 'stencil']) # 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 if mode == 'depth': fmt = gl.GL_DEPTH_COMPONENT shape = (h, w, 1) elif mode == 'stencil': fmt = gl.GL_STENCIL_INDEX8 shape = (h, w, 1) elif alpha: fmt = gl.GL_RGBA shape = (h, w, 4) else: fmt = gl.GL_RGB shape = (h, w, 3) 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 = shape 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() canvas = get_current_canvas() fbo = 0 if canvas is None else canvas._backend._vispy_get_fb_bind_location() gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, fbo) 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(): try: param = fb_param(gl.GL_FRAMEBUFFER, val[0], val[1]) gl.check_error('post-config check') except RuntimeError as exp: logger.warning('Failed to get size %s: %s' % (key, exp)) else: config[key + '_size'] = param try: val = fb_param(gl.GL_FRAMEBUFFER, GL_FRONT_LEFT, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) gl.check_error('post-config check') except RuntimeError as exp: logger.warning('Failed to get sRGB: %s' % (exp,)) else: if val not in (gl.GL_LINEAR, GL_SRGB): logger.warning('unknown value for SRGB: %s' % val) else: config['srgb'] = (val == GL_SRGB) for key, enum in (('stereo', GL_STEREO), ('double_buffer', GL_DOUBLEBUFFER)): val = gl.glGetParameter(enum) try: gl.check_error('post-config check') except RuntimeError as exp: logger.warning('Failed to get %s: %s' % (key, exp)) else: config[key] = bool(val) config['samples'] = gl.glGetParameter(gl.GL_SAMPLES) gl.check_error('post-config check') return config vispy-0.14.1/vispy/gloo/util.py0000644000175000017510000001050314507104216015736 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- 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, str): res = enum.lower() # Check if res is None: raise ValueError('Could not determine string representation 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.14.1/vispy/gloo/texture.py0000644000175000017510000011302614507104216016465 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 .util import check_enum def get_dtype_limits(dtype): if np.issubdtype(dtype, np.floating): info = np.finfo(dtype) else: info = np.iinfo(dtype) return info.min, info.max def convert_dtype_and_clip(data, dtype, copy=False): """ cast dtype to a new one, but first clip data to the new dtype's limits if needed """ old_min, old_max = get_dtype_limits(data.dtype) new_min, new_max = get_dtype_limits(dtype) if new_max >= old_max and new_min <= old_min: # no need to clip return np.array(data, dtype=dtype, copy=copy) else: # to reduce copying, we clip into a pre-generated array of the right dtype new_data = np.empty_like(data, dtype=dtype) # allow "unsafe" casting here as we're explicitly clipping to the # range of the new dtype - this was a default before numpy 1.25 np.clip(data, new_min, new_max, out=new_data, casting="unsafe") return new_data def downcast_to_32bit_if_needed(data, copy=False, dtype=None): """Downcast to 32bit dtype if necessary.""" if dtype is None: dtype = data.dtype dtype = np.dtype(dtype) if dtype.itemsize > 4: warnings.warn( f"GPUs can't support dtypes bigger than 32-bit, but got '{dtype}'. " "Precision will be lost due to downcasting to 32-bit.", stacklevel=2, ) size = min(dtype.itemsize, 4) kind = dtype.kind new_dtype = np.dtype(f'{kind}{size}') return convert_dtype_and_clip(data, new_dtype, copy=copy) 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, 'depth_component': 1, } # NOTE: non-normalized formats ending with 'i' and 'ui' are currently # disabled as they don't work with the current VisPy implementation. # Attempting to use them along with the additional enums defined in # vispy/gloo/glir.py produces an invalid operation from OpenGL. _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'] # , '8i', '8ui', '32i', '32ui'] ] + [ ('luminance', 1), ('alpha', 1), ('red', 1), ('luminance_alpha', 2), ('rg', 2), ('rgb', 3), ('rgba', 4), ('depth_component', 1), ]) 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, stacklevel=2, ) # 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 internalformat(self): """The texture internalformat.""" return self._internalformat @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, str): 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, str): 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 _check_format_change(self, format, num_channels): # Determine format if format is None: format = self._formats[num_channels] # 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 format not in self._inv_formats: raise ValueError('Invalid texture format: %r.' % format) elif num_channels != self._inv_formats[format]: raise ValueError('Format does not match with given shape. ' '(format expects %d elements, data has %d)' % (self._inv_formats[format], num_channels)) return format def _check_internalformat_change(self, internalformat, num_channels): if internalformat is None: # Keep current internalformat if channels match if self._internalformat and \ self._inv_internalformats[self._internalformat] == num_channels: internalformat = self._internalformat else: internalformat = check_enum(internalformat) if internalformat is None: pass elif internalformat not in self._inv_internalformats: raise ValueError( 'Invalid texture internalformat: %r. Allowed formats: %r' % (internalformat, self._inv_internalformats) ) elif num_channels != self._inv_internalformats[internalformat]: raise ValueError('Internalformat does not match with given shape.') return 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") format = self._check_format_change(format, shape[-1]) internalformat = self._check_internalformat_change(internalformat, shape[-1]) # 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 implicitly 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 = downcast_to_32bit_if_needed(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' # --------------------------------------------------------- TextureCube class --- class TextureCube(BaseTexture): """Texture Cube 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 = 'TextureCube' 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) if self._shape[0] != 6: raise ValueError("Texture cube require arrays first dimension to be 6 :" " {} was given.".format(self._shape[0])) @property def height(self): """Texture height""" return self._shape[1] @property def width(self): """Texture width""" return self._shape[2] @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', 'samplerCube' @property def glsl_sampler_type(self): """GLSL type of the sampler.""" return 'samplerCube' @property def glsl_sample(self): """GLSL function that samples the texture.""" return 'textureCube' # ------------------------------------------------- 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] # On Windows with Python 2.7, self._c can end up being a long # integer because Numpy array shapes return long integers. This # causes issues when setting the gloo variables since these are # expected to be native ints, so we cast the integers to ints # to avoid this. # Newer GLSL compilers do not implicitly cast types so these integers # must be converted to floats lastly self._glsl_sample['c'] = float(int(self._c)) self._glsl_sample['r'] = float(int(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). dtype : numpy.dtype object Texture starting data type (default: float32) 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), dtype=np.float32): 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, dtype) 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.14.1/vispy/gloo/tests/0000755000175000017510000000000014507104251015551 5ustar nileshnileshvispy-0.14.1/vispy/gloo/tests/test_wrappers.py0000644000175000017510000002076114507104216021034 0ustar nileshnilesh# -*- 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.0], [0, 1, 0.5], # For drawing a square to top left [-1, 0, 0.0], [0, 0, 0.5]], np.float32) VERT_SHADER = """ // simple vertex shader attribute vec3 a_position; void main (void) { gl_Position = vec4(a_position, 1.0); } """ FRAG_SHADER = """ // simple fragment shader void main() { gl_FragColor = vec4(1,1,1,1); } """ with Canvas() as c: c.set_current() gloo.set_viewport(0, 0, *c.size) gloo.set_state(depth_test=True) 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() # Check that we can read the depth buffer img = read_pixels(mode='depth') assert_equal(img.shape[:2], c.size[::-1]) assert_equal(img.shape[2], 1) unique_img = np.unique(img) # we should have quite a few different depth values assert unique_img.shape[0] > 50 assert unique_img.max() == 255 assert unique_img.min() > 0 run_tests_if_main() vispy-0.14.1/vispy/gloo/tests/test_util.py0000644000175000017510000000357114507104216020146 0ustar nileshnilesh# -*- 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.14.1/vispy/gloo/tests/test_use_gloo.py0000644000175000017510000001453714507104216021011 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import sys import numpy as np from numpy.testing import assert_allclose import pytest 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, IS_TRAVIS_CI) @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: c.app.process_events() c.set_current() if c.app.backend_name.lower() == 'pyqt5': # PyQt5 on OSX for some reason sets this to 1024x768... c.size = shape[::-1] c.app.process_events() 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 c.size == shape[::-1] c.set_current() 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 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.) @pytest.mark.xfail(IS_TRAVIS_CI and 'darwin' in sys.platform, reason='Travis OSX causes segmentation fault on this test for an unknown reason.') @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 = (500, 500) with Canvas(size=shape) as c: c.set_current() 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') c.set_current() 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.14.1/vispy/gloo/tests/test_texture.py0000644000175000017510000006012414507104216020666 0ustar nileshnilesh# -*- 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 import pytest 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'), (1, 'depth_component'), ] ) # ------------------------------------------------ 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']), (1, 'depth_component', ['depth_component']), ] 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)) @requires_pyopengl() @pytest.mark.parametrize('input_dtype', [np.uint8, np.uint16, np.float32, np.float64]) @pytest.mark.parametrize('output_dtype', [np.uint8, np.uint16, np.float32, np.float64]) @pytest.mark.parametrize('ndim', [2, 3]) def test_texture_set_data_different_dtype(input_dtype, output_dtype, ndim): shape = (20,) * ndim data = np.random.rand(*shape).astype(input_dtype) Texture = Texture2D if ndim == 2 else Texture3D tex = Texture(data) tex[:10] = np.array(1, dtype=output_dtype) tex.set_data(data.astype(output_dtype)) run_tests_if_main() vispy-0.14.1/vispy/gloo/tests/test_program.py0000644000175000017510000002664414507104216020646 0ustar nileshnilesh# -*- 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, requires_application from vispy.gloo.context import set_current_canvas, forget_canvas class DummyParser(gloo.glir.BaseGlirParser): @property def shader_compatibility(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): from vispy.gloo.program import VertexShader, FragmentShader # Test ok init, no shaders program = Program() assert program._user_variables == {} assert program._code_variables == {} assert program._pending_variables == {} assert program.shaders[0] is None assert program.shaders[1] is None # Test ok init, with shader program = Program('A', 'B') assert isinstance(program.shaders[0], VertexShader) assert program.shaders[0].code == 'A' assert isinstance(program.shaders[1], FragmentShader) assert program.shaders[1].code == '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): from vispy.gloo.program import VertexShader, FragmentShader program = Program("A", "B") assert isinstance(program.shaders[0], VertexShader) assert program.shaders[0].code == 'A' assert isinstance(program.shaders[1], FragmentShader) assert program.shaders[1].code == 'B' program.set_shaders('C', 'D') assert program.shaders[0].code == "C" assert program.shaders[1].code == "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 'error on this line' in 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_varyings(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_type_aliases(self): program = Program("in bool A; out float B;", "foo") # in aliased to attribute, out to varying assert ('attribute', 'bool', 'A') in program.variables assert ('varying', 'float', 'B') in program.variables 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[-2]) == 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[-2]) == 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.14.1/vispy/gloo/tests/test_globject.py0000644000175000017510000000207614507104216020761 0ustar nileshnilesh# -*- 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.14.1/vispy/gloo/tests/test_glir.py0000644000175000017510000002265414507104216020131 0ustar nileshnilesh# -*- coding: utf-8 -*- import json import tempfile from unittest import mock from vispy import config from vispy.app import Canvas from vispy.gloo import glir from vispy.testing import requires_application, requires_pyopengl, run_tests_if_main import numpy as np 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._shared._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._shared._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 = glir.convert_shader('desktop', shader1) assert 'highp' not in shader2 assert 'mediump' not in shader2 assert 'precision' not in shader2 # Convert for es2 shader3 = glir.convert_shader('es2', shader2) # make sure precision float is still in the shader # it may not be the first (precision int might be there) assert 'precision highp float;' in shader3 # precisions must come before code assert shader3.startswith('precision') # Define shader with version number shader4 = """ #version 100; precision highp float;uniform mediump vec4 u_foo;uniform vec4 u_bar; """.strip().replace(';', ';\n') shader5 = glir.convert_shader('es2', shader4) assert 'precision highp float;' in shader5 # make sure that precision is first (version is removed) # precisions must come before code assert shader3.startswith('precision') @requires_application() def test_log_parser(): """Test GLIR log parsing""" 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 # The FBO argument may be anything based on the backend. expected = json.dumps(['CURRENT', 0, 1]) assert len(lines[i]) >= len(expected) expected = expected.split('1') assert lines[i].startswith(expected[0]) assert lines[i].endswith(expected[1]) assert int(lines[i][len(expected[0]):-len(expected[1])]) is not None # The 'CURRENT' command may have been called multiple times while lines[i].startswith('["CURRENT",'): i += 1 if lines[i] == json.dumps(['FUNC', 'colorMask', False, False, False, True]): # Qt workaround, see #2040 i += 4 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='') glir_file.close() @requires_application() def test_capabilities(): """Test GLIR capability reporting""" with Canvas() as c: capabilities = c.context.shared.parser.capabilities assert capabilities['max_texture_size'] is not None assert capabilities['gl_version'] != 'unknown' @requires_pyopengl() @mock.patch('vispy.gloo.glir._check_pyopengl_3D') @mock.patch('vispy.gloo.glir.gl') def test_texture1d_alignment(gl, check3d): """Test that textures set unpack alignment properly. See https://github.com/vispy/vispy/pull/1758 """ from ..glir import GlirTexture1D check3d.return_value = check3d t = GlirTexture1D(mock.MagicMock(), 3) shape = (393, 1) t.set_size(shape, 'luminance', 'luminance') t.set_data((0,), np.zeros(shape, np.float32)) # number of elements doesn't matter, only data type gl.glPixelStorei.assert_not_called() gl.glPixelStorei.reset_mock() # now with bytes t.set_data((0,), np.zeros(shape, np.uint8)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 1), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() # different shape shape = (394, 1) t.set_size(shape, 'luminance', 'luminance') t.set_data((0,), np.zeros(shape, np.float32)) # number of elements doesn't matter, only data type gl.glPixelStorei.assert_not_called() gl.glPixelStorei.reset_mock() t.set_data((0,), np.zeros(shape, np.uint8)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 1), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() @requires_pyopengl() @mock.patch('vispy.gloo.glir._check_pyopengl_3D') @mock.patch('vispy.gloo.glir.gl') def test_texture2d_alignment(gl, check3d): """Test that textures set unpack alignment properly. See https://github.com/vispy/vispy/pull/1758 """ from ..glir import GlirTexture2D check3d.return_value = gl t = GlirTexture2D(mock.MagicMock(), 3) shape = (296, 393, 1) t.set_size(shape, 'luminance', 'luminance') t.set_data((0, 0), np.zeros(shape, np.float32)) # alignment should have been 4 which is the default gl.glPixelStorei.assert_not_called() gl.glPixelStorei.reset_mock() # now with bytes t.set_data((0, 0), np.zeros(shape, np.uint8)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 1), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() # different shape shape = (296, 394, 1) t.set_size(shape, 'luminance', 'luminance') t.set_data((0, 0), np.zeros(shape, np.float32)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 8), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() t.set_data((0, 0), np.zeros(shape, np.uint8)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 2), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() @requires_pyopengl() @mock.patch('vispy.gloo.glir._check_pyopengl_3D') @mock.patch('vispy.gloo.glir.gl') def test_texture3d_alignment(gl, check3d): """Test that textures set unpack alignment properly. See https://github.com/vispy/vispy/pull/1758 """ from ..glir import GlirTexture3D check3d.return_value = gl t = GlirTexture3D(mock.MagicMock(), 3) shape = (68, 296, 393, 1) t.set_size(shape, 'luminance', 'luminance') t.set_data((0, 0, 0), np.zeros(shape, np.float32)) # alignment should have been 4 which is the default gl.glPixelStorei.assert_not_called() gl.glPixelStorei.reset_mock() # now with bytes t.set_data((0, 0, 0), np.zeros(shape, np.uint8)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 1), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() # different shape shape = (68, 296, 394, 1) t.set_size(shape, 'luminance', 'luminance') t.set_data((0, 0, 0), np.zeros(shape, np.float32)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 8), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() t.set_data((0, 0, 0), np.zeros(shape, np.uint8)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 2), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() @requires_pyopengl() @mock.patch('vispy.gloo.glir._check_pyopengl_3D') @mock.patch('vispy.gloo.glir.gl') def test_texture_cube_alignment(gl, check3d): """Test that textures set unpack alignment properly. See https://github.com/vispy/vispy/pull/1758 """ from ..glir import GlirTextureCube check3d.return_value = gl t = GlirTextureCube(mock.MagicMock(), 3) shape = (68, 296, 393, 1) t.set_size(shape, 'luminance', 'luminance') t.set_data((0, 0, 0), np.zeros(shape, np.float32)) # alignment should have been 4 which is the default gl.glPixelStorei.assert_not_called() gl.glPixelStorei.reset_mock() # now with bytes t.set_data((0, 0, 0), np.zeros(shape, np.uint8)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 1), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() # different shape shape = (68, 296, 394, 1) t.set_size(shape, 'luminance', 'luminance') t.set_data((0, 0, 0), np.zeros(shape, np.float32)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 8), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() t.set_data((0, 0, 0), np.zeros(shape, np.uint8)) gl.glPixelStorei.assert_has_calls([ mock.call(gl.GL_UNPACK_ALIGNMENT, 2), mock.call(gl.GL_UNPACK_ALIGNMENT, 4), ]) gl.glPixelStorei.reset_mock() # The rest is basically tested via our examples run_tests_if_main() vispy-0.14.1/vispy/gloo/tests/test_framebuffer.py0000644000175000017510000001255214507104216021454 0ustar nileshnilesh# -*- 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 == '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' # Also with Texture for depth T1 = gloo.Texture2D((20, 30, 3)) T2 = gloo.Texture2D((20, 30, 1)) assert T1.format == 'rgb' assert T2.format == 'luminance' F = FrameBuffer(T1, T2) assert F.shape == T1.shape[:2] assert F.shape == T2.shape[:2] assert T1.format == 'rgb' assert T2.format == 'luminance' # Resize F.resize((10, 10)) assert F.shape == (10, 10) assert T1.shape == (10, 10, 3) assert T2.shape == (10, 10, 1) assert T1.format == 'rgb' assert T2.format == 'luminance' # 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.14.1/vispy/gloo/tests/test_context.py0000644000175000017510000000571014507104216020652 0ustar nileshnilesh# -*- 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'}) # Capabilites are passed on assert 'gl_version' in c.capabilities 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.14.1/vispy/gloo/tests/test_buffer.py0000644000175000017510000004655314507104216020451 0ustar nileshnilesh# -*- 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 # the below is no longer supported starting with NumPy 1.14.0 # this used to be converted to a single element array of the above # dtype filled with 0s. This is no longer supported. # B[1] = b'' # Gets converted into array of dtype. Lists do not work # the below doesn't work on all systems (I guess?) # B[1] = 0 B[1] = ([0, 0, 0], [0, 0], [0, 0, 0, 0],) 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.14.1/vispy/gloo/tests/__init__.py0000644000175000017510000000000014507104216017651 0ustar nileshnileshvispy-0.14.1/vispy/gloo/program.py0000644000175000017510000005346414507104216016445 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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, TextureCube from ..util import logger from .util import check_enum from .context import get_current_canvas from .preprocessor import preprocess # ------------------------------------------------------------ Constants --- REGEX_VAR = {} for kind in ('uniform', 'attribute', 'varying', 'const', 'in', 'out'): REGEX_VAR[kind] = re.compile(fr"\s*{kind}\s+" # kind of variable r"((highp|mediump|lowp)\s+)?" # Precision (optional) r"(?P\w+)\s+" # type r"(?P\w+)\s*" # name r"(\[(?P\d+)\])?" # size (optional) r"(\s*\=\s*[0-9.]+)?" # default value (optional) r"\s*;", # end flags=re.MULTILINE) # ------------------------------------------------------------ Shader class --- class Shader(GLObject): def __init__(self, code=None): GLObject.__init__(self) if code is not None: self.code = code @property def code(self): return self._code @code.setter def code(self, code): self._code = preprocess(code) # use hardcoded offset of 0 to match other GLIR DATA commands self._glir.command('DATA', self._id, 0, self._code) class VertexShader(Shader): _GLIR_TYPE = 'VertexShader' class FragmentShader(Shader): _GLIR_TYPE = 'FragmentShader' class GeometryShader(Shader): _GLIR_TYPE = 'GeometryShader' # ----------------------------------------------------------- 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.int32, 1), 'bvec2': (bool, 2), 'bvec3': (bool, 3), 'bvec4': (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), 'samplerCube': (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 = None, None # 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, str) and isinstance(frag, str): 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) if numel != 1 else (name, dt)) self._buffer = np.zeros(self._count, dtype=dtype) self.bind(VertexBuffer(self._buffer)) def set_shaders(self, vert, frag, geom=None, update_variables=True): """Set the vertex and fragment shaders. Parameters ---------- vert : str Source code for vertex shader. frag : str Source code for fragment shaders. geom : str (optional) Source code for geometry shader. update_variables : bool If True, then process any pending variables immediately after setting shader code. Default is True. """ if not vert or not frag: raise ValueError('Vertex and fragment code must both be non-empty') # pre-process shader code for #include directives shaders = [VertexShader(vert), FragmentShader(frag)] if geom is not None: shaders.append(GeometryShader(geom)) for shader in shaders: self.glir.associate(shader.glir) self._glir.command('ATTACH', self._id, shader.id) # Store source code, send it to glir, parse the code for variables self._shaders = shaders # Link all shaders into one program. All shaders are detached after # linking is complete. self._glir.command('LINK', self._id) # Delete shaders. We no longer need them and it frees up precious GPU # memory: http://gamedev.stackexchange.com/questions/47910 for shader in shaders: shader.delete() # 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(update_variables=update_variables) @property def shaders(self): """All currently attached shaders""" 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, update_variables=True): """Parse uniforms, attributes and varyings from the source code.""" # Get one string of code with comments removed code = '\n\n'.join([sh.code for sh in self._shaders]) code = re.sub(r'(.*)(//.*)', r'\1', code, re.M) # Parse uniforms, attributes and varyings self._code_variables = {} for kind in ('uniform', 'attribute', 'varying', 'const', 'in', 'out'): # pick regex for the correct kind of var reg = REGEX_VAR[kind] # treat *in* like attribute, *out* like varying if kind == 'in': kind = 'attribute' elif kind == 'out': kind = 'varying' for m in re.finditer(reg, 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. if update_variables: 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 # Forget any pending values for this variable self._pending_variables.pop(name, None) # Delete? if data is None: self._user_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) elif type_ == 'samplerCube': data = TextureCube(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)) divisor = getattr(data, 'divisor', None) 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, divisor) 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)) divisor = getattr(data, 'divisor', None) # 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, divisor) 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 __contains__(self, key): return key in self._code_variables 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', 'lines_adjacency', 'line_strip_adjacency', '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', 'lines_adjacency', 'line_strip_adjacency', '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('Value provided for %r, but this variable was not ' 'found in the shader program.' % name) self._pending_variables = {} # Check attribute sizes attributes = [vbo for vbo in self._user_variables.values() if isinstance(vbo, DataBuffer)] attrs = [a for a in attributes if getattr(a, 'divisor', None) is None] if len(attrs) < 1: raise RuntimeError('Must have at least one attribute') sizes = [a.size for a in attrs] if not all(s == sizes[0] for s in sizes[1:]): msg = '\n'.join([f'{str(a)}: {a.size}' for a in attrs]) raise RuntimeError(f'All attributes must have the same size, got:\n{msg}') attrs_with_div = [a for a in attributes if a not in attrs] if attrs_with_div: sizes = [a.size for a in attrs_with_div] divs = [a.divisor for a in attrs_with_div] instances = sizes[0] * divs[0] if not all(s * d == instances for s, d in zip(sizes, divs)): msg = '\n'.join([f'{str(a)}: {a.size} * {a.divisor} = {a.size * a.divisor}' for a in attrs_with_div]) raise RuntimeError(f'All attributes with divisors must have the same size as the number of instances, got:\n{msg}') else: instances = 1 # 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, instances) 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, instances) else: raise TypeError("Invalid index: %r (must be IndexBuffer)" % indices) # Process GLIR commands canvas.context.flush_commands() vispy-0.14.1/vispy/gloo/preprocessor.py0000644000175000017510000000436314507104216017516 0ustar nileshnilesh # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 = r'\#\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.14.1/vispy/gloo/globject.py0000644000175000017510000000751014507104216016556 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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. Queues that are associated behave as if they are a single queue; this allows GL commands for two or more interdependent GL objects to be combined such that they are always sent to the same context together. 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 and tex1 now share a queue prog2.glir.associate(tex2.glir) # prog2 and tex2 now share a queue # this causes prog1, tex1, and canvas.context to all share a queue: canvas.context.glir.associate(prog1.glir) # and now all objects share a single queue canvas.context.glir.associate(prog2.glir) Now, when the canvas flushes its queue, it takes all the pending commands from prog1, prog2, tex1, and tex2. """ 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 main 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.14.1/vispy/gloo/glir.py0000644000175000017510000020077114507104216015726 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """GL Intermediate Representation Desktop Implementation ===================================================== The glir module holds the desktop implementation of the GL Intermediate Representation (GLIR). Parsing and handling of the GLIR for other platforms can be found in external libraries. We propose the specification of a simple intermediate representation for OpenGL. It provides a means to serialize a visualization, so that the high-level API and the part that does the GL commands can be separated, and even be in separate processes. GLIR is a high level representation that consists of commands without return values. In effect, the commands can be streamed from one node to another without having to wait for a reply. Only in the event of an error information needs to go in the other direction, but this can be done asynchronously. The purpose for GLIR has been to allow the usage gloo (our high level object oriented interface to OpenGL), while executing the visualization in the browser (via JS/WebGL). The fact that the stream of commands is one-directional is essential to realize reactive visualizations. The separation between API and implementation also provides a nice abstraction leading to cleaner code. GLIR commands are represented as tuples. As such the overhead for "parsing" the commands is minimal. The commands can, however, be serialized so they can be send to another process. Further, a series of GLIR commands can be stored in a file. This way we can store visualizations to disk that can be displayed with any application that can interpret GLIR. The GLIR specification is tied to the version of the vispy python library that supports it. The current specification described below was first created for:: VisPy 0.6 The shape of a command ~~~~~~~~~~~~~~~~~~~~~~ GLIR consists of a sequence of commands that are defined as tuples. Each command has the following shape: :: (, , [arg1, [arg2, [arg3]]]) - ```` is one of 15 commands: CURRENT, CREATE, DELETE, UNIFORM, ATTRIBUTE, DRAW, SIZE, DATA, WRAPPING, INTERPOLATION, ATTACH, FRAMEBUFFER, FUNC, SWAP, LINK. - In all commands except SET, ```` is an integer unique within the current GL context that is used as a reference to a GL object. It is the responsibility of the code that generates the command to keep track of id's and to ensure that they are unique. - The number of arguments and their type differs per command and are explained further below. - Some commands accept GL enums in the form of a string. In these cases the enum can also be given as an int, but a string is recommended for better debugging. The string is case insensitive. CURRENT ~~~~~~~ :: ('CURRENT', 0) Will be called when the context is made current. The GLIR implementation can use this to reset some caches. CREATE ~~~~~~ :: ('CREATE', , ) # Example: ('CREATE', 4, 'VertexBuffer') Applies to: All objects The create command is used to create a new GL object. It has one string argument that can be any of 10 classes: 'Program', 'VertexBuffer', 'IndexBuffer', 'Texture2D', 'Texture3D', 'RenderBuffer', 'FrameBuffer', 'VertexShader', 'FragmentShader', 'GeometryShader' DELETE ~~~~~~ :: ('DELETE', ) # Example: ('DELETE', 4) Applies to: All objects The delete command is used to delete the GL object corresponding to the given id. If the id does not exist, this command is ignored. This command does not have arguments. When used with Shader objects, the shader is freed from GPU memory. UNIFORM ~~~~~~~ :: ('UNIFORM', , , , ) # Examples: ('UNIFORM', 4, 'u_scale', 'vec3', ) Applies to: Program This command is used to set the uniform of a program object. A uniform has a string name, a type, and a value. The type can be 'float', 'vec2', 'vec3', 'vec4', 'int', 'ivec2', 'ivec3', 'ivec4', 'bool', 'bvec2', 'bvec3', 'bvec4', 'mat2', 'mat3', 'mat4'. The value must be tuple or array with number of elements that matches with the type. It is an error to provide this command before the shaders are set. After resetting shaders, all uniforms and attributes have to be re-submitted. Discussion: for the uniform and attribute commands, the type argument should not strictly be necessary, but it makes the GLIR implementation simpler. Plus in gloo we *have* this information. TEXTURE ~~~~~~~ :: ('TEXTURE', , , ) # Examples: ('TEXTURE', 4, 'u_texture1', 6) Applies to: Program This command is used to link a texture to a GLSL uniform sampler. ATTRIBUTE ~~~~~~~~~ :: ('ATTRIBUTE', , , , , , ) # Example: Buffer id 5, stride 4, offset 0 ('ATTRIBUTE', 4, 'a_position', 'vec3', 5, 4, 0) Applies to: Program This command is used to set the attribute of a program object. An attribute has a string name, a type, and a value. The type can be 'float', 'vec2', 'vec3', 'vec4'. If the first value element is zero, the remaining elements represent the data to pass to ``glVertexAttribNf``. It is an error to provide this command before the shaders are set. After resetting shaders, all uniforms and attributes have to be re-submitted. DRAW ~~~~ :: ('DRAW', , , , ) # Example: Draw 100 lines with non-instanced rendering ('DRAW', 4, 'lines', (0, 100), 1) # Example: Draw 100 lines using index buffer with id 5 ('DRAW', 4, 'points', (5, 'unsigned_int', 100), 1) # Example: Draw a mesh with 10 vertices 20 times using instanced rendering ('DRAW', 2, 'mesh', (0, 10), 20) Applies to: Program This command is used to draw the program. It has a ``mode`` argument which can be 'points', 'lines', 'line_strip', 'line_loop', 'lines_adjacency', 'line_strip_adjacency', 'triangles', 'triangle_strip', or 'triangle_fan' (case insensitive). If the ``selection`` argument has two elements, it contains two integers ``(start, count)``. If it has three elements, it contains ``(, gtype, count)``, where ``gtype`` is 'unsigned_byte','unsigned_short', or 'unsigned_int'. SIZE ~~~~ :: ('SIZE', , , [], []) # Example: resize a buffer ('SIZE', 4, 500) # Example: resize a 2D texture ('SIZE', 4, (500, 300, 3), 'rgb', None) ('SIZE', 4, (500, 300, 3), 'rgb', 'rgb16f') Applies to: VertexBuffer, IndexBuffer, Texture2D, Texture3D, RenderBuffer This command is used to set the size of the buffer with the given id. The GLIR implementation should be such that if the size/format corresponds to the current size, it is ignored. The high level implementation can use the SIZE command to discard previous DATA commands. For buffers: the size argument is an integer and the format argument is not specified. For textures and render buffer: the size argument is a shape tuple (z,y,x). This tuple may contain the dimension for the color channels, but this information is ignored. The format *should* be set to 'luminance', 'alpha', 'luminance_alpha', 'rgb' or 'rgba'. The internalformat is a hint for backends that can control the internal GL storage format; a value of None is a hint to use the default storage format. The internalformat, if specified, *should* be a base channel configuration of 'r', 'rg', 'rgb', or 'rgba' with a precision qualifying suffix of '8', '16', '16f', or '32f'. For render buffers: the size argument is a shape tuple (z,y,x). This tuple may contain the dimension for the color channels, but this information is ignored. The format *should* be set to 'color', 'depth' or 'stencil'. DATA ~~~~ :: ('DATA', , , ) # Example: ('DATA', 4, 100, ) Applies to: VertexBuffer, IndexBuffer, Texture2D, Texture3D, VertexShader, FragmentShader, GeometryShader The data command is used to set the data of the object with the given id. For VertexBuffer and IndexBuffer the offset is an integer. For textures it is a tuple that matches with the dimension of the texture. For shader objects it is always 0 and the data must be a ``str`` object. WRAPPING ~~~~~~~~ :: ('WRAPPING', , ) # Example: ('WRAPPING', 4, ('CLAMP_TO_EDGE', 'CLAMP_TO_EDGE')) Applies to: Texture2D, Texture3D Set the wrapping mode for each dimension of the texture. Each element must be a string: 'repeat', 'clamp_to_edge' or 'mirrored_repeat'. INTERPOLATION ~~~~~~~~~~~~~ :: ('INTERPOLATION', , , ) # Example: ('INTERPOLATION', 4, True, True) Applies to: Texture2D, Texture3D Set the interpolation mode of the texture for minification and magnification. The min and mag argument can both be either 'nearest' or 'linear'. ATTACH ~~~~~~ :: ('ATTACH', , , ) ('ATTACH', , ) # Example: ('ATTACH', 4, 'color', 5) ('ATTACH', 1, 3) Applies to: FrameBuffer, Program Attach color, depth, or stencil buffer to the framebuffer. The attachment argument can be 'color', 'depth' or 'stencil'. The object argument must be the id for a RenderBuffer or Texture2D. For Program this attaches an existing Shader object to the program. FRAMEBUFFER ~~~~~~~~~~~ :: ('FRAMEBUFFER', , ) # Example: ('FRAMEBUFFER', 4, True) Applies to: FrameBuffer Turn the framebuffer on or off. When deactivating a frame buffer, the GLIR implementation should activate any previously activated framebuffer. FUNC ~~~~ :: ('FUNC', , [arg1, [arg2, [arg3]]]) The ``FUNC`` command is a special command that can be applied to call a variety of OpenGL calls. Use the documentation OpenGL for the required arguments. Any args that are strings are converted to GL enums. Supported functions are in principle all gl functions that do not have a return value or covered by the above commands: glEnable, glDisable, glClear, glClearColor, glClearDepth, glClearStencil, glViewport, glDepthRange, glFrontFace, glCullFace, glPolygonOffset, glBlendFuncSeparate, glBlendEquationSeparate, glBlendColor, glScissor, glStencilFuncSeparate, glStencilMaskSeparate, glStencilOpSeparate, glDepthFunc, glDepthMask, glColorMask, glSampleCoverage, glFlush, glFinish, glHint. SWAP ~~~~ :: ('SWAP',) The ``SWAP`` command is a special synchronization command for remote rendering. This command tells the renderer that it should swap drawing buffers. This is especially important when rendering with WebGL where drawing buffers are implicitly swapped. LINK ~~~~ :: ('LINK', ) Applies to: Program Link the current program together (shaders, etc). Additionally this should cause shaders to be detached and deleted. See the `OpenGL documentation `_ for details on program linking. """ import os import sys import re import json import weakref from packaging.version import Version import numpy as np from . import gl from ..util import logger # TODO: expose these via an extension space in .gl? _internalformats = [ gl.Enum('GL_DEPTH_COMPONENT', 6402), gl.Enum('GL_DEPTH_COMPONENT16', 33189), gl.Enum('GL_DEPTH_COMPONENT32_OES', 33191), 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), # extended formats (not currently supported) # gl.Enum('GL_R32I', 33333), # gl.Enum('GL_RG32I', 33339), # gl.Enum('GL_RGB32I', 36227), # gl.Enum('GL_RGBA32I', 36226), # gl.Enum('GL_R32UI', 33334), # gl.Enum('GL_RG32UI', 33340), # gl.Enum('GL_RGB32UI', 36209), # gl.Enum('GL_RGBA32UI', 36208), ] _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, str): 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 _GlirQueueShare(object): """This class contains the actual queues of GLIR commands that are collected until a context becomes available to execute the commands. Instances of this class are further wrapped by GlirQueue to allow the underlying queues to be transparently merged when GL objects become associated. The motivation for this design is that it allows most glir commands to be added directly to their final queue (the same one used by the context), which reduces the effort required at draw time to determine the complete set of GL commands to be issued. At the same time, all GLObjects begin with their own local queue to allow commands to be queued at any time, even if the GLObject has not been associated yet. This works as expected even for complex topologies of GL objects, when some queues may only be joined at the last possible moment. """ def __init__(self, queue): self._commands = [] # local commands self._verbose = False # queues that have been merged with this one self._associations = weakref.WeakKeyDictionary({queue: None}) 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. """ 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. """ commands = self._commands self._commands = [] return commands def flush(self, parser): """Flush all current commands to the GLIR interpreter.""" 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[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)) 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. Internally, commands are stored in a shared queue object that may be swapped out and merged with other queues when ``associate()`` is called. 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): # We do not actually queue any commands here, but on a shared queue # object that may be joined with others as queues are associated. self._shared = _GlirQueueShare(self) def command(self, *args): """Send a command. See the command spec at: https://github.com/vispy/vispy/wiki/Spec.-GLIR """ self._shared.command(*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._shared.set_verbose(verbose) def clear(self): """Pop the whole queue (and associated queues) and return a list of commands. """ return self._shared.clear() def associate(self, queue): """Merge this queue with another. Both queues will use a shared command list and either one can be used to fill or flush the shared queue. """ assert isinstance(queue, GlirQueue) if queue._shared is self._shared: return # merge commands self._shared._commands.extend(queue.clear()) self._shared._verbose |= queue._shared._verbose self._shared._associations[queue] = None # update queue and all related queues to use the same _shared object for ch in queue._shared._associations: ch._shared = self._shared self._shared._associations[ch] = None queue._shared = self._shared def flush(self, parser): """Flush all current commands to the GLIR interpreter.""" self._shared.flush(parser) def _convert_es2_shader(shader): has_version = False has_prec_float = False has_prec_int = False lines = [] extensions = [] # Iterate over lines for line in shader.lstrip().splitlines(): line_strip = line.lstrip() if line_strip.startswith('#version'): # has_version = True continue if line_strip.startswith('#extension'): extensions.append(line_strip) line = '' if line_strip.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;') # Make sure extensions are at the top before precision # but after version if extensions: for ext_line in extensions: lines.insert(has_version, ext_line) # BUG: fails on WebGL (Chrome) # if not has_version: # lines.insert(has_version, '#version 100') return '\n'.join(lines) def _convert_desktop_shader(shader): has_version = False lines = [] extensions = [] # Iterate over lines for line in shader.lstrip().splitlines(): line_strip = line.lstrip() has_version = has_version or line.startswith('#version') if line_strip.startswith('precision '): line = '' if line_strip.startswith('#extension'): extensions.append(line_strip) line = '' for prec in (' highp ', ' mediump ', ' lowp '): line = line.replace(prec, ' ') lines.append(line.rstrip()) # Write # Make sure extensions are at the top, but after version if extensions: for ext_line in extensions: lines.insert(has_version, ext_line) if not has_version: lines.insert(0, '#version 120\n') return '\n'.join(lines) def convert_shader(backend_type, shader): """Modify shader code to be compatible with `backend_type` backend.""" if backend_type == 'es2': return _convert_es2_shader(shader) elif backend_type == 'desktop': return _convert_desktop_shader(shader) else: raise ValueError('Cannot backend_type shaders to %r.' % backend_type) 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:] elif command[0] == 'UNIFORM': return command[:-1] + (command[-1].tolist(),) return command class BaseGlirParser(object): """Base class for GLIR parsers that can be attached to a GLIR queue.""" def __init__(self): self.capabilities = dict( gl_version='Unknown', max_texture_size=None, ) def is_remote(self): """Whether the code is executed remotely. i.e. gloo.gl cannot be used. """ raise NotImplementedError() @property def shader_compatibility(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): super(GlirParser, self).__init__() self._objects = {} self._invalid_objects = set() self._classmap = {'VertexShader': GlirVertexShader, 'FragmentShader': GlirFragmentShader, 'GeometryShader': GlirGeometryShader, 'Program': GlirProgram, 'VertexBuffer': GlirVertexBuffer, 'IndexBuffer': GlirIndexBuffer, 'Texture1D': GlirTexture1D, 'Texture2D': GlirTexture2D, 'Texture3D': GlirTexture3D, 'TextureCube': GlirTextureCube, '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 = {} @property def shader_compatibility(self): """Type of shader compatibility""" if '.es' in gl.current_backend.__name__: return 'es2' else: return 'desktop' def is_remote(self): return False 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() self.env['fbo'] = args[0] gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, args[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, Shader ob.set_data(*args) elif cmd == 'SIZE': # VertexBuffer, IndexBuffer, ob.set_size(*args) # Texture[1D, 2D, 3D], RenderBuffer elif cmd == 'ATTACH': # FrameBuffer, Program ob.attach(*args) elif cmd == 'FRAMEBUFFER': # FrameBuffer ob.set_framebuffer(*args) # elif cmd == 'SHADERS': # Program # ob.set_shaders(*args) elif cmd == 'LINK': # Program ob.link_program(*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) if self.capabilities['max_texture_size'] is None: # only do once self.capabilities['gl_version'] = gl.glGetParameter(gl.GL_VERSION) self.capabilities['max_texture_size'] = \ gl.glGetParameter(gl.GL_MAX_TEXTURE_SIZE) this_version = self.capabilities['gl_version'].split(' ') if this_version[0] == "OpenGL": # For OpenGL ES, the version string has the format: # "OpenGL ES " this_version = this_version[2] else: this_version = this_version[0] if not this_version: logger.warning("OpenGL version could not be determined, which " "might be a sign that OpenGL is not loaded correctly.") elif Version(this_version) < Version('2.1'): if os.getenv('VISPY_IGNORE_OLD_VERSION', '').lower() != 'true': logger.warning('OpenGL version 2.1 or higher recommended, ' 'got %s. Some functionality may fail.' % self.capabilities['gl_version']) 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, str): 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 GlirShader(GlirObject): _target = None def create(self): self._handle = gl.glCreateShader(self._target) def set_data(self, offset, code): # NOTE: offset will always be 0 to match other DATA commands # convert shader to be compatible with backend convert = self._parser.shader_compatibility if convert: code = convert_shader(convert, code) gl.glShaderSource(self._handle, code) gl.glCompileShader(self._handle) status = gl.glGetShaderParameter(self._handle, gl.GL_COMPILE_STATUS) if not status: errors = gl.glGetShaderInfoLog(self._handle) errormsg = self._get_error(code, errors, 4) raise RuntimeError("Shader compilation error in %s:\n%s" % (self._target, errormsg)) def delete(self): gl.glDeleteShader(self._handle) 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 _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 class GlirVertexShader(GlirShader): _target = gl.GL_VERTEX_SHADER class GlirFragmentShader(GlirShader): _target = gl.GL_FRAGMENT_SHADER class GlirGeometryShader(GlirShader): # _target assignment must be delayed because GL_GEOMETRY_SHADER does not # exist until the user calls use_gl('gl+') _target = None def __init__(self, *args, **kwargs): if not hasattr(gl, 'GL_GEOMETRY_SHADER'): raise RuntimeError(gl.current_backend.__name__ + " backend does not support geometry shaders." " Try gloo.gl.use_gl('gl+').") GlirGeometryShader._target = gl.GL_GEOMETRY_SHADER GlirShader.__init__(self, *args, **kwargs) 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), 'bool': (1, gl.GL_BOOL, np.int32) } def create(self): self._handle = gl.glCreateProgram() self._attached_shaders = [] 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 # For both vertex and fragment shader: set source, compile, check for code, type_ in [(vert, 'vertex'), (frag, 'fragment')]: self.attach_shader(code, type_) self.link_program() def attach(self, id_): """Attach a shader to this program.""" shader = self._parser.get_object(id_) gl.glAttachShader(self._handle, shader.handle) self._attached_shaders.append(shader) def link_program(self): """Link the complete program and check. All shaders are detached and deleted if the program was successfully linked. """ gl.glLinkProgram(self._handle) if not gl.glGetProgramParameter(self._handle, gl.GL_LINK_STATUS): raise RuntimeError('Program linking error:\n%s' % gl.glGetProgramInfoLog(self._handle)) # Detach all shaders to prepare them for deletion (they are no longer # needed after linking is complete) for shader in self._attached_shaders: gl.glDetachShader(self._handle, shader.handle) self._attached_shaders = [] # 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(r"""(?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 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('Not setting texture data for variable %s; ' 'uniform is not active.' % 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('Not setting value for variable %s %s; ' 'uniform is not active.' % (type_, 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, divisor=None): """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('Not setting data for variable %s %s; ' 'attribute is not active.' % (type_, 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:], divisor 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, divisor 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, divisor in self._attributes.values(): if vbo_handle: gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbo_handle) gl.glEnableVertexAttribArray(attr_handle) func(attr_handle, *args) if divisor is not None: gl.glVertexAttribDivisor(attr_handle, divisor) 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.warning('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): raise RuntimeError('Program validation error:\n%s' % gl.glGetProgramInfoLog(self._handle)) 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, instances=1): """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') try: mode = as_enum(mode) except ValueError: if mode == 'lines_adjacency' or mode == 'line_strip_adjacency': raise RuntimeError(gl.current_backend.__name__ + " backend does not support lines_adjacency" " and line_strip_adjacency primitives." " Try gloo.gl.use_gl('gl+').") raise # 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() if instances > 1: gl.glDrawElementsInstanced(mode, count, as_enum(gtype), None, instances) else: 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() if instances > 1: gl.glDrawArraysInstanced(mode, first, count, instances) else: 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.__name__.split(".")[-1] == "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 # 4 is the default alignments = [8, 4, 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] * data.itemsize) 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] * data.itemsize) 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[-2] * data.shape[-1] * data.itemsize) 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 GlirTextureCube(GlirTexture): _target = gl.GL_TEXTURE_CUBE_MAP _cube_targets = [ gl.GL_TEXTURE_CUBE_MAP_POSITIVE_X, gl.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, gl.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, gl.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, gl.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, ] def set_size(self, shape, format, internalformat): 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() for target in self._cube_targets: gl.glTexImage2D(target, 0, internalformat, format, gl.GL_UNSIGNED_BYTE, shape[1:3]) def set_data(self, offset, data): shape, format, internalformat = self._shape_formats y, x = offset[:2] # 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) self.activate() # Set alignment (width is nbytes_per_pixel * npixels_per_line) alignment = self._get_alignment(data.shape[-2] * data.shape[-1] * data.itemsize) if alignment != 4: gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, alignment) # Upload for i, target in enumerate(self._cube_targets): gl.glTexSubImage2D(target, 0, x, y, format, gtype, data[i]) # 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, str): 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: self.activate() if not self._validated: self._validated = True self._validate() else: self.deactivate() def activate(self): stack = self._parser.env.setdefault('fb_stack', [self._parser.env['fbo']]) 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', [self._parser.env['fbo']]) 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: return _bad_map = { 0: 'Target not equal to GL_FRAMEBUFFER', gl.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 'FrameBuffer attachments are incomplete.', gl.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 'No valid attachments in the FrameBuffer.', gl.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 'attachments do not have the same width and height.', # gl.GL_FRAMEBUFFER_INCOMPLETE_FORMATS: \ # not in es 2.0 # 'Internal format of attachment is not renderable.' gl.GL_FRAMEBUFFER_UNSUPPORTED: 'Combination of internal formats used by attachments is ' 'not supported.', } raise RuntimeError(_bad_map.get(res, 'Unknown framebuffer error: %r.' % res)) vispy-0.14.1/vispy/gloo/gl/0000755000175000017510000000000014507104251015011 5ustar nileshnileshvispy-0.14.1/vispy/gloo/gl/tests/0000755000175000017510000000000014507104251016153 5ustar nileshnileshvispy-0.14.1/vispy/gloo/gl/tests/test_use.py0000644000175000017510000000345614507104216020371 0ustar nileshnilesh"""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 gl2 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.14.1/vispy/gloo/gl/tests/test_names.py0000644000175000017510000002561614507104216020702 0ustar nileshnilesh"""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', ' ') # vispy_ext.h constant_names += "GL_MIN GL_MAX" 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.14.1/vispy/gloo/gl/tests/test_functionality.py0000644000175000017510000004167414507104216022471 0ustar nileshnilesh"""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 import pytest # All these tests require a working backend. # # High level tests # def teardown_module(): gl.use_gl() # Reset to default @pytest.mark.xfail(sys.platform == 'darwin', reason='functionality fails on OSX (see #1178)') @requires_application() def test_functionality_desktop(): """Test desktop GL backend for full functionality.""" _test_functionality('gl2') @pytest.mark.xfail(sys.platform == 'darwin', reason='functionality fails on OSX (see #1178)') @requires_application() def test_functionality_proxy(): """Test GL proxy class for full functionality.""" # By using debug mode, we are using the proxy class _test_functionality('gl2 debug') @pytest.mark.xfail(sys.platform == 'darwin', reason='functionality fails on OSX (see #1178)') @requires_application() @requires_pyopengl() def test_functionality_pyopengl(): """Test pyopengl GL backend for full functionality.""" _test_functionality('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_functionality('es2') def _clear_screen(): gl.glClear(gl.GL_COLOR_BUFFER_BIT) gl.glFinish() def _test_functionality(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 = """ #version 120 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 = """ #version 120 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 gl.glGetShaderInfoLog(hvert) == '' assert gl.glGetShaderInfoLog(hfrag) == '' assert gl.glGetShaderParameter(hvert, gl.GL_COMPILE_STATUS) == 1 assert 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) # Bind all attributes - we could let this occur automatically, but some # implementations bind an attribute to index 0, which has the unfortunate # property of being unable to be modified. gl.glBindAttribLocation(hprog, 1, 'a_1') gl.glBindAttribLocation(hprog, 2, 'a_2') gl.glBindAttribLocation(hprog, 3, 'a_3') gl.glBindAttribLocation(hprog, 4, 'a_4') 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) # 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 attributes 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.14.1/vispy/gloo/gl/tests/test_basics.py0000644000175000017510000002200714507104216021032 0ustar nileshnilesh"""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.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') with Canvas(): _test_setting_parameters() _test_enabling_disabling() _test_setting_stuff() _test_object_creation_and_deletion() _test_fbo() try: gl.gl2._get_gl_func('foo', None, ()) except RuntimeError as exp: exp = str(exp) assert 'version' in exp assert 'unknown' not in exp gl.glFinish() @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, str)) 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.14.1/vispy/gloo/gl/tests/__init__.py0000644000175000017510000000000014507104216020253 0ustar nileshnileshvispy-0.14.1/vispy/gloo/gl/pyopengl2.py0000644000175000017510000000515314507104216017307 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/gloo/gl/glplus.py0000644000175000017510000002207214507104216016675 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/gloo/gl/gl2.py0000644000175000017510000000611014507104216016046 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 from ...util import logger # 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: logger.warning('Could not load OpenGL library.') _lib = None else: # Load lib _lib = ctypes.cdll.LoadLibrary(_fname) def _have_context(): return _lib.glGetError() != 1282 # GL_INVALID_OPERATION def _get_gl_version(_lib): """Helper to get the GL version string""" try: return _lib.glGetString(7938).decode('utf-8') except Exception: return 'unknown' def _get_gl_func(name, restype, argtypes): # Based on a function in Pyglet if _lib is None: raise RuntimeError('Could not load OpenGL library, gl cannot be used') 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 ' '(OpenGL version is %s).' % (name, _get_gl_version(_lib))) 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 ' '(OpenGL version is %s).' % (name, _get_gl_version(_lib))) # Inject from . import _gl2 # noqa _copy_gl_functions(_gl2, globals()) vispy-0.14.1/vispy/gloo/gl/es2.py0000644000175000017510000000331214507104216016054 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/gloo/gl/dummy.py0000644000175000017510000000134114507104216016516 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/gloo/gl/_pyopengl2.py0000644000175000017510000002665414507104216017457 0ustar nileshnilesh"""GL definitions converted to Python by codegen/createglapi.py. 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 name, size, type = GL.glGetActiveAttrib(program, index, bufSize=bufsize) return name.decode('utf-8'), size, type 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 GL.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 GL.glGetIntegerv(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.14.1/vispy/gloo/gl/_proxy.py0000644000175000017510000003456114507104216016715 0ustar nileshnilesh"""GL definitions converted to Python by codegen/createglapi.py. 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.14.1/vispy/gloo/gl/_gl2.py0000644000175000017510000015237514507104216016224 0ustar nileshnilesh"""GL definitions converted to Python by codegen/createglapi.py. 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_ssize_t, 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_ssize_t, ctypes.c_ssize_t, 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, GL_DEPTH_COMPONENT t = {6406:1, 6407:3, 6408:4, 6402:1}[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.14.1/vispy/gloo/gl/_es2.py0000644000175000017510000011264314507104216016223 0ustar nileshnilesh"""GL definitions converted to Python by codegen/createglapi.py. 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_ssize_t, 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_ssize_t, ctypes.c_ssize_t, 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.14.1/vispy/gloo/gl/_constants.py0000644000175000017510000004060114507104216017540 0ustar nileshnilesh"""GL definitions converted to Python by codegen/createglapi.py. 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 = Enum('GL_MAX', 32776) 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_MIN = Enum('GL_MIN', 32775) 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 var_name, ob in list(globals().items()): if var_name.startswith('GL_'): ENUM_MAP[int(ob)] = ob del ob, var_name vispy-0.14.1/vispy/gloo/gl/__init__.py0000644000175000017510000001742014507104216017127 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 import os 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) # Instantiate proxy objects proxy = MainProxy() def use_gl(target=None): """Let Vispy use the target OpenGL ES 2.0 implementation Also see ``vispy.use()``. Parameters ---------- target : str The target GL backend to use. Default gl2 or es2, depending on the platform. 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 default_backend.__name__.split(".")[-1] 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(), debug=debug) _copy_gl_functions(mod, globals(), True, debug=debug) else: _copy_gl_functions(mod, globals(), debug=debug) 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, debug=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: if debug and name != 'glGetError': dest[name] = make_debug_wrapper(source[name]) else: 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 _arg_repr(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 make_debug_wrapper(fn): def gl_debug_wrapper(*args): # Log function call argstr = ', '.join(map(_arg_repr, args)) logger.debug("%s(%s)" % (fn.__name__, argstr)) # Call function ret = fn(*args) # Log return value if ret is not None: if fn.__name__ == 'glReadPixels': logger.debug(" <= %s[%s]" % (type(ret), len(ret))) else: logger.debug(" <= %s" % repr(ret)) # Check for errors (raises if an error occured) check_error(fn.__name__) # Return return ret gl_debug_wrapper.__name__ = fn.__name__ + '_debug_wrapper' # Store reference to wrapped function just for introspection gl_debug_wrapper._wrapped_function = fn return gl_debug_wrapper 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 def _fix_osmesa_gl_lib_if_testing(): """ This functions checks if we a running test with the osmesa backends and fix the GL library if needed. Since we have to fix the VISPY_GL_LIB *before* any import from the gl module, we have to run this here. Test discovery utilities (like pytest) will try to import modules before running tests, so we have to modify the GL lib really early. The other solution would be to setup pre-run hooks for the test utility, but there doesn't seem to be a standard way to do that (e.g. conftest.py for py.test) """ test_name = os.getenv('_VISPY_TESTING_APP', None) if test_name == 'osmesa': from ...util.osmesa_gl import fix_osmesa_gl_lib fix_osmesa_gl_lib() _fix_osmesa_gl_lib_if_testing() # Load default gl backend from . import gl2 as default_backend # noqa if default_backend._lib is None: # Probably Android or RPi from . import es2 as default_backend # noqa # Call use to start using our default backend use_gl() vispy-0.14.1/vispy/gloo/framebuffer.py0000644000175000017510000002220614507104216017250 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 # ------------------------------------------------------ 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): """Render Buffer shape""" return self._shape @property def format(self): """Render Buffer 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, str): 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 + (buf._inv_formats[buf.format], ) buf.resize(shape_, buf.format) def read(self, mode='color', alpha=True, crop=None): """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. crop : array-like If not None, specifies pixels to read from buffer. Format is (x, y, w, h). 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 if crop was not specified. If crop was given, the result will match the offset and dimensions of the crop. """ _check_valid('mode', mode, ['color', 'depth', 'stencil']) buffer = getattr(self, mode + '_buffer') if buffer is None: raise ValueError("Can't read pixels for buffer {}, " "buffer does not exist.".format(mode)) if crop is None: h, w = buffer.shape[:2] crop = (0, 0, w, h) # todo: this is ostensibly required, but not available in gloo.gl # gl.glReadBuffer(buffer._target) return read_pixels(crop, alpha=alpha, mode=mode) vispy-0.14.1/vispy/gloo/context.py0000644000175000017510000002070014507104216016445 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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=24, 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 self._last_viewport = None 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 @property def capabilities(self): """The OpenGL capabilities""" return deepcopy(self.shared.parser.capabilities) def flush_commands(self, event=None): """Flush Parameters ---------- event : instance of Event The event. """ if self._do_CURRENT_command: self._do_CURRENT_command = False canvas = get_current_canvas() if canvas and hasattr(canvas, '_backend'): fbo = canvas._backend._vispy_get_fb_bind_location() else: fbo = 0 self.shared.parser.parse([('CURRENT', 0, fbo)]) self.glir.flush(self.shared.parser) def set_viewport(self, *args): BaseGlooFunctions.set_viewport(self, *args) self._last_viewport = args def get_viewport(self): return self._last_viewport 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.14.1/vispy/gloo/buffer.py0000644000175000017510000004055214507104216016241 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 # ------------------------------------------------------------ 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 = weakref.WeakSet() # Views on this buffer 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: view._valid = False self._views = weakref.WeakSet() # -------------------------------------------------------- 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 (i.e. index of starting element). 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 # This works around some strange NumPy bug where a float32 array # of shape (155407, 1) was said to have strides # (4, 9223372036854775807), which is crazy 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.add(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, str): 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 ' '(%d > %d-%d).' % (data.size, stop, start)) # Set data offset = start 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, str): 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) """ def __init__(self, data=None, divisor=None): super().__init__(data) self.divisor = divisor _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)) dtype_def = ('f0', data.dtype.base) if c != 1: # numpy dtypes with size 1 are ambiguous, only add size if it is greater than 1 dtype_def += (c,) data = data.view(dtype=[dtype_def]) self._last_dim = c return data @property def divisor(self): return self._divisor @divisor.setter def divisor(self, value): self._divisor = max(1, int(value)) if value else None 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.14.1/vispy/gloo/__init__.py0000644000175000017510000000374614507104216016533 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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, TextureCube, TextureEmulated3D # noqa from .program import Program # noqa from .framebuffer import FrameBuffer, RenderBuffer # noqa from . import util # noqa vispy-0.14.1/vispy/geometry/0000755000175000017510000000000014507104251015302 5ustar nileshnileshvispy-0.14.1/vispy/geometry/triangulation.py0000644000175000017510000007634014507104216020547 0ustar nileshnilesh# -*- coding: utf-8 -*- from __future__ import division, print_function from itertools import permutations import numpy as np from collections import OrderedDict 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] # 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] idx = 0 while pts[front[idx+1], 0] <= pi[0]: idx += 1 pl = pts[front[idx]] # "(i) middle case" if pi[0] > pl[0]: # Add a single triangle connecting pi,pl,pr self._add_tri(front[idx], front[idx+1], i) front.insert(idx+1, i) # "(ii) left case" else: # Add triangles connecting pi,pl,ps and pi,pl,pr self._add_tri(front[idx], front[idx+1], i) self._add_tri(front[idx-1], front[idx], i) front[idx] = i # Continue adding triangles to smooth out front # (heuristics shown in figs. 9, 10) 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 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]) front.pop(ind1) # "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) def _finalize(self): # Finalize (sec. 3.5) # (i) Add bordering triangles to fill hull front = list(OrderedDict.fromkeys(self._front)) idx = len(front) - 2 k = 1 while k < idx-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]) front.pop(k+1) idx -= 1 continue k += 1 # (ii) Remove all triangles not inside the hull # (not described in article) 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: 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) 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: return # 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: if mode == 1: # crossing from one triangle into another if j in next_tri: # reached endpoint! # update front / polygons upper_polygon.append(j) lower_polygon.append(j) 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)) last_tri = next_tri next_tri = self._adjacent_tri(last_edge, last_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]) elif lower_polygon[-1] == last_edge[1]: upper_polygon.append(last_edge[0]) elif upper_polygon[-1] == last_edge[0]: lower_polygon.append(last_edge[1]) elif upper_polygon[-1] == last_edge[1]: lower_polygon.append(last_edge[0]) 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 mode = 2 next_tri = None # where did we cross the front? # nearest to new point front_index = x + (1 if front_dir == -1 else 0) # 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 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 next_edge = (front[front_index], front[front_index+front_dir]) assert front_index >= 0 if front[front_index] == j: # found endpoint! lower_polygon.append(j) upper_polygon.append(j) 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]) front_holes.append(front_index) if self._edges_intersect((i, j), next_edge): # crossing over front into triangle mode = 1 last_edge = next_edge # we are crossing the front, so this edge only has one # triangle. next_tri = self._tri_from_edge(last_edge) upper_polygon.append(front[front_index+front_dir]) # (iii) triangluate empty areas for polygon in [lower_polygon, upper_polygon]: dist = self._distances_from_line((i, j), polygon) while len(polygon) > 2: ind = np.argmax(dist) self._add_tri(polygon[ind], polygon[ind-1], polygon[ind+1]) polygon.pop(ind) dist.pop(ind) # 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) 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]) 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. # 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 # 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) # 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) 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 # decrement all point indexes > j self.edges[self.edges > j] -= 1 dups_arr[dups_arr > j] -= 1 self.pts = self.pts[pt_mask] # remove zero-length edges mask = self.edges[:, 0] != self.edges[:, 1] self.edges = self.edges[mask] def _distances_from_line(self, edge, points): # Distance of a set of points from a given line 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 _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 _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! 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 _add_tri(self, 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): 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): 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: 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 tri = (a, b, c) self.tris[tri] = None def _remove_tri(self, 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: 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: 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): import triangle 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. This uses a pure Python implementation based on [1]_. If `Triangle` by Jonathan R. Shewchuk [2]_ and the Python bindings `triangle` [3]_ are installed, this will be used instead. Users need to acknowledge and adhere to the licensing terms of these packages. In the VisPy `PolygonCollection Example` [4]_ a speedup of 97% using `Triangle`/`triangle` can be achieved compared to the pure Python implementation. Parameters ---------- vertices : array-like The vertices. Returns ------- vertices : array-like The vertices. triangles : array-like The triangles. References ---------- .. [1] Domiter, V. and Žalik, B. Sweepâ€line algorithm for constrained Delaunay triangulation .. [2] Shewchuk J.R. (1996) Triangle: Engineering a 2D quality mesh generator and Delaunay triangulator. In: Lin M.C., Manocha D. (eds) Applied Computational Geometry Towards Geometric Engineering. WACG 1996. Lecture Notes in Computer Science, vol 1148. Springer, Berlin, Heidelberg. https://doi.org/10.1007/BFb0014497 .. [3] https://rufat.be/triangle/ .. [4] https://github.com/vispy/vispy/blob/main/examples/collections/polygon_collection.py """ 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 try: import triangle # noqa: F401 except (ImportError, AssertionError): vertices_2d, triangles = _triangulate_python(vertices_2d, segments) else: segments_2d = segments.reshape((-1, 2)) vertices_2d, triangles = _triangulate_cpp(vertices_2d, segments_2d) vertices = np.empty((len(vertices_2d), 3)) vertices[:, :2] = vertices_2d vertices[:, 2] = zmean return vertices, triangles vispy-0.14.1/vispy/geometry/torusknot.py0000644000175000017510000001024614507104216017730 0ustar nileshnileshfrom __future__ import division import numpy as np from math 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.14.1/vispy/geometry/tests/0000755000175000017510000000000014507104251016444 5ustar nileshnileshvispy-0.14.1/vispy/geometry/tests/test_triangulation.py0000644000175000017510000003063414507104216022744 0ustar nileshnileshimport sys from unittest import SkipTest 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]]) if sys.version[0] == '3': raise SkipTest('Triangulation differences on Py3k') 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 # 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.14.1/vispy/geometry/tests/test_meshdata.py0000644000175000017510000001020514507104216021642 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.float64) square_faces = np.array([[0, 1, 2], [0, 2, 3]], dtype=np.uint8) square_normals = np.array([[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1]], dtype=np.float64) square_edges = np.array([[0, 1], [0, 2], [0, 3], [1, 2], [2, 3]], dtype=np.uint8) 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()) def test_vertex_normals_indexed_none(): dtype_float = np.float32 dtype_int = np.int64 vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=dtype_float) faces = np.array([[0, 2, 1], [0, 3, 2], [0, 1, 3]], dtype=dtype_int) mesh = MeshData(vertices=vertices, faces=faces) vertex_normals_unnormalized = np.array( [[-1, -1, -1], [0, -1, -1], [-1, 0, -1], [-1, -1, 0]], dtype=dtype_float) norms = np.sqrt((vertex_normals_unnormalized**2).sum(axis=1, keepdims=True)) expected_vertex_normals = vertex_normals_unnormalized / norms computed_vertex_normals = mesh.get_vertex_normals(indexed=None) assert_array_equal(expected_vertex_normals, computed_vertex_normals) def test_vertex_normals_indexed_faces(): dtype_float = np.float32 dtype_int = np.int64 vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=dtype_float) faces = np.array([[0, 2, 1], [0, 3, 2], [0, 1, 3]], dtype=dtype_int) mesh = MeshData(vertices=vertices, faces=faces) vertex_normals_unnormalized = np.array( [[-1, -1, -1], [0, -1, -1], [-1, 0, -1], [-1, -1, 0]], dtype=dtype_float) norms = np.sqrt((vertex_normals_unnormalized**2).sum(axis=1, keepdims=True)) vertex_normals = vertex_normals_unnormalized / norms expected_vertex_normals = vertex_normals[faces] computed_vertex_normals = mesh.get_vertex_normals(indexed="faces") assert_array_equal(expected_vertex_normals, computed_vertex_normals) def test_face_normals_indexed_none(): dtype_float = np.float32 dtype_int = np.int64 vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=dtype_float) faces = np.array([[0, 2, 1], [0, 3, 2], [0, 1, 3]], dtype=dtype_int) mesh = MeshData(vertices=vertices, faces=faces) expected_face_normals = np.array([[0, 0, -1], [-1, 0, 0], [0, -1, 0]], dtype=dtype_float) computed_face_normals = mesh.get_face_normals(indexed=None) assert_array_equal(expected_face_normals, computed_face_normals) def test_face_normals_indexed_faces(): dtype_float = np.float32 dtype_int = np.int64 vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=dtype_float) faces = np.array([[0, 2, 1], [0, 3, 2], [0, 1, 3]], dtype=dtype_int) mesh = MeshData(vertices=vertices, faces=faces) expected_face_normals = np.array([ [[0, 0, -1], [0, 0, -1], [0, 0, -1]], [[-1, 0, 0], [-1, 0, 0], [-1, 0, 0]], [[0, -1, 0], [0, -1, 0], [0, -1, 0]]], dtype=dtype_float) computed_face_normals = mesh.get_face_normals(indexed="faces") assert_array_equal(expected_face_normals, computed_face_normals) run_tests_if_main() vispy-0.14.1/vispy/geometry/tests/test_generation.py0000644000175000017510000000407614507104216022220 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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_box, create_cube, create_cylinder, create_sphere, create_plane) def test_box(): """Test box function""" vertices, filled, outline = create_box() assert_array_equal(np.arange(len(vertices)), np.unique(filled)) assert_array_equal(np.arange(len(vertices)), np.unique(outline)) 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(rows=10, cols=20, radius=10, method='latitude') radii = np.sqrt((md.get_vertices() ** 2).sum(axis=1)) assert radii.dtype.type is np.float32 assert_allclose(radii, np.ones_like(radii) * 10, atol=1e-06) md = create_sphere(subdivisions=5, radius=10, method='ico') radii = np.sqrt((md.get_vertices() ** 2).sum(axis=1)) assert radii.dtype.type is np.float32 assert_allclose(radii, np.ones_like(radii) * 10, atol=1e-06) md = create_sphere(rows=20, cols=20, depth=20, radius=10, method='cube') radii = np.sqrt((md.get_vertices() ** 2).sum(axis=1)) assert radii.dtype.type is np.float32 assert_allclose(radii, np.ones_like(radii) * 10, atol=1e-06) 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) def test_plane(): """Test plane function""" vertices, filled, outline = create_plane() assert_array_equal(np.arange(len(vertices)), np.unique(filled)) assert_array_equal(np.arange(len(vertices)), np.unique(outline)) run_tests_if_main() vispy-0.14.1/vispy/geometry/tests/test_calculations.py0000644000175000017510000000201614507104216022536 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/geometry/tests/__init__.py0000644000175000017510000000000014507104216020544 0ustar nileshnileshvispy-0.14.1/vispy/geometry/rect.py0000644000175000017510000001306414507104216016616 0ustar nileshnileshimport 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 = (w, self.size[1]) @property def height(self): return self.size[1] @height.setter def height(self, h): self.size = (self.size[0], 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]) @property def center(self): return (self.pos[0] + self.size[0] * 0.5, self.pos[1] + self.size[1] * 0.5) @center.setter def center(self, value): delta_x = value[0] - self.center[0] delta_y = value[1] - self.center[1] self.pos = (self.pos[0] + delta_x, self.pos[1] + delta_y) 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.14.1/vispy/geometry/polygon.py0000644000175000017510000001015114507104216017342 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/geometry/parametric.py0000644000175000017510000000362414507104216020011 0ustar nileshnilesh# -*- 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.14.1/vispy/geometry/normals.py0000644000175000017510000000460414507104216017334 0ustar nileshnilesh# -*- 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.14.1/vispy/geometry/meshdata.py0000644000175000017510000006431614507104216017455 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import numpy as np 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 def _compute_face_normals(vertices): if vertices.shape[1:] != (3, 3): raise ValueError("Expected (N, 3, 3) array of vertices repeated on" f" the triangle corners, got {vertices.shape}.") edges1 = vertices[:, 1] - vertices[:, 0] edges2 = vertices[:, 2] - vertices[:, 0] return np.cross(edges1, edges2) def _repeat_face_normals_on_corners(normals): if normals.shape[1:] != (3,): raise ValueError("Expected (F, 3) array of face normals, got" f" {normals.shape}.") n_corners_in_face = 3 new_shape = (normals.shape[0], n_corners_in_face, normals.shape[1]) return np.repeat(normals, n_corners_in_face, axis=0).reshape(new_shape) def _compute_vertex_normals(face_normals, faces, vertices): if face_normals.shape[1:] != (3,): raise ValueError("Expected (F, 3) array of face normals, got" f" {face_normals.shape}.") if faces.shape[1:] != (3,): raise ValueError("Expected (F, 3) array of face vertex indices, got" f" {faces.shape}.") if vertices.shape[1:] != (3,): raise ValueError("Expected (N, 3) array of vertices, got" f" {vertices.shape}.") vertex_normals = np.zeros_like(vertices) n_corners_in_triangle = 3 face_normals_repeated_on_corners = np.repeat(face_normals, n_corners_in_triangle, axis=0) # NOTE: The next line is equivalent to # # vertex_normals[self._faces.ravel()] += face_normals_repeated_on_corners # # except that it accumulates the values from the right hand side at # repeated indices on the left hand side, instead of overwritting them, # like in the above. np.add.at(vertex_normals, faces.ravel(), face_normals_repeated_on_corners) norms = np.sqrt((vertex_normals**2).sum(axis=1)) nonzero_norms = norms > 0 vertex_normals[nonzero_norms] /= norms[nonzero_norms][:, None] return vertex_normals 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. vertex_values : ndarray, shape (Nv,) Vertex values. 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, vertex_values=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, 4) colors self._vertex_colors_indexed_by_faces = None # (Nf, 3, 4) colors self._vertex_colors_indexed_by_edges = None # (Nf, 2, 4) colors self._vertex_values = None # (Nv,) values self._vertex_values_indexed_by_faces = None # (Nv, 3) values self._vertex_values_indexed_by_edges = None # (Nv, 2) values # 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 if vertices is not None: indexed = 'faces' if faces is None else None self.set_vertices(vertices, indexed=indexed) if faces is not None: self.set_faces(faces) if vertex_colors is not None: self.set_vertex_colors(vertex_colors, indexed=indexed) if face_colors is not None: self.set_face_colors(face_colors, indexed=indexed) if vertex_values is not None: self.set_vertex_values(vertex_values, indexed=indexed) 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 ValueError("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 ValueError("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 ValueError("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_vertex_value(self): """Return True if this data set has vertex value information""" for v in (self._vertex_values, self._vertex_values_indexed_by_faces, self._vertex_values_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 indexed not in (None, 'faces'): raise ValueError("Invalid indexing mode. Accepts: None, 'faces'") if self._face_normals is None: vertices = self.get_vertices(indexed='faces') self._face_normals = _compute_face_normals(vertices) if indexed == 'faces' and self._face_normals_indexed_by_faces is None: self._face_normals_indexed_by_faces = \ _repeat_face_normals_on_corners(self._face_normals) return (self._face_normals if indexed is None else self._face_normals_indexed_by_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 indexed not in (None, 'faces'): raise ValueError("Invalid indexing mode. Accepts: None, 'faces'") if self._vertex_normals is None: face_normals = self.get_face_normals() faces = self.get_faces() vertices = self.get_vertices() self._vertex_normals = _compute_vertex_normals(face_normals, faces, vertices) if indexed is None: return self._vertex_normals elif indexed == 'faces': return self._vertex_normals[self.get_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 ValueError("Invalid indexing mode. Accepts: None, 'faces'") def get_vertex_values(self, indexed=None): """Get vertex colors Parameters ---------- indexed : str | None If None, return an array (Nv,) of vertex values. If indexed=='faces', then instead return an indexed array (Nf, 3). Returns ------- values : ndarray The vertex values. """ if indexed is None: return self._vertex_values elif indexed == 'faces': if self._vertex_values_indexed_by_faces is None: self._vertex_values_indexed_by_faces = \ self._vertex_values[self.get_faces()] return self._vertex_values_indexed_by_faces else: raise ValueError("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(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 set_vertex_values(self, values, indexed=None): """Set the vertex value array Parameters ---------- values : array Array of values. Must have shape (Nv,) (indexing by vertex) or shape (Nf, 3) (vertices indexed by face). indexed : str | None Should be 'faces' if colors are indexed by faces. """ values = np.asarray(values) if indexed is None: if values.ndim != 1: raise ValueError('values must be 1D if indexed is None') if values.shape[0] != self.n_vertices: raise ValueError('incorrect number of colors %s, expected %s' % (values.shape[0], self.n_vertices)) self._vertex_values = values self._vertex_values_indexed_by_faces = None elif indexed == 'faces': if values.ndim != 2: raise ValueError('values must be 3D if indexed is "faces"') if values.shape[0] != self.n_faces: raise ValueError('incorrect number of faces') self._vertex_values = None self._vertex_values_indexed_by_faces = values 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 ValueError("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 range(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 range(len(self.get_vertices()))] for i in range(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 ValueError("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]) def is_empty(self): """Check if any vertices or faces are defined.""" return self._faces is None vispy-0.14.1/vispy/geometry/isosurface.py0000644000175000017510000005025314507104216020025 0ustar nileshnileshimport 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 # Because we make use of the strides data attribute below, we have to make # sure that the data is contiguous (which it won't be if the user did # data.transpose() for example). Note that this doesn't copy the data if it # is already contiguous. data = np.ascontiguousarray(data) # 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)[:, 0] faceTableI[faceTableInds] = 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.14.1/vispy/geometry/isocurve.py0000644000175000017510000001324414507104216017520 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 side_idx in range(0, len(sides), 2): # faces for this grid cell edges = sides[side_idx:side_idx+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.14.1/vispy/geometry/generation.py0000644000175000017510000005071514507104216020020 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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, 0], [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_plane(width=1, height=1, width_segments=1, height_segments=1, direction='+z'): """Generate vertices & indices for a filled and outlined plane. Parameters ---------- width : float Plane width. height : float Plane height. width_segments : int Plane segments count along the width. height_segments : float Plane segments count along the height. direction: unicode ``{'-x', '+x', '-y', '+y', '-z', '+z'}`` Direction the plane will be facing. Returns ------- vertices : array Array of vertices suitable for use as a VertexBuffer. faces : array Indices to use to produce a filled plane. outline : array Indices to use to produce an outline of the plane. References ---------- .. [1] Cabello, R. (n.d.). PlaneBufferGeometry.js. Retrieved May 12, 2015, from http://git.io/vU1Fh """ x_grid = width_segments y_grid = height_segments x_grid1 = x_grid + 1 y_grid1 = y_grid + 1 # Positions, normals and texcoords. positions = np.zeros(x_grid1 * y_grid1 * 3) normals = np.zeros(x_grid1 * y_grid1 * 3) texcoords = np.zeros(x_grid1 * y_grid1 * 2) y = np.arange(y_grid1) * height / y_grid - height / 2 x = np.arange(x_grid1) * width / x_grid - width / 2 positions[::3] = np.tile(x, y_grid1) positions[1::3] = -np.repeat(y, x_grid1) normals[2::3] = 1 texcoords[::2] = np.tile(np.arange(x_grid1) / x_grid, y_grid1) texcoords[1::2] = np.repeat(1 - np.arange(y_grid1) / y_grid, x_grid1) # Faces and outline. faces, outline = [], [] for i_y in range(y_grid): for i_x in range(x_grid): a = i_x + x_grid1 * i_y b = i_x + x_grid1 * (i_y + 1) c = (i_x + 1) + x_grid1 * (i_y + 1) d = (i_x + 1) + x_grid1 * i_y faces.extend(((a, b, d), (b, c, d))) outline.extend(((a, b), (b, c), (c, d), (d, a))) positions = np.reshape(positions, (-1, 3)) texcoords = np.reshape(texcoords, (-1, 2)) normals = np.reshape(normals, (-1, 3)) faces = np.reshape(faces, (-1, 3)).astype(np.uint32) outline = np.reshape(outline, (-1, 2)).astype(np.uint32) direction = direction.lower() if direction in ('-x', '+x'): shift, neutral_axis = 1, 0 elif direction in ('-y', '+y'): shift, neutral_axis = -1, 1 elif direction in ('-z', '+z'): shift, neutral_axis = 0, 2 sign = -1 if '-' in direction else 1 positions = np.roll(positions, shift, -1) normals = np.roll(normals, shift, -1) * sign colors = np.ravel(positions) colors = np.hstack((np.reshape(np.interp(colors, (np.min(colors), np.max(colors)), (0, 1)), positions.shape), np.ones((positions.shape[0], 1)))) colors[..., neutral_axis] = 0 vertices = np.zeros(positions.shape[0], [('position', np.float32, 3), ('texcoord', np.float32, 2), ('normal', np.float32, 3), ('color', np.float32, 4)]) vertices['position'] = positions vertices['texcoord'] = texcoords vertices['normal'] = normals vertices['color'] = colors return vertices, faces, outline def create_box(width=1, height=1, depth=1, width_segments=1, height_segments=1, depth_segments=1, planes=None): """Generate vertices & indices for a filled and outlined box. Parameters ---------- width : float Box width. height : float Box height. depth : float Box depth. width_segments : int Box segments count along the width. height_segments : float Box segments count along the height. depth_segments : float Box segments count along the depth. planes: array_like Any combination of ``{'-x', '+x', '-y', '+y', '-z', '+z'}`` Included planes in the box construction. Returns ------- vertices : array Array of vertices suitable for use as a VertexBuffer. faces : array Indices to use to produce a filled box. outline : array Indices to use to produce an outline of the box. """ planes = (('+x', '-x', '+y', '-y', '+z', '-z') if planes is None else [d.lower() for d in planes]) w_s, h_s, d_s = width_segments, height_segments, depth_segments planes_m = [] if '-z' in planes: planes_m.append(create_plane(width, depth, w_s, d_s, '-z')) planes_m[-1][0]['position'][..., 2] -= height / 2 if '+z' in planes: planes_m.append(create_plane(width, depth, w_s, d_s, '+z')) planes_m[-1][0]['position'][..., 2] += height / 2 if '-y' in planes: planes_m.append(create_plane(height, width, h_s, w_s, '-y')) planes_m[-1][0]['position'][..., 1] -= depth / 2 if '+y' in planes: planes_m.append(create_plane(height, width, h_s, w_s, '+y')) planes_m[-1][0]['position'][..., 1] += depth / 2 if '-x' in planes: planes_m.append(create_plane(depth, height, d_s, h_s, '-x')) planes_m[-1][0]['position'][..., 0] -= width / 2 if '+x' in planes: planes_m.append(create_plane(depth, height, d_s, h_s, '+x')) planes_m[-1][0]['position'][..., 0] += width / 2 positions = np.zeros((0, 3), dtype=np.float32) texcoords = np.zeros((0, 2), dtype=np.float32) normals = np.zeros((0, 3), dtype=np.float32) faces = np.zeros((0, 3), dtype=np.uint32) outline = np.zeros((0, 2), dtype=np.uint32) offset = 0 for vertices_p, faces_p, outline_p in planes_m: positions = np.vstack((positions, vertices_p['position'])) texcoords = np.vstack((texcoords, vertices_p['texcoord'])) normals = np.vstack((normals, vertices_p['normal'])) faces = np.vstack((faces, faces_p + offset)) outline = np.vstack((outline, outline_p + offset)) offset += vertices_p['position'].shape[0] vertices = np.zeros(positions.shape[0], [('position', np.float32, 3), ('texcoord', np.float32, 2), ('normal', np.float32, 3), ('color', np.float32, 4)]) colors = np.ravel(positions) colors = np.hstack((np.reshape(np.interp(colors, (np.min(colors), np.max(colors)), (0, 1)), positions.shape), np.ones((positions.shape[0], 1)))) vertices['position'] = positions vertices['texcoord'] = texcoords vertices['normal'] = normals vertices['color'] = colors return vertices, faces, outline def _latitude(rows, cols, radius, offset): 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 _ico(radius, subdivisions): # golden ratio t = (1.0 + np.sqrt(5.0))/2.0 # vertices of a icosahedron verts = [(-1, t, 0), (1, t, 0), (-1, -t, 0), (1, -t, 0), (0, -1, t), (0, 1, t), (0, -1, -t), (0, 1, -t), (t, 0, -1), (t, 0, 1), (-t, 0, -1), (-t, 0, 1)] # faces of the icosahedron faces = [(0, 11, 5), (0, 5, 1), (0, 1, 7), (0, 7, 10), (0, 10, 11), (1, 5, 9), (5, 11, 4), (11, 10, 2), (10, 7, 6), (7, 1, 8), (3, 9, 4), (3, 4, 2), (3, 2, 6), (3, 6, 8), (3, 8, 9), (4, 9, 5), (2, 4, 11), (6, 2, 10), (8, 6, 7), (9, 8, 1)] def midpoint(v1, v2): return ((v1[0]+v2[0])/2, (v1[1]+v2[1])/2, (v1[2]+v2[2])/2) # subdivision for _ in range(subdivisions): for idx in range(len(faces)): i, j, k = faces[idx] a, b, c = verts[i], verts[j], verts[k] ab, bc, ca = midpoint(a, b), midpoint(b, c), midpoint(c, a) verts += [ab, bc, ca] ij, jk, ki = len(verts)-3, len(verts)-2, len(verts)-1 faces.append([i, ij, ki]) faces.append([ij, j, jk]) faces.append([ki, jk, k]) faces[idx] = [jk, ki, ij] verts = np.array(verts, dtype=np.float32) faces = np.array(faces, dtype=np.uint32) # make each vertex to lie on the sphere lengths = np.sqrt((verts*verts).sum(axis=1)) verts /= lengths[:, np.newaxis]/radius return MeshData(vertices=verts, faces=faces) def _cube(rows, cols, depth, radius): # vertices and faces of tessellated cube verts, faces, _ = create_box(1, 1, 1, cols, rows, depth) verts = verts['position'] # make each vertex to lie on the sphere lengths = np.sqrt((verts*verts).sum(axis=1)) verts /= lengths[:, np.newaxis]/radius return MeshData(vertices=verts, faces=faces) def create_sphere(rows=10, cols=10, depth=10, radius=1.0, offset=True, subdivisions=3, method='latitude'): """Create a sphere Parameters ---------- rows : int Number of rows (for method='latitude' and 'cube'). cols : int Number of columns (for method='latitude' and 'cube'). depth : int Number of depth segments (for method='cube'). radius : float Sphere radius. offset : bool Rotate each row by half a column (for method='latitude'). subdivisions : int Number of subdivisions to perform (for method='ico') method : str Method for generating sphere. Accepts 'latitude' for latitude- longitude, 'ico' for icosahedron, and 'cube' for cube based tessellation. Returns ------- sphere : MeshData Vertices and faces computed for a spherical surface. """ if method == 'latitude': return _latitude(rows, cols, radius, offset) elif method == 'ico': return _ico(radius, subdivisions) elif method == 'cube': return _cube(rows, cols, depth, radius) else: raise Exception("Invalid method. Accepts: 'latitude', 'ico', 'cube'") 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) def create_grid_mesh(xs, ys, zs): """Generate vertices and indices for an implicitly connected mesh. The intention is that this makes it simple to generate a mesh from meshgrid data. Parameters ---------- xs : ndarray A 2d array of x coordinates for the vertices of the mesh. Must have the same dimensions as ys and zs. ys : ndarray A 2d array of y coordinates for the vertices of the mesh. Must have the same dimensions as xs and zs. zs : ndarray A 2d array of z coordinates for the vertices of the mesh. Must have the same dimensions as xs and ys. Returns ------- vertices : ndarray The array of vertices in the mesh. indices : ndarray The array of indices for the mesh. """ shape = xs.shape length = shape[0] * shape[1] vertices = np.zeros((length, 3)) vertices[:, 0] = xs.reshape(length) vertices[:, 1] = ys.reshape(length) vertices[:, 2] = zs.reshape(length) basic_indices = np.array([0, 1, 1 + shape[1], 0, 0 + shape[1], 1 + shape[1]], dtype=np.uint32) inner_grid_length = (shape[0] - 1) * (shape[1] - 1) offsets = np.arange(inner_grid_length) offsets += np.repeat(np.arange(shape[0] - 1), shape[1] - 1) offsets = np.repeat(offsets, 6) indices = np.resize(basic_indices, len(offsets)) + offsets indices = indices.reshape((len(indices) // 3, 3)) return vertices, indices vispy-0.14.1/vispy/geometry/curves.py0000644000175000017510000003153114507104216017167 0ustar nileshnilesh# # Anti-Grain Geometry - Version 2.4 # Copyright (C) 2002-2005 Maxim Shemanarev (McSeem) # # 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. # # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. # ---------------------------------------------------------------------------- # # 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 import numpy as np 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 def calc_sq_distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 return dx * dx + dy * dy def _curve3_recursive_bezier(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 _curve3_recursive_bezier(points, x1, y1, x12, y12, x123, y123, level + 1) _curve3_recursive_bezier(points, x123, y123, x23, y23, x3, y3, level + 1) def _curve4_recursive_bezier(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): # 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 _curve4_recursive_bezier( points, x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1) _curve4_recursive_bezier( points, x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1) def curve3_bezier(p1, p2, p3): """ Generate the vertices for a quadratic Bezier curve. The vertices returned by this function can be passed to a LineVisual or ArrowVisual. Parameters ---------- p1 : array 2D coordinates of the start point p2 : array 2D coordinates of the first curve point p3 : array 2D coordinates of the end point Returns ------- coords : list Vertices for the Bezier curve. See Also -------- curve4_bezier Notes ----- For more information about Bezier curves please refer to the `Wikipedia`_ page. .. _Wikipedia: https://en.wikipedia.org/wiki/B%C3%A9zier_curve """ x1, y1 = p1 x2, y2 = p2 x3, y3 = p3 points = [] _curve3_recursive_bezier(points, x1, y1, x2, y2, x3, y3) dx, dy = points[0][0] - x1, points[0][1] - y1 if (dx * dx + dy * dy) > 1e-10: points.insert(0, (x1, y1)) dx, dy = points[-1][0] - x3, points[-1][1] - y3 if (dx * dx + dy * dy) > 1e-10: points.append((x3, y3)) return np.array(points).reshape(len(points), 2) def curve4_bezier(p1, p2, p3, p4): """ Generate the vertices for a third order Bezier curve. The vertices returned by this function can be passed to a LineVisual or ArrowVisual. Parameters ---------- p1 : array 2D coordinates of the start point p2 : array 2D coordinates of the first curve point p3 : array 2D coordinates of the second curve point p4 : array 2D coordinates of the end point Returns ------- coords : list Vertices for the Bezier curve. See Also -------- curve3_bezier Notes ----- For more information about Bezier curves please refer to the `Wikipedia`_ page. .. _Wikipedia: https://en.wikipedia.org/wiki/B%C3%A9zier_curve """ x1, y1 = p1 x2, y2 = p2 x3, y3 = p3 x4, y4 = p4 points = [] _curve4_recursive_bezier(points, x1, y1, x2, y2, x3, y3, x4, y4) dx, dy = points[0][0] - x1, points[0][1] - y1 if (dx * dx + dy * dy) > 1e-10: points.insert(0, (x1, y1)) dx, dy = points[-1][0] - x4, points[-1][1] - y4 if (dx * dx + dy * dy) > 1e-10: points.append((x4, y4)) return np.array(points).reshape(len(points), 2) vispy-0.14.1/vispy/geometry/calculations.py0000644000175000017510000001022514507104216020336 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Miscellaneous functions""" import numpy as np ############################################################################### # 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.astype(np.int32), 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, str) 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.14.1/vispy/geometry/_triangulation_debugger.py0000644000175000017510000001201214507104216022534 0ustar nileshnilesh# -*- coding: utf-8 -*- """ 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 ..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)] num_pts = len(pts) edges = [(i, (i+1) % num_pts) for i in range(num_pts)] pts += [(21, 21), (24, 21), (24, 24), (21, 24)] edges += [(num_pts, num_pts + 1), (num_pts + 1, num_pts + 2), (num_pts + 2, num_pts + 3), (num_pts + 3, num_pts)] 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.14.1/vispy/geometry/__init__.py0000644000175000017510000000205514507104216017416 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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_box', 'create_cone', 'create_cube', 'create_cylinder', 'create_grid_mesh', 'create_plane', '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_box, create_cone, # noqa create_cube, create_cylinder, create_grid_mesh, # noqa create_plane, create_sphere) # noqa vispy-0.14.1/vispy/ext/0000755000175000017510000000000014507104251014247 5ustar nileshnileshvispy-0.14.1/vispy/ext/osmesa.py0000644000175000017510000000652314507104216016117 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """A ctypes-based API to OSMesa""" from __future__ import print_function import os import ctypes import ctypes.util from ctypes import c_int as _c_int, c_uint as _c_uint, c_void_p # See vispy/gloo/gl/_constants.py for reference GL_RGBA = 6408 GL_UNSIGNED_BYTE = 5121 GL_VERSION = 7938 _osmesa_file = None if 'OSMESA_LIBRARY' in os.environ: if os.path.exists(os.environ['OSMESA_LIBRARY']): _osmesa_file = os.path.realpath(os.environ['OSMESA_LIBRARY']) # Else, try to find it if _osmesa_file is None: _osmesa_file = ctypes.util.find_library('OSMesa') # Else, we failed and exit if _osmesa_file is None: raise OSError('OSMesa library not found') # Load it _lib = ctypes.CDLL(_osmesa_file) # Constants OSMESA_RGBA = GL_RGBA # Functions # GLAPI OSMesaContext GLAPIENTRY # OSMesaCreateContext( GLenum format, OSMesaContext sharelist ); _lib.OSMesaCreateContext.argtypes = _c_int, c_void_p _lib.OSMesaCreateContext.restype = c_void_p # GLAPI void GLAPIENTRY # OSMesaDestroyContext( OSMesaContext ctx ); _lib.OSMesaDestroyContext.argtypes = c_void_p, # GLAPI GLboolean GLAPIENTRY # OSMesaMakeCurrent( OSMesaContext ctx, void *buffer, GLenum type, # GLsizei width, GLsizei height ); _lib.OSMesaMakeCurrent.argtypes = c_void_p, c_void_p, _c_int, _c_int, _c_int _lib.OSMesaMakeCurrent.restype = _c_int # GLAPI OSMesaContext GLAPIENTRY # OSMesaGetCurrentContext( void ); _lib.OSMesaGetCurrentContext.restype = c_void_p def allocate_pixels_buffer(width, height): """Helper function to allocate a buffer to contain an image of width * height suitable for OSMesaMakeCurrent """ # Seems like OSMesa has some trouble with non-RGBA buffers, so enforce # RGBA return (_c_uint * width * height * 4)() def OSMesaCreateContext(): return ctypes.cast(_lib.OSMesaCreateContext(OSMESA_RGBA, None), c_void_p) def OSMesaDestroyContext(context): _lib.OSMesaDestroyContext(context) def OSMesaMakeCurrent(context, buffer, width, height): ret = _lib.OSMesaMakeCurrent(context, buffer, GL_UNSIGNED_BYTE, width, height) return ret != 0 def OSMesaGetCurrentContext(): return c_void_p(_lib.OSMesaGetCurrentContext()) if __name__ == '__main__': """This test basic OSMesa functionality""" # If you have OSMesa installed alongside normal OpenGL, execute with # VISPY_GL_LIB=/opt/osmesa_llvmpipe/lib/libGLESv2.so \ # LD_LIBRARY_PATH=/opt/osmesa_llvmpipe/lib/ \ # OSMESA_LIBRARY=/opt/osmesa_llvmpipe/lib/libOSMesa.so \ # python vispy/ext/osmesa.py context = OSMesaCreateContext() w, h = 640, 480 pixels = allocate_pixels_buffer(w, h) ok = OSMesaMakeCurrent(context, pixels, 640, 480) if not ok: raise RuntimeError('Failed OSMesaMakeCurrent') if not (OSMesaGetCurrentContext().value == context.value): raise RuntimeError('OSMesa context not correctly attached') _lib.glGetString.argtypes = (ctypes.c_uint,) _lib.glGetString.restype = ctypes.c_char_p print("OpenGL version : ", _lib.glGetString(GL_VERSION)) OSMesaDestroyContext(context) if OSMesaGetCurrentContext().value is not None: raise RuntimeError('Failed to destroy OSMesa context') vispy-0.14.1/vispy/ext/gdi32plus.py0000644000175000017510000001473114507104216016444 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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.14.1/vispy/ext/fontconfig.py0000644000175000017510000001021414507104216016754 0ustar nileshnilesh# -*- 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', ':scalable=true', 'family']) vals = [v.split(',')[0] for v in stdout_.strip().splitlines(False)] return vals vispy-0.14.1/vispy/ext/egl.py0000644000175000017510000002760014507104216015376 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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_void_p _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.eglCreateWindowSurface.restype = c_void_p _lib.eglCreatePbufferSurface.argtypes = (c_void_p, c_void_p, _POINTER(_c_int)) _lib.eglCreatePbufferSurface.restype = c_void_p _lib.eglCreateContext.argtypes = c_void_p, c_void_p, c_void_p, _POINTER(_c_int) _lib.eglCreateContext.restype = c_void_p _lib.eglMakeCurrent.argtypes = (c_void_p,) * 4 _lib.eglBindAPI.argtypes = _c_int, _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 c_void_p(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): """Terminate an EGL display connection.""" _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) surface = c_void_p(_lib.eglCreateWindowSurface(display, config, window, attribList)) return _check_res(surface) def eglCreatePbufferSurface(display, config, attribList=None): # Deal with attrib list attribList = _convert_attrib_list(attribList) # surface = c_void_p(_lib.eglCreatePbufferSurface(display, config, attribList)) return _check_res(surface) 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 = c_void_p(_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 eglBindAPI(api): """Set the current rendering API (OpenGL, OpenGL ES or OpenVG)""" res = _lib.eglBindAPI(api) if res == EGL_FALSE: raise RuntimeError('Could not bind API %d' % api) return res 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.14.1/vispy/ext/cubehelix.py0000644000175000017510000001233414507104216016575 0ustar nileshnilesh# -*- 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, grn, blu)).T vispy-0.14.1/vispy/ext/cocoapy.py0000644000175000017510000015174014507104216016267 0ustar nileshnilesh# -*- 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 # handle dlopen cache changes in macOS 11 (Big Sur) # ref https://stackoverflow.com/questions/63475461/unable-to-import-opengl-gl-in-python-on-macos try: import OpenGL.GL # noqa except ImportError: # print('Drat, patching for Big Sur') orig_util_find_library = util.find_library def new_util_find_library(name): res = orig_util_find_library(name) if res: return res lut = { 'objc': 'libobjc.dylib', 'quartz': 'Quartz.framework/Quartz' } return lut.get(name, name+'.framework/'+name) util.find_library = new_util_find_library # 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] if platform.machine() != "arm64": 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] if platform.machine() != "arm64": 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 not isinstance(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 ValueError: 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 ValueError: 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 ValueError('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.14.1/vispy/ext/__init__.py0000644000175000017510000000000014507104216016347 0ustar nileshnileshvispy-0.14.1/vispy/conftest.py0000644000175000017510000000075114507104216015652 0ustar nileshnileshdef pytest_configure(config): config.addinivalue_line( 'markers', 'vispy_app_test: Tests that require a valid GUI application.') warning_lines = """ ignore:.*imp module.*: ignore:Using or importing the ABCs.*: """ # noqa: E501 for warning_line in warning_lines.split('\n'): warning_line = warning_line.strip() if warning_line and not warning_line.startswith('#'): config.addinivalue_line('filterwarnings', warning_line) vispy-0.14.1/vispy/color/0000755000175000017510000000000014507104251014565 5ustar nileshnileshvispy-0.14.1/vispy/color/tests/0000755000175000017510000000000014507104251015727 5ustar nileshnileshvispy-0.14.1/vispy/color/tests/test_color.py0000644000175000017510000003116714507104216020467 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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]) x = ColorArray([Color((0, 0, 0)), Color((1, 1, 1))]) assert len(x.rgb) == 2 x = ColorArray([ColorArray((0, 0, 0)), ColorArray((1, 1, 1))]) assert len(x.rgb) == 2 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.],) 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 != "" 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.14.1/vispy/color/tests/__init__.py0000644000175000017510000000000014507104216020027 0ustar nileshnileshvispy-0.14.1/vispy/color/colormap.py0000644000175000017510000012313214507104216016756 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division # just to be safe... import warnings import numpy as np from .color_array import ColorArray from ..ext.cubehelix import cubehelix from hsluv import hsluv_to_rgb from ..util.check_environment import has_matplotlib import vispy.gloo ############################################################################### # Color maps # Length of the texture map used for luminance to RGBA conversion LUT_len = 1024 # 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, colors=None, texture_map_data=None): """Generate a GLSL template function from a given interpolation patterns and control points. Parameters ---------- colors : array-like, shape (n_colors, 4) The control colors used by the colormap. Elements of colors must be convertible to an instance of Color-class. controls : list 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. texture_map_data : ndarray, shape(texture_len, 4) Numpy array of size of 1D texture lookup data for luminance to RGBA conversion. """ assert (controls[0], controls[-1]) == (0., 1.) ncolors = len(controls) assert ncolors >= 2 assert (texture_map_data is not None) LUT = texture_map_data texture_len = texture_map_data.shape[0] # Perform linear interpolation for each RGBA color component. c_rgba = ColorArray(colors)._rgba x = np.linspace(0.0, 1.0, texture_len) LUT[:, 0, 0] = np.interp(x, controls, c_rgba[:, 0]) LUT[:, 0, 1] = np.interp(x, controls, c_rgba[:, 1]) LUT[:, 0, 2] = np.interp(x, controls, c_rgba[:, 2]) LUT[:, 0, 3] = np.interp(x, controls, c_rgba[:, 3]) s2 = "uniform sampler2D texture2D_LUT;" s = "{\n return texture2D(texture2D_LUT, \ vec2(0.0, clamp(t, 0.0, 1.0)));\n} " return "%s\nvec4 colormap(float t) {\n%s\n}" % (s2, s) def _glsl_step(controls=None, colors=None, texture_map_data=None): assert (controls[0], controls[-1]) == (0., 1.) ncolors = len(controls) - 1 assert ncolors >= 2 assert (texture_map_data is not None) LUT = texture_map_data texture_len = texture_map_data.shape[0] LUT_tex_idx = np.linspace(0.0, 1.0, texture_len) # Replicate indices to colormap texture. # The resulting matrix has size of (texture_len,len(controls)). # It is used to perform piecewise constant interpolation # for each RGBA color component. t2 = np.repeat(LUT_tex_idx[:, np.newaxis], len(controls), 1) # Perform element-wise comparison to find # control points for all LUT colors. bn = np.sum(controls.transpose() <= t2, axis=1) j = np.clip(bn-1, 0, ncolors-1) # Copying color data from ColorArray to array-like # makes data assignment to LUT faster. colors_rgba = ColorArray(colors[:])._rgba LUT[:, 0, :] = colors_rgba[j] s2 = "uniform sampler2D texture2D_LUT;" s = "{\n return texture2D(texture2D_LUT, \ vec2(0.0, clamp(t, 0.0, 1.0)));\n} " return "%s\nvec4 colormap(float t) {\n%s\n}" % (s2, 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): u"""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 # Texture map data used by the 'colormap' GLSL function # for luminance to RGBA conversion. texture_map_data = 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 texture_lut(self): """Return a texture2D object for LUT after its value is set. Can be None.""" return None 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' and 'colors' as arguments '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) # use texture map for luminance to RGBA conversion self.texture_map_data = np.zeros((LUT_len, 1, 4), dtype=np.float32) self.glsl_map = self._glsl_map_generator(self._controls, colors, self.texture_map_data) 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) def texture_lut(self): """Return a texture2D object for LUT after its value is set. Can be None.""" if self.texture_map_data is None: return None interp = 'linear' if self.interpolation == 'linear' else 'nearest' texture_LUT = vispy.gloo.Texture2D(np.zeros(self.texture_map_data.shape, dtype=np.float32), interpolation=interp) texture_LUT.set_data(self.texture_map_data, offset=None, copy=True) return texture_LUT class MatplotlibColormap(Colormap): """Use matplotlib colormaps if installed. Parameters ---------- name : string Name of the colormap. """ def __init__(self, name): from matplotlib.cm import ScalarMappable vec = ScalarMappable(cmap=name).to_rgba(np.arange(LUT_len)) Colormap.__init__(self, vec) 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)) class SingleHue(Colormap): """A colormap which is solely defined by the given hue and value. Given the color hue and value, this color map increases the saturation of a color. The start color is almost white but still contains a hint of the given color, and at the end the color is fully saturated. Parameters ---------- hue : scalar, optional The hue refers to a "true" color, without any shading or tinting. Must be in the range [0, 360]. Defaults to 200 (blue). saturation_range : array-like, optional The saturation represents how "pure" a color is. Less saturation means more white light mixed in the color. A fully saturated color means the pure color defined by the hue. No saturation means completely white. This colormap changes the saturation, and with this parameter you can specify the lower and upper bound. Default is [0.2, 0.8]. value : scalar, optional The value defines the "brightness" of a color: a value of 0.0 means completely black while a value of 1.0 means the color defined by the hue without shading. Must be in the range [0, 1.0]. The default value is 1.0. Notes ----- For more information about the hue values see the `wikipedia page`_. .. _wikipedia page: https://en.wikipedia.org/wiki/Hue """ def __init__(self, hue=200, saturation_range=[0.1, 0.8], value=1.0): colors = ColorArray([ (hue, saturation_range[0], value), (hue, saturation_range[1], value) ], color_space='hsv') super(SingleHue, self).__init__(colors) class HSL(Colormap): """A colormap which is defined by n evenly spaced points in a circular color space. This means that we change the hue value while keeping the saturation and value constant. Parameters ---------- n_colors : int, optional The number of colors to generate. hue_start : int, optional The hue start value. Must be in the range [0, 360], the default is 0. saturation : float, optional The saturation component of the colors to generate. The default is fully saturated (1.0). Must be in the range [0, 1.0]. value : float, optional The value (brightness) component of the colors to generate. Must be in the range [0, 1.0], and the default is 1.0 controls : array-like, optional The list of control points for the colors to generate. 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, optional 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). """ def __init__(self, ncolors=6, hue_start=0, saturation=1.0, value=1.0, controls=None, interpolation='linear'): hues = np.linspace(0, 360, ncolors + 1)[:-1] hues += hue_start hues %= 360 colors = ColorArray([(hue, saturation, value) for hue in hues], color_space='hsv') super(HSL, self).__init__(colors, controls=controls, interpolation=interpolation) class HSLuv(Colormap): """A colormap which is defined by n evenly spaced points in the HSLuv space. Parameters ---------- n_colors : int, optional The number of colors to generate. hue_start : int, optional The hue start value. Must be in the range [0, 360], the default is 0. saturation : float, optional The saturation component of the colors to generate. The default is fully saturated (1.0). Must be in the range [0, 1.0]. value : float, optional The value component of the colors to generate or "brightness". Must be in the range [0, 1.0], and the default is 0.7. controls : array-like, optional The list of control points for the colors to generate. 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, optional 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). Notes ----- For more information about HSLuv colors see https://www.hsluv.org/ """ def __init__(self, ncolors=6, hue_start=0, saturation=1.0, value=0.7, controls=None, interpolation='linear'): hues = np.linspace(0, 360, ncolors + 1)[:-1] hues += hue_start hues %= 360 saturation *= 99 value *= 99 colors = ColorArray( [hsluv_to_rgb([hue, saturation, value]) for hue in hues], ) super(HSLuv, self).__init__(colors, controls=controls, interpolation=interpolation) class _HUSL(HSLuv): """Deprecated.""" def __init__(self, *args, **kwargs): warnings.warn("_HUSL Colormap is deprecated. Please use 'HSLuv' instead.") super().__init__(*args, **kwargs) class Diverging(Colormap): def __init__(self, h_pos=20, h_neg=250, saturation=1.0, value=0.7, center="light"): saturation *= 99 value *= 99 start = hsluv_to_rgb([h_neg, saturation, value]) mid = ((0.133, 0.133, 0.133) if center == "dark" else (0.92, 0.92, 0.92)) end = hsluv_to_rgb([h_pos, saturation, value]) colors = ColorArray([start, mid, end]) super(Diverging, self).__init__(colors) class RedYellowBlueCyan(Colormap): """A colormap which goes red-yellow positive and blue-cyan negative Parameters ---------- limits : array-like, optional The limits for the fully transparent, opaque red, and yellow points. """ def __init__(self, limits=(0.33, 0.66, 1.0)): limits = np.array(limits, float).ravel() if len(limits) != 3: raise ValueError('limits must have 3 values') if (np.diff(limits) < 0).any() or (limits <= 0).any(): raise ValueError('limits must be strictly increasing and positive') controls = np.array([-limits[2], -limits[1], -limits[0], limits[0], limits[1], limits[2]]) controls = ((controls / limits[2]) + 1) / 2. colors = [(0., 1., 1., 1.), (0., 0., 1., 1.), (0., 0., 1., 0.), (1., 0., 0., 0.), (1., 0., 0., 1.), (1., 1., 0., 1.)] colors = ColorArray(colors) super(RedYellowBlueCyan, self).__init__( colors, controls=controls, interpolation='linear') # https://github.com/matplotlib/matplotlib/pull/4707/files#diff-893cf0348279e9f4570488a7a297ab1eR774 # noqa # Taken from original Viridis colormap data in matplotlib implementation # # Issue #1331 https://github.com/vispy/vispy/issues/1331 explains that the # 128 viridis sample size fails on some GPUs # but lowering to 64 samples allows more GPUs to use viridis. # # VisPy has beem updated to use a texture map lookup. # Thus, sampling of the Viridis colormap data is no longer necessary. _viridis_data = [[0.267004, 0.004874, 0.329415], [0.268510, 0.009605, 0.335427], [0.269944, 0.014625, 0.341379], [0.271305, 0.019942, 0.347269], [0.272594, 0.025563, 0.353093], [0.273809, 0.031497, 0.358853], [0.274952, 0.037752, 0.364543], [0.276022, 0.044167, 0.370164], [0.277018, 0.050344, 0.375715], [0.277941, 0.056324, 0.381191], [0.278791, 0.062145, 0.386592], [0.279566, 0.067836, 0.391917], [0.280267, 0.073417, 0.397163], [0.280894, 0.078907, 0.402329], [0.281446, 0.084320, 0.407414], [0.281924, 0.089666, 0.412415], [0.282327, 0.094955, 0.417331], [0.282656, 0.100196, 0.422160], [0.282910, 0.105393, 0.426902], [0.283091, 0.110553, 0.431554], [0.283197, 0.115680, 0.436115], [0.283229, 0.120777, 0.440584], [0.283187, 0.125848, 0.444960], [0.283072, 0.130895, 0.449241], [0.282884, 0.135920, 0.453427], [0.282623, 0.140926, 0.457517], [0.282290, 0.145912, 0.461510], [0.281887, 0.150881, 0.465405], [0.281412, 0.155834, 0.469201], [0.280868, 0.160771, 0.472899], [0.280255, 0.165693, 0.476498], [0.279574, 0.170599, 0.479997], [0.278826, 0.175490, 0.483397], [0.278012, 0.180367, 0.486697], [0.277134, 0.185228, 0.489898], [0.276194, 0.190074, 0.493001], [0.275191, 0.194905, 0.496005], [0.274128, 0.199721, 0.498911], [0.273006, 0.204520, 0.501721], [0.271828, 0.209303, 0.504434], [0.270595, 0.214069, 0.507052], [0.269308, 0.218818, 0.509577], [0.267968, 0.223549, 0.512008], [0.266580, 0.228262, 0.514349], [0.265145, 0.232956, 0.516599], [0.263663, 0.237631, 0.518762], [0.262138, 0.242286, 0.520837], [0.260571, 0.246922, 0.522828], [0.258965, 0.251537, 0.524736], [0.257322, 0.256130, 0.526563], [0.255645, 0.260703, 0.528312], [0.253935, 0.265254, 0.529983], [0.252194, 0.269783, 0.531579], [0.250425, 0.274290, 0.533103], [0.248629, 0.278775, 0.534556], [0.246811, 0.283237, 0.535941], [0.244972, 0.287675, 0.537260], [0.243113, 0.292092, 0.538516], [0.241237, 0.296485, 0.539709], [0.239346, 0.300855, 0.540844], [0.237441, 0.305202, 0.541921], [0.235526, 0.309527, 0.542944], [0.233603, 0.313828, 0.543914], [0.231674, 0.318106, 0.544834], [0.229739, 0.322361, 0.545706], [0.227802, 0.326594, 0.546532], [0.225863, 0.330805, 0.547314], [0.223925, 0.334994, 0.548053], [0.221989, 0.339161, 0.548752], [0.220057, 0.343307, 0.549413], [0.218130, 0.347432, 0.550038], [0.216210, 0.351535, 0.550627], [0.214298, 0.355619, 0.551184], [0.212395, 0.359683, 0.551710], [0.210503, 0.363727, 0.552206], [0.208623, 0.367752, 0.552675], [0.206756, 0.371758, 0.553117], [0.204903, 0.375746, 0.553533], [0.203063, 0.379716, 0.553925], [0.201239, 0.383670, 0.554294], [0.199430, 0.387607, 0.554642], [0.197636, 0.391528, 0.554969], [0.195860, 0.395433, 0.555276], [0.194100, 0.399323, 0.555565], [0.192357, 0.403199, 0.555836], [0.190631, 0.407061, 0.556089], [0.188923, 0.410910, 0.556326], [0.187231, 0.414746, 0.556547], [0.185556, 0.418570, 0.556753], [0.183898, 0.422383, 0.556944], [0.182256, 0.426184, 0.557120], [0.180629, 0.429975, 0.557282], [0.179019, 0.433756, 0.557430], [0.177423, 0.437527, 0.557565], [0.175841, 0.441290, 0.557685], [0.174274, 0.445044, 0.557792], [0.172719, 0.448791, 0.557885], [0.171176, 0.452530, 0.557965], [0.169646, 0.456262, 0.558030], [0.168126, 0.459988, 0.558082], [0.166617, 0.463708, 0.558119], [0.165117, 0.467423, 0.558141], [0.163625, 0.471133, 0.558148], [0.162142, 0.474838, 0.558140], [0.160665, 0.478540, 0.558115], [0.159194, 0.482237, 0.558073], [0.157729, 0.485932, 0.558013], [0.156270, 0.489624, 0.557936], [0.154815, 0.493313, 0.557840], [0.153364, 0.497000, 0.557724], [0.151918, 0.500685, 0.557587], [0.150476, 0.504369, 0.557430], [0.149039, 0.508051, 0.557250], [0.147607, 0.511733, 0.557049], [0.146180, 0.515413, 0.556823], [0.144759, 0.519093, 0.556572], [0.143343, 0.522773, 0.556295], [0.141935, 0.526453, 0.555991], [0.140536, 0.530132, 0.555659], [0.139147, 0.533812, 0.555298], [0.137770, 0.537492, 0.554906], [0.136408, 0.541173, 0.554483], [0.135066, 0.544853, 0.554029], [0.133743, 0.548535, 0.553541], [0.132444, 0.552216, 0.553018], [0.131172, 0.555899, 0.552459], [0.129933, 0.559582, 0.551864], [0.128729, 0.563265, 0.551229], [0.127568, 0.566949, 0.550556], [0.126453, 0.570633, 0.549841], [0.125394, 0.574318, 0.549086], [0.124395, 0.578002, 0.548287], [0.123463, 0.581687, 0.547445], [0.122606, 0.585371, 0.546557], [0.121831, 0.589055, 0.545623], [0.121148, 0.592739, 0.544641], [0.120565, 0.596422, 0.543611], [0.120092, 0.600104, 0.542530], [0.119738, 0.603785, 0.541400], [0.119512, 0.607464, 0.540218], [0.119423, 0.611141, 0.538982], [0.119483, 0.614817, 0.537692], [0.119699, 0.618490, 0.536347], [0.120081, 0.622161, 0.534946], [0.120638, 0.625828, 0.533488], [0.121380, 0.629492, 0.531973], [0.122312, 0.633153, 0.530398], [0.123444, 0.636809, 0.528763], [0.124780, 0.640461, 0.527068], [0.126326, 0.644107, 0.525311], [0.128087, 0.647749, 0.523491], [0.130067, 0.651384, 0.521608], [0.132268, 0.655014, 0.519661], [0.134692, 0.658636, 0.517649], [0.137339, 0.662252, 0.515571], [0.140210, 0.665859, 0.513427], [0.143303, 0.669459, 0.511215], [0.146616, 0.673050, 0.508936], [0.150148, 0.676631, 0.506589], [0.153894, 0.680203, 0.504172], [0.157851, 0.683765, 0.501686], [0.162016, 0.687316, 0.499129], [0.166383, 0.690856, 0.496502], [0.170948, 0.694384, 0.493803], [0.175707, 0.697900, 0.491033], [0.180653, 0.701402, 0.488189], [0.185783, 0.704891, 0.485273], [0.191090, 0.708366, 0.482284], [0.196571, 0.711827, 0.479221], [0.202219, 0.715272, 0.476084], [0.208030, 0.718701, 0.472873], [0.214000, 0.722114, 0.469588], [0.220124, 0.725509, 0.466226], [0.226397, 0.728888, 0.462789], [0.232815, 0.732247, 0.459277], [0.239374, 0.735588, 0.455688], [0.246070, 0.738910, 0.452024], [0.252899, 0.742211, 0.448284], [0.259857, 0.745492, 0.444467], [0.266941, 0.748751, 0.440573], [0.274149, 0.751988, 0.436601], [0.281477, 0.755203, 0.432552], [0.288921, 0.758394, 0.428426], [0.296479, 0.761561, 0.424223], [0.304148, 0.764704, 0.419943], [0.311925, 0.767822, 0.415586], [0.319809, 0.770914, 0.411152], [0.327796, 0.773980, 0.406640], [0.335885, 0.777018, 0.402049], [0.344074, 0.780029, 0.397381], [0.352360, 0.783011, 0.392636], [0.360741, 0.785964, 0.387814], [0.369214, 0.788888, 0.382914], [0.377779, 0.791781, 0.377939], [0.386433, 0.794644, 0.372886], [0.395174, 0.797475, 0.367757], [0.404001, 0.800275, 0.362552], [0.412913, 0.803041, 0.357269], [0.421908, 0.805774, 0.351910], [0.430983, 0.808473, 0.346476], [0.440137, 0.811138, 0.340967], [0.449368, 0.813768, 0.335384], [0.458674, 0.816363, 0.329727], [0.468053, 0.818921, 0.323998], [0.477504, 0.821444, 0.318195], [0.487026, 0.823929, 0.312321], [0.496615, 0.826376, 0.306377], [0.506271, 0.828786, 0.300362], [0.515992, 0.831158, 0.294279], [0.525776, 0.833491, 0.288127], [0.535621, 0.835785, 0.281908], [0.545524, 0.838039, 0.275626], [0.555484, 0.840254, 0.269281], [0.565498, 0.842430, 0.262877], [0.575563, 0.844566, 0.256415], [0.585678, 0.846661, 0.249897], [0.595839, 0.848717, 0.243329], [0.606045, 0.850733, 0.236712], [0.616293, 0.852709, 0.230052], [0.626579, 0.854645, 0.223353], [0.636902, 0.856542, 0.216620], [0.647257, 0.858400, 0.209861], [0.657642, 0.860219, 0.203082], [0.668054, 0.861999, 0.196293], [0.678489, 0.863742, 0.189503], [0.688944, 0.865448, 0.182725], [0.699415, 0.867117, 0.175971], [0.709898, 0.868751, 0.169257], [0.720391, 0.870350, 0.162603], [0.730889, 0.871916, 0.156029], [0.741388, 0.873449, 0.149561], [0.751884, 0.874951, 0.143228], [0.762373, 0.876424, 0.137064], [0.772852, 0.877868, 0.131109], [0.783315, 0.879285, 0.125405], [0.793760, 0.880678, 0.120005], [0.804182, 0.882046, 0.114965], [0.814576, 0.883393, 0.110347], [0.824940, 0.884720, 0.106217], [0.835270, 0.886029, 0.102646], [0.845561, 0.887322, 0.099702], [0.855810, 0.888601, 0.097452], [0.866013, 0.889868, 0.095953], [0.876168, 0.891125, 0.095250], [0.886271, 0.892374, 0.095374], [0.896320, 0.893616, 0.096335], [0.906311, 0.894855, 0.098125], [0.916242, 0.896091, 0.100717], [0.926106, 0.897330, 0.104071], [0.935904, 0.898570, 0.108131], [0.945636, 0.899815, 0.112838], [0.955300, 0.901065, 0.118128], [0.964894, 0.902323, 0.123941], [0.974417, 0.903590, 0.130215], [0.983868, 0.904867, 0.136897], [0.993248, 0.906157, 0.143936]] _colormaps = dict( # Some colormap presets 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(), light_blues=SingleHue(), orange=SingleHue(hue=35), viridis=Colormap(ColorArray(_viridis_data)), # Diverging presets coolwarm=Colormap(ColorArray( [ (226, 0.59, 0.92), (222, 0.44, 0.99), (218, 0.26, 0.97), (30, 0.01, 0.87), (20, 0.3, 0.96), (15, 0.5, 0.95), (8, 0.66, 0.86) ], color_space="hsv" )), PuGr=Diverging(145, 280, 0.85, 0.30), GrBu=Diverging(255, 133, 0.75, 0.6), GrBu_d=Diverging(255, 133, 0.75, 0.6, "dark"), RdBu=Diverging(220, 20, 0.75, 0.5), cubehelix=CubeHelixColormap(), single_hue=SingleHue(), hsl=HSL(), husl=HSLuv(), diverging=Diverging(), RdYeBuCy=RedYellowBlueCyan(), ) def get_colormap(name): """Obtain a colormap by name. Parameters ---------- name : str | Colormap Colormap name. Can also be a Colormap for pass-through. Examples -------- >>> get_colormap('autumn') >>> get_colormap('single_hue') .. versionchanged: 0.7 Additional args/kwargs are no longer accepted. Colormap instances are no longer created on the fly. """ if isinstance(name, BaseColormap): return name if not isinstance(name, str): raise TypeError('colormap must be a Colormap or string name') if name in _colormaps: # vispy cmap cmap = _colormaps[name] elif has_matplotlib(): # matplotlib cmap try: cmap = MatplotlibColormap(name) except ValueError: raise KeyError('colormap name %s not found' % name) else: raise KeyError('colormap name %s not found' % name) return cmap def get_colormaps(): """Return the list of colormap names.""" return _colormaps.copy() vispy-0.14.1/vispy/color/color_space.py0000644000175000017510000001364314507104216017440 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from __future__ import division # just to be safe... import numpy as np ############################################################################### # 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, str) 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.14.1/vispy/color/color_array.py0000644000175000017510000003411514507104216017460 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 ..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.lower()] 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, str): 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, (str, ColorArray)) 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 __array__(self, dtype=None): """Get a standard numpy array representing RGBA.""" rgba = self.rgba if dtype is not None: rgba = rgba.astype(dtype) return rgba 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.14.1/vispy/color/_color_dict.py0000644000175000017510000001215014507104216017417 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 = sorted(_color_dict.keys()) 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", "transparent": "#00000000", } vispy-0.14.1/vispy/color/__init__.py0000644000175000017510000000120414507104216016674 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/app/0000755000175000017510000000000014507104251014227 5ustar nileshnileshvispy-0.14.1/vispy/app/timer.py0000644000175000017510000001317214507104216015726 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 .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): """Initiallize timer method.""" 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, str): 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): """Timer is based on this vispy Application instance.""" 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): """Timer is based on this native timer""" 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.14.1/vispy/app/tests/0000755000175000017510000000000014507104251015371 5ustar nileshnileshvispy-0.14.1/vispy/app/tests/test_simultaneous.py0000644000175000017510000001065714507104216021544 0ustar nileshnilesh# -*- coding: utf-8 -*- import sys from time import sleep import pytest import numpy as np from numpy.testing import assert_allclose from vispy.app import use_app, Canvas, Timer from vispy.testing import requires_application, SkipTest, run_tests_if_main, IS_TRAVIS_CI 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 @pytest.mark.xfail(IS_TRAVIS_CI and 'darwin' in sys.platform, reason='Travis OSX causes segmentation fault on this test for an unknown reason.') @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 + 20 # be quite lenient assert n_check <= ct[1] <= n_check + 20 # 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.14.1/vispy/app/tests/test_qt.py0000644000175000017510000000240514507104216017430 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/app/tests/test_context.py0000644000175000017510000000763114507104216020476 0ustar nileshnileshimport sys import pytest from vispy.testing import (requires_application, SkipTest, run_tests_if_main, assert_raises, IS_CI) 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 if a.backend_name.lower() == 'osmesa': return # cannot set config on OSMesa if 'pyqt5' in a.backend_name.lower() or 'pyqt6' in a.backend_name.lower() or 'pyside2' in a.backend_name.lower() or 'pyside6' in a.backend_name.lower(): pytest.xfail("Context sharing is not supported in PyQt5, PyQt6, PySide2, or PySide6 at this time.") # 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 IS_CI: raise SkipTest('Travis SDL cannot set context') for config in configs: n_items = len(config) with Canvas(config=config): if IS_CI: # Travis and Appveyor cannot handle obtaining these values props = config else: props = get_gl_configuration() assert len(config) == n_items for key, val in config.items(): # XXX knownfail for windows samples, and wx/tkinter (all platforms) if key == 'samples': will_fail_backend = a.backend_name.lower() in ('wx', 'tkinter') if not (sys.platform.startswith('win') or will_fail_backend): assert 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() # pyqt5 does not currently support context sharing, pyside6 seg faults on app tests if 'pyqt5' in c1.app.backend_name.lower() or 'pyqt6' in c1.app.backend_name.lower() or 'pyside2' in c1.app.backend_name.lower() or 'pyside6' in c1.app.backend_name.lower(): pytest.xfail("Context sharing is not supported in PyQt5, PyQt6, PySide2, or PySide6 at this time.") # Tkinter does not currently support context sharing if 'tk' in c1.app.backend_name.lower(): pytest.xfail("Context sharing is not supported in Tkinter at this time.") # 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.14.1/vispy/app/tests/test_canvas.py0000644000175000017510000001036714507104216020265 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy.app import Canvas, MouseEvent from vispy.visuals import ImageVisual from vispy.testing import requires_application from vispy.visuals.transforms import STTransform from vispy import gloo import numpy as np import pytest @requires_application() @pytest.mark.parametrize( 'blend_func', [ ('src_alpha', 'one_minus_src_alpha', 'one', 'one_minus_src_alpha'), ('src_alpha', 'one_minus_src_alpha'), None, ]) def test_canvas_render(blend_func): """Test rendering a canvas to an array. Different blending functions are used to test what various Visuals may produce without actually using different types of Visuals. """ with Canvas(size=(125, 125), show=True, title='run') as c: im1 = np.zeros((100, 100, 4)).astype(np.float32) im1[:, :, 0] = 1 im1[:, :, 3] = 1 im2 = np.zeros((50, 50, 4)).astype(np.float32) im2[:, :, 1] = 1 im2[:, :, 3] = 0.4 # Create the image image1 = ImageVisual(im1) image1.transform = STTransform(translate=(20, 20, 0)) image1.transforms.configure(canvas=c, viewport=(0, 0, 125, 125)) image2 = ImageVisual(im2) image2.transform = STTransform(translate=(0, 0, -1)) image2.transforms.configure(canvas=c, viewport=(0, 0, 125, 125)) if blend_func: image1.set_gl_state(preset='translucent', blend_func=blend_func) image2.set_gl_state(preset='translucent', blend_func=blend_func) @c.events.draw.connect def on_draw(ev): gloo.clear('black') gloo.set_viewport(0, 0, *c.physical_size) image1.draw() image2.draw() rgba_result = c.render() rgb_result = c.render(alpha=False) # the results should be the same except for alpha np.testing.assert_allclose(rgba_result[..., :3], rgb_result) # the image should have something drawn in it assert not np.allclose(rgba_result[..., :3], 0) # the alpha should not be completely transparent assert not np.allclose(rgba_result[..., 3], 0) if blend_func is None or 'one' in blend_func: # no transparency np.testing.assert_allclose(rgba_result[..., 3], 255) else: # the alpha should have some transparency # this part of this test fails on macOS 12 at the moment # see https://github.com/vispy/vispy/pull/2324#issuecomment-1163350672 assert (rgba_result[..., 3] != 255).any() @requires_application() @pytest.mark.parametrize( 'preset', [ 'opaque', 'additive', 'translucent', ]) def test_blend_presets(preset): """Test blending presets a canvas to an array. Different blending presets are used to test that they properly set blend equations. """ with Canvas(size=(125, 125), show=True, title='run') as c: im1 = np.zeros((100, 100, 4)).astype(np.float32) im1[:, :, 1] = 1 im1[:, :, 3] = .4 # Create the image image1 = ImageVisual(im1) image1.transform = STTransform(translate=(20, 20, -1)) image1.transforms.configure(canvas=c, viewport=(0, 0, 125, 125)) gloo.set_state(blend_equation='min') image1.set_gl_state(preset) @c.events.draw.connect def on_draw(ev): gloo.clear('black') gloo.set_viewport(0, 0, *c.physical_size) image1.draw() rgba_result = c.render() assert not np.allclose(rgba_result[..., :3], 0) @requires_application() @pytest.mark.parametrize("mouse_event_type, button, buttons, expected_button, expected_buttons", [ ('mouse_press', 1, [], 1, [1]), ('mouse_release', 1, [1], 1, [1]), # left click pressed and held, followed by a right click ('mouse_press', 2, [1], 2, [1, 2]), ('mouse_release', 2, [1, 2], 2, [1, 2]), ]) def test_mouse_event(mouse_event_type, button, buttons, expected_button, expected_buttons): mev = MouseEvent(type=mouse_event_type, button=button, buttons=buttons) assert mev.buttons == expected_buttons assert mev.button == expected_button vispy-0.14.1/vispy/app/tests/test_backends.py0000644000175000017510000001437214507104216020564 0ustar nileshnilesh"""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. """ 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 from vispy.util import _get_args 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 not in ('_egl', '_osmesa'): # skip keys for EGL, osmesa 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', '_pyqt6', '_pyside2', '_pyside6'): 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_fb_bind_location', '_vispy_get_geometry', '_vispy_get_physical_size', '_vispy_sleep', '_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(_get_args(method)) - 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 in ('_egl', '_osmesa'): ignore = ignore.union(['mouse_wheel', '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.14.1/vispy/app/tests/test_app.py0000644000175000017510000003664514507104216017601 0ustar nileshnileshimport sys from collections import namedtuple from io import StringIO from time import sleep import gc import numpy as np from numpy.testing import assert_array_equal import pytest 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, IS_TRAVIS_CI) 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 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, 340, 340, 1, 0) # Shift. backend._on_key_press(_id, 50, 50, 1, 0) # 2 backend._on_key_char(_id, 50) backend._on_key_press(_id, 50, 50, 0, 0) backend._on_key_press(_id, 340, 340, 0, 0) backend._on_key_press(_id, 65, 65, 1, 0) # Plain A backend._on_key_char(_id, 197) # Unicode A. backend._on_key_char(_id, 197) # Repeat A. backend._on_key_press(_id, 65, 65, 0, 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 any(x in backend_name.lower() for x in ('qt', 'pyside')): # 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 elif 'osmesa' in backend_name.lower(): # No events for osmesa backend pass elif 'tk' in backend_name.lower(): event = namedtuple("event", [ "serial", "time", "type", "widget", "width", "height", "char", "keycode", "keysym", "keysym_num", "state", "x", "y", "x_root", "y_root", "num", "delta" ]) event.width, event.height = 10, 20 backend._on_configure(event) # RESIZE event.x, event.y, event.state = 1, 1, 0x0 backend._on_mouse_enter(event) backend._on_mouse_move(event) event.x, event.y, event.num = 1, 1, 1 backend._on_mouse_button_press(event) backend._on_mouse_button_release(event) backend._on_mouse_double_button_press(event) event.delta = 120 backend._on_mouse_wheel(event) event.keysym_num, event.keycode, event.state = 65362, 0, 0x0001 # SHIFT+UP backend._on_key_down(event) backend._on_key_up(event) 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] # bug on 3.3.1 # https://github.com/glfw/glfw/issues/1620 if c.app.backend_name == 'Glfw' and \ c.app.backend_module.glfw.__version__ == (3, 3, 1): good_kwargs.pop('decorate') # 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 'pyqt5' 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): if app.backend_module.capability['position']: # todo: disable more tests based on capability canvas.position = pos canvas.size = size 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 knownfail, 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.0, 0.0, 1.0);}" 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 emit_list: with Canvas(fullscreen=False) as c: assert_equal(c.fullscreen, False) c.fullscreen = True assert_equal(c.fullscreen, True) assert_equal(len(emit_list), 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=True) 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() @pytest.mark.skipif(IS_TRAVIS_CI and 'darwin' in sys.platform, reason='Travis OSX causes segmentation fault on this test for an unknown reason.') @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') del c gc.collect() 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.14.1/vispy/app/tests/qt-designer.ui0000644000175000017510000000301014507104216020145 0ustar nileshnilesh 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.14.1/vispy/app/tests/__init__.py0000644000175000017510000000000014507104216017471 0ustar nileshnileshvispy-0.14.1/vispy/app/qt.py0000644000175000017510000000652314507104216015234 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 .backends import qt_lib if qt_lib is None: raise RuntimeError("Module backends._qt should not be imported directly.") elif qt_lib in 'pyqt4': from PyQt4 import QtGui QWidget, QGridLayout = QtGui.QWidget, QtGui.QGridLayout # Compat elif qt_lib == 'pyside': from PySide import QtGui QWidget, QGridLayout = QtGui.QWidget, QtGui.QGridLayout # Compat elif qt_lib == 'pyqt5': from PyQt5 import QtWidgets QWidget, QGridLayout = QtWidgets.QWidget, QtWidgets.QGridLayout # Compat elif qt_lib == 'pyqt6': from PyQt6 import QtWidgets QWidget, QGridLayout = QtWidgets.QWidget, QtWidgets.QGridLayout # Compat elif qt_lib == 'pyside2': from PySide2 import QtWidgets QWidget, QGridLayout = QtWidgets.QWidget, QtWidgets.QGridLayout # Compat elif qt_lib == 'pyside6': from PySide6 import QtWidgets QWidget, QGridLayout = QtWidgets.QWidget, QtWidgets.QGridLayout # 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.") class QtCanvas(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.') QWidget.__init__(self, parent) self.layout = 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.""" 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.14.1/vispy/app/canvas.py0000644000175000017510000007026114507104216016063 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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, logger from . import Application, use_app from ..gloo.context import (GLContext, set_current_canvas, forget_canvas) from ..gloo import FrameBuffer, RenderBuffer # 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. backend_kwargs : dict Keyword arguments to be supplied to the backend canvas object. 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. Backend-specific arguments can be given through the `backend_kwargs` argument. """ 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, backend_kwargs=None): size = tuple(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._fps_window = 0. 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, str): 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 self._backend_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) if backend_kwargs is not None: self._backend_kwargs.update(**backend_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, str): 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: lower_keys = {} # ensure all are callable for key, val in keys.items(): if isinstance(val, str): 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 lower_keys[key.lower()] = val self._keys_check = lower_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.""" # Note that _px_scale is an additional factor applied in addition to # the scale factor imposed by the backend. 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). When writing Visuals or SceneGraph visualisations, this value is exposed as `TransformSystem.px_scale`. """ return 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: logger.debug('Closing canvas %s' % (self,)) 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, str): 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 _repr_mimebundle_(self, *args, **kwargs): """If the backend implements _repr_mimebundle_, we proxy it here. """ # See https://ipython.readthedocs.io/en/stable/config/integrating.html f = getattr(self._backend, "_repr_mimebundle_", None) if f is not None: return f(*args, **kwargs) else: # Let Jupyter know this failed - otherwise the standard repr is not shown raise NotImplementedError() def _ipython_display_(self): """If the backend implements _ipython_display_, we proxy it here. """ # See https://ipython.readthedocs.io/en/stable/config/integrating.html f = getattr(self._backend, "_ipython_display_", None) if f is not None: return f() else: # Let Jupyter know this failed - otherwise the standard repr is not shown raise NotImplementedError() def __enter__(self): logger.debug('Context manager enter starting for %s' % (self,)) self.show() self._backend._vispy_warmup() return self def __exit__(self, type, value, traceback): # ensure all GL calls are complete logger.debug('Context manager exit starting for %s' % (self,)) if not self._closed: self._backend._vispy_set_current() self.context.finish() self.close() sleep(0.1) # ensure window is really closed/destroyed logger.debug('Context manager exit complete for %s' % (self,)) def render(self, alpha=True): """Render the canvas to an offscreen buffer and return the image array. Parameters ---------- alpha : bool If True (default) produce an RGBA array (M, N, 4). If False, remove the Alpha channel and return the RGB array (M, N, 3). This may be useful if blending of various elements requires a solid background to produce the expected visualization. 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. If ``alpha`` is ``False``, then only 3 channels will be returned (RGB). """ self.set_current() size = self.physical_size fbo = FrameBuffer(color=RenderBuffer(size[::-1]), depth=RenderBuffer(size[::-1])) try: fbo.activate() self.events.draw() result = fbo.read() finally: fbo.deactivate() if not alpha: result = result[..., :3] return result # 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 pressed 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 # Explicitly add button to buttons if newly pressed, check #2344 for more reference newly_pressed_buttons = [button] if button is not None and type == 'mouse_press' else [] self._buttons = [] if (buttons is None) else buttons + newly_pressed_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.14.1/vispy/app/base.py0000644000175000017510000002505714507104216015525 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from ..util import SimpleBunch import time from timeit import default_timer 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 # is called by inputhook.py for pauses # to remove CPU stress # this is virtual so that some backends which have specialize # functionality to deal with user input / latency can use those methods def _vispy_sleep(self, duration_sec): time.sleep(duration_sec) 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, 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): # Note: it is the responsibility of the subclass to call # the __init__ of the mro - we don't call super().__init__() here. from .canvas import Canvas # Avoid circular import assert isinstance(vispy_canvas, Canvas) self._vispy_canvas = vispy_canvas self._last_time = 0 # 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_get_fb_bind_location(self): # Should return the default FrameBuffer bind location # Most backends would not need to implement this return 0 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): if default_timer() - self._last_time < .01: return self._last_time = default_timer() # 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): # Note: it is the responsibility of the subclass to call # the __init__ of the mro - we don't call super().__init__() here. 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.14.1/vispy/app/backends/0000755000175000017510000000000014507104251016001 5ustar nileshnileshvispy-0.14.1/vispy/app/backends/tests/0000755000175000017510000000000014507104251017143 5ustar nileshnileshvispy-0.14.1/vispy/app/backends/tests/test_rfb.py0000644000175000017510000000441114507104216021326 0ustar nileshnilesh# This currenly only tests that the backend exists and can be imported ... import numpy as np from vispy import gloo from vispy.app import Application, Canvas from vispy.app.backends import _jupyter_rfb from vispy.testing import run_tests_if_main, requires_application import pytest try: import jupyter_rfb except ImportError: jupyter_rfb = None def test_rfb_app(): # Raw app_backend = _jupyter_rfb.ApplicationBackend() # Test that run and quit don't do anything - Jupyter is an interactive session! app_backend._vispy_run() app_backend._vispy_quit() class MyCanvas(Canvas): def on_draw(self, event): gloo.set_clear_color((0, 1, 0)) gloo.clear() @pytest.mark.skipif(jupyter_rfb is None, reason='jupyter_rfb is not installed') @requires_application() def test_rfb_canvas(): app = Application("jupyter_rfb") canvas = MyCanvas(app=app) canvas_backend = canvas.native assert isinstance(canvas_backend, _jupyter_rfb.CanvasBackend) # Check that resize works assert "42" not in canvas_backend.css_width canvas.size = 42, 42 assert canvas_backend.css_width == "42px" # Manually mimic what a browser would do, but round to 50 canvas_backend.handle_event({"event_type": "resize", "width": 50, "height": 50, "pixel_ratio": 2.0}) assert canvas.size == (50, 50) assert canvas.physical_size == (100, 100) # Mimic a draw frame = canvas_backend.get_frame() assert frame.shape[:2] == (100, 100) assert np.all(frame[:, :, 0] == 0) assert np.all(frame[:, :, 1] == 255) # Pretend that the user resized in the browser canvas_backend.handle_event({"event_type": "resize", "width": 60, "height": 60, "pixel_ratio": 1.0}) assert canvas.size == (60, 60) assert canvas.physical_size == (60, 60) # Mimic another draw frame = canvas_backend.get_frame() assert frame.shape[:2] == (60, 60) assert np.all(frame[:, :, 0] == 0) assert np.all(frame[:, :, 1] == 255) # Test mouse event events = [] canvas.events.mouse_press.connect(lambda e: events.append(e)) canvas_backend.handle_event({"event_type": "pointer_down", "x": 11, "y": 12, "button": 1, "modifiers": []}) assert len(events) == 1 assert tuple(events[0].pos) == (11, 12) run_tests_if_main() vispy-0.14.1/vispy/app/backends/tests/test_offscreen_util.py0000644000175000017510000000232214507104216023563 0ustar nileshnileshfrom vispy.app.backends._offscreen_util import OffscreenContext, FrameBufferHelper from vispy.testing import run_tests_if_main, requires_application from vispy import gloo import numpy as np @requires_application() def test_offscreen_context(): c1 = OffscreenContext() c2 = OffscreenContext.get_global_instance() c3 = OffscreenContext.get_global_instance() c4 = OffscreenContext() assert c1 is not c2 assert c1 is not c4 assert c2 is c3 c1.make_current() c1.close() class FakeCanvas(object): def __init__(self): self.context = gloo.GLContext() gloo.context.set_current_canvas(self) def flush(self): self.context.flush_commands() @requires_application() def test_frame_buffer_helper(): canvas = FakeCanvas() gl_context = OffscreenContext() fbh = FrameBufferHelper() gl_context.make_current() fbh.set_physical_size(43, 67) with fbh: gloo.set_clear_color((0, 0.5, 1)) gloo.clear() canvas.flush() array = fbh.get_frame() assert array.shape == (67, 43, 4) assert np.all(array[:, :, 0] == 0) assert np.all(array[:, :, 1] == 128) assert np.all(array[:, :, 2] == 255) run_tests_if_main() vispy-0.14.1/vispy/app/backends/tests/__init__.py0000644000175000017510000000000014507104216021243 0ustar nileshnileshvispy-0.14.1/vispy/app/backends/_wx.py0000644000175000017510000003527514507104216017165 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.GUIEventLoop() 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() if hasattr(_wx_app, 'ProcessIdle'): _wx_app.ProcessIdle() else: self._event_loop.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: if hasattr(wx, 'App'): _wx_app = wx.App() else: # legacy wx _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.""" def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) 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.position is None: pos = wx.DefaultPosition else: pos = p.position 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, pos, 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.Show() 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=pos, 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): if self.IsShown(): 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) if hasattr(self, 'SetSize'): # phoenix self.SetSize(w, h) else: # legacy 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_physical_size(self): w, h = self.GetClientSize() ratio = self.GetContentScaleFactor() return int(w * ratio), int(h * ratio) 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())/120.0) 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 = 1 elif evt.MiddleDown(): button = 3 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 = 1 elif evt.MiddleUp(): button = 3 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 = 1 elif evt.MiddleDClick(): button = 3 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(int(interval * 1000.), False) def _vispy_stop(self): self._timer.Stop() def _vispy_timeout(self, evt): self._vispy_timer._timeout() evt.Skip() vispy-0.14.1/vispy/app/backends/_tk.py0000644000175000017510000006277714507104216017154 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """vispy backend for Tkinter.""" from __future__ import division from time import sleep import warnings from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util import keys from ...util.ptime import time from ...gloo import gl # -------------------------------------------------------------------- init --- # Import try: import sys _tk_on_linux, _tk_on_darwin, _tk_on_windows = \ map(sys.platform.startswith, ("linux", "darwin", "win")) _tk_pyopengltk_imported = False import tkinter as tk import pyopengltk except (ModuleNotFoundError, ImportError): available, testable, why_not, which = \ False, False, "Could not import Tkinter or pyopengltk, module(s) not found.", None else: which_pyopengltk = getattr(pyopengltk, "__version__", "???") which = f"Tkinter {tk.TkVersion} (with pyopengltk {which_pyopengltk})" if hasattr(pyopengltk, "OpenGLFrame"): _tk_pyopengltk_imported = True available, testable, why_not = True, True, None else: # pyopengltk does not provide an implementation for this platform available, testable, why_not = \ False, False, f"pyopengltk {which_pyopengltk} is not supported on this platform ({sys.platform})!" if _tk_pyopengltk_imported: # Put OpenGLFrame class in global namespace OpenGLFrame = pyopengltk.OpenGLFrame else: # Create empty placeholder class class OpenGLFrame(object): pass # Map native keys to vispy keys # e.keysym_num -> vispy KEYMAP = { 65505: keys.SHIFT, 65506: keys.SHIFT, 65507: keys.CONTROL, 65508: keys.CONTROL, 65513: keys.ALT, 65514: keys.ALT, 65371: keys.META, 65372: keys.META, 65361: keys.LEFT, 65362: keys.UP, 65363: keys.RIGHT, 65364: keys.DOWN, 65365: keys.PAGEUP, 65366: keys.PAGEDOWN, 65379: keys.INSERT, 65535: keys.DELETE, 65360: keys.HOME, 65367: keys.END, 65307: keys.ESCAPE, 65288: keys.BACKSPACE, 32: keys.SPACE, 65293: keys.ENTER, 65289: keys.TAB, 65470: keys.F1, 65471: keys.F2, 65472: keys.F3, 65473: keys.F4, 65474: keys.F5, 65475: keys.F6, 65476: keys.F7, 65477: keys.F8, 65478: keys.F9, 65479: keys.F10, 65480: keys.F11, 65481: keys.F12, } # e.state -> vispy KEY_STATE_MAP = { 0x0001: keys.SHIFT, # 0x0002: CAPSLOCK, 0x0004: keys.CONTROL, # 0x0008: keys.ALT, # LEFT_ALT: Seems always pressed? # 0x0010: NUMLOCK, # 0x0020: SCROLLLOCK, 0x0080: keys.ALT, # 0x0100: ?, # Mouse button 1. # 0x0200: ?, # Mouse button 2. # 0x0400: ?, # Mouse button 3. 0x20000: keys.ALT # LEFT_ALT ? } # e.num -> vispy MOUSE_BUTTON_MAP = { 1: 1, # Mouse Left == 1 -> Mouse Left 2: 3, # Mouse Middle == 2 -> Mouse Middle 3: 2, # Mouse Right == 3 -> Mouse Right # TODO: If other mouse buttons are needed # and they differ from the Tkinter numbering, add them here. # e.g. BACK/FORWARD buttons or other custom mouse buttons } # -------------------------------------------------------------- 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=True, # can set title on the fly size=True, # can set size on the fly position=True, # can set position on the fly show=True, # can show/hide window vsync=False, # can set window to sync to blank resizable=True, # can toggle resizability (e.g., no user resizing) decorate=True, # can toggle decorations fullscreen=True, # fullscreen window support context=False, # can share contexts between windows multi_window=True, # can use multiple windows at once scroll=True, # scroll-wheel events are supported parent=True, # can pass native widget backend parent always_on_top=True, # can be made always-on-top ) # ------------------------------------------------------- set_configuration --- def _set_config(c): """Set gl configuration for template. Currently not used for Tkinter backend. """ return [] # ------------------------------------------------------------- application --- class _TkInstanceManager: _tk_inst = None # Reference to tk.Tk instance _tk_inst_owned = False # Whether we created the Tk instance or not _canvasses = [] # References to created CanvasBackends @classmethod def get_tk_instance(cls): """Return the Tk instance. Returns ------- tk.Tk The tk.Tk instance. """ if cls._tk_inst is None: if tk._default_root: # There already is a tk.Tk() instance available cls._tk_inst = tk._default_root cls._tk_inst_owned = False else: # Create our own top level Tk instance cls._tk_inst = tk.Tk() cls._tk_inst.withdraw() cls._tk_inst_owned = True return cls._tk_inst @classmethod def get_canvasses(cls): """Return a list of CanvasBackends. Returns ------- list A list with CanvasBackends. """ return cls._canvasses @classmethod def new_toplevel(cls, canvas, *args, **kwargs): """Create and return a new withdrawn Toplevel. Create a tk.Toplevel with the given args and kwargs, minimize it and add it to the list before returning Parameters ---------- canvas : CanvasBackend The CanvasBackend instance that wants a new Toplevel. *args Variable length argument list. **kwargs Arbitrary keyword arguments. Returns ------- tk.Toplevel Return the created tk.Toplevel """ tl = tk.Toplevel(cls._tk_inst, *args, **kwargs) tl.withdraw() cls._canvasses.append(canvas) return tl @classmethod def del_toplevel(cls, canvas=None): """ Destroy the given Toplevel, and if it was the last one, also destroy the Tk instance if we created it. Parameters ---------- canvas : CanvasBackend The CanvasBackend to destroy, defaults to None. """ if canvas: try: canvas.destroy() if canvas.top: canvas.top.destroy() cls._canvasses.remove(canvas) except Exception: pass # If there are no Toplevels left, quit the mainloop. if cls._tk_inst and not cls._canvasses and cls._tk_inst_owned: cls._tk_inst.quit() cls._tk_inst.destroy() cls._tk_inst = None class ApplicationBackend(BaseApplicationBackend): def _vispy_get_backend_name(self): """ Returns ------- str The name of the backend. """ return tk.__name__ def _vispy_process_events(self): """Process events related to the spawned Tk application window. First, update the Tk instance, then call `_delayed_update` on every created Toplevel (to force a redraw), and process some Tkinter GUI events by calling the Tk.mainloop and immediately exiting. """ # Update idle tasks first (probably not required) app = self._vispy_get_native_app() app.update_idletasks() # Update every active Canvas window for c in _TkInstanceManager.get_canvasses(): c._delayed_update() # Process some events in the main Tkinter event loop # And quit so we can continue elsewhere (call blocks normally) app.after(0, lambda: app.quit()) app.mainloop() def _vispy_run(self): """Start the Tk.mainloop. This will block until all Tk windows are destroyed.""" self._vispy_get_native_app().mainloop() def _vispy_quit(self): """Destroy each created Toplevel by calling _vispy_close on it. If there are no Toplevels left, also destroy the Tk instance. """ for c in _TkInstanceManager.get_canvasses(): c._vispy_close() _TkInstanceManager.del_toplevel() def _vispy_get_native_app(self): """Get or create the Tk instance. Returns ------- tk.Tk The tk.Tk instance. """ return _TkInstanceManager.get_tk_instance() # ------------------------------------------------------------------ canvas --- class CanvasBackend(BaseCanvasBackend, OpenGLFrame): """Tkinter backend for Canvas abstract class. Uses pyopengltk.OpenGLFrame as the internal tk.Frame instance that is able to receive OpenGL draw commands and display the results, while also being placeable in another Toplevel window. """ def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) p = self._process_backend_kwargs(kwargs) self._double_click_supported = True # Deal with config # ... use context.config # Deal with context p.context.shared.add_ref('tk', self) if p.context.shared.ref is self: self._native_context = None else: self._native_context = p.context.shared.ref._native_context # Pop args unrecognised by OpenGLFrame kwargs.pop("parent") kwargs.pop("title") kwargs.pop("size") kwargs.pop("position") kwargs.pop("show") kwargs.pop("vsync") kwargs.pop("resizable") kwargs.pop("decorate") kwargs.pop("always_on_top") kwargs.pop("fullscreen") kwargs.pop("context") if p.parent is None: # Create native window and top level self.top = _TkInstanceManager.new_toplevel(self) # Check input args and call appropriate set-up functions. if p.title: self._vispy_set_title(p.title) if p.size: self._vispy_set_size(p.size[0], p.size[1]) if p.position: self._vispy_set_position(p.position[0], p.position[1]) self.top.update_idletasks() if not p.resizable: self.top.resizable(False, False) if not p.decorate: self.top.overrideredirect(True) if p.always_on_top: self.top.wm_attributes("-topmost", "True") self._fullscreen = bool(p.fullscreen) self.top.protocol("WM_DELETE_WINDOW", self._vispy_close) parent = self.top else: # Use given parent as top level self.top = None parent = p.parent self._fullscreen = False self._init = False self.is_destroyed = False self._dynamic_keymap = {} OpenGLFrame.__init__(self, parent, **kwargs) if self.top: # Embed canvas in top (new window) if this was created self.top.configure(bg="black") self.pack(fill=tk.BOTH, expand=True) # Also bind the key events to the top window instead. self.top.bind("", self._on_key_down) self.top.bind("", self._on_key_up) else: # If no top, bind key events to the canvas itself. self.bind("", self._on_key_down) self.bind("", self._on_key_up) # Bind the other events to our internal methods. self.bind("", self._on_mouse_enter) # This also binds MouseWheel self.bind("", self._on_mouse_leave) # This also unbinds MouseWheel self.bind("", self._on_mouse_move) self.bind("", self._on_mouse_button_press) self.bind("", self._on_mouse_double_button_press) self.bind("", self._on_mouse_button_release) self.bind("", self._on_configure, add='+') self._vispy_set_visible(p.show) self.focus_force() def initgl(self): """Overridden from OpenGLFrame Gets called on init or when the frame is remapped into its container. """ if not hasattr(self, "_native_context") or self._native_context is None: # Workaround to get OpenGLFrame.__context for reference here # if access would ever be needed from self._native_context. # FIXME: Context sharing this way seems unsupported self._native_context = vars(self).get("_CanvasBackend__context", None) self.update_idletasks() gl.glClear(gl.GL_COLOR_BUFFER_BIT) gl.glClearColor(0.0, 0.0, 0.0, 0.0) def redraw(self, *args): """Overridden from OpenGLFrame Gets called when the OpenGLFrame redraws itself. It will set the current buffer, call self.redraw() and swap buffers afterwards. """ if self._vispy_canvas is None: return if not self._init: self._initialize() self._vispy_canvas.set_current() self._vispy_canvas.events.draw(region=None) def _delayed_update(self): """ Expose a new frame to the canvas. This will call self.redraw() internally. The self.animate sets the refresh rate in milliseconds. Using this is not necessary because VisPy will use the TimerBackend to periodically call self._vispy_update, resulting in the exact same behaviour. So we set it to `0` to tell OpenGLFrame not to redraw itself on its own. """ if self.is_destroyed: return self.animate = 0 self.tkExpose(None) def _on_configure(self, e): """Called when the frame get configured or resized.""" if self._vispy_canvas is None or not self._init: return size_tup = e if isinstance(e, tuple) else (e.width, e.height) self._vispy_canvas.events.resize(size=size_tup) def _initialize(self): """Initialise the Canvas for drawing.""" self.initgl() if self._vispy_canvas is None: return self._init = True self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() self.update_idletasks() self._on_configure(self._vispy_get_size()) def _vispy_warmup(self): """Provided for VisPy tests, so they can 'warm the canvas up'. Mostly taken from the wxWidgets backend. """ tk_inst = _TkInstanceManager.get_tk_instance() etime = time() + 0.3 while time() < etime: sleep(0.01) self._vispy_canvas.set_current() self._vispy_canvas.app.process_events() tk_inst.after(0, lambda: tk_inst.quit()) tk_inst.mainloop() def _parse_state(self, e): """Helper to parse event.state into modifier keys. Parameters ---------- e : tk.Event The passed in Event. Returns ------- list A list of modifier keys that are active (from vispy's keys) """ return [key for mask, key in KEY_STATE_MAP.items() if e.state & mask] def _parse_keys(self, e): """Helper to parse key states into Vispy keys. Parameters ---------- e : tk.Event The passed in Event. Returns ------- tuple A tuple (key.Key(), chr(key)), which has the vispy key object and the character representation if available. """ if e.keysym_num in KEYMAP: return KEYMAP[e.keysym_num], "" # e.char, e.keycode, e.keysym, e.keysym_num if e.char: self._dynamic_keymap[e.keycode] = e.char return keys.Key(e.char), e.char if e.keycode in self._dynamic_keymap: char = self._dynamic_keymap[e.keycode] return keys.Key(char), char warnings.warn("The key you typed is not supported by the tkinter backend." "Please map your functionality to a different key") return None, None def _on_mouse_enter(self, e): """Event callback when the mouse enters the canvas. Parameters ---------- e : tk.Event The passed in Event. """ if self._vispy_canvas is None: return if _tk_on_linux: # On Linux, bind wheel as buttons instead self.bind_all("", self._on_mouse_wheel) self.bind_all("", self._on_mouse_wheel) else: # Other platforms, bind wheel event # FIXME: What to do on Darwin? self.bind_all("", self._on_mouse_wheel) self._vispy_mouse_move( pos=(e.x, e.y), modifiers=self._parse_state(e)) def _on_mouse_leave(self, e): """Event callback when the mouse leaves the canvas. Parameters ---------- e : tk.Event The passed in Event. """ if self._vispy_canvas is None: return # Unbind mouse wheel events when not over the canvas any more. if _tk_on_linux: self.unbind_all("") self.unbind_all("") else: self.unbind_all("") def _on_mouse_move(self, e): """Event callback when the mouse is moved within the canvas. Parameters ---------- e : tk.Event The passed in Event. """ if self._vispy_canvas is None: return self._vispy_mouse_move( pos=(e.x, e.y), modifiers=self._parse_state(e)) def _on_mouse_wheel(self, e): """Event callback when the mouse wheel changes within the canvas. Parameters ---------- e : tk.Event The passed in Event. """ if self._vispy_canvas is None: return if _tk_on_linux: # Fix mouse wheel delta e.delta = {4: 120, 5: -120}.get(e.num, 0) self._vispy_canvas.events.mouse_wheel( delta=(0.0, float(e.delta / 120)), pos=(e.x, e.y), modifiers=self._parse_state(e)) def _on_mouse_button_press(self, e): """Event callback when a mouse button is pressed within the canvas. Parameters ---------- e : tk.Event The passed in Event. """ if self._vispy_canvas is None: return # Ignore MouseWheel on linux if _tk_on_linux and e.num in (4, 5): return self._vispy_mouse_press( pos=(e.x, e.y), button=MOUSE_BUTTON_MAP.get(e.num, e.num), modifiers=self._parse_state(e)) def _vispy_detect_double_click(self, e): """Override base class function since double click handling is native in Tk. """ pass def _on_mouse_double_button_press(self, e): """Event callback when a mouse button is double clicked within the canvas. Parameters ---------- e : tk.Event The passed in Event. """ if self._vispy_canvas is None: return # Ignore MouseWheel on linux if _tk_on_linux and e.num in (4, 5): return self._vispy_mouse_double_click( pos=(e.x, e.y), button=MOUSE_BUTTON_MAP.get(e.num, e.num), modifiers=self._parse_state(e)) def _on_mouse_button_release(self, e): """Event callback when a mouse button is released within the canvas. Parameters ---------- e : tk.Event The passed in Event. """ if self._vispy_canvas is None: return # Ignore MouseWheel on linux if _tk_on_linux and e.num in (4, 5): return self._vispy_mouse_release( pos=(e.x, e.y), button=MOUSE_BUTTON_MAP.get(e.num, e.num), modifiers=self._parse_state(e)) def _on_key_down(self, e): """Event callback when a key is pressed within the canvas or window. Ignore keys.ESCAPE if this is an embedded canvas, as this would make it unresponsive (because it won't close the entire window), while still being updateable. Parameters ---------- e : tk.Event The passed in Event. """ if self._vispy_canvas is None: return key, text = self._parse_keys(e) if not self.top and key == keys.ESCAPE: return self._vispy_canvas.events.key_press( key=key, text=text, modifiers=self._parse_state(e)) def _on_key_up(self, e): """Event callback when a key is released within the canvas or window. Ignore keys.ESCAPE if this is an embedded canvas, as this would make it unresponsive (because it won't close the entire window), while still being updateable. Parameters ---------- e : tk.Event The passed in Event. """ if self._vispy_canvas is None: return key, text = self._parse_keys(e) if not self.top and key == keys.ESCAPE: return self._vispy_canvas.events.key_release( key=key, text=text, modifiers=self._parse_state(e)) def _vispy_set_current(self): """Make this the current context.""" if not self.is_destroyed: self.tkMakeCurrent() def _vispy_swap_buffers(self): """Swap front and back buffer. This is done internally inside OpenGLFrame.""" self._vispy_canvas.set_current() def _vispy_set_title(self, title): """Set the window title. Has no effect for widgets.""" if self.top: self.top.title(title) def _vispy_set_size(self, w, h): """Set size of the window. Has no effect for widgets.""" if self.top: self.top.geometry(f"{w}x{h}") def _vispy_set_position(self, x, y): """Set location of the window. Has no effect for widgets.""" if self.top: self.top.geometry(f"+{x}+{y}") def _vispy_set_visible(self, visible): """Show or hide the window. Has no effect for widgets.""" if self.top: if visible: self.top.wm_deiconify() self.top.lift() self.top.attributes('-fullscreen', self._fullscreen) else: self.top.withdraw() def _vispy_set_fullscreen(self, fullscreen): """Set the current fullscreen state. Has no effect for widgets. If you want it to become fullscreen, while embedded in another Toplevel window, you should make that window fullscreen instead. """ self._fullscreen = bool(fullscreen) if self.top: self._vispy_set_visible(True) def _vispy_update(self): """Invoke a redraw Delay this by letting Tk call it later, even a delay of 0 will do. Doing this, prevents EventEmitter loops that are caused by wanting to draw too fast. """ self.after(0, self._delayed_update) def _vispy_close(self): """Force the window to close, destroying the canvas in the process. When this was the last VisPy window, also quit the Tk instance. This will not interfere if there is already another user window, unrelated top VisPy open. """ if self.top and not self.is_destroyed: self.is_destroyed = True self._vispy_canvas.close() _TkInstanceManager.del_toplevel(self) def destroy(self): """Callback when the window gets closed. Destroy the VisPy canvas by calling close on it. """ self._vispy_canvas.close() def _vispy_get_size(self): """Return the actual size of the frame.""" if self.top: self.top.update_idletasks() return self.winfo_width(), self.winfo_height() def _vispy_get_position(self): """Return the widget or window position.""" return self.winfo_x(), self.winfo_y() def _vispy_get_fullscreen(self): """Return the last set full screen state, regardless if it's actually in that state. When using the canvas as a widget, it will not go into fullscreen. See _vispy_set_fullscreen """ return self._fullscreen # ------------------------------------------------------------------- timer --- class TimerBackend(BaseTimerBackend): def __init__(self, vispy_timer): BaseTimerBackend.__init__(self, vispy_timer) self._tk = _TkInstanceManager.get_tk_instance() if self._tk is None: raise Exception("TimerBackend: No toplevel?") self._id = None self.last_interval = 1 def _vispy_start(self, interval): """Start the timer. Use Tk.after to schedule timer events. """ self._vispy_stop() self.last_interval = max(0, int(round(interval * 1000))) self._id = self._tk.after(self.last_interval, self._vispy_timeout) def _vispy_stop(self): """Stop the timer. Unschedule the previous callback if it exists. """ if self._id is not None: self._tk.after_cancel(self._id) self._id = None def _vispy_timeout(self): """Callback when the timer finishes. Also reschedules the next callback. """ self._vispy_timer._timeout() self._id = self._tk.after(self.last_interval, self._vispy_timeout) vispy-0.14.1/vispy/app/backends/_test.py0000644000175000017510000000037314507104216017475 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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.14.1/vispy/app/backends/_template.py0000644000175000017510000001742114507104216020333 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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. """ def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) # 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.14.1/vispy/app/backends/_sdl2.py0000644000175000017510000003537414507104216017373 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 any(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.""" def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) 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.14.1/vispy/app/backends/_qt.py0000644000175000017510000010517114507104216017144 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 PySide2, PyQt5 or PyQt6. Note that the automatic backend selection prefers a GUI toolkit that is already imported. The _pyside, _pyqt4, _pyside2, _pyqt5 and _pyside6 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/PySide2/PyQt5/PySide6 backends simultaneously. It is known to cause unpredictable behavior and segfaults. """ from __future__ import division from time import sleep, time import math import os import sys import atexit import ctypes from packaging.version import Version from ...util import logger from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...util import keys 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', 'PyQt6', 'PySide', 'PySide2', 'PySide6'] 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)) def _get_event_xy(ev): # QT6 (and the Python bindings like PyQt6, PySide6) report position differently from previous versions if hasattr(ev, 'pos'): posx, posy = ev.pos().x(), ev.pos().y() else: # Compatibility for PySide6 / PyQt6 posx, posy = ev.position().x(), ev.position().y() return posx, posy # Get what qt lib to try. This tells us wheter this module is imported # via _pyside or _pyqt4 or _pyqt5 QGLWidget = object QT5_NEW_API = False PYSIDE6_API = False PYQT6_API = False if qt_lib == 'pyqt4': _check_imports('PyQt4') if not USE_EGL: from PyQt4.QtOpenGL import QGLWidget, QGLFormat from PyQt4 import QtGui, QtCore, QtTest QWidget, QApplication = QtGui.QWidget, QtGui.QApplication # Compat elif qt_lib == 'pyqt5': _check_imports('PyQt5') if not USE_EGL: from PyQt5.QtCore import QT_VERSION_STR if Version(QT_VERSION_STR) >= Version('5.4.0'): from PyQt5.QtWidgets import QOpenGLWidget as QGLWidget from PyQt5.QtGui import QSurfaceFormat as QGLFormat QT5_NEW_API = True else: from PyQt5.QtOpenGL import QGLWidget, QGLFormat from PyQt5 import QtGui, QtCore, QtWidgets, QtTest QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication # Compat elif qt_lib == 'pyqt6': _check_imports('PyQt6') if not USE_EGL: from PyQt6.QtCore import QT_VERSION_STR if Version(QT_VERSION_STR) >= Version('6.0.0'): from PyQt6.QtOpenGLWidgets import QOpenGLWidget as QGLWidget from PyQt6.QtGui import QSurfaceFormat as QGLFormat PYQT6_API = True else: from PyQt6.QtOpenGL import QGLWidget, QGLFormat from PyQt6 import QtGui, QtCore, QtWidgets, QtTest QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication # Compat elif qt_lib == 'pyside6': _check_imports('PySide6') if not USE_EGL: from PySide6.QtCore import __version__ as QT_VERSION_STR if Version(QT_VERSION_STR) >= Version('6.0.0'): from PySide6.QtOpenGLWidgets import QOpenGLWidget as QGLWidget from PySide6.QtGui import QSurfaceFormat as QGLFormat PYSIDE6_API = True else: from PySide6.QtOpenGL import QGLWidget, QGLFormat from PySide6 import QtGui, QtCore, QtWidgets, QtTest QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication # Compat elif qt_lib == 'pyside2': _check_imports('PySide2') if not USE_EGL: from PySide2.QtCore import __version__ as QT_VERSION_STR if Version(QT_VERSION_STR) >= Version('5.4.0'): from PySide2.QtWidgets import QOpenGLWidget as QGLWidget from PySide2.QtGui import QSurfaceFormat as QGLFormat QT5_NEW_API = True else: from PySide2.QtOpenGL import QGLWidget, QGLFormat from PySide2 import QtGui, QtCore, QtWidgets, QtTest 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, QtTest 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 qt_keys = QtCore.Qt.Key if qt_lib == 'pyqt6' else QtCore.Qt KEYMAP = { qt_keys.Key_Shift: keys.SHIFT, qt_keys.Key_Control: keys.CONTROL, qt_keys.Key_Alt: keys.ALT, qt_keys.Key_AltGr: keys.ALT, qt_keys.Key_Meta: keys.META, qt_keys.Key_Left: keys.LEFT, qt_keys.Key_Up: keys.UP, qt_keys.Key_Right: keys.RIGHT, qt_keys.Key_Down: keys.DOWN, qt_keys.Key_PageUp: keys.PAGEUP, qt_keys.Key_PageDown: keys.PAGEDOWN, qt_keys.Key_Insert: keys.INSERT, qt_keys.Key_Delete: keys.DELETE, qt_keys.Key_Home: keys.HOME, qt_keys.Key_End: keys.END, qt_keys.Key_Escape: keys.ESCAPE, qt_keys.Key_Backspace: keys.BACKSPACE, qt_keys.Key_F1: keys.F1, qt_keys.Key_F2: keys.F2, qt_keys.Key_F3: keys.F3, qt_keys.Key_F4: keys.F4, qt_keys.Key_F5: keys.F5, qt_keys.Key_F6: keys.F6, qt_keys.Key_F7: keys.F7, qt_keys.Key_F8: keys.F8, qt_keys.Key_F9: keys.F9, qt_keys.Key_F10: keys.F10, qt_keys.Key_F11: keys.F11, qt_keys.Key_F12: keys.F12, qt_keys.Key_Space: keys.SPACE, qt_keys.Key_Enter: keys.ENTER, qt_keys.Key_Return: keys.ENTER, qt_keys.Key_Tab: keys.TAB, } if PYQT6_API or PYSIDE6_API: BUTTONMAP = { QtCore.Qt.MouseButton.NoButton: 0, QtCore.Qt.MouseButton.LeftButton: 1, QtCore.Qt.MouseButton.RightButton: 2, QtCore.Qt.MouseButton.MiddleButton: 3, QtCore.Qt.MouseButton.BackButton: 4, QtCore.Qt.MouseButton.ForwardButton: 5 } else: BUTTONMAP = {0: 0, 1: 1, 2: 2, 4: 3, 8: 4, 16: 5} # Properly log Qt messages def message_handler(*args): if qt_lib in ("pyqt4", "pyside"): msg_type, msg = args elif qt_lib in ("pyqt5", "pyqt6", "pyside2", "pyside6"): # Is this correct for pyside2? msg_type, context, msg = args elif qt_lib: raise RuntimeError("Invalid value for qt_lib %r." % qt_lib) else: raise RuntimeError("Module backends._qt ", "should not be imported directly.") BLACKLIST = [ # Ignore spam about tablet input 'QCocoaView handleTabletEvent: This tablet device is unknown', # Not too sure why this warning is emitted when using # Spyder + PyQt5 + Vispy # https://github.com/vispy/vispy/issues/1787 # In either case, it is really annoying. We should filter it away 'QSocketNotifier: Multiple socket notifiers for same', ] for item in BLACKLIST: if msg.startswith(item): return msg = msg.decode() if not isinstance(msg, str) else msg logger.warning(msg) def use_shared_contexts(): """Enable context sharing for PyQt5 5.4+ API applications. This is disabled by default for PyQt5 5.4+ due to occasional segmentation faults and other issues when contexts are shared. """ forced_env_var = os.getenv('VISPY_PYQT5_SHARE_CONTEXT', 'false').lower() == 'true' return not (QT5_NEW_API or PYSIDE6_API or PYQT6_API) or forced_env_var try: QtCore.qInstallMsgHandler(message_handler) except AttributeError: QtCore.qInstallMessageHandler(message_handler) # PyQt5, PyQt6 # -------------------------------------------------------------- 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=use_shared_contexts(), 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']) if QT5_NEW_API: # Qt5 >= 5.4.0 - below options automatically enabled if nonzero. glformat.setSwapBehavior(glformat.DoubleBuffer if c['double_buffer'] else glformat.SingleBuffer) elif PYQT6_API or PYSIDE6_API: glformat.setSwapBehavior(glformat.SwapBehavior.DoubleBuffer if c['double_buffer'] else glformat.SwapBehavior.SingleBuffer) else: # Qt4 and Qt5 < 5.4.0 - buffers must be explicitly requested. 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.setStencil(True if c['stencil_size'] else False) glformat.setSampleBuffers(True if c['samples'] else False) glformat.setDepthBufferSize(c['depth_size'] if c['depth_size'] else 0) glformat.setStencilBufferSize(c['stencil_size'] if c['stencil_size'] else 0) 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) # sharing is currently buggy and causes segmentation faults for tests with PyQt 5.6 if (QT5_NEW_API or PYSIDE6_API) and use_shared_contexts(): # For Qt5 >= 5.4.0 - Enable sharing of context between windows. QApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts, True) elif PYQT6_API and use_shared_contexts(): QApplication.setAttribute(QtCore.Qt.ApplicationAttribute.AA_ShareOpenGLContexts, True) def _vispy_get_backend_name(self): name = QtCore.__name__.split('.')[0] return name def _vispy_process_events(self): app = self._vispy_get_native_app() # sendPostedEvents replaces flush which has been removed from Qt6.0+ # This should be compatible with Qt4.x and Qt5.x app.sendPostedEvents() 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: # app.exec_() for PyQt <=5 and app.exec() for PyQt >=5 exec_func = app.exec if hasattr(app, "exec") else app.exec_ return exec_func() 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 def _vispy_sleep(self, duration_sec): QtTest.QTest.qWait(duration_sec * 1000) # in ms # ------------------------------------------------------------------ canvas --- def _get_qpoint_pos(pos): """Return the coordinates of a QPointF object.""" return pos.x(), pos.y() class QtBaseCanvasBackend(BaseCanvasBackend): """Base functionality of Qt backend. No OpenGL Stuff.""" def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) # 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 # must set physical size before setting visible or fullscreen # operations may make the size invalid if hasattr(self, 'devicePixelRatio'): # handle high DPI displays in PyQt5 ratio = self.devicePixelRatio() else: ratio = 1 self._physical_size = (p.size[0] * ratio, p.size[1] * ratio) 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 try: # see screen_changed docstring for more details self.window().windowHandle().screenChanged.connect(self.screen_changed) except AttributeError: # either not PyQt5 backend or no parent window available pass # QNativeGestureEvent does not keep track of last or total # values like QGestureEvent does self._native_gesture_scale_values = [] self._native_gesture_rotation_values = [] def screen_changed(self, new_screen): """Window moved from one display to another, resize canvas. If display resolutions are the same this is essentially a no-op except for the redraw. If the display resolutions differ (HiDPI versus regular displays) the canvas needs to be redrawn to reset the physical size based on the current `devicePixelRatio()` and redrawn with that new size. """ self.resizeGL(*self._vispy_get_size()) 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_physical_size(self, w, h): self._physical_size = (w, h) def _vispy_get_physical_size(self): if self._vispy_canvas is None: return return self._physical_size 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=_get_event_xy(ev), 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=_get_event_xy(ev), 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=_get_event_xy(ev), 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=_get_event_xy(ev), 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 / PyQt6 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=_get_event_xy(ev), 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 _handle_native_gesture_event(self, ev): if self._vispy_canvas is None: return t = ev.gestureType() # this is a workaround for what looks like a Qt bug where # QNativeGestureEvent gives the wrong local position. # See: https://bugreports.qt.io/browse/QTBUG-59595 try: pos = self.mapFromGlobal(ev.globalPosition().toPoint()) except AttributeError: # globalPos is deprecated in Qt6 pos = self.mapFromGlobal(ev.globalPos()) pos = pos.x(), pos.y() if t == QtCore.Qt.NativeGestureType.BeginNativeGesture: self._vispy_canvas.events.touch( type='gesture_begin', pos=_get_event_xy(ev), ) elif t == QtCore.Qt.NativeGestureType.EndNativeGesture: self._native_touch_total_rotation = [] self._native_touch_total_scale = [] self._vispy_canvas.events.touch( type='gesture_end', pos=_get_event_xy(ev), ) elif t == QtCore.Qt.NativeGestureType.RotateNativeGesture: angle = ev.value() last_angle = ( self._native_gesture_rotation_values[-1] if self._native_gesture_rotation_values else None ) self._native_gesture_rotation_values.append(angle) total_rotation_angle = math.fsum(self._native_gesture_rotation_values) self._vispy_canvas.events.touch( type="gesture_rotate", pos=pos, rotation=angle, last_rotation=last_angle, total_rotation_angle=total_rotation_angle, ) elif t == QtCore.Qt.NativeGestureType.ZoomNativeGesture: scale = ev.value() last_scale = ( self._native_gesture_scale_values[-1] if self._native_gesture_scale_values else None ) self._native_gesture_scale_values.append(scale) total_scale_factor = math.fsum(self._native_gesture_scale_values) self._vispy_canvas.events.touch( type="gesture_zoom", pos=pos, last_scale=last_scale, scale=scale, total_scale_factor=total_scale_factor, ) # QtCore.Qt.NativeGestureType.PanNativeGesture # Qt6 docs seem to imply this is only supported on Wayland but I have # not been able to test it. # Two finger pan events are anyway converted to scroll/wheel events. # On macOS, more fingers are usually swallowed by the OS (by spaces, # mission control, etc.). def event(self, ev): out = super(QtBaseCanvasBackend, self).event(ev) # QNativeGestureEvent is Qt 5+ if ( (QT5_NEW_API or PYSIDE6_API or PYQT6_API) and isinstance(ev, QtGui.QNativeGestureEvent) ): self._handle_native_gesture_event(ev) return out 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 32 <= key <= 127: key = keys.Key(chr(key)) else: key = None mod = self._modifiers(ev) func(native=ev, key=key, text=str(ev.text()), modifiers=mod) def _modifiers(self, event): # Convert the QT modifier state into a tuple of active modifier keys. mod = () qtmod = event.modifiers() qt_keyboard_modifiers = QtCore.Qt.KeyboardModifier if PYQT6_API else QtCore.Qt for q, v in ([qt_keyboard_modifiers.ShiftModifier, keys.SHIFT], [qt_keyboard_modifiers.ControlModifier, keys.CONTROL], [qt_keyboard_modifiers.AltModifier, keys.ALT], [qt_keyboard_modifiers.MetaModifier, keys.META]): if qtmod & q: 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 qt_window_types = QtCore.Qt.WindowType if PYQT6_API else QtCore.Qt if p.always_on_top or not p.decorate: hint = 0 hint |= 0 if p.decorate else qt_window_types.FramelessWindowHint hint |= qt_window_types.WindowStaysOnTopHint if p.always_on_top else 0 else: hint = qt_window_types.Widget # can also be a window type QWidget.__init__(self, p.parent, hint) qt_window_attributes = QtCore.Qt.WidgetAttribute if PYQT6_API else QtCore.Qt if 0: # IS_LINUX or IS_RPI: self.setAutoFillBackground(False) self.setAttribute(qt_window_attributes.WA_NoSystemBackground, True) self.setAttribute(qt_window_attributes.WA_OpaquePaintEvent, True) elif IS_WIN: self.setAttribute(qt_window_attributes.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 ... 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.') qt_window_types = QtCore.Qt.WindowType if PYQT6_API else QtCore.Qt if p.always_on_top or not p.decorate: hint = 0 hint |= 0 if p.decorate else qt_window_types.FramelessWindowHint hint |= qt_window_types.WindowStaysOnTopHint if p.always_on_top else 0 else: hint = qt_window_types.Widget # can also be a window type if QT5_NEW_API or PYSIDE6_API or PYQT6_API: # Qt5 >= 5.4.0 - sharing is automatic QGLWidget.__init__(self, p.parent, hint) # Need to create an offscreen surface so we can get GL parameters # without opening/showing the Widget. PyQt5 >= 5.4 will create the # valid context later when the widget is shown. self._secondary_context = QtGui.QOpenGLContext() self._secondary_context.setShareContext(self.context()) self._secondary_context.setFormat(glformat) self._secondary_context.create() self._surface = QtGui.QOffscreenSurface() self._surface.setFormat(glformat) self._surface.create() self._secondary_context.makeCurrent(self._surface) else: # Qt4 and Qt5 < 5.4.0 - sharing is explicitly requested QGLWidget.__init__(self, p.parent, widget, hint) # unused with this API self._secondary_context = None self._surface = None self.setFormat(glformat) self._initialized = True if not QT5_NEW_API and not PYSIDE6_API and not PYQT6_API and not self.isValid(): # On Qt5 >= 5.4.0, isValid is only true once the widget is shown raise RuntimeError('context could not be created') if not QT5_NEW_API and not PYSIDE6_API and not PYQT6_API: # to make consistent with other backends self.setAutoBufferSwap(False) qt_focus_policies = QtCore.Qt.FocusPolicy if PYQT6_API else QtCore.Qt self.setFocusPolicy(qt_focus_policies.WheelFocus) def _vispy_close(self): # Force the window or widget to shut down self.close() self.doneCurrent() if not QT5_NEW_API and not PYSIDE6_API and not PYQT6_API: self.context().reset() if self._vispy_canvas is not None: self._vispy_canvas.app.process_events() self._vispy_canvas.app.process_events() 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 if QT5_NEW_API or PYSIDE6_API or PYQT6_API: ctx = self.context() ctx.swapBuffers(ctx.surface()) else: self.swapBuffers() def _vispy_get_fb_bind_location(self): if QT5_NEW_API or PYSIDE6_API or PYQT6_API: return self.defaultFramebufferObject() else: return QtBaseCanvasBackend._vispy_get_fb_bind_location(self) 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 if hasattr(self, 'devicePixelRatio'): # We take into account devicePixelRatio, which is non-unity on # e.g HiDPI displays. # self.devicePixelRatio() is a float and should have been in Qt5 according to the documentation ratio = self.devicePixelRatio() w = int(w * ratio) h = int(h * ratio) self._vispy_set_physical_size(w, h) self._vispy_canvas.events.resize(size=(self.width(), self.height()), physical_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) # Clear the alpha channel with QOpenGLWidget (Qt >= 5.4), otherwise the # window is translucent behind non-opaque objects. # Reference: MRtrix3/mrtrix3#266 if QT5_NEW_API or PYSIDE6_API or PYQT6_API: context = self._vispy_canvas.context context.set_color_mask(False, False, False, True) context.clear(color=True, depth=False, stencil=False) context.set_color_mask(True, True, True, True) context.flush() # 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(int(interval * 1000)) def _vispy_stop(self): self.stop() def _vispy_timeout(self): self._vispy_timer._timeout() vispy-0.14.1/vispy/app/backends/_pyside6.py0000644000175000017510000000343014507104216020076 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """PySide6 proxy backend for the qt backend. Based on PySide2 backend. """ import sys from .. import backends from ...util import logger from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') try: # Try importing (QtOpenGL first to fail without import QtCore) if not USE_EGL: from PySide6 import QtOpenGL # noqa from PySide6 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 # What is this for? PyQt5 sets this to true, PySide sets this to false has_uic = False import PySide6 # PySide6 doesn't have qWait as well, redefines it from PySide6 import QtTest @staticmethod def qWait(msec): import time start = time.time() PySide6.QtWidgets.QApplication.processEvents() while time.time() < start + msec * 0.001: PySide6.QtWidgets.QApplication.processEvents() QtTest.QTest.qWait = qWait which = ('PySide6', PySide6.__version__, QtCore.__version__) # Remove _qt module to force an import even if it was already imported sys.modules.pop(__name__.replace('_pyside6', '_qt'), None) # Import _qt. Keep a ref to the module object! if backends.qt_lib is None: backends.qt_lib = 'pyside6' # Signal to _qt what it should import from . import _qt # noqa from ._qt import * # noqa else: logger.warning('%s already imported, cannot switch to %s' % (backends.qt_lib, 'pyside6')) vispy-0.14.1/vispy/app/backends/_pyside2.py0000644000175000017510000000365514507104216020103 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """PySide2 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: # Try importing (QtOpenGL first to fail without import QtCore) if not USE_EGL: from PySide2 import QtOpenGL # noqa from PySide2 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 # What is this for? PyQt5 sets this to true, PySide sets this to false has_uic = False import PySide2 # PySide2 doesn't have qWait for some reason see: # https://github.com/pyqtgraph/pyqtgraph/pull/376/commits/8bdc19be75a7552cc0043bf8b5f5e0ee796edda0 from PySide2 import QtTest if not hasattr(QtTest.QTest, 'qWait'): @staticmethod def qWait(msec): import time start = time.time() PySide2.QtWidgets.QApplication.processEvents() while time.time() < start + msec * 0.001: PySide2.QtWidgets.QApplication.processEvents() QtTest.QTest.qWait = qWait which = ('PySide2', PySide2.__version__, QtCore.__version__) # Remove _qt module to force an import even if it was already imported sys.modules.pop(__name__.replace('_pyside2', '_qt'), None) # Import _qt. Keep a ref to the module object! if backends.qt_lib is None: backends.qt_lib = 'pyside2' # Signal to _qt what it should import from . import _qt # noqa from ._qt import * # noqa else: logger.warning('%s already imported, cannot switch to %s' % (backends.qt_lib, 'pyside2')) vispy-0.14.1/vispy/app/backends/_pyside.py0000644000175000017510000000246614507104216020020 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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: # 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.warning('%s already imported, cannot switch to %s' % (backends.qt_lib, 'PySide')) vispy-0.14.1/vispy/app/backends/_pyqt6.py0000644000175000017510000000261614507104216017603 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """PyQt6 proxy backend for the qt backend. Based on PySIde6 backend. """ import sys from .. import backends from ...util import logger from ... import config USE_EGL = config['gl_backend'].lower().startswith('es') try: # Try importing (QtOpenGL first to fail without import QtCore) if not USE_EGL: from PyQt6 import QtOpenGL # noqa from PyQt6 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 # What is this for? PyQt5 sets this to true, PySide sets this to false has_uic = False which = ('PyQt6', QtCore.PYQT_VERSION_STR, QtCore.PYQT_VERSION_STR) # Remove _qt module to force an import even if it was already imported sys.modules.pop(__name__.replace('_pyqt6', '_qt'), None) # Import _qt. Keep a ref to the module object! if backends.qt_lib is None: backends.qt_lib = 'pyqt6' # Signal to _qt what it should import from . import _qt # noqa from ._qt import * # noqa else: logger.warning('%s already imported, cannot switch to %s' % (backends.qt_lib, 'pyqt6')) vispy-0.14.1/vispy/app/backends/_pyqt5.py0000644000175000017510000000244414507104216017601 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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: # 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.warning('%s already imported, cannot switch to %s' % (backends.qt_lib, 'pyqt5')) vispy-0.14.1/vispy/app/backends/_pyqt4.py0000644000175000017510000000244414507104216017600 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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: # 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.warning('%s already imported, cannot switch to %s' % (backends.qt_lib, 'pyqt4')) vispy-0.14.1/vispy/app/backends/_pyglet.py0000644000175000017510000003540614507104216020027 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """vispy backend for pyglet.""" from __future__ import division from packaging.version import Version 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 Version(version) < Version('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(BaseCanvasBackend, _Window): """Pyglet backend for Canvas abstract class.""" def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) 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_physical_size(self): if self._vispy_canvas is None: return w, h = self.get_framebuffer_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, self.get_size()[1] - 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.14.1/vispy/app/backends/_osmesa.py0000644000175000017510000001666214507104216020015 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """OSMesa backend for offscreen rendering on Linux/Unix.""" from __future__ import division from ...util.ptime import time from ..base import (BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend) from ...gloo import gl from time import sleep try: from ...ext import osmesa except Exception as exp: available, testable, why_not, which = False, False, str(exp), None else: available, testable, why_not, which = True, True, None, 'OSMesa' # -------------------------------------------------------------- capability --- capability = dict( # if True they mean: title=True, # can set title on the fly size=True, # can set size on the fly position=False, # can set position on the fly show=True, # 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=True, # can toggle decorations fullscreen=False, # fullscreen window support context=True, # can share contexts between windows multi_window=True, # 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 ) _VP_OSMESA_ALL_WINDOWS = [] def _get_osmesa_windows(): return [win for win in _VP_OSMESA_ALL_WINDOWS if isinstance(win, CanvasBackend)] # ------------------------------------------------------------- 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 'osmesa' def _vispy_process_events(self): for timer in self._timers: timer._tick() wins = _get_osmesa_windows() for win in wins: if win._needs_draw: win._needs_draw = False win._on_draw() def _vispy_run(self): wins = _get_osmesa_windows() while not all(w.closed for w in wins): self._vispy_process_events() self._vispy_quit() def _vispy_quit(self): wins = _get_osmesa_windows() for win in wins: win._vispy_close() for timer in self._timers: timer._vispy_stop() self._timers = [] def _vispy_get_native_app(self): return osmesa class OSMesaContext(object): """ A wrapper around an OSMesa context that destroy the context when garbage collected """ def __init__(self): self.context = osmesa.OSMesaCreateContext() def make_current(self, pixels, width, height): return osmesa.OSMesaMakeCurrent(self.context, pixels, width, height) def __del__(self): osmesa.OSMesaDestroyContext(self.context) # ------------------------------------------------------------------ canvas --- class CanvasBackend(BaseCanvasBackend): """OSMesa backend for Canvas""" def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) # 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 # TODO: We do not support setting config # ... use context.config # Deal with context p.context.shared.add_ref('osmesa', self) if p.context.shared.ref is self: self._native_context = OSMesaContext() else: self._native_context = p.context.shared.ref._native_context self._closed = False self._pixels = None self._vispy_set_size(*p.size) _VP_OSMESA_ALL_WINDOWS.append(self) self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() def _vispy_set_current(self): if self._native_context is None: raise RuntimeError('Native context is None') if self._pixels is None: raise RuntimeError('Pixel buffer has already been deleted') ok = self._native_context.make_current(self._pixels, self._size[0], self._size[1]) if not ok: raise RuntimeError('Failed attaching OSMesa rendering buffer') def _vispy_swap_buffers(self): if self._pixels is None: raise RuntimeError('No pixel buffer') gl.glFinish() def _vispy_set_title(self, title): pass def _vispy_set_size(self, w, h): self._pixels = osmesa.allocate_pixels_buffer(w, h) self._size = (w, h) self._vispy_canvas.events.resize(size=(w, h)) self._vispy_set_current() self._vispy_update() def _vispy_set_position(self, x, y): pass def _vispy_set_visible(self, visible): if visible: self._vispy_set_current() self._vispy_update() def _vispy_set_fullscreen(self, fullscreen): pass def _vispy_update(self): # This is checked by osmesa ApplicationBackend in process_events self._needs_draw = True def _vispy_close(self): if self.closed: return # We do not set self._native_context = None here because this causes # trouble in case a canvas is closed multiple times (as in # app.test_run()). The problem occurs in gloo's glir._gl_initialize # when it tries to call glGetString(GL_VERSION). # But OSMesa requires a context to be attached when calling # glGetString otherwise it returns an empty string, which gloo doesn't # like self._closed = True return 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_get_size(self): if self._pixels is None: return return self._size @property def closed(self): return self._closed def _vispy_get_position(self): return 0, 0 def _vispy_get_fullscreen(self): return False def _on_draw(self): # This is called by the osmesa ApplicationBackend if self._vispy_canvas is None or self._pixels is None: raise RuntimeError('draw with no canvas or pixels attached') return self._vispy_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.14.1/vispy/app/backends/_offscreen_util.py0000644000175000017510000000716114507104216021527 0ustar nileshnilesh""" Utils for offscreen rendering. """ from .. import Application, Canvas from ... import gloo class OffscreenContext: """ A helper class to provide an OpenGL context. This context is global to the application. """ _global_instance = None _canvas = None @classmethod def get_global_instance(cls): """ Get a global context. Note that any assumptions about OpenGL state being local will not hold. """ if cls._global_instance is None: cls._global_instance = cls() return cls._global_instance def __init__(self): if self._canvas is not None: return # already initialized self._is_closed = False # Glfw is probably the most lightweight approach, so let's try that. # But there are two incompatible packages providing glfw :/ self.glfw = None try: import glfw except ImportError: pass else: need_from_glfw = ["create_window", "make_context_current"] if all(hasattr(glfw, attr) for attr in need_from_glfw): self.glfw = glfw if self.glfw: self.glfw.init() self.glfw.window_hint(self.glfw.VISIBLE, 0) self._canvas = self.glfw.create_window(1, 1, "dummy window", None, None) else: try: _app = Application('default') except Exception: raise RuntimeError( "Cannot find a backend to create an OpenGL context. " "Install e.g. PyQt5, PySide2, or `pip install glfw`." ) self._canvas = Canvas(app=_app) self._canvas.show(False) def make_current(self): """ Make this the currently active context. """ # If an application only used off-screen canvases this would technically # have to be called just once. But note that an application/session # could run both real canvases and off-screen ones. if self.glfw: self.glfw.make_context_current(self._canvas) else: self._canvas.set_current() def close(self): """ Close the context. """ # Cannot close the global instance if self is OffscreenContext._global_instance: return elif not self._is_closed: self._is_closed = True if self.glfw: self.glfw.destroy_window(self._canvas) else: self._canvas.close() def __del__(self): self.close() class FrameBufferHelper: """ Provides a canvas to render to, using an FBO. """ def __init__(self): self._fbo = None self._physical_size = 1, 1 self._fbo_size = -1, -1 def _ensure_fbo(self): if self._fbo_size != self._physical_size: self._fbo_size = self._physical_size w, h = self._fbo_size if self._fbo is None: color_buffer = gloo.Texture2D((h, w, 4)) depth_buffer = gloo.RenderBuffer((h, w)) self._fbo = gloo.FrameBuffer(color_buffer, depth_buffer) else: self._fbo.resize((h, w)) def set_physical_size(self, w, h): """ Set the physical size of the canvas. """ self._physical_size = w, h def get_frame(self): """ Call this within the with-context to obtain the frame buffer contents. """ return self._fbo.read() def __enter__(self): self._ensure_fbo() return self._fbo.__enter__() def __exit__(self, *args): return self._fbo.__exit__(*args) vispy-0.14.1/vispy/app/backends/_jupyter_rfb.py0000644000175000017510000002114514507104216021051 0ustar nileshnileshimport asyncio from ..base import BaseApplicationBackend, BaseCanvasBackend, BaseTimerBackend from ...app import Timer from ...util import keys from ._offscreen_util import OffscreenContext, FrameBufferHelper try: from jupyter_rfb import RemoteFrameBuffer except Exception: RemoteFrameBuffer = object _msg = 'The jupyter_rfb backend relies on a the jupyter_rfb library: ``pip install jupyter_rfb``' available, testable, why_not, which = False, False, _msg, None else: available, testable, why_not = True, False, None which = "jupyter_rfb" # -------------------------------------------------------------- capability --- capability = dict( title=False, size=True, position=False, show=False, vsync=False, resizable=True, decorate=False, fullscreen=False, context=False, # Could work, but not implemented multi_window=True, scroll=True, parent=False, # ipywidgets has layouts, but has no concept of parents always_on_top=False, ) # ------------------------------------------------------- set_configuration --- # The configuration mostly applies to the framebuffer. So if we'd want # to implement some of that, we'd probably have to apply it to the FBO. # ------------------------------------------------------------- application --- class ApplicationBackend(BaseApplicationBackend): def __init__(self): super().__init__() def _vispy_get_backend_name(self): return 'jupyter_rfb' def _vispy_process_events(self): raise RuntimeError("Cannot process events while asyncio event-loop is running.") def _vispy_run(self): pass # We're in IPython; don't enter a mainloop or we'll block! def _vispy_quit(self): pass def _vispy_get_native_app(self): return asyncio # ------------------------------------------------------------------ canvas --- class CanvasBackend(BaseCanvasBackend, RemoteFrameBuffer): _double_click_supported = True def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) RemoteFrameBuffer.__init__(self) # Use a context per canvas, because we seem to make assumptions # about OpenGL state being local to the canvas. self._context = OffscreenContext() # OffscreenContext.get_global_instance() self._helper = FrameBufferHelper() self._loop = asyncio.get_event_loop() self._logical_size = 1, 1 self._physical_size = 1, 1 self._lifecycle = 0 # 0: not initialized, 1: initialized, 2: closed # Init more based on kwargs (could maybe handle, title, show, context) self._vispy_set_size(*kwargs["size"]) self.resizable = kwargs["resizable"] # Need a first update self._vispy_update() def handle_event(self, ev): type = ev["event_type"] if type == "resize": # Note that jupyter_rfb already throttles this event w, h, r = ev["width"], ev["height"], ev["pixel_ratio"] self._logical_size = w, h self._physical_size = int(w * r), int(h * r) self._helper.set_physical_size(*self._physical_size) self._loop.call_soon(self._emit_resize_event) self._vispy_update() # make sure to schedule a new draw elif type == "pointer_down": self._vispy_mouse_press( native=ev, pos=(ev["x"], ev["y"]), button=ev["button"], modifiers=self._modifiers(ev), ) elif type == "pointer_up": self._vispy_mouse_release( native=ev, pos=(ev["x"], ev["y"]), button=ev["button"], modifiers=self._modifiers(ev), ) elif type == "pointer_move": self._vispy_mouse_move( native=ev, pos=(ev["x"], ev["y"]), button=ev["button"], modifiers=self._modifiers(ev), ) elif type == "double_click": self._vispy_mouse_double_click( native=ev, pos=(ev["x"], ev["y"]), button=ev["button"], modifiers=self._modifiers(ev), ) elif type == "wheel": self._vispy_canvas.events.mouse_wheel( native=ev, pos=(ev["x"], ev["y"]), delta=(ev["dx"] / 100, - ev["dy"] / 100), modifiers=self._modifiers(ev), ) elif type == "key_down": # The special key names are all (most?) the same # But the key is actually more like tex, e.g. shift + 3 becomes "#" self._vispy_canvas.events.key_press( native=ev, key=keys.Key(ev["key"]), modifiers=self._modifiers(ev), text=ev["key"], ) elif type == "key_up": self._vispy_canvas.events.key_release( native=ev, key=keys.Key(ev["key"]), modifiers=self._modifiers(ev), text=ev["key"], ) elif type == "close": self._lifecycle = 2 self._context.close() _stop_timers(self._vispy_canvas) else: pass # event ignored / unknown def _modifiers(self, ev): return tuple(getattr(keys, m.upper()) for m in ev["modifiers"]) def _emit_resize_event(self): self._vispy_canvas.events.resize( size=self._logical_size, physical_size=self._physical_size, ) def get_frame(self): # This gets automatically called by the RFB widget # Only draw if the draw region is not null if self._physical_size[0] <= 1 or self._physical_size[1] <= 1: return None # Handle initialization if not self._lifecycle: self._lifecycle = 1 self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() self._emit_resize_event() # Draw and obtain result self._vispy_canvas.set_current() with self._helper: self._vispy_canvas.events.draw(region=None) array = self._helper.get_frame() # Flush commands here to clean up - otherwise we get errors related to # framebuffers not existin. self._vispy_canvas.context.flush_commands() return array def _vispy_warmup(self): self._vispy_canvas.set_current() def _vispy_set_current(self): self._context.make_current() def _vispy_swap_buffers(self): pass def _vispy_set_title(self, title): pass def _vispy_set_size(self, w, h): self.css_width = f"{w}px" self.css_height = f"{h}px" def _vispy_set_position(self, x, y): pass def _vispy_set_visible(self, visible): if not visible: raise NotImplementedError("Cannot hide the RFB widget") def _vispy_set_fullscreen(self, fullscreen): raise NotImplementedError() def _vispy_update(self): self.request_draw() def _vispy_close(self): # ipywidget.Widget.close() -> closes the comm and removes all views self.close() def _vispy_get_size(self): return self._logical_size def _vispy_get_physical_size(self): return self._physical_size def _vispy_get_position(self): return 0, 0 def _vispy_get_fullscreen(self): return False # ------------------------------------------------------------------- timer --- class TimerBackend(BaseTimerBackend): def __init__(self, vispy_timer): super().__init__(vispy_timer) self._loop = asyncio.get_event_loop() self._task = None async def _timer_coro(self, interval): while True: await asyncio.sleep(interval) self._vispy_timeout() def _vispy_start(self, interval): if self._task is not None: self._task.cancel() self._task = asyncio.create_task(self._timer_coro(interval)) def _vispy_stop(self): self._task.cancel() self._task = None def _vispy_timeout(self): self._loop.call_soon(self._vispy_timer._timeout) def _stop_timers(canvas): """Stop all timers associated with a canvas.""" # This is nice and all, but the Canvas object is frozen, so this is never actually used for attr in dir(canvas): try: attr_obj = getattr(canvas, attr) except NotImplementedError: continue # prevent error due to props that we don't implement else: if isinstance(attr_obj, Timer): attr_obj.stop() vispy-0.14.1/vispy/app/backends/_glfw.py0000644000175000017510000004117514507104216017462 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """vispy backend for glfw.""" # To install GLFW on Ubuntu, use sudo apt-get install libglfw3. # 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 --- glfw = None try: import glfw except ImportError: why_not = "Could not import glfw, you may need to `pip install glfw` first." available, testable, why_not, which = False, False, why_not, None except Exception as err: why_not = "Error importing glfw: " + str(err) available, testable, why_not, which = False, False, why_not, 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__) if glfw: # Map native keys to vispy keys KEYMAP = { glfw.KEY_LEFT_SHIFT: keys.SHIFT, glfw.KEY_RIGHT_SHIFT: keys.SHIFT, glfw.KEY_LEFT_CONTROL: keys.CONTROL, glfw.KEY_RIGHT_CONTROL: keys.CONTROL, glfw.KEY_LEFT_ALT: keys.ALT, glfw.KEY_RIGHT_ALT: keys.ALT, glfw.KEY_LEFT_SUPER: keys.META, glfw.KEY_RIGHT_SUPER: keys.META, glfw.KEY_LEFT: keys.LEFT, glfw.KEY_UP: keys.UP, glfw.KEY_RIGHT: keys.RIGHT, glfw.KEY_DOWN: keys.DOWN, glfw.KEY_PAGE_UP: keys.PAGEUP, glfw.KEY_PAGE_DOWN: keys.PAGEDOWN, glfw.KEY_INSERT: keys.INSERT, glfw.KEY_DELETE: keys.DELETE, glfw.KEY_HOME: keys.HOME, glfw.KEY_END: keys.END, glfw.KEY_ESCAPE: keys.ESCAPE, glfw.KEY_BACKSPACE: keys.BACKSPACE, glfw.KEY_F1: keys.F1, glfw.KEY_F2: keys.F2, glfw.KEY_F3: keys.F3, glfw.KEY_F4: keys.F4, glfw.KEY_F5: keys.F5, glfw.KEY_F6: keys.F6, glfw.KEY_F7: keys.F7, glfw.KEY_F8: keys.F8, glfw.KEY_F9: keys.F9, glfw.KEY_F10: keys.F10, glfw.KEY_F11: keys.F11, glfw.KEY_F12: keys.F12, glfw.KEY_SPACE: keys.SPACE, glfw.KEY_ENTER: keys.ENTER, '\r': keys.ENTER, glfw.KEY_TAB: keys.TAB, } BUTTONMAP = {glfw.MOUSE_BUTTON_LEFT: 1, glfw.MOUSE_BUTTON_RIGHT: 2, glfw.MOUSE_BUTTON_MIDDLE: 3 } 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.window_hint(glfw.RED_BITS, c['red_size']) glfw.window_hint(glfw.GREEN_BITS, c['green_size']) glfw.window_hint(glfw.BLUE_BITS, c['blue_size']) glfw.window_hint(glfw.ALPHA_BITS, c['alpha_size']) glfw.window_hint(glfw.ACCUM_RED_BITS, 0) glfw.window_hint(glfw.ACCUM_GREEN_BITS, 0) glfw.window_hint(glfw.ACCUM_BLUE_BITS, 0) glfw.window_hint(glfw.ACCUM_ALPHA_BITS, 0) glfw.window_hint(glfw.DEPTH_BITS, c['depth_size']) glfw.window_hint(glfw.STENCIL_BITS, c['stencil_size']) # glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, c['major_version']) # glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, c['minor_version']) # glfw.window_hint(glfw.SRGB_CAPABLE, c['srgb']) glfw.window_hint(glfw.SAMPLES, c['samples']) glfw.window_hint(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.poll_events() 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 any(w._id is not None and not glfw.window_should_close(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.set_error_callback(_error_callback) try: if not glfw.init(): # only ever call once raise OSError('Could not init glfw:\n%r' % _glfw_errors) finally: os.chdir(cwd) glfw.set_error_callback(None) atexit.register(glfw.terminate) _GLFW_INITIALIZED = True return glfw # ------------------------------------------------------------------ canvas --- class CanvasBackend(BaseCanvasBackend): """Glfw backend for Canvas abstract class.""" def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) 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.window_hint(glfw.REFRESH_RATE, 0) # highest possible glfw.window_hint(glfw.RESIZABLE, int(p.resizable)) glfw.window_hint(glfw.DECORATED, int(p.decorate)) glfw.window_hint(glfw.VISIBLE, 0) # start out hidden glfw.window_hint(glfw.FLOATING, int(p.always_on_top)) if p.fullscreen is not False: self._fullscreen = True if p.fullscreen is True: monitor = glfw.get_primary_monitor() else: monitor = glfw.get_monitors() if p.fullscreen >= len(monitor): raise ValueError('fullscreen must be <= %s' % len(monitor)) monitor = monitor[p.fullscreen] use_size = glfw.get_video_mode(monitor)[0][: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.create_window(width=size[0], height=size[1], title=p.title, monitor=monitor, share=share) if not self._id: raise RuntimeError('Could not create window') glfw.make_context_current(self._id) glfw.swap_interval(1 if p.vsync else 0) # needs a valid context _VP_GLFW_ALL_WINDOWS.append(self) self._mod = list() # Register callbacks glfw.set_window_refresh_callback(self._id, self._on_draw) glfw.set_window_size_callback(self._id, self._on_resize) glfw.set_key_callback(self._id, self._on_key_press) glfw.set_char_callback(self._id, self._on_key_char) glfw.set_mouse_button_callback(self._id, self._on_mouse_button) glfw.set_scroll_callback(self._id, self._on_mouse_scroll) glfw.set_cursor_pos_callback(self._id, self._on_mouse_motion) glfw.set_window_close_callback(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.show_window(self._id) # Init self._initialized = True self._next_key_events = [] self._next_key_text = {} self._vispy_canvas.set_current() self._vispy_canvas.events.initialize() self._on_resize(self._id, size[0], size[1]) 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.make_context_current(self._id) def _vispy_swap_buffers(self): if self._id is None: return # Swap front and back buffer glfw.swap_buffers(self._id) def _vispy_set_title(self, title): if self._id is None: return # Set the window title. Has no effect for widgets glfw.set_window_title(self._id, title) def _vispy_set_size(self, w, h): if self._id is None: return # Set size of the widget or window glfw.set_window_size(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.set_window_pos(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.show_window(self._id) # this ensures that the show takes effect self._vispy_update() else: glfw.hide_window(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.set_window_should_close() # Does not really cause a close self._vispy_set_visible(False) self._id, id_ = None, self._id glfw.destroy_window(id_) gc.collect() # help ensure context gets destroyed def _vispy_get_size(self): if self._id is None: return w, h = glfw.get_window_size(self._id) return w, h def _vispy_get_physical_size(self): if self._id is None: return w, h = glfw.get_framebuffer_size(self._id) return w, h def _vispy_get_position(self): if self._id is None: return x, y = glfw.get_window_pos(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.get_cursor_pos(self._id) if button < 3: # Mouse click event button = BUTTONMAP.get(button, 0) if action == glfw.PRESS: fun = self._vispy_mouse_press elif action == 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.get_cursor_pos(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.PRESS: fun = self._vispy_canvas.events.key_press down = True elif action == glfw.RELEASE: fun = self._vispy_canvas.events.key_release down = False else: return self._process_mod(key, down=down) # NOTE: GLFW only provides localized characters via _on_key_char, so if # this event contains a character we store all other data and dispatch # it once the final unicode character is sent shortly after. if text != '' and action == glfw.PRESS: self._next_key_events.append((fun, key, self._mod)) else: if key in self._next_key_text: text = self._next_key_text[key] del self._next_key_text[key] fun(key=key, text=text, modifiers=self._mod) def _on_key_char(self, _id, text): # Repeat strokes (frequency configured at OS) are sent here only, # no regular _on_key_press events. Currently ignored! if len(self._next_key_events) == 0: return (fun, key, mod) = self._next_key_events.pop(0) fun(key=key, text=chr(text), modifiers=mod) self._next_key_text[key] = text 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.14.1/vispy/app/backends/_egl.py0000644000175000017510000002043614507104216017267 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 from ... import config # -------------------------------------------------------------------- init --- try: import os # 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. # If running remote X11 (for example via ssh), eglInitialize() below will # fail (with NVIDIA EGL drivers) if the DISPLAY environment variable is # set. Temporarily unset the DISPLAY environment variable while calling # eglGetDisplay() in order to work around this. This should be OK since # only headless rendering is currently supported by this backend anyway. x11_dpy = os.getenv('DISPLAY') if x11_dpy is not None: os.unsetenv('DISPLAY') from ...ext import egl _EGL_DISPLAY = egl.eglGetDisplay() if x11_dpy is not None: os.environ['DISPLAY'] = x11_dpy 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" once it works properly, and # remove from ignore list in .coveragerc available, testable, why_not = True, False, '' 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.""" def __init__(self, vispy_canvas, **kwargs): BaseCanvasBackend.__init__(self, vispy_canvas) 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 attribs = [egl.EGL_RED_SIZE, 8, egl.EGL_BLUE_SIZE, 8, egl.EGL_GREEN_SIZE, 8, egl.EGL_ALPHA_SIZE, 8, egl.EGL_COLOR_BUFFER_TYPE, egl.EGL_RGB_BUFFER, egl.EGL_SURFACE_TYPE, egl.EGL_PBUFFER_BIT] api = None if 'es' in config['gl_backend']: attribs.extend([egl.EGL_RENDERABLE_TYPE, egl.EGL_OPENGL_ES2_BIT]) api = egl.EGL_OPENGL_ES_API else: attribs.extend([egl.EGL_RENDERABLE_TYPE, egl.EGL_OPENGL_BIT]) api = egl.EGL_OPENGL_API self._native_config = egl.eglChooseConfig(_EGL_DISPLAY, attribs)[0] egl.eglBindAPI(api) 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 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.14.1/vispy/app/backends/__init__.py0000644000175000017510000000345514507104216020122 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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'), ('PyQt6', '_pyqt6', 'PyQt6'), ('PySide', '_pyside', 'PySide'), ('PySide2', '_pyside2', 'PySide2'), ('PySide6', '_pyside6', 'PySide6'), ('Pyglet', '_pyglet', 'pyglet'), ('Glfw', '_glfw', 'vispy.ext.glfw'), ('SDL2', '_sdl2', 'sdl2'), ('wx', '_wx', 'wx'), ('EGL', '_egl', 'vispy.ext.egl'), ('osmesa', '_osmesa', 'vispy.ext.osmesa'), ('tkinter', '_tk', 'tkinter'), ] # Whereas core backends really represents libraries that can create a # canvas, the pseudo backends act more like a proxy. PSEUDO_BACKENDS = [ ('jupyter_rfb', '_jupyter_rfb', 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, _pyside2 _pyqt4 and _qt modules to communicate. qt_lib = None vispy-0.14.1/vispy/app/application.py0000644000175000017510000002460714507104216017116 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """Implements the global singleton app object.""" from __future__ import division import builtins import os import sys from . import backends from .backends import CORE_BACKENDS, BACKEND_NAMES, BACKENDMAP, TRIED_BACKENDS from .. import config from .base import BaseApplicationBackend as ApplicationBackend # noqa from ._detect_eventloop import _get_running_interactive_framework from ..util import logger 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 sleep(self, duration_sec): """Sleep for the given duration in seconds. This is used to reduce CPU stress when VisPy is run in interactive mode. Parameters ---------- duration_sec: float Time to sleep in seconds """ self._backend._vispy_sleep(duration_sec) 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 not hasattr(builtins, '__IPYTHON__'): return False # Then we check the application singleton and determine based on # a variable it sets. try: try: # ipython >=3.0 from traitlets.config.application import Application as App except ImportError: # ipython <3.0 from IPython.config.application import Application as App return App.initialized() and App.instance().interact except (ImportError, AttributeError): return False def is_notebook(self): """Determine if the user is executing in a Jupyter Notebook""" try: # 'get_ipython' is available in globals when running from # IPython/Jupyter ip = get_ipython() if ip.has_trait('kernel'): # There doesn't seem to be an easy way to detect the frontend # That said, if using a kernel, the user can choose to have an # event loop, we therefore make sure the event loop isn't # specified before assuming it is a notebook # https://github.com/vispy/vispy/issues/1708 # https://github.com/ipython/ipython/issues/11920 return _get_running_interactive_framework() is None else: # `jupyter console` is used return False except NameError: 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 os.getenv("_VISPY_RUNNING_GALLERY_EXAMPLES"): # Custom sphinx-gallery scraper in doc/conf.py will handle # rendering/running the application. To make example scripts look # like what a user actually has to run to view the window, we let # them run "app.run()" but immediately return here. # Without this the application would block until someone closed the # window that opens. return 0 elif not allow_interactive or not self.is_interactive(): 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 elif self.is_notebook(): backend_name = 'jupyter_rfb' # 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) elif backend_name is not None: # Inform only if one isn't available logger.warning(msg) else: # Success! self._backend_module = mod logger.info('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.14.1/vispy/app/_detect_eventloop.py0000644000175000017510000001451214507104216020307 0ustar nileshnilesh# Taken from Matplotlib to automatically detect any event loop currently # in use. # This code is copyright of Matplotlib, and their license is inlcuded at the # bottom of this file. import sys import os def _get_running_interactive_framework(): """ Return the interactive framework whose event loop is currently running, if any, or "headless" if no event loop can be started, or None. Returns ------- Optional[str] One of the following values: "qt5", "qt4", "gtk3", "wx", "tk", "macosx", "headless", ``None``. """ QtWidgets = (sys.modules.get("PyQt5.QtWidgets") or sys.modules.get("PySide2.QtWidgets")) if QtWidgets and QtWidgets.QApplication.instance(): return "qt5" QtGui = (sys.modules.get("PyQt4.QtGui") or sys.modules.get("PySide.QtGui")) if QtGui and QtGui.QApplication.instance(): return "qt4" Gtk = sys.modules.get("gi.repository.Gtk") if Gtk and Gtk.main_level(): return "gtk3" wx = sys.modules.get("wx") if wx and wx.GetApp(): return "wx" tkinter = sys.modules.get("tkinter") if tkinter: for frame in sys._current_frames().values(): while frame: if frame.f_code == tkinter.mainloop.__code__: return "tk" frame = frame.f_back if 'matplotlib.backends._macosx' in sys.modules: if sys.modules["matplotlib.backends._macosx"].event_loop_is_running(): return "macosx" if sys.platform.startswith("linux") and not os.environ.get("DISPLAY"): return "headless" return None """ License agreement for matplotlib versions 1.3.0 and later ========================================================= 1. This LICENSE AGREEMENT is between the Matplotlib Development Team ("MDT"), and the Individual or Organization ("Licensee") accessing and otherwise using matplotlib software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, MDT hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use matplotlib alone or in any derivative version, provided, however, that MDT's License Agreement and MDT's notice of copyright, i.e., "Copyright (c) 2012- Matplotlib Development Team; All Rights Reserved" are retained in matplotlib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates matplotlib or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to matplotlib . 4. MDT is making matplotlib available to Licensee on an "AS IS" basis. MDT MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDT MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between MDT and Licensee. This License Agreement does not grant permission to use MDT trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using matplotlib , Licensee agrees to be bound by the terms and conditions of this License Agreement. License agreement for matplotlib versions prior to 1.3.0 ======================================================== 1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the Individual or Organization ("Licensee") accessing and otherwise using matplotlib software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, JDH hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use matplotlib alone or in any derivative version, provided, however, that JDH's License Agreement and JDH's notice of copyright, i.e., "Copyright (c) 2002-2011 John D. Hunter; All Rights Reserved" are retained in matplotlib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates matplotlib or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to matplotlib. 4. JDH is making matplotlib available to Licensee on an "AS IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB , OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between JDH and Licensee. This License Agreement does not grant permission to use JDH trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using matplotlib, Licensee agrees to be bound by the terms and conditions of this License Agreement. """ vispy-0.14.1/vispy/app/_default_app.py0000644000175000017510000000456514507104216017237 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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, already using ' '%s.' % names) 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.14.1/vispy/app/__init__.py0000644000175000017510000000116114507104216016340 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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 .timer import Timer # noqa from . import base # noqa vispy-0.14.1/vispy/__init__.py0000644000175000017510000000160614507104216015564 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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'] try: from .version import version as __version__ # noqa except ImportError: # package is not installed pass from .util import config, set_log_level, keys, sys_info # noqa from .util.wrappers import use, test # noqa def _get_sg_image_scraper(): from .util.gallery_scraper import VisPyGalleryScraper return VisPyGalleryScraper() vispy-0.14.1/setup.py0000644000175000017510000001213414507104216014011 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # 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. Define the version and release: * tag the tip changeset as version x.x.x; `git tag -a 'vX.Y.Z' -m "Version X.Y.Z"` * push tag to github * verify that azure pipelines complete * verify that `.tar.gz` sdist and binary wheels are available on PyPI Announcing: * It can be worth waiting a day for eager users to report critical bugs * Announce in scipy-user, vispy mailing list, twitter (@vispyproject) """ import os from os import path as op from setuptools import setup, find_packages import numpy as np from Cython.Build import cythonize from Cython.Distutils import Extension name = 'vispy' description = 'Interactive visualization in Python' # Special commands for building jupyter notebook extension here = os.path.dirname(os.path.abspath(__file__)) node_root = os.path.join(here, 'js') is_repo = os.path.exists(os.path.join(here, '.git')) npm_path = os.pathsep.join([ os.path.join(node_root, 'node_modules', '.bin'), os.environ.get('PATH', os.defpath), ]) def set_builtin(name, value): if isinstance(__builtins__, dict): __builtins__[name] = value else: setattr(__builtins__, name, value) extensions = [Extension('vispy.visuals.text._sdf_cpu', sources=[op.join('vispy', 'visuals', 'text', '_sdf_cpu.pyx')], include_dirs=[np.get_include()], cython_directives={"language_level": "3"}, define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")], ), ] readme = open('README.rst', 'r').read() setup( name=name, use_scm_version={ 'write_to': 'vispy/version.py', # uses setuptools_scm.version.get_local_dirty_tag (+dirty or empty string) 'local_scheme': 'dirty-tag', }, author='Vispy contributors', author_email='vispy@googlegroups.com', license='BSD-3-Clause', url='http://vispy.org', download_url='https://pypi.python.org/pypi/vispy', keywords=[ 'visualization', 'OpenGl', 'ES', 'medical', 'imaging', '3D', 'plotting', 'numpy', 'bigdata', 'ipython', 'jupyter', 'widgets', ], description=description, long_description=readme, long_description_content_type='text/x-rst', platforms='any', provides=['vispy'], python_requires='>=3.8', install_requires=['numpy', 'freetype-py', 'hsluv', 'kiwisolver', 'packaging'], extras_require={ 'ipython-static': ['ipython'], 'pyglet': ['pyglet>=1.2'], 'pyqt5': ['pyqt5'], 'pyqt6': ['pyqt6'], 'pyside': ['PySide'], 'pyside2': ['PySide2'], 'pyside6': ['PySide6'], 'sdl2': ['PySDL2'], 'wx': ['wxPython'], 'tk': ['pyopengltk'], 'doc': ['pydata-sphinx-theme', 'numpydoc', 'sphinxcontrib-apidoc', 'sphinx-gallery', 'myst-parser', 'pillow', 'pytest', 'pyopengl'], 'io': ['meshio', 'Pillow'], }, packages=find_packages(exclude=['make']), ext_modules=cythonize(extensions, language_level=3), package_dir={'vispy': 'vispy'}, data_files=[], include_package_data=True, package_data={ 'vispy': [op.join('io', '_data', '*'), op.join('app', 'tests', 'qt-designer.ui'), op.join('util', 'fonts', 'data', '*.ttf'), ], 'vispy.glsl': ['*.vert', '*.frag', "*.glsl"], 'vispy.glsl.antialias': ['*.vert', '*.frag', "*.glsl"], 'vispy.glsl.arrowheads': ['*.vert', '*.frag', "*.glsl"], 'vispy.glsl.arrows': ['*.vert', '*.frag', "*.glsl"], 'vispy.glsl.collections': ['*.vert', '*.frag', "*.glsl"], 'vispy.glsl.colormaps': ['*.vert', '*.frag', "*.glsl"], 'vispy.glsl.lines': ['*.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 :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Framework :: IPython' ], ) vispy-0.14.1/setup.cfg0000644000175000017510000000031414507104251014114 0ustar nileshnilesh[flake8] max-line-length = 120 ignore = E226,W291,W293,W503,F999,E305,F405,W504,N,D exclude = _proxy.py,_es2.py,_gl2.py,_pyopengl2.py, _constants.py,cubehelix.py [egg_info] tag_build = tag_date = 0 vispy-0.14.1/pytest.ini0000644000175000017510000000010414507104216014322 0ustar nileshnilesh[pytest] norecursedirs = .* build dist CVS _darcs arch *.egg venv jsvispy-0.14.1/pyproject.toml0000644000175000017510000000034514507104216015214 0ustar nileshnilesh[build-system] requires = [ "wheel", "oldest-supported-numpy", "setuptools>=42", "setuptools_scm[toml]>=7.1", "Cython>=3.0.0" ] [tools.setuptools_scm] write_to = "vispy/version.py" local_scheme = "dirty-tag" vispy-0.14.1/examples/0000755000175000017510000000000014507104250014112 5ustar nileshnileshvispy-0.14.1/examples/tutorial/0000755000175000017510000000000014507104250015755 5ustar nileshnileshvispy-0.14.1/examples/tutorial/visuals/0000755000175000017510000000000014507104251017444 5ustar nileshnileshvispy-0.14.1/examples/tutorial/visuals/T05_viewer_location.py0000644000175000017510000000570014507104216023642 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Tutorial: Creating Visuals ========================== 05. Camera location ------------------- In this tutorial we will demonstrate how to determine the direction from which a Visual is being viewed. """ from vispy import app, gloo, visuals, scene, io vertex_shader = """ varying vec4 color; void main() { vec4 visual_pos = vec4($position, 1); vec4 doc_pos = $visual_to_doc(visual_pos); gl_Position = $doc_to_render(doc_pos); vec4 visual_pos2 = $doc_to_visual(doc_pos + vec4(0, 0, -1, 0)); vec4 view_direction = (visual_pos2 / visual_pos2.w) - visual_pos; view_direction = vec4(normalize(view_direction.xyz), 0); color = vec4(view_direction.rgb, 1); } """ fragment_shader = """ varying vec4 color; void main() { gl_FragColor = color; } """ class MyMeshVisual(visuals.Visual): """ """ def __init__(self): visuals.Visual.__init__(self, vertex_shader, fragment_shader) # Create an interesting mesh shape for demonstration. fname = io.load_data_file('orig/triceratops.obj.gz') vertices, faces, normals, tex = io.read_mesh(fname) self._ibo = gloo.IndexBuffer(faces) self.shared_program.vert['position'] = gloo.VertexBuffer(vertices) # self.program.vert['normal'] = gloo.VertexBuffer(normals) self.set_gl_state('additive', cull_face=False) self._draw_mode = 'triangles' self._index_buffer = self._ibo def _prepare_transforms(self, view): # Note we use the "additive" GL blending settings so that we do not # have to sort the mesh triangles back-to-front before each draw. tr = view.transforms view_vert = view.view_program.vert view_vert['visual_to_doc'] = tr.get_transform('visual', 'document') view_vert['doc_to_visual'] = tr.get_transform('document', 'visual') view_vert['doc_to_render'] = tr.get_transform('document', 'render') # Auto-generate a Visual+Node class for use in the scenegraph. MyMesh = scene.visuals.create_visual_node(MyMeshVisual) # Finally we will test the visual by displaying in a scene. canvas = scene.SceneCanvas(keys='interactive', show=True) # Add a ViewBox to let the user zoom/rotate view = canvas.central_widget.add_view() view.camera = 'turntable' view.camera.fov = 50 view.camera.distance = 2 mesh = MyMesh(parent=view.scene) mesh.transform = visuals.transforms.MatrixTransform() # mesh.transform.translate([-25, -25, -25]) mesh.transform.rotate(90, (1, 0, 0)) axis = scene.visuals.XYZAxis(parent=view.scene) # ..and optionally start the event loop if __name__ == '__main__': import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/tutorial/visuals/T04_fragment_programs.py0000644000175000017510000000460614507104216024171 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Tutorial: Creating Visuals ========================== 04. Fragment Programs --------------------- In this tutorial, we will demonstrate the use of the fragment shader as a raycaster to draw complex shapes on a simple rectanglular mesh. Previous tutorials focused on the use of forward transformation functions to map vertices from the local coordinate system of the visual to the "render coordinates" output of the vertex shader. In this tutorial, we will use inverse transformation functions in the fragment shader to map backward from the current fragment location to the visual's local coordinate system. """ import numpy as np from vispy import app, gloo, visuals, scene vertex_shader = """ void main() { gl_Position = vec4($position, 0, 1); } """ fragment_shader = """ void main() { vec4 pos = $fb_to_visual(gl_FragCoord); gl_FragColor = vec4(sin(pos.x / 10.), sin(pos.y / 10.), 0, 1); } """ class MyRectVisual(visuals.Visual): """ """ def __init__(self): visuals.Visual.__init__(self, vertex_shader, fragment_shader) self.vbo = gloo.VertexBuffer(np.array([ [-1, -1], [1, -1], [1, 1], [-1, -1], [1, 1], [-1, 1] ], dtype=np.float32)) self.shared_program.vert['position'] = self.vbo self.set_gl_state(cull_face=False) self._draw_mode = 'triangle_fan' def _prepare_transforms(self, view): view.view_program.frag['fb_to_visual'] = \ view.transforms.get_transform('framebuffer', 'visual') # As in the previous tutorial, we auto-generate a Visual+Node class for use # in the scenegraph. MyRect = scene.visuals.create_visual_node(MyRectVisual) # Finally we will test the visual by displaying in a scene. canvas = scene.SceneCanvas(keys='interactive', show=True) # This time we add a ViewBox to let the user zoom/pan view = canvas.central_widget.add_view() view.camera = 'panzoom' view.camera.rect = (0, 0, 800, 800) vis = MyRect() view.add(vis) # ..and optionally start the event loop if __name__ == '__main__': import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/tutorial/visuals/T03_antialiasing.py0000644000175000017510000001766614507104216023130 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Tutorial: Creating Visuals ========================== 03. Antialiasing ---------------- In [tutorial 1] we learned how to draw a simple rectangle, and in [tutorial 2] we expanded on this by using the Document coordinate system to draw a rectangular border of a specific width. In this tutorial we introduce the Framebuffer coordinate system, which is used for antialiasing measurements. In order to antialias our edges, we need to introduce a calculation to the fragment shader that computes, for each pixel being drawn, the fraction of the pixel that is covered by the visual's geometry. At first glance, it may seem that the Document coordinate system is sufficient for this purpose because it has unit-length pixels. However, there are two situations when the actual pixels being filled by the fragment shader are not the same size as the pixels on the canvas: 1. High-resolution displays (such as retina displays) that report a canvas resolution smaller than the actual framebuffer resolution. 2. When exporting to an image with a different size than the canvas. In most cases the discrepancy between Document and Framebuffer coordinates can be corrected by a simple scale factor. However, this fails for some interesting corner cases where the transform is more complex, such as in VR applications using optical distortion correction. Decide for yourself: is this Visual for my personal use, or is it intended for a broader audience? For simplicity in this example, we will use a simple scale factor. """ from vispy import app, gloo, visuals, scene import numpy as np # Here we use almost the same vertex shader as in tutorial 2. # The important difference is the addition of the line_pos variable, # which measures position across the width of the border line. vertex_shader = """ varying float line_pos; // how far we are across the border line void main() { // First map the vertex to document coordinates vec4 doc_pos = $visual_to_doc(vec4($position, 0, 1)); vec4 adjusted; if ( $adjust_dir.x == 0. ) { adjusted = doc_pos; line_pos = $line_width; // at the outside of the border } else { // Inner vertexes must be adjusted for line width vec4 doc_x = $visual_to_doc(vec4($adjust_dir.x, 0, 0, 0)) - $visual_to_doc(vec4(0, 0, 0, 0)); vec4 doc_y = $visual_to_doc(vec4(0, $adjust_dir.y, 0, 0)) - $visual_to_doc(vec4(0, 0, 0, 0)); doc_x = normalize(doc_x); doc_y = normalize(doc_y); vec4 proj_y_x = dot(doc_x, doc_y) * doc_x; // project y onto x float cur_width = length(doc_y - proj_y_x); // measure current weight // And now we can adjust vertex position for line width: adjusted = doc_pos + ($line_width / cur_width) * (doc_x + doc_y); line_pos = 0; // at the inside of the border } // Finally map the remainder of the way to render coordinates gl_Position = $doc_to_render(adjusted); } """ # The fragment shader is updated to change the opacity of the color based on # the amount of the fragment that is covered by the visual's geometry. fragment_shader = """ varying float line_pos; void main() { // Decrease the alpha linearly as we come within 1 pixel of the edge. // Note: this only approximates the actual fraction of the pixel that is // covered by the visual's geometry. A more accurate measurement would // produce better antialiasing, but the effect would be subtle. float alpha = 1.0; if ((line_pos * $doc_fb_scale) < 1) { alpha = $color.a * line_pos; } else if ((line_pos * $doc_fb_scale) > ($line_width - 1)) { alpha = $color.a * ($line_width - line_pos); } gl_FragColor = vec4($color.rgb, alpha); } """ # The visual class is defined almost exactly as in [tutorial 2]. The only # major difference is that the draw() method now calculates a scale factor # for converting between document and framebuffer coordinates. class MyRectVisual(visuals.Visual): """Visual that draws a rectangular outline. Parameters ---------- x : float x coordinate of rectangle origin y : float y coordinate of rectangle origin w : float width of rectangle h : float height of rectangle weight : float width of border (in px) """ def __init__(self, x, y, w, h, weight=4.0): self.weight = weight visuals.Visual.__init__(self, vertex_shader, fragment_shader) # 10 vertices for 8 triangles (using triangle_strip) forming a # rectangular outline self.vert_buffer = gloo.VertexBuffer(np.array([ [x, y], [x, y], [x+w, y], [x+w, y], [x+w, y+h], [x+w, y+h], [x, y+h], [x, y+h], [x, y], [x, y], ], dtype=np.float32)) # Direction each vertex should move to correct for line width # (the length of this vector will be corrected in the shader) self.adj_buffer = gloo.VertexBuffer(np.array([ [0, 0], [1, 1], [0, 0], [-1, 1], [0, 0], [-1, -1], [0, 0], [1, -1], [0, 0], [1, 1], ], dtype=np.float32)) self.shared_program.vert['position'] = self.vert_buffer self.shared_program.vert['adjust_dir'] = self.adj_buffer # To compensate for antialiasing, add 1 to border width: self.shared_program.vert['line_width'] = weight + 1 self.shared_program.frag['color'] = (1, 0, 0, 1) self._draw_mode = 'triangle_strip' self.set_gl_state(cull_face=False) def _prepare_transforms(self, view): # Set the two transforms required by the vertex shader: tr = view.transforms view_vert = view.view_program.vert view_vert['visual_to_doc'] = tr.get_transform('visual', 'document') view_vert['doc_to_render'] = tr.get_transform('document', 'render') # Set the scale factor between document and framebuffer coordinate # systems. This assumes a simple linear / isotropic scale; more complex # transforms will yield strange results! doc_to_fb = tr.get_transform('document', 'framebuffer') fbs = np.linalg.norm(doc_to_fb.map([1, 0]) - doc_to_fb.map([0, 0])) view_frag = view.view_program.frag view_frag['doc_fb_scale'] = fbs view_frag['line_width'] = (self.weight + 1) * fbs # As in the previous tutorial, we auto-generate a Visual+Node class for use # in the scenegraph. MyRect = scene.visuals.create_visual_node(MyRectVisual) # Finally we will test the visual by displaying in a scene. canvas = scene.SceneCanvas(keys='interactive', show=True) # This time we add a ViewBox to let the user zoom/pan view = canvas.central_widget.add_view() view.camera = 'panzoom' view.camera.rect = (0, 0, 800, 800) # ..and add the rects to the view instead of canvas.scene rects = [MyRect(100, 100, 200, 300, parent=view.scene), MyRect(500, 100, 200, 300, parent=view.scene)] # Again, rotate one rectangle to ensure the transforms are working as we # expect. tr = visuals.transforms.MatrixTransform() tr.rotate(25, (0, 0, 1)) rects[1].transform = tr # Add some text instructions text = scene.visuals.Text("Drag right mouse button to zoom.", color='w', anchor_x='left', parent=view, pos=(20, 30)) # ..and optionally start the event loop if __name__ == '__main__': import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/tutorial/visuals/T02_measurements.py0000644000175000017510000002006014507104216023152 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Tutorial: Creating Visuals ========================== 02. Making physical measurements -------------------------------- In the last tutorial we created a simple Visual subclass that draws a rectangle. In this tutorial, we will make two additions: 1. Draw a rectangular border instead of a solid rectangle 2. Make the border a fixed pixel width, even when displayed inside a user-zoomable ViewBox. The border is made by drawing a line_strip with 10 vertices:: 1--------------3 | | | 2------4 | [ note that points 9 and 10 are | | | | the same as points 1 and 2 ] | 8------6 | | | 7--------------5 In order to ensure that the border has a fixed width in pixels, we need to adjust the spacing between the inner and outer rectangles whenever the user changes the zoom of the ViewBox. How? Recall that each time the visual is drawn, it is given a TransformSystem instance that carries information about the size of logical and physical pixels relative to the visual [link to TransformSystem documentation]. Essentially, we have 4 coordinate systems: Visual -> Document -> Framebuffer -> Render The user specifies the position and size of the rectangle in Visual coordinates, and in [tutorial 1] we used the vertex shader to convert directly from Visual coordinates to render coordinates. In this tutorial we will convert first to document coordinates, then make the adjustment for the border width, then convert the remainder of the way to render coordinates. Let's say, for example that the user specifies the box width to be 20, and the border width to be 5. To draw the border correctly, we cannot simply add/subtract 5 from the inner rectangle coordinates; if the user zooms in by a factor of 2 then the border would become 10 px wide. Another way to say this is that a vector with length=1 in Visual coordinates does not _necessarily_ have a length of 1 pixel on the canvas. Instead, we must make use of the Document coordinate system, in which a vector of length=1 does correspond to 1 pixel. There are a few ways we could make this measurement of pixel length. Here's how we'll do it in this tutorial: 1. Begin with vertices for a rectangle with border width 0 (that is, vertex 1 is the same as vertex 2, 3=4, and so on). 2. In the vertex shader, first map the vertices to the document coordinate system using the visual->document transform. 3. Add/subtract the line width from the mapped vertices. 4. Map the rest of the way to render coordinates with a second transform: document->framebuffer->render. Note that this problem _cannot_ be solved using a simple scale factor! It is necessary to use these transformations in order to draw correctly when there is rotation or anosotropic scaling involved. """ from vispy import app, gloo, visuals, scene import numpy as np vertex_shader = """ void main() { // First map the vertex to document coordinates vec4 doc_pos = $visual_to_doc(vec4($position, 0, 1)); // Also need to map the adjustment direction vector, but this is tricky! // We need to adjust separately for each component of the vector: vec4 adjusted; if ( $adjust_dir.x == 0. ) { // If this is an outer vertex, no adjustment for line weight is needed. // (In fact, trying to make the adjustment would result in no // triangles being drawn, hence the if/else block) adjusted = doc_pos; } else { // Inner vertexes must be adjusted for line width, but this is // surprisingly tricky given that the rectangle may have been scaled // and rotated! vec4 doc_x = $visual_to_doc(vec4($adjust_dir.x, 0, 0, 0)) - $visual_to_doc(vec4(0, 0, 0, 0)); vec4 doc_y = $visual_to_doc(vec4(0, $adjust_dir.y, 0, 0)) - $visual_to_doc(vec4(0, 0, 0, 0)); doc_x = normalize(doc_x); doc_y = normalize(doc_y); // Now doc_x + doc_y points in the direction we need in order to // correct the line weight of _both_ segments, but the magnitude of // that correction is wrong. To correct it we first need to // measure the width that would result from using doc_x + doc_y: vec4 proj_y_x = dot(doc_x, doc_y) * doc_x; // project y onto x float cur_width = length(doc_y - proj_y_x); // measure current weight // And now we can adjust vertex position for line width: adjusted = doc_pos + ($line_width / cur_width) * (doc_x + doc_y); } // Finally map the remainder of the way to render coordinates gl_Position = $doc_to_render(adjusted); } """ fragment_shader = """ void main() { gl_FragColor = $color; } """ class MyRectVisual(visuals.Visual): """Visual that draws a rectangular outline. Parameters ---------- x : float x coordinate of rectangle origin y : float y coordinate of rectangle origin w : float width of rectangle h : float height of rectangle weight : float width of border (in px) """ def __init__(self, x, y, w, h, weight=4.0): visuals.Visual.__init__(self, vertex_shader, fragment_shader) # 10 vertices for 8 triangles (using triangle_strip) forming a # rectangular outline self.vert_buffer = gloo.VertexBuffer(np.array([ [x, y], [x, y], [x+w, y], [x+w, y], [x+w, y+h], [x+w, y+h], [x, y+h], [x, y+h], [x, y], [x, y], ], dtype=np.float32)) # Direction each vertex should move to correct for line width # (the length of this vector will be corrected in the shader) self.adj_buffer = gloo.VertexBuffer(np.array([ [0, 0], [1, 1], [0, 0], [-1, 1], [0, 0], [-1, -1], [0, 0], [1, -1], [0, 0], [1, 1], ], dtype=np.float32)) self.shared_program.vert['position'] = self.vert_buffer self.shared_program.vert['adjust_dir'] = self.adj_buffer self.shared_program.vert['line_width'] = weight self.shared_program.frag['color'] = (1, 0, 0, 1) self.set_gl_state(cull_face=False) self._draw_mode = 'triangle_strip' def _prepare_transforms(self, view): # Set the two transforms required by the vertex shader: tr = view.transforms view_vert = view.view_program.vert view_vert['visual_to_doc'] = tr.get_transform('visual', 'document') view_vert['doc_to_render'] = tr.get_transform('document', 'render') # As in the previous tutorial, we auto-generate a Visual+Node class for use # in the scenegraph. MyRect = scene.visuals.create_visual_node(MyRectVisual) # Finally we will test the visual by displaying in a scene. canvas = scene.SceneCanvas(keys='interactive', show=True) # This time we add a ViewBox to let the user zoom/pan view = canvas.central_widget.add_view() view.camera = 'panzoom' view.camera.rect = (0, 0, 800, 800) # ..and add the rects to the view instead of canvas.scene rects = [MyRect(100, 100, 200, 300, parent=view.scene), MyRect(500, 100, 200, 300, parent=view.scene)] # Again, rotate one rectangle to ensure the transforms are working as we # expect. tr = visuals.transforms.MatrixTransform() tr.rotate(25, (0, 0, 1)) rects[1].transform = tr # Add some text instructions text = scene.visuals.Text("Drag right mouse button to zoom.", color='w', anchor_x='left', parent=view, pos=(20, 30)) # ..and optionally start the event loop if __name__ == '__main__': import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/tutorial/visuals/T01_basic_visual.py0000644000175000017510000001453614507104216023120 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Tutorial: Creating Visuals -------------------------- This tutorial is intended to guide developers who are interested in creating new subclasses of Visual. In most cases, this will not be necessary because vispy's base library of visuals will be sufficient to create complex scenes as needed. However, there are cases where a particular visual effect is desired that is not supported in the base library, or when a custom visual is needed to optimize performance for a specific use case. The purpose of a Visual is to encapsulate a single drawable object. This drawable can be as simple or complex as desired. Some of the simplest visuals draw points, lines, or triangles, whereas more complex visuals invove multiple drawing stages or make use of sub-visuals to construct larger objects. In this example we will create a very simple Visual that draws a rectangle. Visuals are defined by: 1. Creating a subclass of vispy.visuals.Visual that specifies the GLSL code and buffer objects to use. 2. Defining a _prepare_transforms() method that will be called whenever the user (or scenegraph) assigns a new set of transforms to the visual. """ from vispy import app, gloo, visuals, scene import numpy as np # Define a simple vertex shader. We use $template variables as placeholders for # code that will be inserted later on. In this example, $position will become # an attribute, and $transform will become a function. Important: using # $transform in this way ensures that users of this visual will be able to # apply arbitrary transformations to it. vertex_shader = """ void main() { gl_Position = $transform(vec4($position, 0, 1)); } """ # Very simple fragment shader. Again we use a template variable "$color", which # allows us to decide later how the color should be defined (in this case, we # will just use a uniform red color). fragment_shader = """ void main() { gl_FragColor = $color; } """ # Start the new Visual class. # By convention, all Visual subclass names end in 'Visual'. # (Custom visuals may ignore this convention, but for visuals that are built # in to vispy, this is required to ensure that the VisualNode subclasses are # generated correctly.) class MyRectVisual(visuals.Visual): """Visual that draws a red rectangle. Parameters ---------- x : float x coordinate of rectangle origin y : float y coordinate of rectangle origin w : float width of rectangle h : float height of rectangle All parameters are specified in the local (arbitrary) coordinate system of the visual. How this coordinate system translates to the canvas will depend on the transformation functions used during drawing. """ # There are no constraints on the signature of the __init__ method; use # whatever makes the most sense for your visual. def __init__(self, x, y, w, h): # Initialize the visual with a vertex shader and fragment shader visuals.Visual.__init__(self, vertex_shader, fragment_shader) # vertices for two triangles forming a rectangle self.vbo = gloo.VertexBuffer(np.array([ [x, y], [x+w, y], [x+w, y+h], [x, y], [x+w, y+h], [x, y+h] ], dtype=np.float32)) # Assign values to the $position and $color template variables in # the shaders. ModularProgram automatically handles generating the # necessary attribute and uniform declarations with unique variable # names. self.shared_program.vert['position'] = self.vbo self.shared_program.frag['color'] = (1, 0, 0, 1) self._draw_mode = 'triangles' def _prepare_transforms(self, view): # This method is called when the user or the scenegraph has assigned # new transforms to this visual (ignore the *view* argument for now; # we'll get to that later). This method is thus responsible for # connecting the proper transform functions to the shader program. # The most common approach here is to simply take the complete # transformation from visual coordinates to render coordinates. Later # tutorials detail more complex transform handling. view.view_program.vert['transform'] = view.get_transform() # At this point the visual is ready to use, but it takes some extra effort to # set up a Canvas and TransformSystem for drawing (the examples in # examples/basics/visuals/ all follow this approach). # # An easier approach is to make the visual usable in a scenegraph, in which # case the canvas will take care of drawing the visual and setting up the # TransformSystem for us. # # To be able to use our new Visual in a scenegraph, it needs to be # a subclass of scene.Node. In vispy we achieve this by creating a parallel # set of classes that inherit from both Node and each Visual subclass. # This can be done automatically using scene.visuals.create_visual_node(): MyRect = scene.visuals.create_visual_node(MyRectVisual) # By convention, these classes have the same name as the Visual they inherit # from, but without the 'Visual' suffix. # The auto-generated class MyRect is basically equivalent to:: # # class MyRect(MyRectVisual, scene.Node): # def __init__(self, *args, **kwds): # parent = kwds.pop('parent', None) # name = kwds.pop('name', None) # MyRectVisual.__init__(self, *args, **kwds) # Node.__init__(self, parent=parent, name=name) # # Finally we will test the visual by displaying in a scene. # Create a canvas to display our visual canvas = scene.SceneCanvas(keys='interactive', show=True) # Create two instances of MyRect, each using canvas.scene as their parent rects = [MyRect(100, 100, 200, 300, parent=canvas.scene), MyRect(500, 100, 200, 300, parent=canvas.scene)] # To test that the user-specified transforms work correctly, I'll rotate # one rectangle slightly. tr = visuals.transforms.MatrixTransform() tr.rotate(5, (0, 0, 1)) rects[1].transform = tr # ..and optionally start the event loop if __name__ == '__main__': import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/tutorial/app/0000755000175000017510000000000014507104251016536 5ustar nileshnileshvispy-0.14.1/examples/tutorial/app/simple_wx.py0000644000175000017510000000331214507104216021117 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This is a very minimal example that opens a window and makes the background color to change from black to white to black ... The wx backend is used to embed the canvas in a simple wx Frame with a menubar. """ import wx import math from vispy import app, gloo class Canvas(app.Canvas): def __init__(self, *args, **kwargs): app.Canvas.__init__(self, *args, **kwargs) self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.tick = 0 def on_draw(self, event): gloo.clear(color=True) def on_timer(self, event): self.tick += 1 / 60.0 c = abs(math.sin(self.tick)) gloo.set_clear_color((c, c, c, 1)) self.update() def stop_timer(self): self._timer.stop() class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Vispy Test", wx.DefaultPosition, size=(500, 500)) MenuBar = wx.MenuBar() file_menu = wx.Menu() file_menu.Append(wx.ID_EXIT, "&Quit") self.Bind(wx.EVT_MENU, self.on_quit, id=wx.ID_EXIT) self.Bind(wx.EVT_SHOW, self.on_show) MenuBar.Append(file_menu, "&File") self.SetMenuBar(MenuBar) self.canvas = Canvas(app="wx", parent=self) def on_quit(self, event): self.canvas.stop_timer() self.Close(True) def on_show(self, event): self.canvas.show() event.Skip() if __name__ == '__main__': myapp = wx.App(0) frame = TestFrame() frame.Show(True) myapp.MainLoop() vispy-0.14.1/examples/tutorial/app/simple.py0000644000175000017510000000172614507104216020410 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This is a very minimal example that opens a window and makes the background color to change from black to white to black ... The backend is chosen automatically depending on what is available on your machine. """ import math from vispy import app, gloo class Canvas(app.Canvas): def __init__(self, *args, **kwargs): app.Canvas.__init__(self, *args, **kwargs) self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.tick = 0 def on_draw(self, event): gloo.clear(color=True) def on_timer(self, event): self.tick += 1 / 60.0 c = abs(math.sin(self.tick)) gloo.set_clear_color((c, c, c, 1)) self.update() if __name__ == '__main__': canvas = Canvas(keys='interactive', always_on_top=True) canvas.show() app.run() vispy-0.14.1/examples/tutorial/app/shared_context.py0000644000175000017510000000553114507104216022127 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ This is a very simple example that demonstrates using a shared context between two Qt widgets. """ # XXX THIS IS CURRENTLY BROKEN from PyQt5 import QtWidgets, QtCore # can also use pyside from functools import partial from vispy.app import Timer from vispy.scene.visuals import Text from vispy.scene.widgets import ViewBox from vispy.scene import SceneCanvas def on_resize(canvas, vb, event): vb.pos = 1, 1 vb.size = (canvas.size[0] - 2, canvas.size[1] - 2) class Window(QtWidgets.QWidget): def __init__(self): super(Window, self).__init__() box = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.LeftToRight, self) self.resize(500, 200) self.setLayout(box) self.canvas_0 = SceneCanvas(bgcolor='w') self.vb_0 = ViewBox(parent=self.canvas_0.scene, bgcolor='r') self.vb_0.camera.rect = -1, -1, 2, 2 self.canvas_0.events.initialize.connect(self.on_init) self.canvas_0.events.resize.connect(partial(on_resize, self.canvas_0, self.vb_0)) box.addWidget(self.canvas_0.native) # pass the context from the first canvas to the second self.canvas_1 = SceneCanvas(bgcolor='w', shared=self.canvas_0.context) self.vb_1 = ViewBox(parent=self.canvas_1.scene, bgcolor='b') self.vb_1.camera.rect = -1, -1, 2, 2 self.canvas_1.events.resize.connect(partial(on_resize, self.canvas_1, self.vb_1)) box.addWidget(self.canvas_1.native) self.tick_count = 0 self.timer = Timer(interval=1., connect=self.on_timer, start=True) self.setWindowTitle('Shared contexts') self.show() def on_init(self, event): self.text = Text('Initialized', font_size=40., anchor_x='left', anchor_y='top', parent=[self.vb_0.scene, self.vb_1.scene]) def on_timer(self, event): self.tick_count += 1 self.text.text = 'Tick #%s' % self.tick_count self.canvas_0.update() self.canvas_1.update() def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Escape: self.close() elif event.key() == QtCore.Qt.Key_F11: self.showNormal() if self.isFullScreen() else self.showFullScreen() if __name__ == '__main__': qt_app = QtWidgets.QApplication([]) ex = Window() qt_app.exec_() vispy-0.14.1/examples/tutorial/app/fps.py0000644000175000017510000000147614507104216017711 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This is a very minimal example that opens a window and makes the background color to change from black to white to black ... The backend is chosen automatically depending on what is available on your machine. """ import math import time from vispy import app class Canvas(app.Canvas): def __init__(self, *args, **kwargs): app.Canvas.__init__(self, *args, **kwargs) self.show() def on_draw(self, event): c = 0.5 + math.sin(math.pi * time.time()) / 2. self.context.clear([c] * 3) self.update() if __name__ == '__main__': canvas = Canvas(keys='interactive', vsync=False) canvas.measure_fps() app.run() vispy-0.14.1/examples/tutorial/app/app_events.py0000644000175000017510000000346614507104216021266 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This example shows how to retrieve event information from a callback. You should see information displayed for any event you triggered. """ from vispy import gloo, app class Canvas(app.Canvas): def __init__(self, *args, **kwargs): app.Canvas.__init__(self, *args, **kwargs) self.title = 'App demo' def on_close(self, event): print('closing!') def on_resize(self, event): print('Resize %r' % (event.size, )) def on_key_press(self, event): modifiers = [key.name for key in event.modifiers] print('Key pressed - text: %r, key: %s, modifiers: %r' % ( event.text, event.key.name, modifiers)) def on_key_release(self, event): modifiers = [key.name for key in event.modifiers] print('Key released - text: %r, key: %s, modifiers: %r' % ( event.text, event.key.name, modifiers)) def on_mouse_press(self, event): self.print_mouse_event(event, 'Mouse press') def on_mouse_release(self, event): self.print_mouse_event(event, 'Mouse release') def on_mouse_move(self, event): self.print_mouse_event(event, 'Mouse move') def on_mouse_wheel(self, event): self.print_mouse_event(event, 'Mouse wheel') def print_mouse_event(self, event, what): modifiers = ', '.join([key.name for key in event.modifiers]) print('%s - pos: %r, button: %s, modifiers: %s, delta: %r' % (what, event.pos, event.button, modifiers, event.delta)) def on_draw(self, event): gloo.clear(color=True, depth=True) if __name__ == '__main__': canvas = Canvas(keys='interactive') canvas.show() app.run() vispy-0.14.1/examples/scene/0000755000175000017510000000000014507104251015210 5ustar nileshnileshvispy-0.14.1/examples/scene/volume_plane.py0000644000175000017510000001030514507104216020250 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 10:200:5 """ Rendering Planes through 3D Data ================================ Controls: * 1 - toggle between volume rendering methods * 2 - toggle between volume rendering modes ('volume', 'plane') * [] - shift plane along plane normal * {} - decrease/increase plane thickness * Spacebar - stop/start animation * x/y/z/o - set plane normal along x/y/z or [1,1,1] oblique axis """ import sys import numpy as np from vispy import app, scene, io from vispy.visuals.transforms import STTransform # Read volume vol = np.load(io.load_data_file('volume/stent.npz'))['arr_0'] # Prepare canvas canvas = scene.SceneCanvas(keys='interactive', show=True) view = canvas.central_widget.add_view() # Create the volume visual for plane rendering plane = scene.visuals.Volume( vol, parent=view.scene, raycasting_mode='plane', method='mip', plane_thickness=3.0, plane_position=(128, 60, 64), plane_normal=(1, 0, 0), ) volume = scene.visuals.Volume( vol, parent=view.scene, raycasting_mode='volume', method='mip', ) volume.set_gl_state('additive') volume.opacity = 0.25 # Create a camera cam = scene.cameras.TurntableCamera( parent=view.scene, fov=60.0, azimuth=-42.0, elevation=30.0 ) view.camera = cam # Create an XYZAxis visual axis = scene.visuals.XYZAxis(parent=view) s = STTransform(translate=(50, 50), scale=(50, 50, 50, 1)) affine = s.as_matrix() axis.transform = affine def update_axis_visual(): """Sync XYZAxis visual with camera angles""" axis.transform.reset() axis.transform.rotate(cam.roll, (0, 0, 1)) axis.transform.rotate(cam.elevation, (1, 0, 0)) axis.transform.rotate(cam.azimuth, (0, 1, 0)) axis.transform.scale((50, 50, 0.001)) axis.transform.translate((50., 50.)) axis.update() update_axis_visual() @canvas.events.mouse_move.connect def on_mouse_move(event): if event.button == 1 and event.is_dragging: update_axis_visual() # Implement key presses @canvas.events.key_press.connect def on_key_press(event): if event.text == '1': methods = ['mip', 'average'] method = methods[(methods.index(plane.method) + 1) % 2] print("Volume render method: %s" % method) plane.method = method elif event.text == '2': modes = ['volume', 'plane'] if plane.raycasting_mode == modes[0]: plane.raycasting_mode = modes[1] print(modes[1]) else: plane.raycasting_mode = modes[0] print(modes[0]) elif event.text != '' and event.text in '{}': t = -1 if event.text == '{' else 1 plane.plane_thickness += t plane.plane_thickness += t print(f"plane thickness: {plane.plane_thickness}") elif event.text != '' and event.text in '[]': shift = plane.plane_normal / np.linalg.norm(plane.plane_normal) if event.text == '[': plane.plane_position -= 2 * shift elif event.text == ']': plane.plane_position += 2 * shift print(f"plane position: {plane.plane_position}") elif event.text == 'x': plane.plane_normal = [0, 0, 1] elif event.text == 'y': plane.plane_normal = [0, 1, 0] elif event.text == 'z': plane.plane_normal = [1, 0, 0] elif event.text == 'o': plane.plane_normal = [1, 1, 1] elif event.text == ' ': if timer.running: timer.stop() else: timer.start() def move_plane(event): z_pos = plane.plane_position[0] if z_pos < 32: plane.plane_position = plane.plane_position + [1, 0, 0] elif 32 < z_pos <= 220: plane.plane_position = plane.plane_position - [1, 0, 0] else: plane.plane_position = (220, 64, 64) timer = app.Timer('auto', connect=move_plane, start=True) if __name__ == '__main__': canvas.show() print(__doc__) if sys.flags.interactive == 0: plane.plane_position = (220, 64, 64) app.run() vispy-0.14.1/examples/scene/volume.py0000644000175000017510000001230414507104216017072 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 2 """ Volume Rendering ================ Example volume rendering Controls: * 1 - toggle camera between first person (fly), regular 3D (turntable) and arcball * 2 - toggle between volume rendering methods * 3 - toggle between stent-CT / brain-MRI image * 4 - toggle between colormaps * 5 - toggle between interpolation methods * 0 - reset cameras * [] - decrease/increase isosurface threshold With fly camera: * WASD or arrow keys - move around * SPACE - brake * FC - move up-down * IJKL or mouse - look around """ from itertools import cycle import numpy as np from vispy import app, scene, io from vispy.color import get_colormaps, BaseColormap from vispy.visuals.transforms import STTransform # Read volume vol1 = np.load(io.load_data_file('volume/stent.npz'))['arr_0'] vol2 = np.load(io.load_data_file('brain/mri.npz'))['data'] vol2 = np.flipud(np.rollaxis(vol2, 1)) # Prepare canvas canvas = scene.SceneCanvas(keys='interactive', size=(800, 600), show=True) canvas.measure_fps() # Set up a viewbox to display the image with interactive pan/zoom view = canvas.central_widget.add_view() # Create the volume visuals, only one is visible volume1 = scene.visuals.Volume(vol1, parent=view.scene, threshold=0.225) volume1.transform = scene.STTransform(translate=(64, 64, 0)) volume2 = scene.visuals.Volume(vol2, parent=view.scene, threshold=0.2) volume2.visible = False # Create three cameras (Fly, Turntable and Arcball) fov = 60. cam1 = scene.cameras.FlyCamera(parent=view.scene, fov=fov, name='Fly') cam2 = scene.cameras.TurntableCamera(parent=view.scene, fov=fov, name='Turntable') cam3 = scene.cameras.ArcballCamera(parent=view.scene, fov=fov, name='Arcball') view.camera = cam2 # Select turntable at first # Create an XYZAxis visual axis = scene.visuals.XYZAxis(parent=view) s = STTransform(translate=(50, 50), scale=(50, 50, 50, 1)) affine = s.as_matrix() axis.transform = affine # create colormaps that work well for translucent and additive volume rendering class TransFire(BaseColormap): glsl_map = """ vec4 translucent_fire(float t) { return vec4(pow(t, 0.5), t, t*t, max(0, t*1.05 - 0.05)); } """ class TransGrays(BaseColormap): glsl_map = """ vec4 translucent_grays(float t) { return vec4(t, t, t, t*0.05); } """ # Setup colormap iterators opaque_cmaps = cycle(get_colormaps()) translucent_cmaps = cycle([TransFire(), TransGrays()]) opaque_cmap = next(opaque_cmaps) translucent_cmap = next(translucent_cmaps) interp_methods = cycle(volume1.interpolation_methods) interp = next(interp_methods) # Implement axis connection with cam2 @canvas.events.mouse_move.connect def on_mouse_move(event): if event.button == 1 and event.is_dragging: axis.transform.reset() axis.transform.rotate(cam2.roll, (0, 0, 1)) axis.transform.rotate(cam2.elevation, (1, 0, 0)) axis.transform.rotate(cam2.azimuth, (0, 1, 0)) axis.transform.scale((50, 50, 0.001)) axis.transform.translate((50., 50.)) axis.update() # Implement key presses @canvas.events.key_press.connect def on_key_press(event): global opaque_cmap, translucent_cmap if event.text == '1': cam_toggle = {cam1: cam2, cam2: cam3, cam3: cam1} view.camera = cam_toggle.get(view.camera, cam2) print(view.camera.name + ' camera') if view.camera is cam2: axis.visible = True else: axis.visible = False elif event.text == '2': methods = ['mip', 'translucent', 'iso', 'additive'] method = methods[(methods.index(volume1.method) + 1) % 4] print("Volume render method: %s" % method) cmap = opaque_cmap if method in ['mip', 'iso'] else translucent_cmap volume1.method = method volume1.cmap = cmap volume2.method = method volume2.cmap = cmap elif event.text == '3': volume1.visible = not volume1.visible volume2.visible = not volume1.visible elif event.text == '4': if volume1.method in ['mip', 'iso']: cmap = opaque_cmap = next(opaque_cmaps) else: cmap = translucent_cmap = next(translucent_cmaps) volume1.cmap = cmap volume2.cmap = cmap elif event.text == '5': interp = next(interp_methods) volume1.interpolation = interp volume2.interpolation = interp print(f"Interpolation method: {interp}") elif event.text == '0': cam1.set_range() cam3.set_range() elif event.text != '' and event.text in '[]': s = -0.025 if event.text == '[' else 0.025 volume1.threshold += s volume2.threshold += s th = volume1.threshold if volume1.visible else volume2.threshold print("Isosurface threshold: %0.3f" % th) # for testing performance # @canvas.connect # def on_draw(ev): # canvas.update() if __name__ == '__main__': print(__doc__) app.run() vispy-0.14.1/examples/scene/viewbox.py0000644000175000017510000000565714507104216017263 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ ViewBox with Clipping Methods ============================= Demonstrate ViewBox using various clipping methods. """ import sys import numpy as np from vispy import app from vispy import scene # Create canvas canvas = scene.SceneCanvas(size=(800, 600), show=True, keys='interactive') grid = canvas.central_widget.add_grid() # Create two ViewBoxes, place side-by-side vb1 = grid.add_view(name='vb1', border_color='yellow') # First ViewBox uses a 2D pan/zoom camera vb1.camera = 'panzoom' # Second ViewBox uses a 3D perspective camera vb2 = grid.add_view(name='vb2', border_color='yellow') vb2.parent = canvas.scene vb2.camera = scene.TurntableCamera(elevation=30, azimuth=30, up='+y') # # Now add visuals to the viewboxes. # # First a plot line: N = 1000 color = np.ones((N, 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, N) color[:, 1] = color[::-1, 0] pos = np.empty((N, 2), np.float32) pos[:, 0] = np.linspace(-1., 1., N) pos[:, 1] = np.random.normal(0.0, 0.5, size=N) pos[:20, 1] = -0.5 # So we can see which side is down # make a single plot line and display in both viewboxes line1 = scene.visuals.Line(pos=pos.copy(), color=color, method='gl', antialias=False, name='line1', parent=vb1.scene) line2 = scene.visuals.Line(pos=pos.copy(), color=color, method='gl', antialias=False, name='line1', parent=vb2.scene) # And some squares: box = np.array([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0], [0, 0, 0]], dtype=np.float32) z = np.array([[0, 0, 1]], dtype=np.float32) # First two boxes are added to both views box1 = scene.visuals.Line(pos=box, color=(0.7, 0, 0, 1), method='gl', name='unit box', parent=vb1.scene) box2 = scene.visuals.Line(pos=box, color=(0.7, 0, 0, 1), method='gl', name='unit box', parent=vb2.scene) box2 = scene.visuals.Line(pos=(box * 2 - 1), color=(0, 0.7, 0, 1), method='gl', name='nd box', parent=vb1.scene) box3 = scene.visuals.Line(pos=(box * 2 - 1), color=(0, 0.7, 0, 1), method='gl', name='nd box', parent=vb2.scene) # These boxes are only added to the 3D view. box3 = scene.visuals.Line(pos=box + z, color=(1, 0, 0, 1), method='gl', name='unit box', parent=vb2.scene) box5 = scene.visuals.Line(pos=((box + z) * 2 - 1), color=(0, 1, 0, 1), method='gl', name='nd box', parent=vb2.scene) if __name__ == '__main__' and sys.flags.interactive == 0: print(canvas.scene.describe_tree(with_transform=True)) app.run() vispy-0.14.1/examples/scene/turntable_box.py0000644000175000017510000000172214507104216020435 0ustar nileshnilesh# -*-coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Display a Cube ============== Simple use of SceneCanvas to display a cube with an arcball camera. """ import sys from vispy import scene from vispy.color import Color canvas = scene.SceneCanvas(keys='interactive', size=(800, 600), show=True) # Set up a viewbox to display the cube with interactive arcball view = canvas.central_widget.add_view() view.bgcolor = '#efefef' view.camera = 'turntable' view.padding = 100 color = Color("#3f51b5") cube = scene.visuals.Box(1, 1, 1, color=color, edge_color="black", parent=view.scene) if __name__ == '__main__' and sys.flags.interactive == 0: canvas.app.run() vispy-0.14.1/examples/scene/text.py0000644000175000017510000000331314507104216016547 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Text in a Scene and ViewBox =========================== Demonstrate the use of text in the root scene and a viewbox. Note how the point size is independent of scaling of viewbox and canvas. """ import sys import numpy as np from vispy import scene from vispy.scene.visuals import Text # Create canvas with a viewbox at the lower half canvas = scene.SceneCanvas(keys='interactive') vb = scene.widgets.ViewBox(parent=canvas.scene, border_color='b') vb.camera = scene.TurntableCamera(elevation=30, azimuth=30, up='+z') axis = scene.visuals.XYZAxis(parent=vb.scene) vb.camera.rect = 0, 0, 1, 1 @canvas.events.resize.connect def resize(event=None): vb.pos = 1, canvas.size[1] // 2 - 1 vb.size = canvas.size[0] - 2, canvas.size[1] // 2 - 2 t1 = Text('Text in root scene (24 pt)', parent=canvas.scene, color='red') t1.font_size = 24 t1.pos = canvas.size[0] // 2, canvas.size[1] // 3 t2 = Text('Text in viewbox (18 pt)', parent=vb.scene, color='green', rotation=30) t2.font_size = 18 t2.pos = 0.5, 0.3 # Add a line so you can see translate/scale of camera N = 1000 linedata = np.empty((N, 2), np.float32) linedata[:, 0] = np.linspace(0, 1, N) linedata[:, 1] = np.random.uniform(0.5, 0.1, (N,)) scene.visuals.Line(pos=linedata, color='#f006', method='gl', parent=vb.scene) if __name__ == '__main__': canvas.show() if sys.flags.interactive != 1: canvas.app.run() vispy-0.14.1/examples/scene/surface_plot.py0000644000175000017510000000356514507104216020262 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Draw a SurfacePlot ================== This example demonstrates the use of the SurfacePlot visual. """ import sys import numpy as np from vispy import app, scene from vispy.util.filter import gaussian_filter canvas = scene.SceneCanvas(keys='interactive', bgcolor='w') view = canvas.central_widget.add_view() view.camera = scene.TurntableCamera(up='z', fov=60) # Simple surface plot example # x, y values are not specified, so assumed to be 0:50 z = np.random.normal(size=(250, 250), scale=200) z[100, 100] += 50000 z = gaussian_filter(z, (10, 10)) p1 = scene.visuals.SurfacePlot(z=z, color=(0.3, 0.3, 1, 1)) p1.transform = scene.transforms.MatrixTransform() p1.transform.scale([1/249., 1/249., 1/249.]) p1.transform.translate([-0.5, -0.5, 0]) view.add(p1) # p1._update_data() # cheating. # cf = scene.filters.ZColormapFilter('fire', zrange=(z.max(), z.min())) # p1.attach(cf) xax = scene.Axis(pos=[[-0.5, -0.5], [0.5, -0.5]], tick_direction=(0, -1), font_size=16, axis_color='k', tick_color='k', text_color='k', parent=view.scene) xax.transform = scene.STTransform(translate=(0, 0, -0.2)) yax = scene.Axis(pos=[[-0.5, -0.5], [-0.5, 0.5]], tick_direction=(-1, 0), font_size=16, axis_color='k', tick_color='k', text_color='k', parent=view.scene) yax.transform = scene.STTransform(translate=(0, 0, -0.2)) # Add a 3D axis to keep us oriented axis = scene.visuals.XYZAxis(parent=view.scene) if __name__ == '__main__': canvas.show() if sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/sphere.py0000644000175000017510000000252514507104216017055 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 2 """ Draw a Sphere ============= This example demonstrates how to create a sphere. """ import sys from vispy import scene from vispy.visuals.transforms import STTransform canvas = scene.SceneCanvas(keys='interactive', bgcolor='white', size=(800, 600), show=True) view = canvas.central_widget.add_view() view.camera = 'arcball' sphere1 = scene.visuals.Sphere(radius=1, method='latitude', parent=view.scene, edge_color='black') sphere2 = scene.visuals.Sphere(radius=1, method='ico', parent=view.scene, edge_color='black') sphere3 = scene.visuals.Sphere(radius=1, rows=10, cols=10, depth=10, method='cube', parent=view.scene, edge_color='black') sphere1.transform = STTransform(translate=[-2.5, 0, 0]) sphere3.transform = STTransform(translate=[2.5, 0, 0]) view.camera.set_range(x=[-3, 3]) if __name__ == '__main__' and sys.flags.interactive == 0: canvas.app.run() vispy-0.14.1/examples/scene/shape_draw.py0000644000175000017510000003154614507104216017711 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) 2018, Felix Schill. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # vispy: gallery 2 """ Draw and Edit Shapes with Mouse =============================== Simple demonstration of drawing and editing shapes with the mouse This demo implements mouse picking on visuals and markers using the vispy scene and "visual_at" mechanism. Left mouse button on empty space creates new objects. Objects can be selected by clicking, and moved by dragging. Dragging control points changes the size of the object. Vispy takes care of coordinate transforms from screen to ViewBox - the demo works on different zoom levels. Lastly, additional objects are added to the view in a fixed position as "buttons" to select which type of object is being created. Selecting the arrow symbol will switch into select/pan mode where the left drag moves the workplane or moves objects/controlpoints. """ import numpy as np from vispy import app, scene from vispy.color import Color class ControlPoints(scene.visuals.Compound): def __init__(self, parent): scene.visuals.Compound.__init__(self, []) self.unfreeze() self.parent = parent self._center = [0, 0] self._width = 0.0 self._height = 0.0 self.selected_cp = None self.opposed_cp = None self.control_points = [scene.visuals.Markers(parent=self) for i in range(0, 4)] for c in self.control_points: c.set_data(pos=np.array([[0, 0]], dtype=np.float32), symbol="s", edge_color="red", size=6) c.interactive = True self.freeze() def update_bounds(self): self._center = [0.5 * (self.parent.bounds(0)[1] + self.parent.bounds(0)[0]), 0.5 * (self.parent.bounds(1)[1] + self.parent.bounds(1)[0])] self._width = self.parent.bounds(0)[1] - self.parent.bounds(0)[0] self._height = self.parent.bounds(1)[1] - self.parent.bounds(1)[0] self.update_points() def update_points(self): self.control_points[0].set_data( pos=np.array([[self._center[0] - 0.5 * self._width, self._center[1] + 0.5 * self._height]])) self.control_points[1].set_data( pos=np.array([[self._center[0] + 0.5 * self._width, self._center[1] + 0.5 * self._height]])) self.control_points[2].set_data( pos=np.array([[self._center[0] + 0.5 * self._width, self._center[1] - 0.5 * self._height]])) self.control_points[3].set_data( pos=np.array([[self._center[0] - 0.5 * self._width, self._center[1] - 0.5 * self._height]])) def select(self, val, obj=None): self.visible(val) self.selected_cp = None self.opposed_cp = None if obj is not None: n_cp = len(self.control_points) for i in range(0, n_cp): c = self.control_points[i] if c == obj: self.selected_cp = c self.opposed_cp = \ self.control_points[int((i + n_cp / 2)) % n_cp] def start_move(self, start): None def move(self, end): if not self.parent.editable: return if self.selected_cp is not None: self._width = 2 * (end[0] - self._center[0]) self._height = 2 * (end[1] - self._center[1]) self.update_points() self.parent.update_from_controlpoints() def visible(self, v): for c in self.control_points: c.visible = v def get_center(self): return self._center def set_center(self, val): self._center = val self.update_points() class EditVisual(scene.visuals.Compound): def __init__(self, editable=True, selectable=True, on_select_callback=None, callback_argument=None, *args, **kwargs): scene.visuals.Compound.__init__(self, [], *args, **kwargs) self.unfreeze() self.editable = editable self._selectable = selectable self._on_select_callback = on_select_callback self._callback_argument = callback_argument self.control_points = ControlPoints(parent=self) self.drag_reference = [0, 0] self.freeze() def add_subvisual(self, visual): scene.visuals.Compound.add_subvisual(self, visual) visual.interactive = True self.control_points.update_bounds() self.control_points.visible(False) def select(self, val, obj=None): if self.selectable: self.control_points.visible(val) if self._on_select_callback is not None: self._on_select_callback(self._callback_argument) def start_move(self, start): self.drag_reference = start[0:2] - self.control_points.get_center() def move(self, end): if self.editable: shift = end[0:2] - self.drag_reference self.set_center(shift) def update_from_controlpoints(self): None @property def selectable(self): return self._selectable @selectable.setter def selectable(self, val): self._selectable = val @property def center(self): return self.control_points.get_center() @center.setter # this method redirects to set_center. Override set_center in subclasses. def center(self, val): self.set_center(val) # override this method in subclass def set_center(self, val): self.control_points.set_center(val[0:2]) def select_creation_controlpoint(self): self.control_points.select(True, self.control_points.control_points[2]) class EditRectVisual(EditVisual): def __init__(self, center=[0, 0], width=20, height=20, *args, **kwargs): EditVisual.__init__(self, *args, **kwargs) self.unfreeze() self.rect = scene.visuals.Rectangle(center=center, width=width, height=height, color=Color("#e88834"), border_color="white", radius=0, parent=self) self.rect.interactive = True self.freeze() self.add_subvisual(self.rect) self.control_points.update_bounds() self.control_points.visible(False) def set_center(self, val): self.control_points.set_center(val[0:2]) self.rect.center = val[0:2] def update_from_controlpoints(self): try: self.rect.width = abs(self.control_points._width) except ValueError: None try: self.rect.height = abs(self.control_points._height) except ValueError: None class EditEllipseVisual(EditVisual): def __init__(self, center=[0, 0], radius=[2, 2], *args, **kwargs): EditVisual.__init__(self, *args, **kwargs) self.unfreeze() self.ellipse = scene.visuals.Ellipse(center=center, radius=radius, color=Color("#e88834"), border_color="white", parent=self) self.ellipse.interactive = True self.freeze() self.add_subvisual(self.ellipse) self.control_points.update_bounds() self.control_points.visible(False) def set_center(self, val): self.control_points.set_center(val) self.ellipse.center = val def update_from_controlpoints(self): try: self.ellipse.radius = [0.5 * abs(self.control_points._width), 0.5 * abs(self.control_points._height)] except ValueError: None class Canvas(scene.SceneCanvas): """ A simple test canvas for drawing demo """ def __init__(self): scene.SceneCanvas.__init__(self, keys='interactive', size=(800, 800)) self.unfreeze() self.view = self.central_widget.add_view() self.view.camera = scene.PanZoomCamera(rect=(-100, -100, 200, 200), aspect=1.0) # the left mouse button pan has to be disabled in the camera, as it # interferes with dragging line points # Proposed change in camera: make mouse buttons configurable self.view.camera._viewbox.events.mouse_move.disconnect( self.view.camera.viewbox_mouse_event) scene.visuals.Text("Click and drag to add objects, " + "right-click to delete.", color='w', anchor_x='left', parent=self.view, pos=(20, 30)) self.select_arrow = \ EditVisual(parent=self.view, editable=False, on_select_callback=self.set_creation_mode, callback_argument=None) arrow = scene.visuals.Arrow(parent=self.select_arrow, pos=np.array([[50, 60], [60, 70]]), arrows=np.array([[60, 70, 50, 60]]), width=5, arrow_size=15.0, arrow_type="angle_60", color="w", arrow_color="w", method="agg" ) self.select_arrow.add_subvisual(arrow) self.rect_button = \ EditRectVisual(parent=self.view, editable=False, on_select_callback=self.set_creation_mode, callback_argument=EditRectVisual, center=[50, 120], width=30, height=30) self.ellipse_button = \ EditEllipseVisual(parent=self.view, editable=False, on_select_callback=self.set_creation_mode, callback_argument=EditEllipseVisual, center=[50, 170], radius=[15, 10]) self.objects = [] self.show() self.selected_point = None self.selected_object = None self.creation_mode = EditRectVisual self.mouse_start_pos = [0, 0] scene.visuals.GridLines(parent=self.view.scene) self.freeze() def set_creation_mode(self, object_kind): self.creation_mode = object_kind def on_mouse_press(self, event): tr = self.scene.node_transform(self.view.scene) pos = tr.map(event.pos) self.view.interactive = False selected = self.visual_at(event.pos) self.view.interactive = True if self.selected_object is not None: self.selected_object.select(False) self.selected_object = None if event.button == 1: if selected is not None: self.selected_object = selected.parent # update transform to selected object tr = self.scene.node_transform(self.selected_object) pos = tr.map(event.pos) self.selected_object.select(True, obj=selected) self.selected_object.start_move(pos) self.mouse_start_pos = event.pos # create new object: if self.selected_object is None and self.creation_mode is not None: # new_object = EditRectVisual(parent=self.view.scene) new_object = self.creation_mode(parent=self.view.scene) self.objects.append(new_object) new_object.select_creation_controlpoint() new_object.set_center(pos[0:2]) self.selected_object = new_object.control_points if event.button == 2: # right button deletes object if selected is not None and selected.parent in self.objects: self.objects.remove(selected.parent) selected.parent.parent = None self.selected_object = None def on_mouse_move(self, event): if event.button == 1: if self.selected_object is not None: self.view.camera._viewbox.events.mouse_move.disconnect( self.view.camera.viewbox_mouse_event) # update transform to selected object tr = self.scene.node_transform(self.selected_object) pos = tr.map(event.pos) self.selected_object.move(pos[0:2]) else: self.view.camera._viewbox.events.mouse_move.connect( self.view.camera.viewbox_mouse_event) else: None if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/scene/sensitivity.py0000644000175000017510000000256414507104216020164 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # vispy: gallery 2 """ Zoom Sensitivity ================ For testing zoom sensitivity on various platforms """ import numpy as np import vispy.scene from vispy.scene import visuals canvas = vispy.scene.SceneCanvas(keys='interactive', show=True) vb = canvas.central_widget.add_view() vb.camera = 'panzoom' vb.camera.rect = (-10, -10, 20, 20) centers = np.random.normal(size=(50, 2)) pos = np.random.normal(size=(100000, 2), scale=0.2) indexes = np.random.normal(size=100000, loc=centers.shape[0]/2., scale=centers.shape[0]/3.) indexes = np.clip(indexes, 0, centers.shape[0]-1).astype(int) scales = 10**(np.linspace(-2, 0.5, centers.shape[0]))[indexes][:, np.newaxis] pos *= scales pos += centers[indexes] scatter = visuals.Markers() scatter.set_gl_state('translucent', depth_test=False) scatter.set_data(pos, edge_width=0, face_color=(1, 1, 1, 0.3), size=5) vb.add(scatter) @canvas.connect def on_key_press(ev): if ev.key.name in '+=': vb.camera.zoom_factor *= 1.1 elif ev.key.name == '-': vb.camera.zoom_factor /= 1.1 print("Zoom factor: %0.4f" % vb.camera.zoom_factor) if __name__ == '__main__': import sys if sys.flags.interactive != 1: vispy.app.run() vispy-0.14.1/examples/scene/save_animation.py0000644000175000017510000000326514507104216020566 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery-exports animation.gif # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Create and Save Animation ========================= This example demonstrates how to create a sphere. """ import imageio from vispy import scene from vispy.visuals.transforms import STTransform output_filename = 'animation.gif' n_steps = 18 step_angle = 10. axis = [0, 0.707, 0.707] canvas = scene.SceneCanvas(keys='interactive', bgcolor='white', size=(800, 600), show=True) view = canvas.central_widget.add_view() view.camera = 'arcball' sphere1 = scene.visuals.Sphere(radius=1, method='latitude', parent=view.scene, edge_color='black') sphere2 = scene.visuals.Sphere(radius=1, method='ico', parent=view.scene, edge_color='black') sphere3 = scene.visuals.Sphere(radius=1, rows=10, cols=10, depth=10, method='cube', parent=view.scene, edge_color='black') sphere1.transform = STTransform(translate=[-2.5, 0, 0]) sphere3.transform = STTransform(translate=[2.5, 0, 0]) view.camera.set_range(x=[-3, 3]) writer = imageio.get_writer('animation.gif') for i in range(n_steps * 2): im = canvas.render(alpha=True) writer.append_data(im) if i >= n_steps: view.camera.transform.rotate(step_angle, axis) else: view.camera.transform.rotate(-step_angle, axis) writer.close() vispy-0.14.1/examples/scene/realtime_data/0000755000175000017510000000000014507104251020003 5ustar nileshnileshvispy-0.14.1/examples/scene/realtime_data/ex03c_data_sources_threaded_events.py0000644000175000017510000001614414507104216027266 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 10:120:10 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Update data using timer events in a background thread ===================================================== Update VisPy visualizations from timer events in a background QThread. """ import time from math import sin, pi import numpy as np from PyQt5 import QtWidgets, QtCore from vispy.scene import SceneCanvas, visuals from vispy.app import use_app IMAGE_SHAPE = (600, 800) # (height, width) CANVAS_SIZE = (800, 600) # (width, height) NUM_LINE_POINTS = 200 COLORMAP_CHOICES = ["viridis", "reds", "blues"] LINE_COLOR_CHOICES = ["black", "red", "blue"] class Controls(QtWidgets.QWidget): def __init__(self, parent=None): super().__init__(parent) layout = QtWidgets.QVBoxLayout() self.colormap_label = QtWidgets.QLabel("Image Colormap:") layout.addWidget(self.colormap_label) self.colormap_chooser = QtWidgets.QComboBox() self.colormap_chooser.addItems(COLORMAP_CHOICES) layout.addWidget(self.colormap_chooser) self.line_color_label = QtWidgets.QLabel("Line color:") layout.addWidget(self.line_color_label) self.line_color_chooser = QtWidgets.QComboBox() self.line_color_chooser.addItems(LINE_COLOR_CHOICES) layout.addWidget(self.line_color_chooser) layout.addStretch(1) self.setLayout(layout) class CanvasWrapper: def __init__(self): self.canvas = SceneCanvas(size=CANVAS_SIZE) self.grid = self.canvas.central_widget.add_grid() self.view_top = self.grid.add_view(0, 0, bgcolor='cyan') image_data = _generate_random_image_data(IMAGE_SHAPE) self.image = visuals.Image( image_data, texture_format="auto", cmap=COLORMAP_CHOICES[0], parent=self.view_top.scene, ) self.view_top.camera = "panzoom" self.view_top.camera.set_range(x=(0, IMAGE_SHAPE[1]), y=(0, IMAGE_SHAPE[0]), margin=0) self.view_bot = self.grid.add_view(1, 0, bgcolor='#c0c0c0') line_data = _generate_random_line_positions(NUM_LINE_POINTS) self.line = visuals.Line(line_data, parent=self.view_bot.scene, color=LINE_COLOR_CHOICES[0]) self.view_bot.camera = "panzoom" self.view_bot.camera.set_range(x=(0, NUM_LINE_POINTS), y=(0, 1)) def set_image_colormap(self, cmap_name: str): print(f"Changing image colormap to {cmap_name}") self.image.cmap = cmap_name def set_line_color(self, color): print(f"Changing line color to {color}") self.line.set_data(color=color) def update_data(self, new_data_dict): print("Updating data...") self.image.set_data(new_data_dict["image"]) self.line.set_data(new_data_dict["line"]) def _generate_random_image_data(shape, dtype=np.float32): rng = np.random.default_rng() data = rng.random(shape, dtype=dtype) return data def _generate_random_line_positions(num_points, dtype=np.float32): rng = np.random.default_rng() pos = np.empty((num_points, 2), dtype=np.float32) pos[:, 0] = np.arange(num_points) pos[:, 1] = rng.random((num_points,), dtype=dtype) return pos class MyMainWindow(QtWidgets.QMainWindow): closing = QtCore.pyqtSignal() def __init__(self, canvas_wrapper: CanvasWrapper, *args, **kwargs): super().__init__(*args, **kwargs) central_widget = QtWidgets.QWidget() main_layout = QtWidgets.QHBoxLayout() self._controls = Controls() main_layout.addWidget(self._controls) self._canvas_wrapper = canvas_wrapper main_layout.addWidget(self._canvas_wrapper.canvas.native) central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) self._connect_controls() def _connect_controls(self): self._controls.colormap_chooser.currentTextChanged.connect(self._canvas_wrapper.set_image_colormap) self._controls.line_color_chooser.currentTextChanged.connect(self._canvas_wrapper.set_line_color) def closeEvent(self, event): print("Closing main window!") self.closing.emit() return super().closeEvent(event) class DataSource(QtCore.QObject): """Object representing a complex data producer.""" new_data = QtCore.pyqtSignal(dict) finished = QtCore.pyqtSignal() def __init__(self, num_iterations=1000, parent=None): super().__init__(parent) self._should_end = False self._count = 0 self._num_iters = num_iterations self._image_data = _generate_random_image_data(IMAGE_SHAPE) self._line_data = _generate_random_line_positions(NUM_LINE_POINTS) def run_data_creation(self): if self._should_end or self._count >= self._num_iters: print("Data source finishing") self.finished.emit() return time.sleep(1.0) image_data = self._update_image_data(self._count) line_data = self._update_line_data(self._count) self._count += 1 data_dict = { "image": image_data, "line": line_data, } self.new_data.emit(data_dict) QtCore.QTimer.singleShot(0, self.run_data_creation) def _update_image_data(self, count): img_count = count % IMAGE_SHAPE[1] self._image_data[:, img_count] = img_count / IMAGE_SHAPE[1] rdata_shape = (IMAGE_SHAPE[0], IMAGE_SHAPE[1] - img_count - 1) self._image_data[:, img_count + 1:] = _generate_random_image_data(rdata_shape) return self._image_data.copy() def _update_line_data(self, count): self._line_data[:, 1] = np.roll(self._line_data[:, 1], -1) self._line_data[-1, 1] = abs(sin((count / self._num_iters) * 16 * pi)) return self._line_data def stop_data(self): print("Data source is quitting...") self._should_end = True if __name__ == "__main__": app = use_app("pyqt5") app.create() canvas_wrapper = CanvasWrapper() win = MyMainWindow(canvas_wrapper) data_thread = QtCore.QThread(parent=win) data_source = DataSource() data_source.moveToThread(data_thread) # update the visualization when there is new data data_source.new_data.connect(canvas_wrapper.update_data) # start data generation when the thread is started data_thread.started.connect(data_source.run_data_creation) # if the data source finishes before the window is closed, kill the thread # to clean up resources data_source.finished.connect(data_thread.quit) # if the window is closed, tell the data source to stop win.closing.connect(data_source.stop_data) # when the thread has ended, delete the data source from memory data_thread.finished.connect(data_source.deleteLater) win.show() data_thread.start() app.run() print("Waiting for data source to close gracefully...") data_thread.quit() data_thread.wait(5000) vispy-0.14.1/examples/scene/realtime_data/ex03b_data_sources_threaded_loop.py0000644000175000017510000001630614507104216026732 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 10:120:10 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Update data using a loop in a background thread =============================================== Update VisPy visualizations from a background QThread. """ import time from math import sin, pi import numpy as np from PyQt5 import QtWidgets, QtCore from vispy.scene import SceneCanvas, visuals from vispy.app import use_app IMAGE_SHAPE = (600, 800) # (height, width) CANVAS_SIZE = (800, 600) # (width, height) NUM_LINE_POINTS = 200 COLORMAP_CHOICES = ["viridis", "reds", "blues"] LINE_COLOR_CHOICES = ["black", "red", "blue"] class Controls(QtWidgets.QWidget): def __init__(self, parent=None): super().__init__(parent) layout = QtWidgets.QVBoxLayout() self.colormap_label = QtWidgets.QLabel("Image Colormap:") layout.addWidget(self.colormap_label) self.colormap_chooser = QtWidgets.QComboBox() self.colormap_chooser.addItems(COLORMAP_CHOICES) layout.addWidget(self.colormap_chooser) self.line_color_label = QtWidgets.QLabel("Line color:") layout.addWidget(self.line_color_label) self.line_color_chooser = QtWidgets.QComboBox() self.line_color_chooser.addItems(LINE_COLOR_CHOICES) layout.addWidget(self.line_color_chooser) layout.addStretch(1) self.setLayout(layout) class CanvasWrapper: def __init__(self): self.canvas = SceneCanvas(size=CANVAS_SIZE) self.grid = self.canvas.central_widget.add_grid() self.view_top = self.grid.add_view(0, 0, bgcolor='cyan') image_data = _generate_random_image_data(IMAGE_SHAPE) self.image = visuals.Image( image_data, texture_format="auto", cmap=COLORMAP_CHOICES[0], parent=self.view_top.scene, ) self.view_top.camera = "panzoom" self.view_top.camera.set_range(x=(0, IMAGE_SHAPE[1]), y=(0, IMAGE_SHAPE[0]), margin=0) self.view_bot = self.grid.add_view(1, 0, bgcolor='#c0c0c0') line_data = _generate_random_line_positions(NUM_LINE_POINTS) self.line = visuals.Line(line_data, parent=self.view_bot.scene, color=LINE_COLOR_CHOICES[0]) self.view_bot.camera = "panzoom" self.view_bot.camera.set_range(x=(0, NUM_LINE_POINTS), y=(0, 1)) def set_image_colormap(self, cmap_name: str): print(f"Changing image colormap to {cmap_name}") self.image.cmap = cmap_name def set_line_color(self, color): print(f"Changing line color to {color}") self.line.set_data(color=color) def update_data(self, new_data_dict): print("Updating data...") self.image.set_data(new_data_dict["image"]) self.line.set_data(new_data_dict["line"]) def _generate_random_image_data(shape, dtype=np.float32): rng = np.random.default_rng() data = rng.random(shape, dtype=dtype) return data def _generate_random_line_positions(num_points, dtype=np.float32): rng = np.random.default_rng() pos = np.empty((num_points, 2), dtype=np.float32) pos[:, 0] = np.arange(num_points) pos[:, 1] = rng.random((num_points,), dtype=dtype) return pos class MyMainWindow(QtWidgets.QMainWindow): closing = QtCore.pyqtSignal() def __init__(self, canvas_wrapper: CanvasWrapper, *args, **kwargs): super().__init__(*args, **kwargs) central_widget = QtWidgets.QWidget() main_layout = QtWidgets.QHBoxLayout() self._controls = Controls() main_layout.addWidget(self._controls) self._canvas_wrapper = canvas_wrapper main_layout.addWidget(self._canvas_wrapper.canvas.native) central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) self._connect_controls() def _connect_controls(self): self._controls.colormap_chooser.currentTextChanged.connect(self._canvas_wrapper.set_image_colormap) self._controls.line_color_chooser.currentTextChanged.connect(self._canvas_wrapper.set_line_color) def closeEvent(self, event): print("Closing main window!") self.closing.emit() return super().closeEvent(event) class DataSource(QtCore.QObject): """Object representing a complex data producer.""" new_data = QtCore.pyqtSignal(dict) finished = QtCore.pyqtSignal() def __init__(self, num_iterations=1000, parent=None): super().__init__(parent) self._should_end = False self._num_iters = num_iterations self._image_data = _generate_random_image_data(IMAGE_SHAPE) self._line_data = _generate_random_line_positions(NUM_LINE_POINTS) def run_data_creation(self): print("Run data creation is starting") for count in range(self._num_iters): if self._should_end: print("Data source saw that it was told to stop") break time.sleep(1.0) # Uncomment to mimic a long-running computation # time.sleep(3) image_data = self._update_image_data(count) line_data = self._update_line_data(count) data_dict = { "image": image_data, "line": line_data, } self.new_data.emit(data_dict) print("Data source finishing") self.finished.emit() def _update_image_data(self, count): img_count = count % IMAGE_SHAPE[1] self._image_data[:, img_count] = img_count / IMAGE_SHAPE[1] rdata_shape = (IMAGE_SHAPE[0], IMAGE_SHAPE[1] - img_count - 1) self._image_data[:, img_count + 1:] = _generate_random_image_data(rdata_shape) return self._image_data.copy() def _update_line_data(self, count): self._line_data[:, 1] = np.roll(self._line_data[:, 1], -1) self._line_data[-1, 1] = abs(sin((count / self._num_iters) * 16 * pi)) return self._line_data def stop_data(self): print("Data source is quitting...") self._should_end = True if __name__ == "__main__": app = use_app("pyqt5") app.create() canvas_wrapper = CanvasWrapper() win = MyMainWindow(canvas_wrapper) data_thread = QtCore.QThread(parent=win) data_source = DataSource() data_source.moveToThread(data_thread) # update the visualization when there is new data data_source.new_data.connect(canvas_wrapper.update_data) # start data generation when the thread is started data_thread.started.connect(data_source.run_data_creation) # if the data source finishes before the window is closed, kill the thread data_source.finished.connect(data_thread.quit, QtCore.Qt.DirectConnection) # if the window is closed, tell the data source to stop win.closing.connect(data_source.stop_data, QtCore.Qt.DirectConnection) # when the thread has ended, delete the data source from memory data_thread.finished.connect(data_source.deleteLater) win.show() data_thread.start() app.run() print("Waiting for data source to close gracefully...") data_thread.wait(5000) vispy-0.14.1/examples/scene/realtime_data/ex03a_data_sources_timer.py0000644000175000017510000002050614507104216025235 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 10:120:10 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Update data using timer-based events ==================================== Update VisPy visualizations from a QTimer in the main GUI thread. Data Source ----------- The important part of this script compared to the previous examples is the ``DataSource`` class. This class generates new image data in an event-based workflow instead of a ``for`` or ``while`` loop. The events in this case are generated by a QTimer in the main part of the script. The data generation being event-based works well with Qt's event loop to avoid blocking the main GUI thread in basic cases (see below). Note that this class sends the same numpy array instance every iteration to avoid creating unnecessary copies of the data. This should not cause issues with Qt or VisPy which will typically not modify data and will copy arrays if necessary. The ``run_data_creation`` method includes a commented piece of code to sleep for a small but significant amount of time. This is to demonstrate that this Timer-based data generation will block the main GUI thread and affect user experience if data generation is not fast. This may also occur if the data generated is large. Examples of data generation that would not be well-suited for this would be those that require contacting a remote resource (database, website, instrument, etc) or a complex algorithm. The ``run_data_creation`` uses a Qt signal to notify the VisPy Canvas wrapper of new data with which to update the visualization. The function/method being connected to the signal is called a "slot". To make use of signals and slots requires ``DataSource`` to be a subclass of ``QObject``. This is also needed if you plan on using the class with a ``QThread`` in the future. Timer ----- This script uses a ``QTimer`` object to trigger the data creation method every N seconds (1.0 seconds by default). If the interval is set to "auto" the timer will trigger as fast as possible. Other Options ------------- If this script performs fast enough for your use case and on your users systems then using a QTimer may be the easiest option for generating realtime data for your visualization. If your workflow does not fit well into this structure then a threaded option is likely your best bet. See the other data source scripts in this section of the gallery for other examples. """ import time # noqa from math import sin, pi import numpy as np from PyQt5 import QtWidgets, QtCore from vispy.scene import SceneCanvas, visuals from vispy.app import use_app, Timer IMAGE_SHAPE = (600, 800) # (height, width) CANVAS_SIZE = (800, 600) # (width, height) NUM_LINE_POINTS = 200 COLORMAP_CHOICES = ["viridis", "reds", "blues"] LINE_COLOR_CHOICES = ["black", "red", "blue"] class Controls(QtWidgets.QWidget): def __init__(self, parent=None): super().__init__(parent) layout = QtWidgets.QVBoxLayout() self.colormap_label = QtWidgets.QLabel("Image Colormap:") layout.addWidget(self.colormap_label) self.colormap_chooser = QtWidgets.QComboBox() self.colormap_chooser.addItems(COLORMAP_CHOICES) layout.addWidget(self.colormap_chooser) self.line_color_label = QtWidgets.QLabel("Line color:") layout.addWidget(self.line_color_label) self.line_color_chooser = QtWidgets.QComboBox() self.line_color_chooser.addItems(LINE_COLOR_CHOICES) layout.addWidget(self.line_color_chooser) layout.addStretch(1) self.setLayout(layout) class CanvasWrapper: def __init__(self): self.canvas = SceneCanvas(size=CANVAS_SIZE) self.grid = self.canvas.central_widget.add_grid() self.view_top = self.grid.add_view(0, 0, bgcolor='cyan') image_data = _generate_random_image_data(IMAGE_SHAPE) self.image = visuals.Image( image_data, texture_format="auto", cmap=COLORMAP_CHOICES[0], parent=self.view_top.scene, ) self.view_top.camera = "panzoom" self.view_top.camera.set_range(x=(0, IMAGE_SHAPE[1]), y=(0, IMAGE_SHAPE[0]), margin=0) self.view_bot = self.grid.add_view(1, 0, bgcolor='#c0c0c0') line_data = _generate_random_line_positions(NUM_LINE_POINTS) self.line = visuals.Line(line_data, parent=self.view_bot.scene, color=LINE_COLOR_CHOICES[0]) self.view_bot.camera = "panzoom" self.view_bot.camera.set_range(x=(0, NUM_LINE_POINTS), y=(0, 1)) def set_image_colormap(self, cmap_name: str): print(f"Changing image colormap to {cmap_name}") self.image.cmap = cmap_name def set_line_color(self, color): print(f"Changing line color to {color}") self.line.set_data(color=color) def update_data(self, new_data_dict): print("Updating data...") self.image.set_data(new_data_dict["image"]) self.line.set_data(new_data_dict["line"]) def _generate_random_image_data(shape, dtype=np.float32): rng = np.random.default_rng() data = rng.random(shape, dtype=dtype) return data def _generate_random_line_positions(num_points, dtype=np.float32): rng = np.random.default_rng() pos = np.empty((num_points, 2), dtype=np.float32) pos[:, 0] = np.arange(num_points) pos[:, 1] = rng.random((num_points,), dtype=dtype) return pos class MyMainWindow(QtWidgets.QMainWindow): def __init__(self, canvas_wrapper: CanvasWrapper, *args, **kwargs): super().__init__(*args, **kwargs) central_widget = QtWidgets.QWidget() main_layout = QtWidgets.QHBoxLayout() self._controls = Controls() main_layout.addWidget(self._controls) self._canvas_wrapper = canvas_wrapper main_layout.addWidget(self._canvas_wrapper.canvas.native) central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) self._connect_controls() def _connect_controls(self): self._controls.colormap_chooser.currentTextChanged.connect(self._canvas_wrapper.set_image_colormap) self._controls.line_color_chooser.currentTextChanged.connect(self._canvas_wrapper.set_line_color) class DataSource(QtCore.QObject): """Object representing a complex data producer.""" new_data = QtCore.pyqtSignal(dict) def __init__(self, num_iterations=1000, parent=None): super().__init__(parent) self._count = 0 self._num_iters = num_iterations self._image_data = _generate_random_image_data(IMAGE_SHAPE) self._line_data = _generate_random_line_positions(NUM_LINE_POINTS) def run_data_creation(self, timer_event): if self._count >= self._num_iters: return # Uncomment to mimic a long-running computation # time.sleep(3) image_data = self._update_image_data(self._count) line_data = self._update_line_data(self._count) self._count += 1 data_dict = { "image": image_data, "line": line_data, } self.new_data.emit(data_dict) def _update_image_data(self, count): img_count = count % IMAGE_SHAPE[1] self._image_data[:, img_count] = img_count / IMAGE_SHAPE[1] rdata_shape = (IMAGE_SHAPE[0], IMAGE_SHAPE[1] - img_count - 1) self._image_data[:, img_count + 1:] = _generate_random_image_data(rdata_shape) return self._image_data.copy() def _update_line_data(self, count): self._line_data[:, 1] = np.roll(self._line_data[:, 1], -1) self._line_data[-1, 1] = abs(sin((count / self._num_iters) * 16 * pi)) return self._line_data if __name__ == "__main__": app = use_app("pyqt5") app.create() data_source = DataSource() canvas_wrapper = CanvasWrapper() win = MyMainWindow(canvas_wrapper) data_source.new_data.connect(canvas_wrapper.update_data) # Change "1.0" to "auto" to run connected function as quickly as possible timer = Timer("1.0", connect=data_source.run_data_creation, start=True) # stop the timer when the window is closed and destroyed # not always needed, but needed for vispy gallery creation win.destroyed.connect(timer.stop) win.show() app.run() vispy-0.14.1/examples/scene/realtime_data/ex02_control_vispy_from_qt.py0000644000175000017510000001001614507104216025653 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Control VisPy from Qt ===================== Control a VisPy visualization with Qt-based (PyQt5) UI elements. """ import numpy as np from PyQt5 import QtWidgets from vispy.scene import SceneCanvas, visuals from vispy.app import use_app IMAGE_SHAPE = (600, 800) # (height, width) CANVAS_SIZE = (800, 600) # (width, height) NUM_LINE_POINTS = 200 COLORMAP_CHOICES = ["viridis", "reds", "blues"] LINE_COLOR_CHOICES = ["black", "red", "blue"] class MyMainWindow(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) central_widget = QtWidgets.QWidget() main_layout = QtWidgets.QHBoxLayout() self._controls = Controls() main_layout.addWidget(self._controls) self._canvas_wrapper = CanvasWrapper() main_layout.addWidget(self._canvas_wrapper.canvas.native) central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) self._connect_controls() def _connect_controls(self): self._controls.colormap_chooser.currentTextChanged.connect(self._canvas_wrapper.set_image_colormap) self._controls.line_color_chooser.currentTextChanged.connect(self._canvas_wrapper.set_line_color) class Controls(QtWidgets.QWidget): def __init__(self, parent=None): super().__init__(parent) layout = QtWidgets.QVBoxLayout() self.colormap_label = QtWidgets.QLabel("Image Colormap:") layout.addWidget(self.colormap_label) self.colormap_chooser = QtWidgets.QComboBox() self.colormap_chooser.addItems(COLORMAP_CHOICES) layout.addWidget(self.colormap_chooser) self.line_color_label = QtWidgets.QLabel("Line color:") layout.addWidget(self.line_color_label) self.line_color_chooser = QtWidgets.QComboBox() self.line_color_chooser.addItems(LINE_COLOR_CHOICES) layout.addWidget(self.line_color_chooser) layout.addStretch(1) self.setLayout(layout) class CanvasWrapper: def __init__(self): self.canvas = SceneCanvas(size=CANVAS_SIZE) self.grid = self.canvas.central_widget.add_grid() self.view_top = self.grid.add_view(0, 0, bgcolor='cyan') image_data = _generate_random_image_data(IMAGE_SHAPE) self.image = visuals.Image( image_data, texture_format="auto", cmap=COLORMAP_CHOICES[0], parent=self.view_top.scene, ) self.view_top.camera = "panzoom" self.view_top.camera.set_range(x=(0, IMAGE_SHAPE[1]), y=(0, IMAGE_SHAPE[0]), margin=0) self.view_bot = self.grid.add_view(1, 0, bgcolor='#c0c0c0') line_data = _generate_random_line_positions(NUM_LINE_POINTS) self.line = visuals.Line(line_data, parent=self.view_bot.scene, color=LINE_COLOR_CHOICES[0]) self.view_bot.camera = "panzoom" self.view_bot.camera.set_range(x=(0, NUM_LINE_POINTS), y=(0, 1)) def set_image_colormap(self, cmap_name: str): print(f"Changing image colormap to {cmap_name}") self.image.cmap = cmap_name def set_line_color(self, color): print(f"Changing line color to {color}") self.line.set_data(color=color) def _generate_random_image_data(shape, dtype=np.float32): rng = np.random.default_rng() data = rng.random(shape, dtype=dtype) return data def _generate_random_line_positions(num_points, dtype=np.float32): rng = np.random.default_rng() pos = np.empty((num_points, 2), dtype=np.float32) pos[:, 0] = np.arange(num_points) pos[:, 1] = rng.random((num_points,), dtype=dtype) return pos if __name__ == "__main__": app = use_app("pyqt5") app.create() win = MyMainWindow() win.show() app.run() vispy-0.14.1/examples/scene/realtime_data/ex01_embedded_vispy.py0000644000175000017510000000656014507104216024205 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Embed VisPy into Qt =================== Display VisPy visualizations in a PyQt5 application. """ import numpy as np from PyQt5 import QtWidgets from vispy.scene import SceneCanvas, visuals from vispy.app import use_app IMAGE_SHAPE = (600, 800) # (height, width) CANVAS_SIZE = (800, 600) # (width, height) NUM_LINE_POINTS = 200 class MyMainWindow(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) central_widget = QtWidgets.QWidget() main_layout = QtWidgets.QHBoxLayout() self._controls = Controls() main_layout.addWidget(self._controls) self._canvas_wrapper = CanvasWrapper() main_layout.addWidget(self._canvas_wrapper.canvas.native) central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) class Controls(QtWidgets.QWidget): def __init__(self, parent=None): super().__init__(parent) layout = QtWidgets.QVBoxLayout() self.colormap_label = QtWidgets.QLabel("Image Colormap:") layout.addWidget(self.colormap_label) self.colormap_chooser = QtWidgets.QComboBox() self.colormap_chooser.addItems(["viridis", "reds", "blues"]) layout.addWidget(self.colormap_chooser) self.line_color_label = QtWidgets.QLabel("Line color:") layout.addWidget(self.line_color_label) self.line_color_chooser = QtWidgets.QComboBox() self.line_color_chooser.addItems(["black", "red", "blue"]) layout.addWidget(self.line_color_chooser) layout.addStretch(1) self.setLayout(layout) class CanvasWrapper: def __init__(self): self.canvas = SceneCanvas(size=CANVAS_SIZE) self.grid = self.canvas.central_widget.add_grid() self.view_top = self.grid.add_view(0, 0, bgcolor='cyan') image_data = _generate_random_image_data(IMAGE_SHAPE) self.image = visuals.Image( image_data, texture_format="auto", cmap="viridis", parent=self.view_top.scene, ) self.view_top.camera = "panzoom" self.view_top.camera.set_range(x=(0, IMAGE_SHAPE[1]), y=(0, IMAGE_SHAPE[0]), margin=0) self.view_bot = self.grid.add_view(1, 0, bgcolor='#c0c0c0') line_data = _generate_random_line_positions(NUM_LINE_POINTS) self.line = visuals.Line(line_data, parent=self.view_bot.scene, color='black') self.view_bot.camera = "panzoom" self.view_bot.camera.set_range(x=(0, NUM_LINE_POINTS), y=(0, 1)) def _generate_random_image_data(shape, dtype=np.float32): rng = np.random.default_rng() data = rng.random(shape, dtype=dtype) return data def _generate_random_line_positions(num_points, dtype=np.float32): rng = np.random.default_rng() pos = np.empty((num_points, 2), dtype=np.float32) pos[:, 0] = np.arange(num_points) pos[:, 1] = rng.random((num_points,), dtype=dtype) return pos if __name__ == "__main__": app = use_app("pyqt5") app.create() win = MyMainWindow() win.show() app.run() vispy-0.14.1/examples/scene/realtime_data/README.rst0000644000175000017510000000211014507104216021465 0ustar nileshnileshRealtime Data Tutorial ====================== Examples that progressively build a Qt-based visualization application with updating data. The data in this tutorial is artificial, but is created and used in a way resembling real world data streams. In early examples data is created in the main GUI thread, but creation is later moved to an external thread to promote better responsiveness from the GUI. Each example is a self-contained working application in some sense and can be used as a reference for the particular feature it is demonstrating. However, each example builds on the example before it so features and vispy application best practices are improved at the cost of more complex code. Lastly, these examples use PySide2, but the application structure and demonstrated concepts should apply and be transferable to other backends (especially the Qt ones) with only a few exceptions. At the time of writing PySide2 is the newest version of PySide available through conda-forge conda channels. If/when PySide6 is available, pull requests to update these examples would be welcome. ;) vispy-0.14.1/examples/scene/polygon.py0000644000175000017510000000343214507104216017254 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # vispy: gallery 2 """ Shape Visuals ============= Demonstration of PolygonVisual, EllipseVisual, RectangleVisual and RegularPolygon """ from vispy import app import sys from vispy.scene import SceneCanvas from vispy.scene.visuals import Polygon, Ellipse, Rectangle, RegularPolygon from vispy.color import Color white = Color("#ecf0f1") gray = Color("#121212") red = Color("#e74c3c") blue = Color("#2980b9") orange = Color("#e88834") canvas = SceneCanvas(keys='interactive', title='Polygon Example', show=True) v = canvas.central_widget.add_view() v.bgcolor = gray v.camera = 'panzoom' cx, cy = (0.2, 0.2) halfx, halfy = (0.1, 0.1) poly_coords = [(cx - halfx, cy - halfy), (cx + halfx, cy - halfy), (cx + halfx, cy + halfy), (cx - halfx, cy + halfy)] poly = Polygon(poly_coords, color=red, border_color=white, border_width=3, parent=v.scene) ellipse = Ellipse(center=(0.4, 0.2), radius=(0.1, 0.05), color=blue, border_width=2, border_color=white, num_segments=1, parent=v.scene) ellipse.num_segments = 10 ellipse.start_angle = 0 ellipse.span_angle = 120 rect = Rectangle(center=(0.6, 0.2), width=0.1, height=0.2, color=orange, border_color=white, radius=0.02, parent=v.scene) regular_poly = RegularPolygon(center=(0.8, 0.2), radius=0.1, sides=6, color=blue, border_color=white, border_width=2, parent=v.scene) if __name__ == '__main__': if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/scene/point_cloud.py0000644000175000017510000000306414507104216020105 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 10 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Create a Point Cloud ==================== Demonstrates use of visual.Markers to create a point cloud with a standard turntable camera to fly around with and a centered 3D Axis. """ import numpy as np import vispy.scene from vispy.scene import visuals # # Make a canvas and add simple view # canvas = vispy.scene.SceneCanvas(keys='interactive', show=True) view = canvas.central_widget.add_view() # generate data pos = np.random.normal(size=(100000, 3), scale=0.2) # one could stop here for the data generation, the rest is just to make the # data look more interesting. Copied over from magnify.py centers = np.random.normal(size=(50, 3)) indexes = np.random.normal(size=100000, loc=centers.shape[0] / 2, scale=centers.shape[0] / 3) indexes = np.clip(indexes, 0, centers.shape[0] - 1).astype(int) symbols = np.random.choice(['o', '^'], len(pos)) scales = 10**(np.linspace(-2, 0.5, centers.shape[0]))[indexes][:, np.newaxis] pos *= scales pos += centers[indexes] # create scatter object and fill in the data scatter = visuals.Markers() scatter.set_data(pos, edge_width=0, face_color=(1, 1, 1, .5), size=5, symbol=symbols) view.add(scatter) view.camera = 'turntable' # or try 'arcball' # add a colored 3D axis for orientation axis = visuals.XYZAxis(parent=view.scene) if __name__ == '__main__': import sys if sys.flags.interactive != 1: vispy.app.run() vispy-0.14.1/examples/scene/one_scene_four_cams.py0000644000175000017510000000334214507104216021561 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 2 """ Single Scene in Multiple View boxes =================================== Demonstrating a single scene that is shown in four different viewboxes, each with a different camera. Note: This example just creates four scenes using the same visual. Multiple views are currently not available. See #1124 how this could be achieved. """ import sys from vispy import app, scene, io canvas = scene.SceneCanvas(keys='interactive') canvas.size = 800, 600 canvas.show() # Create four ViewBoxes vb1 = scene.widgets.ViewBox(border_color='white', parent=canvas.scene) vb2 = scene.widgets.ViewBox(border_color='white', parent=canvas.scene) vb3 = scene.widgets.ViewBox(border_color='white', parent=canvas.scene) vb4 = scene.widgets.ViewBox(border_color='white', parent=canvas.scene) scenes = vb1.scene, vb2.scene, vb3.scene, vb4.scene # Put viewboxes in a grid grid = canvas.central_widget.add_grid() grid.padding = 6 grid.add_widget(vb1, 0, 0) grid.add_widget(vb2, 0, 1) grid.add_widget(vb3, 1, 0) grid.add_widget(vb4, 1, 1) # Create some visuals to show im1 = io.load_crate().astype('float32') / 255 for par in scenes: image = scene.visuals.Image(im1, grid=(20, 20), parent=par) # Assign cameras vb1.camera = scene.BaseCamera() vb2.camera = scene.PanZoomCamera() vb3.camera = scene.TurntableCamera() vb4.camera = scene.FlyCamera() if __name__ == '__main__': if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/scene/one_cam_two_scenes.py0000644000175000017510000000326014507104216021416 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 2 """ Share Camera Views ================== Demonstrating two scenes that share the same camera view by linking the cameras. """ import numpy as np from vispy import app, scene, io canvas = scene.SceneCanvas(keys='interactive') canvas.size = 800, 600 canvas.show() # Create two ViewBoxes, place side-by-side vb1 = scene.widgets.ViewBox(border_color='yellow', parent=canvas.scene) vb2 = scene.widgets.ViewBox(border_color='blue', parent=canvas.scene) # grid = canvas.central_widget.add_grid() grid.padding = 6 grid.add_widget(vb1, 0, 0) grid.add_widget(vb2, 0, 1) # Create the image im1 = io.load_crate().astype('float32') / 255 # Make gray, smooth, and take derivatives: edge enhancement im2 = im1[:, :, 1] im2 = (im2[1:-1, 1:-1] + im2[0:-2, 1:-1] + im2[2:, 1:-1] + im2[1:-1, 0:-2] + im2[1:-1, 2:]) / 5 im2 = 0.5 + (np.abs(im2[0:-2, 1:-1] - im2[1:-1, 1:-1]) + np.abs(im2[1:-1, 0:-2] - im2[1:-1, 1:-1])) image1 = scene.visuals.Image(im1, parent=vb1.scene) image2 = scene.visuals.Image(im2, parent=vb2.scene) # Set 2D camera (PanZoomCamera, TurnTableCamera) vb1.camera, vb2.camera = scene.PanZoomCamera(), scene.PanZoomCamera() vb1.camera.aspect = vb2.camera.aspect = 1 # no auto-scale vb1.camera.link(vb2.camera) # Set the view bounds to show the entire image with some padding vb1.camera.set_range() if __name__ == '__main__': app.run() vispy-0.14.1/examples/scene/nested_viewbox.py0000644000175000017510000001067314507104216020617 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 2 """ Nested Viewboxes ================ Simple test of nested viewboxes, demonstrating the three methods that can be used by a viewbox to provide clipping. In the root scene are two viewboxes: the left viewbox uses the 'viewport' clipping method and a PanZoomCamera, whereas the right viewbox uses the 'fbo' clipping method and a base Camera (null transform). Each of these viewboxes contains again two viewboxes, with the same differences. In this way we test embedding each type of viewbox inside each type. This is what it should look like: The plot line has a "marker" region on the left side that points in the +y direction. In pixel coordinates, this is normally expected to point downward (because the pixel y-axis points down). However, the default behavior for PanZoomCamera is to reverse its internal y-axis relative to its parent. +-----------------+-----------------+ | | vb1 uses | | vb2 uses | | | PanZoomCamera | | base Camera | | | (+y upward) | | (+y downward) | +=================+=================+ | | | | +y upward | +y upward | | | | +-----------------+-----------------+ | | | | +y downward | +y downward | | | | +-----------------+-----------------+ """ from __future__ import division import numpy as np from vispy import app from vispy import scene # gloo.gl.use('desktop debug') # Create lines for use in ndc and pixel coordinates N = 1000 color = np.ones((N, 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, N) color[:, 1] = color[::-1, 0] pos = np.empty((N, 2), np.float32) pos[:, 0] = np.linspace(0., 1., N) pos[:, 1] = np.random.normal(loc=0.5, scale=0.03, size=N) pos[N//2:N//2+20, 1] = 0.9 # So we can see which side is up # Create canvas canvas = scene.SceneCanvas(size=(800, 600), show=True, keys='interactive') # # Create viewboxes on left ... # w, h = canvas.size w2 = w / 2. h2 = h / 2. # left (+y up) vb1 = scene.widgets.ViewBox(parent=canvas.scene, name='vb1', margin=2, border_color='red') vb1.pos = 0, 0 vb1.size = w2, h vb1.camera = 'panzoom' vb1.camera.rect = (0, 0, 1, 1) vb1.camera.interactive = False # bottom-left (+y down) vb11 = scene.widgets.ViewBox(parent=vb1.scene, name='vb11', border_width=2e-3, margin=0.02, border_color='green') vb11.pos = 0, 0 vb11.size = 1, 0.5 vb11.camera = 'panzoom' vb11.camera.rect = (0, 0, 1, 1) line11 = scene.visuals.Line(pos=pos, color=color, method='gl', parent=vb11.scene) # top-left (+y up) vb12 = scene.widgets.ViewBox(parent=vb1.scene, name='vb12', border_width=2e-3, margin=0.02, border_color='blue') vb12.pos = 0, 0.5 vb12.size = 1, 0.5 vb12.camera = 'base' # use parent cs # vb12 does not apply any scaling, so we do that manually here to match vb11 line12 = scene.visuals.Line(pos=pos * [[1.0, 0.5]], color=color, method='gl', parent=vb12.scene) # # Create viewboxes on right ... # # right (+y down) vb2 = scene.widgets.ViewBox(parent=canvas.scene, name='vb2', margin=2, border_color='yellow') vb2.pos = w2, 0 vb2.size = w2, h vb2.camera = 'base' vb2.camera.interactive = False # top-right (+y up) vb21 = scene.widgets.ViewBox(parent=vb2.scene, name='vb21', margin=10, border_color='purple') vb21.pos = 0, 0 vb21.size = w2, h2 vb21.camera = 'panzoom' vb21.camera.rect = (0, 0, 1, 1) line21 = scene.visuals.Line(pos=pos, color=color, method='gl', parent=vb21.scene) # bottom-right (+y down) vb22 = scene.widgets.ViewBox(parent=vb2.scene, name='vb22', margin=10, border_color='teal') vb22.pos = 0, h2 vb22.size = w2, h2 vb22.camera = 'base' # use parent cs # vb22 does not apply any scaling, so we do that manually here to match vb21 line22 = scene.visuals.Line(pos=pos * [[w2, h2]], color=color, method='gl', parent=vb22.scene) if __name__ == '__main__': app.run() vispy-0.14.1/examples/scene/mesh_texture.py0000644000175000017510000000460414507104216020303 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Texture Filter on Meshes ======================== Show how to use the texture filter on meshes. """ import argparse import numpy as np from vispy import app, scene from vispy.io import imread, load_data_file, read_mesh from vispy.scene.visuals import Mesh from vispy.scene import transforms from vispy.visuals.filters import TextureFilter parser = argparse.ArgumentParser() parser.add_argument('--shading', default='smooth', choices=['none', 'flat', 'smooth'], help="shading mode") args, _ = parser.parse_known_args() mesh_path = load_data_file('spot/spot.obj.gz') texture_path = load_data_file('spot/spot.png') vertices, faces, normals, texcoords = read_mesh(mesh_path) texture = np.flipud(imread(texture_path)) canvas = scene.SceneCanvas(keys='interactive', bgcolor='white', size=(800, 600)) view = canvas.central_widget.add_view() view.camera = 'arcball' # Adapt the depth to the scale of the mesh to avoid rendering artefacts. view.camera.depth_value = 10 * (vertices.max() - vertices.min()) shading = None if args.shading == 'none' else args.shading mesh = Mesh(vertices, faces, shading=shading, color='white') mesh.transform = transforms.MatrixTransform() mesh.transform.rotate(90, (1, 0, 0)) mesh.transform.rotate(135, (0, 0, 1)) mesh.shading_filter.shininess = 1e+1 view.add(mesh) texture_filter = TextureFilter(texture, texcoords) mesh.attach(texture_filter) @canvas.events.key_press.connect def on_key_press(event): if event.key == "t": texture_filter.enabled = not texture_filter.enabled mesh.update() def attach_headlight(mesh, view, canvas): light_dir = (0, 1, 0, 0) mesh.shading_filter.light_dir = light_dir[:3] initial_light_dir = view.camera.transform.imap(light_dir) @view.scene.transform.changed.connect def on_transform_change(event): transform = view.camera.transform mesh.shading_filter.light_dir = transform.map(initial_light_dir)[:3] attach_headlight(mesh, view, canvas) canvas.show() if __name__ == "__main__": app.run() vispy-0.14.1/examples/scene/mesh_shading.py0000644000175000017510000000736014507104216020222 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Shading a Mesh ============== Show mesh filter usage for shading (lighting) a mesh and displaying a wireframe. """ import argparse from vispy import app, scene from vispy.io import read_mesh, load_data_file from vispy.scene.visuals import Mesh from vispy.scene import transforms from vispy.visuals.filters import ShadingFilter, WireframeFilter parser = argparse.ArgumentParser() default_mesh = load_data_file('orig/triceratops.obj.gz') parser.add_argument('--mesh', default=default_mesh) parser.add_argument('--shininess', default=100) parser.add_argument('--wireframe-width', default=1) args, _ = parser.parse_known_args() vertices, faces, normals, texcoords = read_mesh(args.mesh) canvas = scene.SceneCanvas(keys='interactive', bgcolor='white') view = canvas.central_widget.add_view() view.camera = 'arcball' view.camera.depth_value = 1e3 # Create a colored `MeshVisual`. mesh = Mesh(vertices, faces, color=(.5, .7, .5, 1)) mesh.transform = transforms.MatrixTransform() mesh.transform.rotate(90, (1, 0, 0)) mesh.transform.rotate(-45, (0, 0, 1)) view.add(mesh) # Use filters to affect the rendering of the mesh. wireframe_filter = WireframeFilter(width=args.wireframe_width) # Note: For convenience, this `ShadingFilter` would be created automatically by # the `MeshVisual with, e.g. `mesh = MeshVisual(..., shading='smooth')`. It is # created manually here for demonstration purposes. shading_filter = ShadingFilter(shininess=args.shininess) # The wireframe filter is attached before the shading filter otherwise the # wireframe is not shaded. mesh.attach(wireframe_filter) mesh.attach(shading_filter) def attach_headlight(view): light_dir = (0, 1, 0, 0) shading_filter.light_dir = light_dir[:3] initial_light_dir = view.camera.transform.imap(light_dir) @view.scene.transform.changed.connect def on_transform_change(event): transform = view.camera.transform shading_filter.light_dir = transform.map(initial_light_dir)[:3] attach_headlight(view) shading_states = ( dict(shading=None), dict(shading='flat'), dict(shading='smooth'), ) shading_state_index = shading_states.index( dict(shading=shading_filter.shading)) wireframe_states = ( dict(wireframe_only=False, faces_only=False,), dict(wireframe_only=True, faces_only=False,), dict(wireframe_only=False, faces_only=True,), ) wireframe_state_index = wireframe_states.index(dict( wireframe_only=wireframe_filter.wireframe_only, faces_only=wireframe_filter.faces_only, )) def cycle_state(states, index): new_index = (index + 1) % len(states) return states[new_index], new_index @canvas.events.key_press.connect def on_key_press(event): global shading_state_index global wireframe_state_index if event.key == 's': state, shading_state_index = cycle_state(shading_states, shading_state_index) for attr, value in state.items(): setattr(shading_filter, attr, value) mesh.update() elif event.key == 'w': wireframe_filter.enabled = not wireframe_filter.enabled mesh.update() elif event.key == 'f': state, wireframe_state_index = cycle_state(wireframe_states, wireframe_state_index) for attr, value in state.items(): setattr(wireframe_filter, attr, value) mesh.update() canvas.show() if __name__ == "__main__": app.run() vispy-0.14.1/examples/scene/mesh_normals.py0000644000175000017510000000323614507104216020256 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Display Mesh Normals ==================== Show how to display mesh normals on a mesh. """ from vispy import app, scene from vispy.io import read_mesh, load_data_file from vispy.scene.visuals import Mesh, MeshNormals from vispy.visuals.filters import WireframeFilter mesh_file = load_data_file('orig/triceratops.obj.gz') vertices, faces, _, _ = read_mesh(mesh_file) mesh = Mesh(vertices, faces, shading='flat') meshdata = mesh.mesh_data wireframe_filter = WireframeFilter(color='lightblue') mesh.attach(wireframe_filter) face_normals = MeshNormals(meshdata, primitive='face', color='yellow') vertex_normals = MeshNormals(meshdata, primitive='vertex', color='orange', width=2) canvas = scene.SceneCanvas(keys='interactive', bgcolor='white') view = canvas.central_widget.add_view() view.camera = 'arcball' view.add(mesh) face_normals.parent = mesh vertex_normals.parent = mesh @canvas.events.key_press.connect def on_key_press(event): if event.key == 'f': face_normals.visible = not face_normals.visible canvas.update() elif event.key == 'v': vertex_normals.visible = not vertex_normals.visible canvas.update() canvas.show() if __name__ == "__main__": print('Key bindings:') print(' f : toggle face normals') print(' v : toggle vertex normals') app.run() vispy-0.14.1/examples/scene/marker_spheres.py0000644000175000017510000000275414507104216020605 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Spheres and Sticks ================== Draw spherical Markers with Tube meshes connecting them. """ import numpy as np from vispy import app, scene # Create canvas and view canvas = scene.SceneCanvas(keys='interactive', size=(600, 600), show=True) view = canvas.central_widget.add_view() view.camera = scene.cameras.ArcballCamera(fov=0) view.camera.scale_factor = 500 # Prepare data np.random.seed(57983) data = np.random.normal(size=(40, 3), loc=0, scale=100) size = np.random.rand(40) * 100 colors = np.random.rand(40, 3) data = np.concatenate([data, [[0, 0, 0]]], axis=0) size = np.concatenate([size, [100]], axis=0) colors = np.concatenate([colors, [[1, 0, 0]]], axis=0) # Create and show visual vis = scene.visuals.Markers( pos=data, size=100, antialias=0, face_color=colors, edge_color='white', edge_width=0, scaling=True, spherical=True, ) vis.parent = view.scene lines = np.array([[data[i], data[-1]] for i in range(len(data) - 1)]) line_vis = [] for line in lines: vis2 = scene.visuals.Tube(line, radius=5) vis2.parent = view.scene line_vis.append(vis2) if __name__ == "__main__": app.run() vispy-0.14.1/examples/scene/marker_picking.py0000644000175000017510000001036514507104216020555 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Picking Markers =============== Demonstrates how to identify (pick) markers. Hover markers to change their symbol and color. Controls: * p - Toggle picking view - shows the colors encoding marker ID * r - Reset marker symbols and colors """ import random import time import numpy as np from scipy.constants import golden as GOLDEN from vispy import app, scene from vispy.scene.visuals import Markers from vispy.visuals.filters import MarkerPickingFilter canvas = scene.SceneCanvas(keys='interactive', bgcolor='black') view = canvas.central_widget.add_view(camera="panzoom") view.camera.rect = (-1, -1, 2, 2) # floret pattern n = 10_000 radius = np.linspace(0, 0.9, n)**0.6 # prevent extreme density at center theta = np.arange(n) * GOLDEN pos = np.column_stack([radius * np.cos(theta), radius * np.sin(theta)]) COLORS = [ (1, 0, 0, 1), # red (1, 0.5, 0, 1), # orange (1, 1, 0, 1), # yellow (0, 1, 0, 1), # green (0, 0, 1, 1), # blue (0.29, 0, 0.51, 1), # indigo (0.93, 0.51, 0.93, 1), # violet ] colors = np.zeros((n, 4), dtype=np.float32) colors[:, 0] = 1 # red colors[:, -1] = 1 # alpha _colors = colors.copy() symbols = list(Markers._symbol_shader_values.keys()) symbols_ring = dict(zip(symbols, symbols[1:])) symbols_ring[symbols[-1]] = symbols[0] EDGE_COLOR = "white" MARKER_SIZE = 0.0125 EDGE_WDITH = MARKER_SIZE / 10 markers = Markers( pos=pos, edge_color=EDGE_COLOR, face_color=colors, size=MARKER_SIZE, edge_width=EDGE_WDITH, scaling="scene", ) markers.update_gl_state(depth_test=True) view.add(markers) # Use filters to affect the rendering of the mesh. picking_filter = MarkerPickingFilter() markers.attach(picking_filter) @view.events.connect def on_viewbox_change(event): # workaround for vispy/#2501 markers.update_gl_state(blend=not picking_filter.enabled) throttle = time.monotonic() @canvas.events.mouse_move.connect def on_mouse_move(event): global throttle # throttle mouse events to 50ms if time.monotonic() - throttle < 0.05: return throttle = time.monotonic() # adjust the event position for hidpi screens render_size = tuple(d * canvas.pixel_scale for d in canvas.size) x_pos = event.pos[0] * canvas.pixel_scale y_pos = render_size[1] - (event.pos[1] * canvas.pixel_scale) # render a small patch around the mouse cursor restore_state = not picking_filter.enabled picking_filter.enabled = True markers.update_gl_state(blend=False) picking_render = canvas.render( crop=(x_pos - 2, y_pos - 2, 5, 5), bgcolor=(0, 0, 0, 0), alpha=True, ) if restore_state: picking_filter.enabled = False markers.update_gl_state(blend=not picking_filter.enabled) # unpack the face index from the color in the center pixel marker_idx = (picking_render.view(np.uint32) - 1)[2, 2, 0] if marker_idx >= 0 and marker_idx < len(pos): new_symbols = list(markers.symbol) new_symbol = symbols_ring[new_symbols[marker_idx]] new_symbols[marker_idx] = new_symbol colors[marker_idx] = random.choice(COLORS) markers.set_data( pos=pos, edge_color=EDGE_COLOR, face_color=colors, size=MARKER_SIZE, edge_width=EDGE_WDITH, symbol=new_symbols, ) @canvas.events.key_press.connect def on_key_press(event): global colors if event.key == 'p': # toggle face picking view picking_filter.enabled = not picking_filter.enabled markers.update_gl_state(blend=not picking_filter.enabled) markers.update() if event.key == 'r': # reset marker symbols colors = _colors.copy() markers.set_data( pos=pos, edge_color=EDGE_COLOR, face_color=colors, size=MARKER_SIZE, edge_width=EDGE_WDITH, ) canvas.show() if __name__ == "__main__": print(__doc__) app.run() vispy-0.14.1/examples/scene/magnify.py0000644000175000017510000000646114507104216017224 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 10 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Zoom in using MagnifyCamera =========================== Demonstrates use of special Camera subclasses to implement a (flashy) data-exploration tool. Here we use MagnifyCamera to allow the user to zoom in on a particular region of data, while also keeping the entire data set visible for reference. The MagnifyCamera classes are responsible for inserting MagnifyTransform at the transform of each viewbox scene, while also updating those transforms to respond to user input. """ import numpy as np import vispy.scene from vispy.scene import visuals from vispy.scene.cameras import MagnifyCamera, Magnify1DCamera from vispy.visuals.transforms import STTransform from vispy.util import filter # # Make a canvas and partition it into 3 viewboxes. # canvas = vispy.scene.SceneCanvas(keys='interactive', show=True) canvas._send_hover_events = True # temporary workaround grid = canvas.central_widget.add_grid() vb1 = grid.add_view(row=0, col=0, col_span=2) vb2 = grid.add_view(row=1, col=0) vb3 = grid.add_view(row=1, col=1) # # Top viewbox: Show a plot line containing fine structure with a 1D # magnification transform. # pos = np.empty((100000, 2)) pos[:, 0] = np.arange(100000) pos[:, 1] = np.random.normal(size=100000, loc=50, scale=10) pos[:, 1] = filter.gaussian_filter(pos[:, 1], 20) pos[:, 1] += np.random.normal(size=100000, loc=0, scale=2) pos[:, 1][pos[:, 1] > 55] += 100 pos[:, 1] = filter.gaussian_filter(pos[:, 1], 2) line = visuals.Line(pos, color='white', parent=vb1.scene) line.transform = STTransform(translate=(0, 0, -0.1)) grid1 = visuals.GridLines(parent=vb1.scene) vb1.camera = Magnify1DCamera(mag=4, size_factor=0.6, radius_ratio=0.6) vb1.camera.rect = 0, 30, 100000, 100 # # Bottom-left viewbox: Image with circular magnification lens. # size = (100, 100) img_data = np.random.normal(size=size+(3,), loc=58, scale=20).astype(np.ubyte) image = visuals.Image(img_data, parent=vb2.scene, method='impostor') vb2.camera = MagnifyCamera(mag=3, size_factor=0.3, radius_ratio=0.6) vb2.camera.rect = (-10, -10, size[0]+20, size[1]+20) # # Bottom-right viewbox: Scatter plot with many clusters of varying scale. # centers = np.random.normal(size=(50, 2)) pos = np.random.normal(size=(100000, 2), scale=0.2) indexes = np.random.normal(size=100000, loc=centers.shape[0]/2., scale=centers.shape[0]/3.) indexes = np.clip(indexes, 0, centers.shape[0]-1).astype(int) scales = 10**(np.linspace(-2, 0.5, centers.shape[0]))[indexes][:, np.newaxis] pos *= scales pos += centers[indexes] scatter = visuals.Markers() scatter.set_data(pos, edge_color=None, face_color=(1, 1, 1, 0.3), size=5) vb3.add(scatter) grid2 = visuals.GridLines(parent=vb3.scene) vb3.camera = MagnifyCamera(mag=3, size_factor=0.3, radius_ratio=0.9) vb3.camera.rect = (-5, -5, 10, 10) # Add helpful text text1 = visuals.Text("mouse wheel = magnify", pos=(100, 15), font_size=14, color='white', parent=canvas.scene) text2 = visuals.Text("right button = zoom", pos=(100, 30), font_size=14, color='white', parent=canvas.scene) if __name__ == '__main__': import sys if sys.flags.interactive != 1: vispy.app.run() vispy-0.14.1/examples/scene/linear_region.py0000644000175000017510000000460014507104216020400 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 5 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Draw an LinearRegion ==================== Demonstration of LinearRegion visual. Allows drawing of infinite horizontal or vertical region for 2D plots. """ import sys import numpy as np from vispy import app, scene # vertex positions of data to draw N = 200 pos = np.zeros((N, 2), dtype=np.float32) x_lim = [50., 750.] y_lim = [-2., 2.] pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N) pos[:, 1] = np.random.normal(size=N) # color array color = np.ones((N, 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, N) color[:, 1] = color[::-1, 0] canvas = scene.SceneCanvas(keys='interactive', show=True) grid = canvas.central_widget.add_grid(spacing=0) viewbox = grid.add_view(row=0, col=1, camera='panzoom') # add some axes x_axis = scene.AxisWidget(orientation='bottom') x_axis.stretch = (1, 0.1) grid.add_widget(x_axis, row=1, col=1) x_axis.link_view(viewbox) y_axis = scene.AxisWidget(orientation='left') y_axis.stretch = (0.1, 1) grid.add_widget(y_axis, row=0, col=0) y_axis.link_view(viewbox) # add a line plot inside the viewbox line = scene.Line(pos, color, parent=viewbox.scene) # add vertical lines color = np.array([[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 1.0, 1.0], [0.0, 1.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0]]) pos = np.array([100, 120, 140, 160, 180, 200], dtype=np.float32) vert_region1 = scene.LinearRegion(pos, color, parent=viewbox.scene) vert_region2 = scene.LinearRegion([549.2, 700], [0.0, 1.0, 0.0, 0.5], vertical=True, parent=viewbox.scene) # add horizontal lines pos = np.array([0.3, 0.0, -0.1], dtype=np.float32) hor_region1 = scene.LinearRegion(pos, [1.0, 0.0, 0.0, 0.5], vertical=False, parent=viewbox.scene) hor_region2 = scene.LinearRegion([-5.1, -2.0], [0.0, 0.0, 1.0, 0.5], vertical=False, parent=viewbox.scene) # auto-scale to see the whole line. viewbox.camera.set_range() if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/line_update.py0000644000175000017510000000304014507104216020051 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 5:10:1 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Animated Line Visual ==================== Demonstration of animated Line visual. """ import sys import numpy as np from vispy import app, scene # vertex positions of data to draw N = 200 pos = np.zeros((N, 2), dtype=np.float32) x_lim = [50., 750.] y_lim = [-2., 2.] pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N) pos[:, 1] = np.random.normal(size=N) # color array color = np.ones((N, 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, N) color[:, 1] = color[::-1, 0] canvas = scene.SceneCanvas(keys='interactive', show=True) grid = canvas.central_widget.add_grid(spacing=0) viewbox = grid.add_view(row=0, col=1, camera='panzoom') # add some axes x_axis = scene.AxisWidget(orientation='bottom') x_axis.stretch = (1, 0.1) grid.add_widget(x_axis, row=1, col=1) x_axis.link_view(viewbox) y_axis = scene.AxisWidget(orientation='left') y_axis.stretch = (0.1, 1) grid.add_widget(y_axis, row=0, col=0) y_axis.link_view(viewbox) # add a line plot inside the viewbox line = scene.Line(pos, color, parent=viewbox.scene) # auto-scale to see the whole line. viewbox.camera.set_range() def update(ev): global pos, color, line pos[:, 1] = np.random.normal(size=N) color = np.roll(color, 1, axis=0) line.set_data(pos=pos, color=color) timer = app.Timer() timer.connect(update) timer.start(0) if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/line.py0000644000175000017510000000262714507104216016521 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 5:20:2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Draw a Line =========== Simple demonstration of SceneCanvas containing a single line entity as its entire scenegraph. """ import sys import numpy as np from vispy import app, scene canvas = scene.SceneCanvas(size=(800, 600), keys='interactive') N = 1000 pos = np.empty((N, 2), np.float32) pos[:, 0] = np.linspace(50., 750., N) # color = np.ones((N, 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, N) color[:, 1] = color[::-1, 0] lines = [] print('Generating points...') for i in range(20): pos = pos.copy() pos[:, 1] = np.random.normal(scale=5, loc=(i+1)*30, size=N) line = scene.visuals.Line(pos=pos, color=color, parent=canvas.scene) lines.append(line) line.transform = scene.transforms.STTransform() print('Done') def update(event): for line in lines: scale = [np.sin(np.pi * event.elapsed)+2, np.cos(np.pi * event.elapsed)+2] line.transform.scale = scale timer = app.Timer('auto', connect=update, start=True) if __name__ == '__main__': canvas.show() if sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/lasso.py0000644000175000017510000001431314507104216016706 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Vispy Lasso =========== Demonstrate the use of lasso selection. The lasso selection is done on a 2D scatter but could be extended further by user. """ import sys import warnings import numpy as np from vispy import app, scene from vispy.geometry import curves from vispy.scene import visuals try: from matplotlib import path except ImportError: warnings.warn("Lasso example requires matplotlib for more accurate selection. Falling back to numpy based selection.") path = None LASSO_COLOR = (1, .1, .1) FILTERED_COLOR = (1, 1, 1, 0.3) SELECTED_COLOR = (0.3, 0, 1, 1.0) PEN_RADIUS = 2 MIN_MOVE_UPDATE_THRESHOLD = 5 NUMBER_POINT = 2000000 SCATTER_SIZE = 5 canvas = scene.SceneCanvas(keys='interactive', show=True) view = canvas.central_widget.add_view() pointer = scene.visuals.Ellipse(center=(0., 0.), radius=(PEN_RADIUS, PEN_RADIUS,), color=None, border_width=0.2, border_color="white", num_segments=10, parent=view.scene) lasso = scene.visuals.Line(pos=np.array([[0, 0], [0, 0]]), color = LASSO_COLOR, parent=view.scene, width = PEN_RADIUS , antialias=True) px, py = 0, 0 # generate data pos = 360 * np.random.normal(size=(NUMBER_POINT, 2), scale=1) # one could stop here for the data generation, the rest is just to make the # data look more interesting. Copied over from magnify.py centers = np.random.normal(size=(NUMBER_POINT, 2), scale = 1) * 960 indexes = np.random.normal(size=NUMBER_POINT, loc=centers.shape[0] / 2, scale=centers.shape[0] / 3) indexes = np.clip(indexes, 0, centers.shape[0] - 1).astype(int) pos += centers[indexes] # create scatter object and fill in the data scatter = visuals.Markers() point_color = np.full((NUMBER_POINT, 4), FILTERED_COLOR) selected_mask = np.full(NUMBER_POINT, False) scatter.set_data(pos, edge_width=0, face_color=point_color, size=SCATTER_SIZE) view.add(scatter) def points_in_polygon(polygon, pts): """Get boolean mask of points in a polygon reusing matplotlib implementation. The fallback code is based from StackOverflow answer by ``Ta946`` in this question: https://stackoverflow.com/questions/36399381/whats-the-fastest-way-of-checking-if-a-point-is-inside-a-polygon-in-python This is a proof of concept and depending on your use case, willingness to add other dependencies, and your performance needs one of the other answers on the above question would serve you better (ex. shapely, etc). """ # Filter vertices out of the polygon's bounding box, this serve as an early optimization whenever number of vertices # to filter out is huge. x1, x2, y1, y2 = min(polygon[:, 0]), max(polygon[:, 0]), min(polygon[:, 1]), max(polygon[:, 1]) selection_mask = (x1 < pts[:, 0]) & (pts[:, 0] < x2) & (y1 < pts[:, 1]) & (pts[:, 1] < y2) pts_in_bbox = pts[selection_mask] # Select vertices inside the polygon. if path is not None: polygon = path.Path(polygon[:, :2], closed = True) polygon_mask = polygon.contains_points(pts_in_bbox[:, :2]) else: contour2 = np.vstack((polygon[1:], polygon[:1])) test_diff = contour2-polygon m1 = (polygon[:,1] > pts_in_bbox[:,None,1]) != (contour2[:,1] > pts_in_bbox[:,None,1]) slope = ((pts_in_bbox[:,None,0]-polygon[:,0])*test_diff[:,1])-(test_diff[:,0]*(pts_in_bbox[:,None,1]-polygon[:,1])) m2 = slope == 0 mask2 = (m1 & m2).any(-1) m3 = (slope < 0) != (contour2[:,1] < polygon[:,1]) m4 = m1 & m3 count = np.count_nonzero(m4, axis=-1) mask3 = ~(count%2==0) polygon_mask = mask2 | mask3 # Return the full selection mask based on bounding box & polygon selection. selection_mask[np.where(selection_mask == True)] &= polygon_mask return selection_mask def select(polygon_vertices, points): # Set default mask to filter everything since user selection # is not yet calculated. selected_mask = np.full((NUMBER_POINT, 4), FILTERED_COLOR) if polygon_vertices is not None: # Optimization: It's faster to convert lasso selection straight to visual coordinates since there's generally less vertices # this would speed up the processing depending on the scene. polygon_vertices = scatter.get_transform('canvas', 'visual').map(polygon_vertices) selected_mask = points_in_polygon(polygon_vertices, points) return selected_mask @canvas.connect def on_mouse_press(event): global point_color, selected_mask if event.button == 1: # Reset lasso state. lasso.set_data(pos=np.empty((1, 2))) # Reset selected vertices to the filtered color, this would earn some time in case # scene contains a lot of vertices. point_color[selected_mask] = FILTERED_COLOR scatter.set_data(pos, edge_width=0, face_color=point_color, size=SCATTER_SIZE) @canvas.connect def on_mouse_move(event): global pointer, px, py pp = event.pos # Optimization: to avoid too much recalculation/update we can update scene only if the mouse # moved a certain amount of pixel. if (abs(px - pp[0]) > MIN_MOVE_UPDATE_THRESHOLD or abs(py - pp[1]) > MIN_MOVE_UPDATE_THRESHOLD): pointer.center = pp if event.button == 1: polygon_vertices = event.trail() lasso.set_data(pos = np.insert(polygon_vertices, len(polygon_vertices), polygon_vertices[0], axis=0)) px, py = pp @canvas.connect def on_mouse_release(event): global point_color, selected_mask if event.button == 1: selected_mask = select(event.trail(), pos) # Set selected points with selection color point_color[selected_mask] = SELECTED_COLOR scatter.set_data(pos, edge_width=0, face_color=point_color, size=SCATTER_SIZE) if __name__ == '__main__': canvas.show() if sys.flags.interactive == 0: app.run()vispy-0.14.1/examples/scene/isosurface.py0000644000175000017510000000350314507104216017727 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 2 """ Isosurface Visual ================= This example demonstrates the use of the Isosurface visual. """ import sys import numpy as np from vispy import app, scene # Create a canvas with a 3D viewport canvas = scene.SceneCanvas(keys='interactive') view = canvas.central_widget.add_view() # Define a scalar field from which we will generate an isosurface def psi(i, j, k, offset=(25, 25, 50)): x = i-offset[0] y = j-offset[1] z = k-offset[2] th = np.arctan2(z, (x**2+y**2)**0.5) r = (x**2 + y**2 + z**2)**0.5 a0 = 1 ps = ((1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1)) return ps print("Generating scalar field..") data = np.abs(np.fromfunction(psi, (50, 50, 100))) # Create isosurface visual surface = scene.visuals.Isosurface(data, level=data.max()/4., color=(0.5, 0.6, 1, 1), shading='smooth', parent=view.scene) surface.transform = scene.transforms.STTransform(translate=(-25, -25, -50)) # Add a 3D axis to keep us oriented axis = scene.visuals.XYZAxis(parent=view.scene) # Use a 3D camera # Manual bounds; Mesh visual does not provide bounds yet # Note how you can set bounds before assigning the camera to the viewbox cam = scene.TurntableCamera(elevation=30, azimuth=30) cam.set_range((-10, 10), (-10, 10), (-10, 10)) view.camera = cam if __name__ == '__main__': canvas.show() if sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/isocurve_updates.py0000644000175000017510000001070514507104216021152 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 10:50:5 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Update Image and Isocurve Visuals ================================= Show use of SceneCanvas to display and update Image and Isocurve visuals using ViewBox visual. """ import sys import numpy as np from itertools import cycle from vispy import app, scene from vispy.scene import STTransform from vispy.util.filter import gaussian_filter from vispy.color import get_colormaps, get_color_names canvas = scene.SceneCanvas(keys='interactive', title='Show update capabilities of Isocurve Visual', show=True) canvas.show() # Setting up four viewboxes vb1 = scene.widgets.ViewBox(border_color='yellow', parent=canvas.scene) vb2 = scene.widgets.ViewBox(border_color='blue', parent=canvas.scene) vb3 = scene.widgets.ViewBox(border_color='red', parent=canvas.scene) vb4 = scene.widgets.ViewBox(border_color='purple', parent=canvas.scene) vb = (vb1, vb2, vb3, vb4) # add grid as central widget, add viewboxes into grid grid = canvas.central_widget.add_grid() grid.padding = 0 grid.add_widget(vb1, 0, 0) grid.add_widget(vb2, 0, 1) grid.add_widget(vb3, 1, 0) grid.add_widget(vb4, 1, 1) # panzoom cameras for every viewbox for box in vb: box.camera = 'panzoom' box.camera.aspect = 1.0 # Create random image img_data1 = np.empty((200, 100, 3), dtype=np.ubyte) noise = np.random.normal(size=(200, 100), loc=50, scale=150) noise = gaussian_filter(noise, (4, 4, 0)).astype(np.float32) img_data1[:] = noise[..., np.newaxis] # create 2d array with some function x, y = np.mgrid[0:2*np.pi:201j, 0:2*np.pi:101j] myfunc = np.cos(2*x[:-1, :-1]) + np.sin(2*y[:-1, :-1]) myfunc = myfunc.astype(np.float32) # add image to viewbox1 image1 = scene.visuals.Image(noise, parent=vb1.scene, cmap='cubehelix') # move image behind curves image1.transform = STTransform(translate=(0, 0, 0.5)) vb1.camera.set_range() # add image to viewbox2 image2 = scene.visuals.Image(myfunc, parent=vb2.scene, cmap='cubehelix') # move image behind curves image2.transform = STTransform(translate=(0, 0, 0.5)) vb2.camera.set_range() # create some level for the isocurves levels1 = np.linspace(noise.min(), noise.max(), num=52, endpoint=True)[1:-1] levels2 = np.linspace(myfunc.min(), myfunc.max(), num=52, endpoint=True)[1:-1] # create curve 1a (image overlay, black) and 1b (plain, cubehelix colored) # to viewboxes 1 and 3 curve1a = scene.visuals.Isocurve( noise, levels=levels1[::4], color_lev='k', parent=vb1.scene) curve1b = scene.visuals.Isocurve( noise, levels=levels1, color_lev='cubehelix', parent=vb3.scene) # create curve 2a (2darray overlay, black) and 2b (plain, cubehelix colored) # to viewboxes 2 and 4 curve2a = scene.visuals.Isocurve( myfunc, levels=levels2[::4], color_lev='k', parent=vb2.scene) curve2b = scene.visuals.Isocurve( myfunc, levels=levels2, color_lev='cubehelix', parent=vb4.scene) # set viewport vb3.camera.set_range((-100, 200), (0, 200)) vb4.camera.set_range((0, 100), (0, 200)) # setup update parameters up = 1 index = 1 clip = np.linspace(myfunc.min(), myfunc.max(), num=51) cmap = cycle(get_colormaps()) color = cycle(get_color_names()) def update(ev): global myfunc, index, up, levels2, noise, cmap, color if index > 0 and index < 25: # update left panes rolling upwards noise = np.roll(noise, 1, axis=0) image1.set_data(noise) curve1a.set_data(noise) curve1b.set_data(noise) # update colors/colormap if (index % 5) == 0: curve1b.color = next(color) cm = next(cmap) image2.cmap = cm curve2b.color = cm # change isocurves by clipping data/or changing limits # update curve1b levels (clip) curve1b.levels = levels1[index:-index] # update curve2b data with clipped data im2 = np.clip(myfunc, clip[index], clip[-index]) curve2b.set_data(im2) index += up else: # change index direction up = -up index += up canvas.update() # setup timer timer = app.Timer() timer.connect(update) # slow this down a bit to better see what happens timer.start(0) if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/isocurve_for_trisurface_qt.py0000644000175000017510000001070614507104216023227 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 2 """ Isocurve for Triangular Mesh with Qt Interface ============================================== This example demonstrates isocurve for triangular mesh with vertex data and a qt interface. """ import numpy as np from vispy import scene, app from vispy.geometry.generation import create_sphere from vispy.color.colormap import get_colormaps try: from sip import setapi setapi("QVariant", 2) setapi("QString", 2) except ImportError: pass try: from PyQt4 import QtCore from PyQt4.QtCore import Qt from PyQt4.QtGui import (QMainWindow, QWidget, QLabel, QSpinBox, QComboBox, QGridLayout, QVBoxLayout, QSplitter) except Exception: # To switch between PyQt5 and PySide2 bindings just change the from import from PyQt5 import QtCore from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QMainWindow, QWidget, QLabel, QSpinBox, QComboBox, QGridLayout, QVBoxLayout, QSplitter) # Provide automatic signal function selection for PyQtX/PySide2 pyqtsignal = QtCore.pyqtSignal if hasattr(QtCore, 'pyqtSignal') else QtCore.Signal class ObjectWidget(QWidget): """ Widget for editing OBJECT parameters """ signal_object_changed = pyqtsignal(name='objectChanged') def __init__(self, parent=None): super(ObjectWidget, self).__init__(parent) l_nbr_steps = QLabel("Nbr Steps ") self.nbr_steps = QSpinBox() self.nbr_steps.setMinimum(3) self.nbr_steps.setMaximum(100) self.nbr_steps.setValue(6) self.nbr_steps.valueChanged.connect(self.update_param) l_cmap = QLabel("Cmap ") self.cmap = sorted(get_colormaps().keys()) self.combo = QComboBox(self) self.combo.addItems(self.cmap) self.combo.currentIndexChanged.connect(self.update_param) gbox = QGridLayout() gbox.addWidget(l_cmap, 0, 0) gbox.addWidget(self.combo, 0, 1) gbox.addWidget(l_nbr_steps, 1, 0) gbox.addWidget(self.nbr_steps, 1, 1) vbox = QVBoxLayout() vbox.addLayout(gbox) vbox.addStretch(1) self.setLayout(vbox) def update_param(self, option): self.signal_object_changed.emit() class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.resize(700, 500) self.setWindowTitle('vispy example ...') splitter = QSplitter(Qt.Horizontal) self.canvas = Canvas() self.canvas.create_native() self.canvas.native.setParent(self) self.props = ObjectWidget() splitter.addWidget(self.props) splitter.addWidget(self.canvas.native) self.setCentralWidget(splitter) self.props.signal_object_changed.connect(self.update_view) self.update_view() def update_view(self): # banded, nbr_steps, cmap self.canvas.set_data(self.props.nbr_steps.value(), self.props.combo.currentText()) class Canvas(scene.SceneCanvas): def __init__(self): scene.SceneCanvas.__init__(self, keys=None) self.size = 800, 600 self.unfreeze() self.view = self.central_widget.add_view() self.radius = 2.0 self.view.camera = 'turntable' mesh = create_sphere(20, 20, radius=self.radius) vertices = mesh.get_vertices() tris = mesh.get_faces() cl = np.linspace(-self.radius, self.radius, 6 + 2)[1:-1] self.iso = scene.visuals.Isoline(vertices=vertices, tris=tris, data=vertices[:, 2], levels=cl, color_lev='autumn', parent=self.view.scene) self.freeze() # Add a 3D axis to keep us oriented scene.visuals.XYZAxis(parent=self.view.scene) def set_data(self, n_levels, cmap): self.iso.set_color(cmap) cl = np.linspace(-self.radius, self.radius, n_levels + 2)[1:-1] self.iso.levels = cl if __name__ == '__main__': app.create() win = MainWindow() win.show() app.run() vispy-0.14.1/examples/scene/isocurve_for_trisurface.py0000644000175000017510000000256314507104216022525 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 2 """ Isocurve for Triangular Mesh ============================ This example demonstrates isocurve for triangular mesh with vertex data. """ import numpy as np from vispy import app, scene from vispy.geometry.generation import create_sphere import sys # Create a canvas with a 3D viewport canvas = scene.SceneCanvas(keys='interactive', title='Isocurve for Triangular Mesh Example') canvas.show() view = canvas.central_widget.add_view() cols = 10 rows = 10 radius = 2 nbr_level = 20 mesh = create_sphere(cols, rows, radius=radius) vertices = mesh.get_vertices() tris = mesh.get_faces() cl = np.linspace(-radius, radius, nbr_level+2)[1:-1] scene.visuals.Isoline(vertices=vertices, tris=tris, data=vertices[:, 2], levels=cl, color_lev='winter', parent=view.scene) # Add a 3D axis to keep us oriented scene.visuals.XYZAxis(parent=view.scene) view.camera = scene.TurntableCamera() view.camera.set_range((-1, 1), (-1, 1), (-1, 1)) if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/isocurve.py0000644000175000017510000000333414507104216017425 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Display an Isocurve =================== Simple use of SceneCanvas to display an Isocurve visual. """ import sys from vispy import app, scene, visuals from vispy.util.filter import gaussian_filter import numpy as np canvas = scene.SceneCanvas(keys='interactive', title='Isocurve(s) overlayed ' 'over Random Image Example') canvas.size = 800, 600 canvas.show() # Set up a viewbox to display the image with interactive pan/zoom view = canvas.central_widget.add_view() # Create the image img_data = np.empty((200, 100, 3), dtype=np.ubyte) noise = np.random.normal(size=(200, 100), loc=50, scale=150) noise = gaussian_filter(noise, (4, 4, 0)) img_data[:] = noise[..., np.newaxis] image = scene.visuals.Image(img_data, parent=view.scene) # move image behind curves image.transform = visuals.transforms.STTransform(translate=(0, 0, 0.5)) # level and color setup levels = [40, 50, 60] color_lev = [(1, 0, 0, 1), (1, 0.5, 0, 1), (1, 1, 0, 1)] # Create isocurve, make a child of the image to ensure the two are always # aligned. curve = scene.visuals.Isocurve(noise, levels=levels, color_lev=color_lev, parent=view.scene) # Set 2D camera view.camera = scene.PanZoomCamera(aspect=1) # the camera will scale to the contents in the scene view.camera.set_range() if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/instanced_quad_visual.py0000644000175000017510000000520114507104216022126 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Custom Visual for instanced rendering of a colored quad ======================================================= # this example is based on the tutorial: T01_basic_visual.py """ from vispy import app, gloo, visuals, scene, use import numpy as np # full gl+ context is required for instanced rendering use(gl='gl+') vertex_shader = """ // both these attributes will be defined on an instance basis (not per vertex) attribute vec2 shift; attribute vec4 color; varying vec4 v_color; void main() { v_color = color; gl_Position = $transform(vec4($position + shift, 0, 1)); } """ fragment_shader = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class InstancedRectVisual(visuals.Visual): def __init__(self, x, y, w, h): visuals.Visual.__init__(self, vertex_shader, fragment_shader) # vertices for two triangles forming a rectangle self.vbo = gloo.VertexBuffer(np.array([ [x, y], [x+w, y], [x+w, y+h], [x, y], [x+w, y+h], [x, y+h] ], dtype=np.float32)) self.shared_program.vert['position'] = self.vbo self._draw_mode = 'triangles' # create a vertex buffer with a divisor argument of 1. This means that the # attribute value is set to the next element of the array every 1 instance. # The length of the array multiplied by the divisor determines the number # of instances self.shifts = gloo.VertexBuffer(np.random.rand(100, 2).astype(np.float32) * 500, divisor=1) self.shared_program['shift'] = self.shifts # we can provide additional buffers with different divisors, as long as the # amount of instances (length * divisor) is the same. In this case, we will change # color every 5 instances self.color = gloo.VertexBuffer(np.random.rand(20, 4).astype(np.float32), divisor=5) self.shared_program['color'] = self.color def _prepare_transforms(self, view): view.view_program.vert['transform'] = view.get_transform() # create a visual node class to add it to the canvas InstancedRect = scene.visuals.create_visual_node(InstancedRectVisual) canvas = scene.SceneCanvas(keys='interactive', show=True) rect = InstancedRect(0, 0, 20, 40, parent=canvas.scene) if __name__ == '__main__': import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/scene/instanced_mesh_visual.py0000644000175000017510000000630314507104216022134 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Instanced Mesh Visual ===================== Show usage of the InstancedMesh visual and its filters. """ from itertools import cycle import numpy as np from scipy.spatial.transform import Rotation from vispy import app, scene, use from vispy.io import imread, load_data_file, read_mesh from vispy.scene.visuals import InstancedMesh from vispy.visuals.filters import InstancedShadingFilter, WireframeFilter, TextureFilter # needed for instanced rendering to work use(gl='gl+') mesh_path = load_data_file('spot/spot.obj.gz') texture_path = load_data_file('spot/spot.png') vertices, faces, normals, texcoords = read_mesh(mesh_path) texture = np.flipud(imread(texture_path)) canvas = scene.SceneCanvas(keys='interactive', bgcolor='white', show=True) view = canvas.central_widget.add_view() view.camera = 'arcball' view.camera.depth_value = 10 * (vertices.max() - vertices.min()) n_instances = 100 instance_colors = np.random.rand(n_instances, 3).astype(np.float32) instance_positions = ((np.random.rand(n_instances, 3) - 0.5) * 10).astype(np.float32) face_colors = np.random.rand(len(faces), 3) instance_transforms = Rotation.random(n_instances).as_matrix().astype(np.float32) # Create a colored `MeshVisual`. mesh = InstancedMesh( vertices, faces, instance_colors=instance_colors, face_colors=face_colors, instance_positions=instance_positions, instance_transforms=instance_transforms, parent=view.scene, ) wireframe_filter = WireframeFilter(width=1) shading_filter = InstancedShadingFilter('smooth', shininess=1) texture_filter = TextureFilter(texture, texcoords) mesh.attach(wireframe_filter) mesh.attach(shading_filter) mesh.attach(texture_filter) def attach_headlight(view): light_dir = (0, 1, 0, 0) shading_filter.light_dir = light_dir[:3] initial_light_dir = view.camera.transform.imap(light_dir) @view.scene.transform.changed.connect def on_transform_change(event): transform = view.camera.transform shading_filter.light_dir = transform.map(initial_light_dir)[:3] attach_headlight(view) shading_cycle = cycle(['flat', None, 'smooth']) color_cycle = cycle([None, instance_colors]) face_color_cycle = cycle([None, face_colors]) @canvas.events.key_press.connect def on_key_press(event): if event.key == "t": texture_filter.enabled = not texture_filter.enabled canvas.update() if event.key == 's': shading_filter.shading = next(shading_cycle) canvas.update() if event.key == 'c': mesh.instance_colors = next(color_cycle) canvas.update() if event.key == 'f': mesh.set_data( vertices=vertices, faces=faces, face_colors=next(face_color_cycle), ) canvas.update() if event.key == 'w': wireframe_filter.enabled = not wireframe_filter.enabled canvas.update() if __name__ == "__main__": app.run() vispy-0.14.1/examples/scene/instanced_mesh.py0000644000175000017510000001045414507104216020553 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 5 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Instanced rendering of arbitrarily transformed meshes ===================================================== """ from vispy import app, gloo, visuals, scene, use import numpy as np from scipy.spatial.transform import Rotation from vispy.io import read_mesh, load_data_file # full gl+ context is required for instanced rendering use(gl='gl+') vertex_shader = """ // these attributes will be defined on an instance basis attribute vec3 shift; attribute vec4 color; attribute vec3 transform_x; attribute vec3 transform_y; attribute vec3 transform_z; varying vec4 v_color; void main() { v_color = color; // transform is generated from column vectors (new basis vectors) // https://en.wikibooks.org/wiki/GLSL_Programming/Vector_and_Matrix_Operations#Constructors mat3 instance_transform = mat3(transform_x, transform_y, transform_z); vec3 pos_rotated = instance_transform * $position; vec4 pos_shifted = vec4(pos_rotated + shift, 1); gl_Position = $transform(pos_shifted); } """ fragment_shader = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class InstancedMeshVisual(visuals.Visual): def __init__(self, vertices, faces, positions, colors, transforms, subdivisions=5): visuals.Visual.__init__(self, vertex_shader, fragment_shader) self.set_gl_state('translucent', depth_test=True, cull_face=True) self._draw_mode = 'triangles' # set up vertex and index buffer self.vbo = gloo.VertexBuffer(vertices.astype(np.float32)) self.shared_program.vert['position'] = self.vbo self._index_buffer = gloo.IndexBuffer(data=faces.astype(np.uint32)) # create a vertex buffer with a divisor argument of 1. This means that the # attribute value is set to the next element of the array every 1 instance. # The length of the array multiplied by the divisor determines the number # of instances self.shifts = gloo.VertexBuffer(positions.astype(np.float32), divisor=1) self.shared_program['shift'] = self.shifts # vispy does not handle matrix attributes (likely requires some big changes in GLIR) # so we decompose it into three vec3; (column vectors of the matrix) transforms = transforms.astype(np.float32) self.transforms_x = gloo.VertexBuffer(transforms[..., 0].copy(), divisor=1) self.transforms_y = gloo.VertexBuffer(transforms[..., 1].copy(), divisor=1) self.transforms_z = gloo.VertexBuffer(transforms[..., 2].copy(), divisor=1) self.shared_program['transform_x'] = self.transforms_x self.shared_program['transform_y'] = self.transforms_y self.shared_program['transform_z'] = self.transforms_z # we can provide additional buffers with different divisors, as long as the # amount of instances (length * divisor) is the same. In this case, we will change # color every 5 instances self.color = gloo.VertexBuffer(colors.astype(np.float32), divisor=1) self.shared_program['color'] = self.color def _prepare_transforms(self, view): view.view_program.vert['transform'] = view.get_transform() # create a visual node class to add it to the canvas InstancedMesh = scene.visuals.create_visual_node(InstancedMeshVisual) # set up vanvas canvas = scene.SceneCanvas(keys='interactive', show=True) view = canvas.central_widget.add_view() view.camera = 'arcball' view.camera.scale_factor = 1000 N = 1000 mesh_file = load_data_file('orig/triceratops.obj.gz') vertices, faces, _, _ = read_mesh(mesh_file) np.random.seed(0) pos = (np.random.rand(N, 3) - 0.5) * 1000 colors = np.random.rand(N, 4) transforms = Rotation.random(N).as_matrix() multimesh = InstancedMesh(vertices * 10, faces, pos, colors, transforms, parent=view.scene) # global transforms are applied correctly after the individual instance transforms! multimesh.transform = visuals.transforms.STTransform(scale=(3, 2, 1)) if __name__ == '__main__': import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/scene/infinite_line.py0000644000175000017510000000353314507104216020403 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # vispy: gallery 2 """ Draw an InfiniteLine ==================== Demonstration of InfiniteLine visual. """ import sys import numpy as np from vispy import app, scene # vertex positions of data to draw N = 200 pos = np.zeros((N, 2), dtype=np.float32) x_lim = [50., 750.] y_lim = [-2., 2.] pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N) pos[:, 1] = np.random.normal(size=N) # color array color = np.ones((N, 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, N) color[:, 1] = color[::-1, 0] canvas = scene.SceneCanvas(keys='interactive', show=True) grid = canvas.central_widget.add_grid(spacing=0) viewbox = grid.add_view(row=0, col=1, camera='panzoom') # add some axes x_axis = scene.AxisWidget(orientation='bottom') x_axis.stretch = (1, 0.1) grid.add_widget(x_axis, row=1, col=1) x_axis.link_view(viewbox) y_axis = scene.AxisWidget(orientation='left') y_axis.stretch = (0.1, 1) grid.add_widget(y_axis, row=0, col=0) y_axis.link_view(viewbox) # add a line plot inside the viewbox line = scene.Line(pos, color, parent=viewbox.scene) # add vertical lines vert_line1 = scene.InfiniteLine(100, [1.0, 0.0, 0.0, 1.0], parent=viewbox.scene) vert_line2 = scene.InfiniteLine(549.2, [0.0, 1.0, 0.0, 1.0], vertical=True, parent=viewbox.scene) # add horizontal lines hor_line1 = scene.InfiniteLine(0.3, [1.0, 0.0, 1.0, 1.0], vertical=False, parent=viewbox.scene) hor_line2 = scene.InfiniteLine(-5.1, [1.0, 1.0, 0.0, 1.0], vertical=False, parent=viewbox.scene) # auto-scale to see the whole line. viewbox.camera.set_range() if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/image_custom_kernel.py0000644000175000017510000000503014507104216021575 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Custom image sampling ===================== Use custom interpolation kernels for image sampling. Press k to switch kernel. """ import sys from itertools import cycle from vispy import scene from vispy import app from vispy.io import load_data_file, read_png from scipy.signal.windows import gaussian from scipy.ndimage import gaussian_filter import numpy as np canvas = scene.SceneCanvas(keys='interactive') canvas.size = 800, 600 canvas.show() # Set up a viewbox to display the image with interactive pan/zoom view = canvas.central_widget.add_view() # Load the image with a slight blur (so we can later show the sharpening filter) img_data = gaussian_filter( read_png(load_data_file('mona_lisa/mona_lisa_sm.png')), sigma=1, ) # build gaussian kernel small_gaussian_window = gaussian(5, 1) small_gaussian_kernel = np.outer(small_gaussian_window, small_gaussian_window) # normalize small_gaussian_kernel = small_gaussian_kernel / small_gaussian_kernel.sum() # do the same but larget and with bigger sigma big_gaussian_window = gaussian(20, 10) big_gaussian_kernel = np.outer(big_gaussian_window, big_gaussian_window) big_gaussian_kernel = big_gaussian_kernel / big_gaussian_kernel.sum() # sharpening kernel sharpen_kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) kernels = { 'null': np.ones((1, 1)), 'small gaussian': small_gaussian_kernel, 'big gaussian': big_gaussian_kernel, 'sharpening': sharpen_kernel, } k_names = cycle(kernels.keys()) k = next(k_names) image = scene.visuals.Image( img_data, interpolation='custom', custom_kernel=kernels[k], parent=view.scene, ) canvas.title = f'Custom sampling with {k} kernel' # Set 2D camera (the camera will scale to the contents in the scene) view.camera = scene.PanZoomCamera(aspect=1) # flip y-axis to have correct aligment view.camera.flip = (0, 1, 0) view.camera.set_range() # Implement key presses @canvas.events.key_press.connect def on_key_press(event): if event.key == 'k': k = next(k_names) image.custom_kernel = kernels[k] canvas.title = f'Custom sampling with {k} kernel' canvas.update() if __name__ == '__main__' and sys.flags.interactive == 0: app.run() print(__doc__) vispy-0.14.1/examples/scene/image.py0000644000175000017510000000357014507104216016652 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Display an Image ================ Simple use of SceneCanvas to display an Image. """ import sys from vispy import scene from vispy import app from vispy.io import load_data_file, read_png canvas = scene.SceneCanvas(keys='interactive') canvas.size = 800, 600 canvas.show() # Set up a viewbox to display the image with interactive pan/zoom view = canvas.central_widget.add_view() # Create the image img_data = read_png(load_data_file('mona_lisa/mona_lisa_sm.png')) interpolation = 'nearest' image = scene.visuals.Image(img_data, interpolation=interpolation, parent=view.scene, method='subdivide') canvas.title = 'Spatial Filtering using %s Filter' % interpolation # Set 2D camera (the camera will scale to the contents in the scene) view.camera = scene.PanZoomCamera(aspect=1) # flip y-axis to have correct aligment view.camera.flip = (0, 1, 0) view.camera.set_range() view.camera.zoom(0.1, (250, 200)) # get interpolation functions from Image names = image.interpolation_functions names = sorted(names) act = 17 # Implement key presses @canvas.events.key_press.connect def on_key_press(event): global act if event.key in ['Left', 'Right']: if event.key == 'Right': step = 1 else: step = -1 act = (act + step) % len(names) interpolation = names[act] image.interpolation = interpolation canvas.title = 'Spatial Filtering using %s Filter' % interpolation canvas.update() if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/grid_layout/0000755000175000017510000000000014507104251017532 5ustar nileshnileshvispy-0.14.1/examples/scene/grid_layout/grid_x_y_viewbox.py0000644000175000017510000000232714507104216023460 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Plot-like Grid Layout ===================== +----+-------------+ | | | | y | viewbox | | | | +----+-------------+ | sp | x | +----+-------------+ """ import sys from vispy import scene, app canvas = scene.SceneCanvas(keys='interactive') canvas.size = 600, 600 canvas.show() grid = canvas.central_widget.add_grid() widget_y_axis = grid.add_widget(row=0, col=0) widget_y_axis.bgcolor = "#999999" widget_viewbox = grid.add_widget(row=0, col=1) widget_viewbox.bgcolor = "#dd0000" widget_spacer_bottom = grid.add_widget(row=1, col=0) widget_spacer_bottom.bgcolor = "#efefef" widget_x_axis = grid.add_widget(row=1, col=1) widget_x_axis.bgcolor = "#0000dd" widget_y_axis.width_min = 50 widget_y_axis.width_max = 50 widget_x_axis.height_min = 50 widget_x_axis.height_max = 50 if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/grid_layout/grid_uneven_col.py0000644000175000017510000000175514507104216023257 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Another Grid Layout =================== +-----+-----+-----+ | 1 | 2 | 2 | +-----+-----+-----+ | 3 | 3 | 4 | +-----+-----+-----+ """ import sys from vispy import scene, app canvas = scene.SceneCanvas(keys='interactive') canvas.size = 600, 600 canvas.show() grid = canvas.central_widget.add_grid() # top_left grid.add_widget(row=0, col=0, bgcolor="#999999") # top_right grid.add_widget(row=0, col=1, col_span=2, bgcolor="#dd0000") # bottom_left grid.add_widget(row=1, col=0, col_span=2, bgcolor="#0000dd") # bottom_right grid.add_widget(row=1, col=2, bgcolor="#00dd00") if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/grid_layout/grid_large.py0000644000175000017510000000237314507104216022211 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # disabled due to segfaults on travis # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Multiple Line Views on a Grid ============================= Test automatic layout of multiple viewboxes using Grid. """ import sys from vispy import scene from vispy import app import numpy as np canvas = scene.SceneCanvas(keys='interactive') canvas.size = 600, 600 canvas.show() grid = canvas.central_widget.add_grid() N = 10000 lines = [] for i in range(10): lines.append([]) for j in range(10): vb = grid.add_view(row=i, col=j) vb.camera = 'panzoom' vb.camera.rect = (0, -5), (100, 10) # vb.border = (1, 1, 1, 0.4) pos = np.empty((N, 2), dtype=np.float32) pos[:, 0] = np.linspace(0, 100, N) pos[:, 1] = np.random.normal(size=N) line = scene.visuals.Line(pos=pos, color=(1, 1, 1, 0.5), method='gl') vb.add(line) if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/grid_layout/grid_holed.py0000644000175000017510000000214614507104216022210 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ More complex grid layout ======================== :: +---+-------+---+ | 1 | 1 | 2 | |---+-------+---+ | 3 | Empty | 2 | +---+-------+---+ | 3 | 4 | 4 | +---+-------+---+ """ import sys from vispy import scene, app canvas = scene.SceneCanvas(keys='interactive') canvas.size = 600, 600 canvas.bgcolor = "#000000" canvas.show() grid = canvas.central_widget.add_grid() # top_left grid.add_widget(row=0, col=0, col_span=2, bgcolor="#ffffff") # top_right grid.add_widget(row=0, col=2, row_span=2, bgcolor="#dddddd") # bottom_left grid.add_widget(row=1, col=0, row_span=2, bgcolor="#444444") # bottom_right grid.add_widget(row=2, col=1, col_span=2, bgcolor="#888888") if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/grid_layout/grid_basic.py0000644000175000017510000000175414507104216022202 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Simple use of a grid layout =========================== +-----+-----+ | | | | 1 | 2 | | | | +-----+-----+ """ import sys from vispy import scene, app canvas = scene.SceneCanvas(keys='interactive') canvas.size = 600, 600 # This is the top-level widget that will hold three ViewBoxes, which will # be automatically resized whenever the grid is resized. grid = canvas.central_widget.add_grid() widget_left = grid.add_widget(row=0, col=0) widget_left.bgcolor = "#dd0000" widget_right = grid.add_widget(row=0, col=1) widget_right.bgcolor = "#0000dd" canvas.show() if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/grid_layout/grid.py0000644000175000017510000000542714507104216021042 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Multiple ViewBoxes on a Grid ============================ Test automatic layout of multiple viewboxes using Grid. """ import sys import numpy as np from vispy import scene, app canvas = scene.SceneCanvas(keys='interactive') canvas.size = 600, 600 canvas.show() # This is the top-level widget that will hold three ViewBoxes, which will # be automatically resized whenever the grid is resized. grid = canvas.central_widget.add_grid() # Add 4 ViewBoxes to the grid b1 = grid.add_view(row=0, col=0) b1.border_color = (0.5, 0.5, 0.5, 1) b1.camera = scene.PanZoomCamera(rect=(-0.5, -5, 11, 10)) b2 = grid.add_view(row=0, col=1) b2.camera = 'turntable' b2.border_color = (0.5, 0.5, 0.5, 1) b3 = grid.add_view(row=1, col=0) b3.border_color = (0.5, 0.5, 0.5, 1) b3.camera = scene.PanZoomCamera(rect=(-10, -5, 15, 10)) b4 = grid.add_view(row=1, col=1) b4.border_color = (0.5, 0.5, 0.5, 1) b4.camera = scene.PanZoomCamera(rect=(-5, -5, 10, 10)) # Generate some random vertex data and a color gradient N = 10000 pos = np.empty((N, 2), dtype=np.float32) pos[:, 0] = np.linspace(0, 10, N) pos[:, 1] = np.random.normal(size=N) pos[5000, 1] += 50 color = np.ones((N, 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, N) color[:, 1] = color[::-1, 0] # Top grid cell shows plot data in a rectangular coordinate system. l1 = scene.visuals.Line(pos=pos, color=color, antialias=False, method='gl') b1.add(l1) grid1 = scene.visuals.GridLines(parent=b1.scene) # Bottom-left grid cell shows the same data with log-transformed X grid2 = scene.visuals.GridLines(parent=b2.scene) # Bottom-left grid cell shows the same data with log-transformed X e2 = scene.Node(parent=b3.scene) e2.transform = scene.transforms.LogTransform(base=(2, 0, 0)) l2 = scene.visuals.Line(pos=pos, color=color, antialias=False, parent=e2, method='gl') grid3 = scene.visuals.GridLines(parent=e2) # Bottom-right grid cell shows the same data again, but with a much more # interesting transformation. e3 = scene.Node(parent=b4.scene) affine = scene.transforms.MatrixTransform() affine.scale((1, 0.1)) affine.rotate(10, (0, 0, 1)) affine.translate((0, 1)) e3.transform = scene.transforms.ChainTransform([ scene.transforms.PolarTransform(), affine]) l3 = scene.visuals.Line(pos=pos, color=color, antialias=False, parent=e3, method='gl') grid4 = scene.visuals.GridLines(scale=(np.pi/6., 1.0), parent=e3) if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/grid_layout/README.rst0000644000175000017510000000014114507104216021216 0ustar nileshnileshGrid Layouts ============ Examples of using the GridWidget to layout elements in a SceneCanvas. vispy-0.14.1/examples/scene/graph.py0000644000175000017510000000231314507104216016663 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 5:55:5 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Visualize NetworkX Graph ======================== This example demonstrates how to visualise a NetworkX graph using a VisPy Graph. """ import sys import networkx as nx from vispy import app, scene from vispy.visuals.graphs import layouts canvas = scene.SceneCanvas(title='Simple NetworkX Graph', size=(600, 600), bgcolor='white', show=True) view = canvas.central_widget.add_view('panzoom') graph = nx.adjacency_matrix( nx.fast_gnp_random_graph(500, 0.005, directed=True)) layout = layouts.get_layout('force_directed', iterations=100) visual = scene.visuals.Graph( graph, layout=layout, line_color='black', arrow_type="stealth", arrow_size=30, node_symbol="disc", node_size=20, face_color=(1, 0, 0, 0.2), border_width=0.0, animate=True, directed=False, parent=view.scene) @canvas.events.draw.connect def on_draw(event): if not visual.animate_layout(): canvas.update() if __name__ == '__main__': if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/scene/flipped_axis.py0000644000175000017510000000541314507104216020235 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 2 """ Aspect Ratios ============= Example demonstrating the use of aspect ratio, and also the flipping of axis using negative aspect ratios. Keys: * 1: flip x dimension * 2: flip y dimension * 3: flip z dimension * 4: cycle through up-vectors * 5: cycle through cameras """ from itertools import cycle import numpy as np from vispy import app, scene, io # Read volume vol1 = np.load(io.load_data_file('volume/stent.npz'))['arr_0'] # Prepare canvas canvas = scene.SceneCanvas(keys='interactive', size=(800, 600), show=True) canvas.measure_fps() # Set up a viewbox to display the image with interactive pan/zoom view = canvas.central_widget.add_view() # Create the volume visuals, only one is visible volume1 = scene.visuals.Volume(vol1, parent=view.scene, threshold=0.5) # volume1.method = 'iso' volume1.threshold = 0.1 # Plot a line that shows where positive x is, with at the end a small # line pointing at positive y arr = np.array([(100, -1, -1), (-1, -1, -1), (-1, 10, -1)]) line1 = scene.visuals.Line(arr, color='red', parent=view.scene) # Create cameras cam1 = scene.cameras.PanZoomCamera(parent=view.scene, aspect=1, name='PanZoom') cam2 = scene.cameras.FlyCamera(parent=view.scene, name='Fly') cam3 = scene.cameras.TurntableCamera(fov=60, parent=view.scene, name='Turntable') cam4 = scene.cameras.ArcballCamera(fov=60, parent=view.scene, name='Arcball') cams = (cam1, cam2, cam3, cam4) view.camera = cam3 # Select turntable at first ups = cycle(('+z', '-z', '+y', '-y', '+x', '-x')) # Implement key presses @canvas.events.key_press.connect def on_key_press(event): if event.text == '1': for cam in cams: flip = cam.flip cam.flip = not flip[0], flip[1], flip[2] elif event.text == '2': for cam in cams: flip = cam.flip cam.flip = flip[0], not flip[1], flip[2] elif event.text == '3': for cam in cams: flip = cam.flip cam.flip = flip[0], flip[1], not flip[2] elif event.text == '4': up = next(ups) print('up: ' + up) for cam in cams: cam.up = up if event.text == '5': cam_toggle = {cam1: cam2, cam2: cam3, cam3: cam4, cam4: cam1} view.camera = cam_toggle.get(view.camera, cam2) print(view.camera.name + ' camera') elif event.text == '0': for cam in cams: cam.set_range() if __name__ == '__main__': print(__doc__) app.run() vispy-0.14.1/examples/scene/face_picking.py0000644000175000017510000001041314507104216020164 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Picking Faces from a Mesh ========================= Demonstrates how to identify (pick) individual faces on a mesh. Arguments: * --mesh - Path to a mesh file (OBJ/OBJ.GZ) [optional] Controls: * p - Toggle face picking view - shows the colors encoding face ID * r - Clear painted faces * s - Cycle shading modes (None, 'flat', 'smooth') * w - Toggle wireframe """ import argparse import itertools import time import numpy as np from vispy import app, scene from vispy.io import read_mesh, load_data_file from vispy.scene.visuals import Mesh from vispy.scene import transforms from vispy.visuals.filters import ShadingFilter, WireframeFilter, FacePickingFilter parser = argparse.ArgumentParser() default_mesh = load_data_file('orig/triceratops.obj.gz') parser.add_argument('--mesh', default=default_mesh) args, _ = parser.parse_known_args() vertices, faces, _normals, _texcoords = read_mesh(args.mesh) canvas = scene.SceneCanvas(keys='interactive', bgcolor='white') view = canvas.central_widget.add_view() view.camera = 'arcball' view.camera.depth_value = 1e3 # Create a colored `MeshVisual`. face_colors = np.tile((0.5, 0.0, 0.5, 1.0), (len(faces), 1)) mesh = Mesh( vertices, faces, face_colors=face_colors.copy() ) mesh.transform = transforms.MatrixTransform() mesh.transform.rotate(90, (1, 0, 0)) mesh.transform.rotate(-45, (0, 0, 1)) view.add(mesh) # Use filters to affect the rendering of the mesh. wireframe_filter = WireframeFilter() shading_filter = ShadingFilter() face_picking_filter = FacePickingFilter() mesh.attach(wireframe_filter) mesh.attach(shading_filter) mesh.attach(face_picking_filter) def attach_headlight(view): light_dir = (0, 1, 0, 0) shading_filter.light_dir = light_dir[:3] initial_light_dir = view.camera.transform.imap(light_dir) @view.scene.transform.changed.connect def on_transform_change(event): transform = view.camera.transform shading_filter.light_dir = transform.map(initial_light_dir)[:3] attach_headlight(view) shading = itertools.cycle(("flat", "smooth", None)) shading_filter.shading = next(shading) throttle = time.monotonic() @canvas.events.mouse_move.connect def on_mouse_move(event): global throttle # throttle mouse events to 50ms if time.monotonic() - throttle < 0.05: return throttle = time.monotonic() # adjust the event position for hidpi screens render_size = tuple(d * canvas.pixel_scale for d in canvas.size) x_pos = event.pos[0] * canvas.pixel_scale y_pos = render_size[1] - (event.pos[1] * canvas.pixel_scale) # render a small patch around the mouse cursor restore_state = not face_picking_filter.enabled face_picking_filter.enabled = True mesh.update_gl_state(blend=False) picking_render = canvas.render( region=(x_pos - 1, y_pos - 1, 3, 3), size=(3, 3), bgcolor=(0, 0, 0, 0), alpha=True, ) if restore_state: face_picking_filter.enabled = False mesh.update_gl_state(blend=not face_picking_filter.enabled) # unpack the face index from the color in the center pixel face_idx = (picking_render.view(np.uint32) - 1)[1, 1, 0] if face_idx > 0 and face_idx < len(face_colors): # this may be less safe, but it's faster than set_data mesh.mesh_data._face_colors_indexed_by_faces[face_idx] = (0, 1, 0, 1) mesh.mesh_data_changed() @canvas.events.key_press.connect def on_key_press(event): if event.key == 'p': face_picking_filter.enabled = not face_picking_filter.enabled mesh.update_gl_state(blend=not face_picking_filter.enabled) mesh.update() if event.key == 'r': mesh.set_data(vertices, faces, face_colors=face_colors) if event.key == 's': shading_filter.shading = next(shading) mesh.update() if event.key == 'w': wireframe_filter.enabled = not wireframe_filter.enabled mesh.update() canvas.show() if __name__ == "__main__": print(__doc__) app.run() vispy-0.14.1/examples/scene/example.png0000644000175000017510000000533214507104216017355 0ustar nileshnilesh‰PNG  IHDR Xšv‚p ¡IDATxÚí×1İÿžÁ—HèÖJ28Ð0 0 0 €0 €0 € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 €0 €0 € € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 0 €0 €0 € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 €0 €0 € € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 0 €0 €0 € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 €0 €0 € € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 0 €0 €0 € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 €0 €0 € € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 0 €0 €0 € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 €0 €0 € € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 0 €0 €0 € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 €0 €0 € € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 0 €0 €0 € € € `@ `@ `@`@`@À€À€À€À€À€0 0 0 €0 €0 € € € € `@ `@ `@`@`@À€À€À€À€_,°q¯D@—IEND®B`‚vispy-0.14.1/examples/scene/example..gif0000644000175000017510000001560114507104216017414 0ustar nileshnileshGIF89a X‡!ÿ NETSCAPE2.0ÿÿ!ù , XÿH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€ L¸°áÈ+^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУKŸN½ºõëØ³kßν»÷ïàËÿO¾¼ùóèÓ«_Ͼ½û÷ðãËŸO¿¾ýûøóëßÏ¿¿ÿÿ(à€hà&¨à‚ 6èàƒF(á„Vhá…f¨á†vèᇠ†(âˆ$–hâ‰(¦¨âŠ,¶èâ‹0Æ(ãŒ4Öhã8æ¨ãŽ<öèã@)äDiä‘H&©ä’L6éä“PF)å”TViå•Xf©å–\véå—`†)æ˜d–iæ™h¦©æšl¶éæ›pÆ)çœtÖiçxæ©çž|öé矀*è „j衈&ªè¢Œ6êè£F*餔Vj饘fªé¦œvêé§ †*ꨤ–jꩨ¦ªêª¬¶êê«°Æÿ*무Öjë­¸æªë®¼öêë¯À+ì°Äkì±È&«ì²Ì6ëì³ÐF+í´ÔVkíµØf«í¶Üvëí·à†+î¸ä–kî¹è¦«îºì¶ëî»ðÆ+ï¼ôÖkï½øæ«ï¾üöëï¿,ðÀlðÁ'¬ð 7ìðÃG,ñÄWlñÅg¬ñÆwìñÇ ‡,òÈ$—lòÉ(§¬òÊ,·ìòË0Ç,óÌ4×lóÍ8ç¬óÎ<÷ìóÏ@-ôÐDmôÑH'­ôÒL7íôÓPG-õÔTWmõÕXg­õÖ\wíõ×`‡-öØd—möÙh§­öÚl·íöÛpÇ-÷Üt×m÷Ýxç­÷Þ|÷ÿí÷߀.øà„nøáˆ'®øâŒ7îøãG.ùä”Wnùå˜g®ùæœwîùç ‡.ú褗nú騧®úꬷîúë°Ç.ûì´×nûí¸ç®ûî¼÷îûïÀ/üðÄoüñÈ'¯üòÌ7ïüóÐG/ýôÔWoýõØg¯ýöÜwïý÷à‡/þøä—oþù觯þúì·ïþûðÇ/ÿüô×oÿýøç¯ÿþü÷ïÿÿ  HÀð€L ÈÀ:ðŒ 'HÁ Zð‚Ì 7ÈÁzðƒ ¡GHšð„(L¡ WȺð…0Œ¡ gHÃÚð†8Ì¡wÈÃúð‡@ ¢ˇHÄ"ñˆHL¢—ÈÄ&:ñ‰PŒ¢§HÅ*ZñŠXÌ¢·ÈÅ.zñ‹` £ÇHÆ2šñŒhL£×ÈÆ6ºñpŒ£çHÇ:ÚñŽxÌ£÷ÈÇ>úñ€ ¤ IÈBòˆL¤"ÉÈF:ò‘Œ¤$'IÉJZò’˜Ì¤&7ÉÉNzò“  ¥(GIÊRšò”¨L¥*WÉÊVºò•°Œ¥,gIËZÚò–¸Ì¥.wÉË^úò—À ¦0‡IÌbó˜ÈL¦2—ÉÌf:ó™ÐŒ¦4§)¢€!ù , XÿH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€ L¸°áÈ+^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУKŸN½ºõëØ³kßν»÷ïàËÿO¾¼ùóèÓ«_Ͼ½û÷ðãËŸO¿¾ýûøóëßÏ¿¿ÿÿ(à€hà&¨à‚ 6èàƒF(á„Vhá…f¨á†vèᇠ†(âˆ$–hâ‰(¦¨âŠ,¶èâ‹0Æ(ãŒ4Öhã8æ¨ãŽ<öèã@)äDiä‘H&©ä’L6éä“PF)å”TViå•Xf©å–\véå—`†)æ˜d–iæ™h¦©æšl¶éæ›pÆ)çœtÖiçxæ©çž|öé矀*è „j衈&ªè¢Œ6êè£F*餔Vj饘fªé¦œvêé§ †*ꨤ–jꩨ¦ªêª¬¶êê«°Æÿ*무Öjë­¸æªë®¼öêë¯À+ì°Äkì±È&«ì²Ì6ëì³ÐF+í´ÔVkíµØf«í¶Üvëí·à†+î¸ä–kî¹è¦«îºì¶ëî»ðÆ+ï¼ôÖkï½øæ«ï¾üöëï¿,ðÀlðÁ'¬ð 7ìðÃG,ñÄWlñÅg¬ñÆwìñÇ ‡,òÈ$—lòÉ(§¬òÊ,·ìòË0Ç,óÌ4×lóÍ8ç¬óÎ<÷ìóÏ@-ôÐDmôÑH'­ôÒL7íôÓPG-õÔTWmõÕXg­õÖ\wíõ×`‡-öØd—möÙh§­öÚl·íöÛpÇ-÷Üt×m÷Ýxç­÷Þ|÷ÿí÷߀.øà„nøáˆ'®øâŒ7îøãG.ùä”Wnùå˜g®ùæœwîùç ‡.ú褗nú騧®úꬷîúë°Ç.ûì´×nûí¸ç®ûî¼÷îûïÀ/üðÄoüñÈ'¯üòÌ7ïüóÐG/ýôÔWoýõØg¯ýöÜwïý÷à‡/þøä—oþù觯þúì·ïþûðÇ/ÿüô×oÿýøç¯ÿþü÷ïÿÿ  HÀð€L ÈÀ:ðŒ 'HÁ Zð‚Ì 7ÈÁzðƒ ¡GHšð„(L¡ WȺð…0Œ¡ gHÃÚð†8Ì¡wÈÃúð‡@ ¢ˇHÄ"ñˆHL¢—ÈÄ&:ñ‰PŒ¢§HÅ*ZñŠXÌ¢·ÈÅ.zñ‹` £ÇHÆ2šñŒhL£×ÈÆ6ºñpŒ£çHÇ:ÚñŽxÌ£÷ÈÇ>úñ€ ¤ IÈBòˆL¤"ÉÈF:ò‘Œ¤$'IÉJZò’˜Ì¤&7ÉÉNzò“  ¥(GIÊRšò”¨L¥*WÉÊVºò•°Œ¥,gIËZÚò–¸Ì¥.wÉË^úò—À ¦0‡IÌbó˜ÈL¦2—ÉÌf:ó™ÐŒ¦4§)¢€!ù , XÿH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€ L¸°áÈ+^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУKŸN½ºõëØ³kßν»÷ïàËÿO¾¼ùóèÓ«_Ͼ½û÷ðãËŸO¿¾ýûøóëßÏ¿¿ÿÿ(à€hà&¨à‚ 6èàƒF(á„Vhá…f¨á†vèᇠ†(âˆ$–hâ‰(¦¨âŠ,¶èâ‹0Æ(ãŒ4Öhã8æ¨ãŽ<öèã@)äDiä‘H&©ä’L6éä“PF)å”TViå•Xf©å–\véå—`†)æ˜d–iæ™h¦©æšl¶éæ›pÆ)çœtÖiçxæ©çž|öé矀*è „j衈&ªè¢Œ6êè£F*餔Vj饘fªé¦œvêé§ †*ꨤ–jꩨ¦ªêª¬¶êê«°Æÿ*무Öjë­¸æªë®¼öêë¯À+ì°Äkì±È&«ì²Ì6ëì³ÐF+í´ÔVkíµØf«í¶Üvëí·à†+î¸ä–kî¹è¦«îºì¶ëî»ðÆ+ï¼ôÖkï½øæ«ï¾üöëï¿,ðÀlðÁ'¬ð 7ìðÃG,ñÄWlñÅg¬ñÆwìñÇ ‡,òÈ$—lòÉ(§¬òÊ,·ìòË0Ç,óÌ4×lóÍ8ç¬óÎ<÷ìóÏ@-ôÐDmôÑH'­ôÒL7íôÓPG-õÔTWmõÕXg­õÖ\wíõ×`‡-öØd—möÙh§­öÚl·íöÛpÇ-÷Üt×m÷Ýxç­÷Þ|÷ÿí÷߀.øà„nøáˆ'®øâŒ7îøãG.ùä”Wnùå˜g®ùæœwîùç ‡.ú褗nú騧®úꬷîúë°Ç.ûì´×nûí¸ç®ûî¼÷îûïÀ/üðÄoüñÈ'¯üòÌ7ïüóÐG/ýôÔWoýõØg¯ýöÜwïý÷à‡/þøä—oþù觯þúì·ïþûðÇ/ÿüô×oÿýøç¯ÿþü÷ïÿÿ  HÀð€L ÈÀ:ðŒ 'HÁ Zð‚Ì 7ÈÁzðƒ ¡GHšð„(L¡ WȺð…0Œ¡ gHÃÚð†8Ì¡wÈÃúð‡@ ¢ˇHÄ"ñˆHL¢—ÈÄ&:ñ‰PŒ¢§HÅ*ZñŠXÌ¢·ÈÅ.zñ‹` £ÇHÆ2šñŒhL£×ÈÆ6ºñpŒ£çHÇ:ÚñŽxÌ£÷ÈÇ>úñ€ ¤ IÈBòˆL¤"ÉÈF:ò‘Œ¤$'IÉJZò’˜Ì¤&7ÉÉNzò“  ¥(GIÊRšò”¨L¥*WÉÊVºò•°Œ¥,gIËZÚò–¸Ì¥.wÉË^úò—À ¦0‡IÌbó˜ÈL¦2—ÉÌf:ó™ÐŒ¦4§)¢€!ù , XÿH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€ L¸°áÈ+^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУKŸN½ºõëØ³kßν»÷ïàËÿO¾¼ùóèÓ«_Ͼ½û÷ðãËŸO¿¾ýûøóëßÏ¿¿ÿÿ(à€hà&¨à‚ 6èàƒF(á„Vhá…f¨á†vèᇠ†(âˆ$–hâ‰(¦¨âŠ,¶èâ‹0Æ(ãŒ4Öhã8æ¨ãŽ<öèã@)äDiä‘H&©ä’L6éä“PF)å”TViå•Xf©å–\véå—`†)æ˜d–iæ™h¦©æšl¶éæ›pÆ)çœtÖiçxæ©çž|öé矀*è „j衈&ªè¢Œ6êè£F*餔Vj饘fªé¦œvêé§ †*ꨤ–jꩨ¦ªêª¬¶êê«°Æÿ*무Öjë­¸æªë®¼öêë¯À+ì°Äkì±È&«ì²Ì6ëì³ÐF+í´ÔVkíµØf«í¶Üvëí·à†+î¸ä–kî¹è¦«îºì¶ëî»ðÆ+ï¼ôÖkï½øæ«ï¾üöëï¿,ðÀlðÁ'¬ð 7ìðÃG,ñÄWlñÅg¬ñÆwìñÇ ‡,òÈ$—lòÉ(§¬òÊ,·ìòË0Ç,óÌ4×lóÍ8ç¬óÎ<÷ìóÏ@-ôÐDmôÑH'­ôÒL7íôÓPG-õÔTWmõÕXg­õÖ\wíõ×`‡-öØd—möÙh§­öÚl·íöÛpÇ-÷Üt×m÷Ýxç­÷Þ|÷ÿí÷߀.øà„nøáˆ'®øâŒ7îøãG.ùä”Wnùå˜g®ùæœwîùç ‡.ú褗nú騧®úꬷîúë°Ç.ûì´×nûí¸ç®ûî¼÷îûïÀ/üðÄoüñÈ'¯üòÌ7ïüóÐG/ýôÔWoýõØg¯ýöÜwïý÷à‡/þøä—oþù觯þúì·ïþûðÇ/ÿüô×oÿýøç¯ÿþü÷ïÿÿ  HÀð€L ÈÀ:ðŒ 'HÁ Zð‚Ì 7ÈÁzðƒ ¡GHšð„(L¡ WȺð…0Œ¡ gHÃÚð†8Ì¡wÈÃúð‡@ ¢ˇHÄ"ñˆHL¢—ÈÄ&:ñ‰PŒ¢§HÅ*ZñŠXÌ¢·ÈÅ.zñ‹` £ÇHÆ2šñŒhL£×ÈÆ6ºñpŒ£çHÇ:ÚñŽxÌ£÷ÈÇ>úñ€ ¤ IÈBòˆL¤"ÉÈF:ò‘Œ¤$'IÉJZò’˜Ì¤&7ÉÉNzò“  ¥(GIÊRšò”¨L¥*WÉÊVºò•°Œ¥,gIËZÚò–¸Ì¥.wÉË^úò—À ¦0‡IÌbó˜ÈL¦2—ÉÌf:ó™ÐŒ¦4§)¢€!ù , XÿH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Ñ£H“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯`ÊK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€ L¸°áÈ+^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N¼¸ñãÈ“+_μ¹óçУKŸN½ºõëØ³kßν»÷ïàËÿO¾¼ùóèÓ«_Ͼ½û÷ðãËŸO¿¾ýûøóëßÏ¿¿ÿÿ(à€hà&¨à‚ 6èàƒF(á„Vhá…f¨á†vèᇠ†(âˆ$–hâ‰(¦¨âŠ,¶èâ‹0Æ(ãŒ4Öhã8æ¨ãŽ<öèã@)äDiä‘H&©ä’L6éä“PF)å”TViå•Xf©å–\véå—`†)æ˜d–iæ™h¦©æšl¶éæ›pÆ)çœtÖiçxæ©çž|öé矀*è „j衈&ªè¢Œ6êè£F*餔Vj饘fªé¦œvêé§ †*ꨤ–jꩨ¦ªêª¬¶êê«°Æÿ*무Öjë­¸æªë®¼öêë¯À+ì°Äkì±È&«ì²Ì6ëì³ÐF+í´ÔVkíµØf«í¶Üvëí·à†+î¸ä–kî¹è¦«îºì¶ëî»ðÆ+ï¼ôÖkï½øæ«ï¾üöëï¿,ðÀlðÁ'¬ð 7ìðÃG,ñÄWlñÅg¬ñÆwìñÇ ‡,òÈ$—lòÉ(§¬òÊ,·ìòË0Ç,óÌ4×lóÍ8ç¬óÎ<÷ìóÏ@-ôÐDmôÑH'­ôÒL7íôÓPG-õÔTWmõÕXg­õÖ\wíõ×`‡-öØd—möÙh§­öÚl·íöÛpÇ-÷Üt×m÷Ýxç­÷Þ|÷ÿí÷߀.øà„nøáˆ'®øâŒ7îøãG.ùä”Wnùå˜g®ùæœwîùç ‡.ú褗nú騧®úꬷîúë°Ç.ûì´×nûí¸ç®ûî¼÷îûïÀ/üðÄoüñÈ'¯üòÌ7ïüóÐG/ýôÔWoýõØg¯ýöÜwïý÷à‡/þøä—oþù觯þúì·ïþûðÇ/ÿüô×oÿýøç¯ÿþü÷ïÿÿ  HÀð€L ÈÀ:ðŒ 'HÁ Zð‚Ì 7ÈÁzðƒ ¡GHšð„(L¡ WȺð…0Œ¡ gHÃÚð†8Ì¡wÈÃúð‡@ ¢ˇHÄ"ñˆHL¢—ÈÄ&:ñ‰PŒ¢§HÅ*ZñŠXÌ¢·ÈÅ.zñ‹` £ÇHÆ2šñŒhL£×ÈÆ6ºñpŒ£çHÇ:ÚñŽxÌ£÷ÈÇ>úñ€ ¤ IÈBòˆL¤"ÉÈF:ò‘Œ¤$'IÉJZò’˜Ì¤&7ÉÉNzò“  ¥(GIÊRšò”¨L¥*WÉÊVºò•°Œ¥,gIËZÚò–¸Ì¥.wÉË^úò—À ¦0‡IÌbó˜ÈL¦2—ÉÌf:ó™ÐŒ¦4§)¢€;vispy-0.14.1/examples/scene/contour.py0000644000175000017510000000452414507104216017261 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 5 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Apply Contour Filter on an Image ================================ Simple use of SceneCanvas to display an Image. """ import sys from vispy import scene, app from vispy.visuals.filters import IsolineFilter from vispy.io import load_data_file, read_png canvas = scene.SceneCanvas(keys='interactive') canvas.size = 600, 800 canvas.show() # Set up a viewbox to display the image with interactive pan/zoom view = canvas.central_widget.add_view() interpolation = 'cubic' img_data = read_png(load_data_file('mona_lisa/mona_lisa_sm.png')) image = scene.visuals.Image(img_data, interpolation=interpolation, parent=view.scene, method='impostor') level = 10 iso = IsolineFilter(level=level, width=1., color='white') # Set 2D camera (the camera will scale to the contents in the scene) view.camera = scene.PanZoomCamera(aspect=1) # flip y-axis to have correct aligment view.camera.flip = (0, 1, 0) # select face part view.camera.rect = (160, 130, 240, 200) canvas.title = ('Spatial Filtering using %s Filter - Isoline %d level' % (image.interpolation, iso.level)) # get interpolation functions from Image names = image.interpolation_functions act = names.index(interpolation) # Implement key presses @canvas.events.key_press.connect def on_key_press(event): global act, level, first, interpolation if event.key in ['Left', 'Right']: if event.key == 'Right': step = 1 else: step = -1 act = (act + step) % len(names) image.interpolation = names[act] if event.key in ['Up', 'Down']: iso.level += 1 if event.key == 'Up' else -1 canvas.title = ('Spatial Filtering using %s Filter - Isoline %d level' % (image.interpolation, iso.level)) canvas.update() # attaching of isoline filter via timer def on_timer1(event): image.attach(iso) canvas.update() timer1 = app.Timer('auto', iterations=1, connect=on_timer1, start=True) if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/console.py0000644000175000017510000000314514507104216017230 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Vispy Console ============= Demonstrate the use of the vispy console. Note how the console size is independent of the canvas scaling. """ import sys from vispy import scene, app from vispy.scene.widgets import Console from vispy.scene.visuals import Text canvas = scene.SceneCanvas(keys='interactive', size=(400, 400)) grid = canvas.central_widget.add_grid() vb = scene.widgets.ViewBox(border_color='b') vb.camera = 'panzoom' vb.camera.rect = -1, -1, 2, 2 grid.add_widget(vb, row=0, col=0) text = Text('Starting timer...', color='w', font_size=24, parent=vb.scene) console = Console(text_color='g', font_size=12., border_color='g') grid.add_widget(console, row=1, col=0) def on_timer(event): text.text = 'Tick #%s' % event.iteration if event.iteration > 1 and event.iteration % 10 == 0: console.clear() console.write('Elapsed:\n %s' % event.elapsed) canvas.update() timer = app.Timer(2.0, connect=on_timer, start=True) console.write('This is a line that will be wrapped automatically by the ' 'console.\n') console.write('This line will be truncated ....................,\n' 'but this next line will survive.\n', wrap=False) if __name__ == '__main__': canvas.show() if sys.flags.interactive != 1: canvas.app.run() vispy-0.14.1/examples/scene/complex_image.py0000644000175000017510000000432414507104216020377 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Complex image data ================== Simple use of SceneCanvas to display an image consisting of complex numbers. The left and right arrow keys can be used to cycle the view between the real, imaginary, phase, or magnitude of the data. """ import sys import numpy as np from vispy import app, scene def complex_ramp(size=512, phase_range=(-np.pi, np.pi), mag_range=(0, 10)): """Returns a complex array where X ramps phase and Y ramps magnitude.""" p0, p1 = phase_range phase_ramp = np.linspace(p0, p1 - 1 / size, size) m0, m1 = mag_range mag_ramp = np.linspace(m1, m0 + 1 / size, size) phase_ramp, mag_ramp = np.meshgrid(phase_ramp, mag_ramp) return (mag_ramp * np.exp(1j * phase_ramp)).astype(np.complex64) canvas = scene.SceneCanvas(keys="interactive", title="Complex number view: phase") canvas.size = 512, 512 canvas.show() # Set up a viewbox to display the image with interactive pan/zoom view = canvas.central_widget.add_view() # Create the image img_data = complex_ramp() # View it with "complex_mode=imaginary" image = scene.visuals.ComplexImage(img_data, parent=view.scene, complex_mode="phase") # Set 2D camera (the camera will scale to the contents in the scene) view.camera = scene.PanZoomCamera(aspect=1) view.camera.set_range() view.camera.zoom(1) complex_modes = [ "real", "imaginary", "magnitude", "phase", ] mode_index = 3 @canvas.connect def on_key_press(event): global mode_index if event.key not in ['Left', 'Right']: return if event.key == 'Right': step = 1 else: step = -1 mode_index = (mode_index + step) % len(complex_modes) complex_mode = complex_modes[mode_index] image.complex_mode = complex_mode canvas.title = f'Complex number view: {complex_mode}' canvas.update() if __name__ == "__main__" and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/colored_line.py0000644000175000017510000000265614507104216020232 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 10:120:10 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Changing Line Colors ==================== """ import itertools import numpy as np from vispy import app, scene from vispy.color import get_colormaps from vispy.visuals.transforms import STTransform colormaps = itertools.cycle(get_colormaps()) # vertex positions of data to draw N = 200 pos = np.zeros((N, 2), dtype=np.float32) pos[:, 0] = np.linspace(10, 390, N) pos[:, 1] = np.random.normal(size=N, scale=20, loc=0) canvas = scene.SceneCanvas(keys='interactive', size=(400, 200), show=True) # Create a visual that updates the line with different colormaps color = next(colormaps) line = scene.Line(pos=pos, color=color, method='gl') line.transform = STTransform(translate=[0, 140]) line.parent = canvas.central_widget text = scene.Text(color, bold=True, font_size=24, color='w', pos=(200, 40), parent=canvas.central_widget) def on_timer(event): global colormaps, line, text, pos color = next(colormaps) line.set_data(pos=pos, color=color) text.text = color timer = app.Timer(.5, connect=on_timer, start=True) if __name__ == '__main__': canvas.app.run() vispy-0.14.1/examples/scene/colorbar_widget.py0000644000175000017510000000175214507104216020736 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Display a ColorBar ================== Simple use of SceneCanvas and ColorBarWidget to display a ColorBar """ import sys from vispy import scene from vispy import app canvas = scene.SceneCanvas(keys='interactive') canvas.size = 800, 600 canvas.show() grid = canvas.central_widget.add_grid(margin=10) cbar_widget = scene.ColorBarWidget(label="ColorBarWidget", clim=(0, 99), cmap="cool", orientation="right", border_width=1) grid.add_widget(cbar_widget) cbar_widget.border_color = "#212121" grid.bgcolor = "#ffffff" if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/clipping_planes.py0000644000175000017510000000520514507104216020734 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 5 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Clipping planes with volume and markers ======================================= Controls: - x/y/z/o - add new clipping plane with normal along x/y/z or [1,1,1] oblique axis - r - remove a clipping plane """ import numpy as np from vispy import app, scene, io from vispy.visuals.filters.clipping_planes import PlanesClipper # Prepare canvas canvas = scene.SceneCanvas(keys='interactive', size=(800, 600), show=True) # Set up a viewbox to display the image with interactive pan/zoom view = canvas.central_widget.add_view() # Create the visuals vol = np.load(io.load_data_file('volume/stent.npz'))['arr_0'] volume = scene.visuals.Volume(vol, parent=view.scene, threshold=0.225) np.random.seed(1) points = np.random.rand(100, 3) * (128, 128, 128) markers = scene.visuals.Markers(pos=points, parent=view.scene) # add a transform to markers, to show clipping is in scene coordinates markers.transform = scene.STTransform(translate=(0, 0, 128)) # Create the clipping planes filter for the markers (Volume has its own clipping logic) clipper = PlanesClipper() # and attach it to the markers markers.attach(clipper) # Create and set the camera fov = 60. cam = scene.cameras.TurntableCamera( parent=view.scene, fov=fov, name='Turntable' ) view.camera = cam # since volume data is in 'zyx' coordinates, we have to reverse the coordinates # we use as a center volume_center = (np.array(vol.shape) / 2)[::-1] # clipping planes around the origin clip_modes = { 'x': np.array([[volume_center, [1, 0, 0]]]), 'y': np.array([[volume_center, [0, 1, 0]]]), 'z': np.array([[volume_center, [0, 0, 1]]]), 'o': np.array([[volume_center, [1, 1, 1]]]), } def add_clip(mode): if mode not in clip_modes: return clipping_planes = np.concatenate([volume.clipping_planes, clip_modes[mode]]) volume.clipping_planes = clipping_planes clipper.clipping_planes = clipping_planes def remove_clip(): if volume.clipping_planes.shape[0] > 0: volume.clipping_planes = volume.clipping_planes[:-1] clipper.clipping_planes = clipper.clipping_planes[:-1] # Implement key presses @canvas.events.key_press.connect def on_key_press(event): if event.text in 'xyzo': add_clip(event.text) elif event.text == 'r': remove_clip() if __name__ == '__main__': print(__doc__) app.run() vispy-0.14.1/examples/scene/background_borders.py0000644000175000017510000000166414507104216021431 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Demonstration of borders and background colors ============================================== """ from vispy.scene import SceneCanvas canvas = SceneCanvas(keys='interactive', bgcolor='w', show=True) grid = canvas.central_widget.add_grid(spacing=0, bgcolor='gray', border_color='k') view1 = grid.add_view(row=0, col=0, margin=10, bgcolor=(1, 0, 0, 0.5), border_color=(1, 0, 0)) view2 = grid.add_view(row=0, col=1, margin=10, bgcolor=(0, 1, 0, 0.5), border_color=(0, 1, 0)) if __name__ == '__main__': canvas.app.run() vispy-0.14.1/examples/scene/axes_plot.py0000644000175000017510000000333414507104216017564 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Simple Line with Axis ===================== """ import sys import numpy as np from vispy import scene, app canvas = scene.SceneCanvas(keys='interactive', size=(600, 600), show=True) grid = canvas.central_widget.add_grid(margin=10) grid.spacing = 0 title = scene.Label("Plot Title", color='white') title.height_max = 40 grid.add_widget(title, row=0, col=0, col_span=2) yaxis = scene.AxisWidget(orientation='left', axis_label='Y Axis', axis_font_size=12, axis_label_margin=50, tick_label_margin=5) yaxis.width_max = 80 grid.add_widget(yaxis, row=1, col=0) xaxis = scene.AxisWidget(orientation='bottom', axis_label='X Axis', axis_font_size=12, axis_label_margin=50, tick_label_margin=5) xaxis.height_max = 80 grid.add_widget(xaxis, row=2, col=1) right_padding = grid.add_widget(row=1, col=2, row_span=1) right_padding.width_max = 50 view = grid.add_view(row=1, col=1, border_color='white') data = np.random.normal(size=(1000, 2)) data[0] = -10, -10 data[1] = 10, -10 data[2] = 10, 10 data[3] = -10, 10 data[4] = -10, -10 plot = scene.Line(data, parent=view.scene) view.camera = 'panzoom' xaxis.link_view(view) yaxis.link_view(view) if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/scene/README.rst0000644000175000017510000000023414507104216016677 0ustar nileshnileshScene ===== More example scripts are available in the VisPy repository's `example scripts directory `_. vispy-0.14.1/examples/plotting/0000755000175000017510000000000014507104250015752 5ustar nileshnileshvispy-0.14.1/examples/plotting/volume_plot.py0000644000175000017510000000246714507104216020704 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # vispy: gallery 1 """ Multi-view Volume Plot ====================== Plot various views of a structural MRI. """ import numpy as np from vispy import io, plot as vp fig = vp.Fig(bgcolor='k', size=(800, 800), show=False) vol_data = np.load(io.load_data_file('brain/mri.npz'))['data'] vol_data = np.flipud(np.rollaxis(vol_data, 1)) vol_data = vol_data.astype(np.float32) clim = [32, 192] texture_format = "auto" # None for CPUScaled, "auto" for GPUScaled vol_pw = fig[0, 0] v = vol_pw.volume(vol_data, clim=clim, texture_format=texture_format) vol_pw.view.camera.elevation = 30 vol_pw.view.camera.azimuth = 30 vol_pw.view.camera.scale_factor /= 1.5 shape = vol_data.shape fig[1, 0].image(vol_data[:, :, shape[2] // 2], cmap='grays', clim=clim, fg_color=(0.5, 0.5, 0.5, 1), texture_format=texture_format) fig[0, 1].image(vol_data[:, shape[1] // 2, :], cmap='grays', clim=clim, fg_color=(0.5, 0.5, 0.5, 1), texture_format=texture_format) fig[1, 1].image(vol_data[shape[0] // 2, :, :].T, cmap='grays', clim=clim, fg_color=(0.5, 0.5, 0.5, 1), texture_format=texture_format) if __name__ == '__main__': fig.show(run=True) vispy-0.14.1/examples/plotting/spectrogram.py0000644000175000017510000000134314507104216020655 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # vispy: gallery 1 """ Spectrogram and Line Plot ========================= A spectrogram and waveform plot of 1D data. """ import numpy as np from vispy import plot as vp # Create a logarithmic chirp fs = 1000. N = 10000 t = np.arange(N) / float(fs) f0, f1 = 1., 500. phase = (t[-1] / np.log(f1 / f0)) * f0 * (pow(f1 / f0, t / t[-1]) - 1.0) data = np.cos(2 * np.pi * phase) fig = vp.Fig(size=(800, 400), show=False) fig[0:2, 0].spectrogram(data, fs=fs, clim=(-100, -20)) fig[2, 0].plot(np.array((t, data)).T, marker_size=0) if __name__ == '__main__': fig.show(run=True) vispy-0.14.1/examples/plotting/scatter_histogram.py0000755000175000017510000000214714507104216022057 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 30 """ Scatter plot and histograms =========================== A scatter plot of 2D points with matching histograms. """ import numpy as np import vispy.plot as vp np.random.seed(2324) n = 100000 data = np.empty((n, 2)) lasti = 0 for i in range(1, 20): nexti = lasti + (n - lasti) // 2 scale = np.abs(np.random.randn(2)) + 0.1 scale[1] = scale.mean() data[lasti:nexti] = np.random.normal(size=(nexti-lasti, 2), loc=np.random.randn(2), scale=scale / i) lasti = nexti data = data[:lasti] color = (0.3, 0.5, 0.8) n_bins = 100 fig = vp.Fig(show=False) line = fig[0:4, 0:4].plot(data, symbol='o', width=0, face_color=color + (0.02,), edge_color=None, marker_size=4) line.set_gl_state(depth_test=False) fig[4, 0:4].histogram(data[:, 0], bins=n_bins, color=color, orientation='h') fig[0:4, 4].histogram(data[:, 1], bins=n_bins, color=color, orientation='v') if __name__ == '__main__': fig.show(run=True) vispy-0.14.1/examples/plotting/plot_colorbars.py0000644000175000017510000000100314507104216021344 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Line plot and colorbar ====================== """ import vispy.plot as vp fig = vp.Fig(size=(600, 500), show=False) plotwidget = fig[0, 0] fig.title = "bollu" plotwidget.plot([(x, x**2) for x in range(0, 100)], title="y = x^2") plotwidget.colorbar(position="top", cmap="autumn") if __name__ == '__main__': fig.show(run=True) vispy-0.14.1/examples/plotting/plot.py0000644000175000017510000000341214507104216017304 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 2 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Plot data with different styles =============================== """ import numpy as np from vispy import plot as vp fig = vp.Fig(size=(600, 500), show=False) # Plot the target square wave shape x = np.linspace(0, 10, 1000) y = np.zeros(1000) y[1:500] = 1 y[500:-1] = -1 line = fig[0, 0].plot((x, y), width=3, color='k', title='Square Wave Fourier Expansion', xlabel='x', ylabel='4/Ï€ Σ[ 1/n sin(nÏ€x/L) | n=1,3,5,...]') y = np.zeros(1000) L = 5 colors = [(0.8, 0, 0, 1), (0.8, 0, 0.8, 1), (0, 0, 1.0, 1), (0, 0.7, 0, 1), ] plot_nvals = [1, 3, 7, 31] for i in range(16): n = i * 2 + 1 y += (4. / np.pi) * (1. / n) * np.sin(n * np.pi * x / L) if n in plot_nvals: tmp_line = fig[0, 0].plot((x, y), color=colors[plot_nvals.index(n)], width=2) tmp_line.update_gl_state(depth_test=False) labelgrid = fig[0, 0].view.add_grid(margin=10) box = vp.Widget(bgcolor=(1, 1, 1, 0.6), border_color='k') box_widget = labelgrid.add_widget(box, row=0, col=1) box_widget.width_max = 90 box_widget.height_max = 120 bottom_spacer = vp.Widget() labelgrid.add_widget(bottom_spacer, row=1, col=0) labels = [vp.Label('n=%d' % plot_nvals[i], color=colors[i], anchor_x='left') for i in range(len(plot_nvals))] boxgrid = box.add_grid() for i, label in enumerate(labels): label_widget = boxgrid.add_widget(label, row=i, col=0) grid = vp.visuals.GridLines(color=(0, 0, 0, 0.5)) grid.set_gl_state('translucent') fig[0, 0].view.add(grid) if __name__ == '__main__': fig.show(run=True) vispy-0.14.1/examples/plotting/ipython_fig_playground.py0000755000175000017510000000112014507104216023106 0ustar nileshnilesh#!/usr/bin/env ipython -i ipython_fig_playground.py # -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Boilerplate Interactive Plotting Session ======================================== Bare bones plotting region that can be used with the python interpreter as a playground. Run with: .. code-block:: bash python -i ipython_fig_playground.py """ from vispy import plot as vp # noqa import numpy as np # noqa fig = vp.Fig(size=(600, 500)) # noqa plotwidget = fig[0, 0] vispy-0.14.1/examples/plotting/export.py0000644000175000017510000000202114507104216017642 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # vispy: gallery 2 # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Plot a scaled Image =================== Demonstrates rendering a canvas to an image at higher resolution than the original display. NOTE: This example is currently broken. """ import vispy.plot as vp # Create a canvas showing plot data fig = vp.Fig() fig[0, 0].plot([1, 6, 2, 4, 3, 8, 5, 7, 6, 3]) # Render the canvas scene to a numpy array image with higher resolution # than the original canvas scale = 4 image = fig.render(size=(fig.size[0]*scale, fig.size[1]*scale)) # Display the data in the array, sub-sampled down to the original canvas # resolution fig_2 = vp.Fig() fig_2[0, 0].image(image[::-scale, ::scale]) # By default, the view adds some padding when setting its range. # We'll remove that padding so the image looks exactly like the original # canvas: fig_2[0, 0].camera.set_range(margin=0) if __name__ == '__main__': fig.app.run() vispy-0.14.1/examples/plotting/colorbar.py0000644000175000017510000000234514507104216020135 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # vispy: gallery 2 """ Plot different styles of ColorBar ================================= """ from vispy import plot as vp import numpy as np # arg( e^(1/z) ) def exp_z_inv(x, y): z = complex(x, y) f = np.exp(1.0 / z) return np.angle(f, deg=True) # create a 2d grid whose elements are of exp_z_inv def gen_image(width, height): x_vals = np.linspace(-0.5, 0.5, width) y_vals = np.linspace(-0.5, 0.5, height) grid = np.meshgrid(x_vals, y_vals) v_fn = np.vectorize(exp_z_inv) return v_fn(*grid).astype(np.float32) fig = vp.Fig(size=(800, 600), show=False) plot = fig[0, 0] plot.bgcolor = "#efefef" img = gen_image(500, 500) plot.image(img, cmap="hsl") plot.camera.set_range((100, 400), (100, 400)) positions = ["top", "bottom", "left", "right"] for position in positions: plot.colorbar(position=position, label="argument of e^(1/z)", clim=("0°", "180°"), cmap="hsl", border_width=1, border_color="#aeaeae") if __name__ == '__main__': fig.show(run=True) vispy-0.14.1/examples/plotting/README.rst0000644000175000017510000000024214507104216017441 0ustar nileshnileshPlotting ======== More example scripts are available in the VisPy repository's `example scripts directory `_. vispy-0.14.1/examples/offscreen/0000755000175000017510000000000014507104250016064 5ustar nileshnileshvispy-0.14.1/examples/offscreen/simple_osmesa.py0000644000175000017510000000250314507104216021300 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This is a simple osmesa example that produce an image of a cube If you have both osmesa and normal (X) OpenGL installed, execute with something like the following to pickup the OSMesa libraries:: VISPY_GL_LIB=/opt/osmesa_llvmpipe/lib/libGLESv2.so \ LD_LIBRARY_PATH=/opt/osmesa_llvmpipe/lib/ \ OSMESA_LIBRARY=/opt/osmesa_llvmpipe/lib/libOSMesa.so \ python examples/offscreen/simple_osmesa.py """ import numpy as np import vispy import vispy.plot as vp import vispy.io as io vispy.use(app='osmesa') # noqa # Check the application correctly picked up osmesa assert vispy.app.use_app().backend_name == 'osmesa', 'Not using OSMesa' data = np.load(io.load_data_file('electrophys/iv_curve.npz'))['arr_0'] time = np.arange(0, data.shape[1], 1e-4) fig = vp.Fig(size=(800, 800), show=False) x = np.linspace(0, 10, 20) y = np.cos(x) line = fig[0, 0].plot((x, y), symbol='o', width=3, title='I/V Curve', xlabel='Current (pA)', ylabel='Membrane Potential (mV)') grid = vp.visuals.GridLines(color=(0, 0, 0, 0.5)) grid.set_gl_state('translucent') fig[0, 0].view.add(grid) fig.show() img = fig.render() io.write_png("osmesa.png", img) vispy-0.14.1/examples/offscreen/simple_egl.py0000644000175000017510000000216214507104216020561 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This is a simple egl example that produces 2d plot. Was tested on Linux with Nvidia drivers. Requires libEGLso.1 to be found in the system. """ import vispy import numpy as np import vispy.plot as vp import vispy.io as io vispy.use(app='egl') # Check the application correctly picked up egl assert vispy.app.use_app().backend_name == 'egl', 'Not using EGL' if __name__ == '__main__': data = np.load(io.load_data_file('electrophys/iv_curve.npz'))['arr_0'] time = np.arange(0, data.shape[1], 1e-4) fig = vp.Fig(size=(800, 800), show=False) x = np.linspace(0, 10, 20) y = np.cos(x) line = fig[0, 0].plot((x, y), symbol='o', width=3, title='I/V Curve', xlabel='Current (pA)', ylabel='Membrane Potential (mV)') grid = vp.visuals.GridLines(color=(0, 0, 0, 0.5)) grid.set_gl_state('translucent') fig[0, 0].view.add(grid) fig.show() img = fig.render() io.write_png("egl.png", img) vispy-0.14.1/examples/jupyter/0000755000175000017510000000000014507104250015614 5ustar nileshnileshvispy-0.14.1/examples/jupyter/gloo_molecular_viewer.ipynb0000644000175000017510000002246114507104216023252 0ustar nileshnilesh{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Jupyter backend demo: molecular viewer in the notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example shows how vispy's low-level gloo interface can be used to display a VisPy Canvas in a notebook. The higher level Scene and Plotting API are also supported. By default, vispy will detect that it is being run in a notebook and load the \"jupyter_rfb\" backend automatically. This does require that the \"jupyter_rfb\" package be installed.\n", "\n", "Due to the way widgets work in Jupyter, you can display the Canvas multiple times, but they will continue to represent the same object. In the case of timers, they will continue to run in the backgroudn until they are stopped, even if the output of your notebook cell is cleared and no canvas is visible." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "from vispy import gloo\n", "from vispy import app\n", "from vispy.util.transforms import perspective, translate, rotate\n", "from vispy.io import load_data_file" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vertex = \"\"\"\n", "#version 120\n", "\n", "uniform mat4 u_model;\n", "uniform mat4 u_view;\n", "uniform mat4 u_projection;\n", "uniform vec3 u_light_position;\n", "uniform vec3 u_light_spec_position;\n", "\n", "attribute vec3 a_position;\n", "attribute vec3 a_color;\n", "attribute float a_radius;\n", "\n", "varying vec3 v_color;\n", "varying vec4 v_eye_position;\n", "varying float v_radius;\n", "varying vec3 v_light_direction;\n", "\n", "void main (void) {\n", " v_radius = a_radius;\n", " v_color = a_color;\n", "\n", " v_eye_position = u_view * u_model * vec4(a_position,1.0);\n", " v_light_direction = normalize(u_light_position);\n", " float dist = length(v_eye_position.xyz);\n", "\n", " gl_Position = u_projection * v_eye_position;\n", "\n", " // stackoverflow.com/questions/8608844/...\n", " // ... resizing-point-sprites-based-on-distance-from-the-camera\n", " vec4 proj_corner = u_projection * vec4(a_radius, a_radius, v_eye_position.z, v_eye_position.w); // # noqa\n", " gl_PointSize = 512.0 * proj_corner.x / proj_corner.w;\n", "}\n", "\"\"\"\n", "\n", "fragment = \"\"\"\n", "#version 120\n", "\n", "uniform mat4 u_model;\n", "uniform mat4 u_view;\n", "uniform mat4 u_projection;\n", "uniform vec3 u_light_position;\n", "uniform vec3 u_light_spec_position;\n", "\n", "varying vec3 v_color;\n", "varying vec4 v_eye_position;\n", "varying float v_radius;\n", "varying vec3 v_light_direction;\n", "void main()\n", "{\n", " // r^2 = (x - x0)^2 + (y - y0)^2 + (z - z0)^2\n", " vec2 texcoord = gl_PointCoord* 2.0 - vec2(1.0);\n", " float x = texcoord.x;\n", " float y = texcoord.y;\n", " float d = 1.0 - x*x - y*y;\n", " if (d <= 0.0)\n", " discard;\n", "\n", " float z = sqrt(d);\n", " vec4 pos = v_eye_position;\n", " pos.z += v_radius*z;\n", " vec3 pos2 = pos.xyz;\n", " pos = u_projection * pos;\n", " //gl_FragDepth = 0.5*(pos.z / pos.w)+0.5;\n", " vec3 normal = vec3(x,y,z);\n", " float diffuse = clamp(dot(normal, v_light_direction), 0.0, 1.0);\n", "\n", " // Specular lighting.\n", " vec3 M = pos2.xyz;\n", " vec3 O = v_eye_position.xyz;\n", " vec3 L = u_light_spec_position;\n", " vec3 K = normalize(normalize(L - M) + normalize(O - M));\n", " // WARNING: abs() is necessary, otherwise weird bugs may appear with some\n", " // GPU drivers...\n", " float specular = clamp(pow(abs(dot(normal, K)), 40.), 0.0, 1.0);\n", " vec3 v_light = vec3(1., 1., 1.);\n", " gl_FragColor.rgba = vec4(.15*v_color + .55*diffuse * v_color\n", " + .35*specular * v_light, 1.0);\n", "}\n", "\"\"\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Canvas(app.Canvas):\n", "\n", " def __init__(self):\n", " app.Canvas.__init__(self, title='Molecular viewer',\n", " keys='interactive', size=(640, 480))\n", " self.ps = self.pixel_scale\n", "\n", " self.translate = 40\n", " self.program = gloo.Program(vertex, fragment)\n", " self.view = translate((0, 0, -self.translate))\n", " self.model = np.eye(4, dtype=np.float32)\n", " self.projection = np.eye(4, dtype=np.float32)\n", "\n", " self.apply_zoom()\n", "\n", " fname = load_data_file('molecular_viewer/micelle.npz')\n", " self.load_molecule(fname)\n", " self.load_data()\n", "\n", " self.theta = 0\n", " self.phi = 0\n", "\n", " gloo.set_state(depth_test=True, clear_color='black')\n", " self.timer = app.Timer('auto', connect=self.on_timer, start=True)\n", "\n", " self.show()\n", "\n", " def load_molecule(self, fname):\n", " molecule = np.load(fname)['molecule']\n", " self._nAtoms = molecule.shape[0]\n", "\n", " # The x,y,z values store in one array\n", " self.coords = molecule[:, :3]\n", "\n", " # The array that will store the color and alpha scale for all the atoms\n", " self.atomsColours = molecule[:, 3:6]\n", "\n", " # The array that will store the scale for all the atoms.\n", " self.atomsScales = molecule[:, 6]\n", "\n", " def load_data(self):\n", " n = self._nAtoms\n", "\n", " data = np.zeros(n, [('a_position', np.float32, 3),\n", " ('a_color', np.float32, 3),\n", " ('a_radius', np.float32)])\n", "\n", " data['a_position'] = self.coords\n", " data['a_color'] = self.atomsColours\n", " data['a_radius'] = self.atomsScales*self.ps\n", "\n", " self.program.bind(gloo.VertexBuffer(data))\n", "\n", " self.program['u_model'] = self.model\n", " self.program['u_view'] = self.view\n", " self.program['u_light_position'] = 0., 0., 2.\n", " self.program['u_light_spec_position'] = -5., 5., -5.\n", "\n", " def on_key_press(self, event):\n", " if event.text == ' ':\n", " if self.timer.running:\n", " self.timer.stop()\n", " else:\n", " self.timer.start()\n", "\n", " def on_timer(self, event):\n", " self.theta += .25\n", " self.phi += .25\n", " self.model = np.dot(rotate(self.theta, (0, 0, 1)),\n", " rotate(self.phi, (0, 1, 0)))\n", " self.program['u_model'] = self.model\n", " self.update()\n", "\n", " def on_resize(self, event):\n", " width, height = event.physical_size\n", " gloo.set_viewport(0, 0, width, height)\n", " self.projection = perspective(25.0, width / float(height), 2.0, 100.0)\n", " self.program['u_projection'] = self.projection\n", "\n", " def apply_zoom(self):\n", " width, height = self.physical_size\n", " gloo.set_viewport(0, 0, width, height)\n", " self.projection = perspective(25.0, width / float(height), 2.0, 100.0)\n", " self.program['u_projection'] = self.projection\n", "\n", " def on_mouse_wheel(self, event):\n", " self.translate -= event.delta[1]\n", " self.translate = max(-1, self.translate)\n", " self.view = translate((0, 0, -self.translate))\n", "\n", " self.program['u_view'] = self.view\n", " self.update()\n", "\n", " def on_draw(self, event):\n", " gloo.clear()\n", " self.program.draw('points')\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "c = Canvas()\n", "c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To pause the rotation you can either hit the Spacebar or manually call the `stop(`)/`start()` methods on the timer:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "c.timer.stop()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "c.timer.start()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.10" } }, "nbformat": 4, "nbformat_minor": 4 } vispy-0.14.1/examples/jupyter/gloo_display_lines.ipynb0000644000175000017510000001555714507104216022555 0ustar nileshnilesh{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Jupyter Notebook backend demo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example shows how vispy's low-level gloo interface can be used to display a Canvas in a notebook. By default, vispy will detect that it is being run in a notebook and load the \"jupyter_rfb\" backend automatically. This does require that the \"jupyter_rfb\" package be installed.\n", "\n", "Due to the way widgets work in Jupyter, you can display the Canvas multiple times, but they will continue to represent the same object. In the case of timers, they will continue to run in the backgroudn until they are stopped, even if the output of your notebook cell is cleared and no canvas is visible." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import vispy\n", "import vispy.gloo as gloo\n", "from vispy import app\n", "from vispy.util.transforms import perspective, translate, rotate" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n = 100\n", "a_position = np.random.uniform(-1, 1, (n, 3)).astype(np.float32)\n", "a_id = np.random.randint(0, 30, (n, 1))\n", "a_id = np.sort(a_id, axis=0).astype(np.float32)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "VERT_SHADER = \"\"\"\n", "uniform mat4 u_model;\n", "uniform mat4 u_view;\n", "uniform mat4 u_projection;\n", "attribute vec3 a_position;\n", "attribute float a_id;\n", "varying float v_id;\n", "void main (void) {\n", " v_id = a_id;\n", " gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0);\n", "}\n", "\"\"\"\n", "\n", "FRAG_SHADER = \"\"\"\n", "varying float v_id;\n", "void main()\n", "{\n", " float f = fract(v_id);\n", " // The second useless test is needed on OSX 10.8 (fuck)\n", " if( (f > 0.0001) && (f < .9999) )\n", " discard;\n", " else\n", " gl_FragColor = vec4(0,0,0,1);\n", "}\n", "\"\"\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Canvas(app.Canvas):\n", "\n", " # ---------------------------------\n", " def __init__(self, size=None, show=True):\n", " app.Canvas.__init__(self, keys='interactive', size=size)\n", "\n", " self.program = gloo.Program(VERT_SHADER, FRAG_SHADER)\n", "\n", " # Set uniform and attribute\n", " self.program['a_id'] = gloo.VertexBuffer(a_id)\n", " self.program['a_position'] = gloo.VertexBuffer(a_position)\n", "\n", " self.translate = 5\n", " self.view = translate((0, 0, -self.translate), dtype=np.float32)\n", " self.model = np.eye(4, dtype=np.float32)\n", "\n", " gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1])\n", " self.projection = perspective(45.0, self.size[0] /\n", " float(self.size[1]), 1.0, 1000.0)\n", " self.program['u_projection'] = self.projection\n", "\n", " self.program['u_model'] = self.model\n", " self.program['u_view'] = self.view\n", "\n", " self.theta = 0\n", " self.phi = 0\n", "\n", " self.context.set_clear_color('white')\n", " self.context.set_state('translucent')\n", "\n", " self.timer = app.Timer('auto', connect=self.on_timer, start=True)\n", "\n", " if show:\n", " self.show()\n", "\n", " # ---------------------------------\n", " def on_key_press(self, event):\n", " if event.text == ' ':\n", " if self.timer.running:\n", " self.timer.stop()\n", " else:\n", " self.timer.start()\n", "\n", " # ---------------------------------\n", " def on_timer(self, event):\n", " self.theta += .5\n", " self.phi += .5\n", " self.model = np.dot(rotate(self.theta, (0, 0, 1)),\n", " rotate(self.phi, (0, 1, 0)))\n", " self.program['u_model'] = self.model\n", " self.update()\n", "\n", " # ---------------------------------\n", " def on_resize(self, event):\n", " gloo.set_viewport(0, 0, event.physical_size[0], event.physical_size[1])\n", " self.projection = perspective(45.0, event.size[0] /\n", " float(event.size[1]), 1.0, 1000.0)\n", " self.program['u_projection'] = self.projection\n", "\n", " # ---------------------------------\n", " def on_mouse_wheel(self, event):\n", " self.translate += event.delta[1]\n", " self.translate = max(2, self.translate)\n", " self.view = translate((0, 0, -self.translate))\n", " self.program['u_view'] = self.view\n", " self.update()\n", "\n", " # ---------------------------------\n", " def on_draw(self, event):\n", " self.context.clear()\n", " self.program.draw('line_strip')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Every cell above was preparing our GL Canvas for operation. Now we will create the Canvas instance and because of the `self.show()` in our `__init__` method our canvas will be shown and its timer started immediately." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "c = Canvas(size=(300, 300))\n", "c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When timers are involved we can run the `stop` and `start` methods to turn them on/off and see the result in the widget displayed above." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "c.timer.stop()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "c.timer.start()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.10" } }, "nbformat": 4, "nbformat_minor": 4 } vispy-0.14.1/examples/jupyter/colormaps.ipynb0000644000175000017510000000741314507104216020665 0ustar nileshnilesh{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# VisPy colormaps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook illustrates the colormap API provided by VisPy." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## List all colormaps" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "import numpy as np\n", "from vispy.color import (get_colormap, get_colormaps, Colormap)\n", "from IPython.display import display_html" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "for cmap in get_colormaps():\n", " display_html('

%s

' % cmap, raw=True)\n", " display_html(get_colormap(cmap))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Discrete colormaps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Discrete colormaps can be created by giving a list of colors, and an optional list of control points (in $[0,1]$, the first and last points need to be $0$ and $1$ respectively). The colors can be specified in many ways (1-character shortcuts, hexadecimal values, arrays or RGB values, `ColorArray` instances, and so on)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "Colormap(['r', 'g', 'b'], interpolation='zero')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "Colormap(['r', 'g', 'y'], interpolation='zero')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "Colormap(np.array([[0, .75, 0],\n", " [.75, .25, .5]]), \n", " [0., .25, 1.], \n", " interpolation='zero')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "Colormap(['r', 'g', '#123456'],\n", " interpolation='zero')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Linear gradients" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "Colormap(['r', 'g', '#123456'])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "Colormap([[1,0,0], [1,1,1], [1,0,1]], \n", " [0., .75, 1.])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.10" } }, "nbformat": 4, "nbformat_minor": 4 } vispy-0.14.1/examples/jupyter/Rotating Cube.ipynb0000644000175000017510000000703614507104216021315 0ustar nileshnilesh{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Rotating Cube\n", "\n", "This is a notebook version of the \"examples/basics/visuals/cube.py\" example." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import vispy\n", "vispy.use(\"jupyter_rfb\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sys\n", "\n", "from vispy import app, gloo\n", "from vispy.visuals import CubeVisual, transforms\n", "\n", "\n", "class Canvas(app.Canvas):\n", " def __init__(self):\n", " app.Canvas.__init__(self, 'Cube', keys='interactive',\n", " size=(400, 400))\n", "\n", " self.cube = CubeVisual((1.0, 0.5, 0.25), color='red', edge_color=\"k\")\n", " self.theta = 0\n", " self.phi = 0\n", "\n", " # Create a TransformSystem that will tell the visual how to draw\n", " self.cube_transform = transforms.MatrixTransform()\n", " self.cube.transform = self.cube_transform\n", "\n", " self.timer = app.Timer('auto', connect=self.on_timer, start=True)\n", "\n", " def on_resize(self, event):\n", " # Set canvas viewport and reconfigure visual transforms to match.\n", " vp = (0, 0, self.physical_size[0], self.physical_size[1])\n", " self.context.set_viewport(*vp)\n", " self.cube.transforms.configure(canvas=self, viewport=vp)\n", "\n", " def on_draw(self, event):\n", " gloo.set_viewport(0, 0, *self.physical_size)\n", " gloo.clear('white', depth=True)\n", "\n", " self.cube.draw()\n", "\n", " def on_timer(self, event):\n", " self.theta += .5\n", " self.phi += .5\n", " self.cube_transform.reset()\n", " self.cube_transform.rotate(self.theta, (0, 0, 1))\n", " self.cube_transform.rotate(self.phi, (0, 1, 0))\n", " self.cube_transform.scale((100, 100, 0.001))\n", " self.cube_transform.translate((200, 200))\n", " self.update()\n", "\n", "\n", "win = Canvas()\n", "win.show()\n", "win" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The above implementation uses a timer to continuously update the coordinates of the cube so it appears to rotate. This will go until the timer is stopped which you can do by running:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "win.timer.stop()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can restart the rotation by calling the `.start()` method on the timer:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "win.timer.start()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.10" } }, "nbformat": 4, "nbformat_minor": 4 } vispy-0.14.1/examples/gloo/0000755000175000017510000000000014507104250015052 5ustar nileshnileshvispy-0.14.1/examples/gloo/textured_quad.py0000644000175000017510000000361314507104216020307 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- """ Show a textured quad ==================== """ import numpy as np from vispy import gloo, app from vispy.gloo import Program vertex = """ attribute vec2 position; attribute vec2 texcoord; varying vec2 v_texcoord; void main() { gl_Position = vec4(position, 0.0, 1.0); v_texcoord = texcoord; } """ fragment = """ uniform sampler2D texture; varying vec2 v_texcoord; void main() { gl_FragColor = texture2D(texture, v_texcoord); } """ def checkerboard(grid_num=8, grid_size=32): row_even = grid_num // 2 * [0, 1] row_odd = grid_num // 2 * [1, 0] Z = np.row_stack(grid_num // 2 * (row_even, row_odd)).astype(np.uint8) return 255 * Z.repeat(grid_size, axis=0).repeat(grid_size, axis=1) class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Textured quad', keys='interactive') # Build program & data self.program = Program(vertex, fragment, count=4) self.program['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] self.program['texcoord'] = [(0, 0), (1, 0), (0, 1), (1, 1)] self.program['texture'] = checkerboard() gloo.set_viewport(0, 0, *self.physical_size) self.show() def on_draw(self, event): gloo.set_clear_color('white') gloo.clear(color=True) self.program.draw('triangle_strip') def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/gloo/textured_cube.py0000644000175000017510000000613114507104216020271 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 # ----------------------------------------------------------------------------- """ Show a rotating textured cube ============================= """ import numpy as np from vispy import gloo, app from vispy.gloo import Program, VertexBuffer, IndexBuffer from vispy.util.transforms import perspective, translate, rotate from vispy.geometry import create_cube vertex = """ uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform sampler2D texture; attribute vec3 position; attribute vec2 texcoord; attribute vec3 normal; attribute vec4 color; varying vec2 v_texcoord; void main() { gl_Position = projection * view * model * vec4(position,1.0); v_texcoord = texcoord; } """ fragment = """ uniform sampler2D texture; varying vec2 v_texcoord; void main() { gl_FragColor = texture2D(texture, v_texcoord); } """ def checkerboard(grid_num=8, grid_size=32): row_even = grid_num // 2 * [0, 1] row_odd = grid_num // 2 * [1, 0] Z = np.row_stack(grid_num // 2 * (row_even, row_odd)).astype(np.uint8) return 255 * Z.repeat(grid_size, axis=0).repeat(grid_size, axis=1) class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Textured cube', keys='interactive') self.timer = app.Timer('auto', self.on_timer) # Build cube data V, I, _ = create_cube() vertices = VertexBuffer(V) self.indices = IndexBuffer(I) # Build program self.program = Program(vertex, fragment) self.program.bind(vertices) # Build view, model, projection & normal view = translate((0, 0, -5)) model = np.eye(4, dtype=np.float32) self.program['model'] = model self.program['view'] = view self.program['texture'] = checkerboard() self.activate_zoom() self.phi, self.theta = 0, 0 # OpenGL initalization gloo.set_state(clear_color=(0.30, 0.30, 0.35, 1.00), depth_test=True) self.timer.start() self.show() def on_draw(self, event): gloo.clear(color=True, depth=True) self.program.draw('triangles', self.indices) def on_resize(self, event): self.activate_zoom() def activate_zoom(self): gloo.set_viewport(0, 0, *self.physical_size) projection = perspective(45.0, self.size[0] / float(self.size[1]), 2.0, 10.0) self.program['projection'] = projection def on_timer(self, event): self.theta += .5 self.phi += .5 self.program['model'] = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.update() if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/gloo/texture_precision.py0000644000175000017510000001001714507104216021200 0ustar nileshnilesh#!/usr/bin/env python # ----------------------------------------------------------------------------- # Copyright 2015 University of Southern California. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Karl Czajkowski # Date: 2015-01-22 # ----------------------------------------------------------------------------- """ Example using texture internalformat for higher precision ========================================================= Generates a gradient texture with high dynamic range and renders it with a fragment shader that tests for quantization errors by comparing adjacent texels and decomposes the gradient values into high and low significance bits, mapping them to separate display color channels. Pressing the 'f' key cycles through a list of different texture formats to show the different levels of precision available. """ import numpy as np from vispy import gloo from vispy import app W, H = 1024, 1024 # prepare a gradient field with high dynamic range data = np.zeros((H, W, 3), np.float32) for i in range(W): data[:, i, :] = i**2 for i in range(H): data[i, :, :] *= i**2 data *= 1./data.max() # prepare a simple quad to cover the viewport quad = np.zeros(4, dtype=[ ('a_position', np.float32, 2), ('a_texcoord', np.float32, 2) ]) quad['a_position'] = np.array([[-1, -1], [+1, -1], [-1, +1], [+1, +1]]) quad['a_texcoord'] = np.array([[0, 0], [1, 0], [0, 1], [1, 1]]) vert_shader = """ attribute vec2 a_position; attribute vec2 a_texcoord; varying vec2 v_texcoord; void main() { v_texcoord = a_texcoord; gl_Position = vec4(a_position, 0.0, 1.0); } """ frag_shader = """ uniform sampler2D u_texture; varying vec2 v_texcoord; void main() { float ndiff; // an adjacent texel is 1/W further over in normalized texture coordinates vec2 v_texcoord2 = vec2(clamp(v_texcoord.x + 1.0/%(W)d., 0.0, 1.0), v_texcoord.y); vec4 texel1 = texture2D(u_texture, v_texcoord); vec4 texel2 = texture2D(u_texture, v_texcoord2); // test for quantized binning of adjacent texels if (texel1.r == texel2.r && v_texcoord2.x < 1.0 && v_texcoord.y > 0.0) ndiff = 1.0; else ndiff = 0.0; gl_FragColor = vec4( fract(texel1.r * 255.0), // render low-significance bits as red texel1.r, // render high-significance bits as green ndiff, // flag quantized bands as blue 1); } """ % dict(W=W) class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(W, H), keys='interactive') self._internalformats = [ 'rgb8', 'rgb16', 'rgb16f', 'rgb32f' ] self.program = gloo.Program(vert_shader, frag_shader) self.program.bind(gloo.VertexBuffer(quad)) self._internalformat = -1 self.texture = gloo.Texture2D( shape=(H, W, 3), interpolation='nearest' ) gloo.set_viewport(0, 0, *self.physical_size) self.toggle_internalformat() self.show() def on_key_press(self, event): if event.key == 'F': self.toggle_internalformat() def toggle_internalformat(self): self._internalformat = ( (self._internalformat + 1) % len(self._internalformats) ) internalformat = self._internalformats[self._internalformat] print("Requesting texture internalformat %s" % internalformat) self.texture.resize( data.shape, format='rgb', internalformat=internalformat ) self.texture.set_data(data) self.program['u_texture'] = self.texture self.update() def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) def on_draw(self, event): gloo.clear(color=True, depth=True) self.program.draw('triangle_strip') if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/gloo/start_shaders.py0000644000175000017510000000216214507104216020275 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 1 """ Simplest Shader Program ======================= """ import sys from vispy import gloo from vispy import app import numpy as np VERT_SHADER = """ attribute vec2 a_position; uniform float u_size; void main() { gl_Position = vec4(a_position, 0.0, 1.0); gl_PointSize = u_size; } """ FRAG_SHADER = """ void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive') ps = self.pixel_scale self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) data = np.random.uniform(-0.5, 0.5, size=(20, 2)) self.program['a_position'] = data.astype(np.float32) self.program['u_size'] = 20.*ps self.show() def on_resize(self, event): width, height = event.size gloo.set_viewport(0, 0, width, height) def on_draw(self, event): gloo.clear('white') self.program.draw('points') if __name__ == '__main__': canvas = Canvas() if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/gloo/start.py0000755000175000017510000000061314507104216016566 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 1 """ Simplest Possible Script ======================== """ import sys from vispy import app, gloo canvas = app.Canvas(keys='interactive') @canvas.connect def on_draw(event): gloo.set_clear_color((0.2, 0.4, 0.6, 1.0)) gloo.clear() canvas.show() if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/gloo/spatial_filters.py0000644000175000017510000000650414507104216020620 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2 """ Spatial Filtering ================= Example demonstrating spatial filtering using spatial-filters fragment shader. Left and Right Arrow Keys toggle through available filters. """ import numpy as np from vispy.io import load_spatial_filters from vispy import gloo from vispy import app from vispy.util.logs import set_log_level # turn off INFO messages, see PR #1363 # Some shader compilers will optimize out the 'u_shape' and 'u_kernel' # uniforms for the Nearest filter since they are unused, resulting in # an INFO message about them not being active set_log_level('warning') # create 5x5 matrix with border pixels 0, center pixels 1 # and other pixels 0.5 img_array = np.zeros(25).reshape((5, 5)).astype(np.float32) img_array[1:4, 1::2] = 0.5 img_array[1::2, 2] = 0.5 img_array[2, 2] = 1.0 # loading interpolation kernel kernel, names = load_spatial_filters() names = [name + '2D' for name in names] # A simple texture quad data = np.zeros(4, dtype=[('a_position', np.float32, 2), ('a_texcoord', np.float32, 2)]) data['a_position'] = np.array([[-1, -1], [+1, -1], [-1, +1], [+1, +1]]) data['a_texcoord'] = np.array([[1, 0], [1, 1], [0, 0], [0, 1]]) VERT_SHADER = """ // Attributes attribute vec2 a_position; attribute vec2 a_texcoord; // Varyings varying vec2 v_texcoord; // Main void main (void) { v_texcoord = a_texcoord; gl_Position = vec4(a_position,0.0,1.0); } """ FRAG_SHADER = """ #include "misc/spatial-filters.frag" uniform sampler2D u_texture; uniform vec2 u_shape; varying vec2 v_texcoord; void main() { gl_FragColor = %s(u_texture, u_shape, v_texcoord); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=((512), (512))) self.program = gloo.Program(VERT_SHADER, FRAG_SHADER % 'Nearest2D') self.texture = gloo.Texture2D(img_array, interpolation='nearest') # using packed data as discussed in pr #1069 self.kernel = gloo.Texture2D(kernel, interpolation='nearest') self.program['u_texture'] = self.texture self.program['u_shape'] = img_array.shape[1], img_array.shape[0] self.program['u_kernel'] = self.kernel self.names = names self.filter = 16 self.title = 'Spatial Filtering using %s Filter' % \ self.names[self.filter] self.program.bind(gloo.VertexBuffer(data)) self.context.set_clear_color('white') self.context.set_viewport(0, 0, 512, 512) self.show() def on_key_press(self, event): if event.key in ['Left', 'Right']: if event.key == 'Right': step = 1 else: step = -1 self.filter = (self.filter + step) % 17 self.program.set_shaders(VERT_SHADER, FRAG_SHADER % self.names[self.filter]) self.title = 'Spatial Filtering using %s Filter' % \ self.names[self.filter] self.update() def on_resize(self, event): self.context.set_viewport(0, 0, *event.physical_size) def on_draw(self, event): self.context.clear(color=True, depth=True) self.program.draw('triangle_strip') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/rotating_quad.py0000644000175000017510000000426214507104216020273 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 1:35:2 # ----------------------------------------------------------------------------- # Copyright (c) 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 # ----------------------------------------------------------------------------- """ Rotating Quad ============= Use a Timer to animate a quad """ from vispy import gloo, app from vispy.gloo import Program vertex = """ uniform float theta; attribute vec4 color; attribute vec2 position; varying vec4 v_color; void main() { float ct = cos(theta); float st = sin(theta); float x = 0.75* (position.x*ct - position.y*st); float y = 0.75* (position.x*st + position.y*ct); gl_Position = vec4(x, y, 0.0, 1.0); v_color = color; } """ fragment = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): super().__init__(size=(512, 512), title='Rotating quad', keys='interactive') # Build program & data self.program = Program(vertex, fragment, count=4) self.program['color'] = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1), (1, 1, 0, 1)] self.program['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] self.program['theta'] = 0.0 gloo.set_viewport(0, 0, *self.physical_size) gloo.set_clear_color('white') self.timer = app.Timer('auto', self.on_timer) self.clock = 0 self.timer.start() self.show() def on_draw(self, event): gloo.clear() self.program.draw('triangle_strip') def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) def on_timer(self, event): self.clock += 0.001 * 1000.0 / 60. self.program['theta'] = self.clock self.update() if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/gloo/rotate_cube.py0000644000175000017510000001230514507104216017723 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 5:105:5 """ Draw 3D rotating Cube ===================== This example shows how to display 3D objects. """ import numpy as np from vispy import app, gloo from vispy.util.transforms import perspective, translate, rotate vert = """ // Uniforms // ------------------------------------ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform vec4 u_color; // Attributes // ------------------------------------ attribute vec3 a_position; attribute vec4 a_color; attribute vec3 a_normal; // Varying // ------------------------------------ varying vec4 v_color; void main() { v_color = a_color * u_color; gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0); } """ frag = """ // Varying // ------------------------------------ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ # ----------------------------------------------------------------------------- def cube(): """ Build vertices for a colored cube. V is the vertices I1 is the indices for a filled cube (use with GL_TRIANGLES) I2 is the indices for an outline cube (use with GL_LINES) """ vtype = [('a_position', np.float32, 3), ('a_normal', np.float32, 3), ('a_color', np.float32, 4)] # Vertices positions v = [[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 = [[0, 0, 1], [1, 0, 0], [0, 1, 0], [-1, 0, 0], [0, -1, 0], [0, 0, -1]] # Vertice colors c = [[0, 1, 1, 1], [0, 0, 1, 1], [0, 0, 0, 1], [0, 1, 0, 1], [1, 1, 0, 1], [1, 1, 1, 1], [1, 0, 1, 1], [1, 0, 0, 1]] V = np.array([(v[0], n[0], c[0]), (v[1], n[0], c[1]), (v[2], n[0], c[2]), (v[3], n[0], c[3]), (v[0], n[1], c[0]), (v[3], n[1], c[3]), (v[4], n[1], c[4]), (v[5], n[1], c[5]), (v[0], n[2], c[0]), (v[5], n[2], c[5]), (v[6], n[2], c[6]), (v[1], n[2], c[1]), (v[1], n[3], c[1]), (v[6], n[3], c[6]), (v[7], n[3], c[7]), (v[2], n[3], c[2]), (v[7], n[4], c[7]), (v[4], n[4], c[4]), (v[3], n[4], c[3]), (v[2], n[4], c[2]), (v[4], n[5], c[4]), (v[7], n[5], c[7]), (v[6], n[5], c[6]), (v[5], n[5], c[5])], dtype=vtype) I1 = np.resize(np.array([0, 1, 2, 0, 2, 3], dtype=np.uint32), 6 * (2 * 3)) I1 += np.repeat(4 * np.arange(2 * 3, dtype=np.uint32), 6) I2 = np.resize( np.array([0, 1, 1, 2, 2, 3, 3, 0], dtype=np.uint32), 6 * (2 * 4)) I2 += np.repeat(4 * np.arange(6, dtype=np.uint32), 8) return V, I1, I2 # ----------------------------------------------------------------------------- class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 600)) self.vertices, self.filled, self.outline = cube() self.filled_buf = gloo.IndexBuffer(self.filled) self.outline_buf = gloo.IndexBuffer(self.outline) self.program = gloo.Program(vert, frag) self.program.bind(gloo.VertexBuffer(self.vertices)) self.view = translate((0, 0, -5)) self.model = np.eye(4, dtype=np.float32) gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self.projection = perspective(45.0, self.size[0] / float(self.size[1]), 2.0, 10.0) self.program['u_projection'] = self.projection self.program['u_model'] = self.model self.program['u_view'] = self.view self.theta = 0 self.phi = 0 gloo.set_clear_color('white') gloo.set_state('opaque') gloo.set_polygon_offset(1, 1) self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() # --------------------------------- def on_timer(self, event): self.theta += .5 self.phi += .5 self.model = np.dot(rotate(self.theta, (0, 1, 0)), rotate(self.phi, (0, 0, 1))) self.program['u_model'] = self.model self.update() # --------------------------------- def on_resize(self, event): gloo.set_viewport(0, 0, event.physical_size[0], event.physical_size[1]) self.projection = perspective(45.0, event.size[0] / float(event.size[1]), 2.0, 10.0) self.program['u_projection'] = self.projection # --------------------------------- def on_draw(self, event): gloo.clear() # Filled cube gloo.set_state(blend=False, depth_test=True, polygon_offset_fill=True) self.program['u_color'] = 1, 1, 1, 1 self.program.draw('triangles', self.filled_buf) # Outline gloo.set_state(blend=True, depth_test=True, polygon_offset_fill=False) gloo.set_depth_mask(False) self.program['u_color'] = 0, 0, 0, 1 self.program.draw('lines', self.outline_buf) gloo.set_depth_mask(True) # ----------------------------------------------------------------------------- if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/post_processing.py0000644000175000017510000001141114507104216020645 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 5:105:5 # ----------------------------------------------------------------------------- # Copyright (c) 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 # Abstract: Show post-processing technique using framebuffer # Keywords: framebuffer, gloo, cube, post-processing # ----------------------------------------------------------------------------- """ Show post-processing using a FrameBuffer ======================================== """ import numpy as np from vispy import app from vispy.geometry import create_cube from vispy.util.transforms import perspective, translate, rotate from vispy.gloo import (Program, VertexBuffer, IndexBuffer, Texture2D, clear, FrameBuffer, RenderBuffer, set_viewport, set_state) cube_vertex = """ uniform mat4 model; uniform mat4 view; uniform mat4 projection; attribute vec3 position; attribute vec2 texcoord; attribute vec3 normal; // not used in this example attribute vec4 color; // not used in this example varying vec2 v_texcoord; void main() { gl_Position = projection * view * model * vec4(position,1.0); v_texcoord = texcoord; } """ cube_fragment = """ uniform sampler2D texture; varying vec2 v_texcoord; void main() { float r = texture2D(texture, v_texcoord).r; gl_FragColor = vec4(r,r,r,1); } """ quad_vertex = """ attribute vec2 position; attribute vec2 texcoord; varying vec2 v_texcoord; void main() { gl_Position = vec4(position, 0.0, 1.0); v_texcoord = texcoord; } """ quad_fragment = """ uniform sampler2D texture; varying vec2 v_texcoord; void main() { vec2 d = 5.0 * vec2(sin(v_texcoord.y*50.0),0)/512.0; // Inverse video if( v_texcoord.x > 0.5 ) { gl_FragColor.rgb = 1.0-texture2D(texture, v_texcoord+d).rgb; } else { gl_FragColor = texture2D(texture, v_texcoord); } } """ def checkerboard(grid_num=8, grid_size=32): row_even = grid_num // 2 * [0, 1] row_odd = grid_num // 2 * [1, 0] Z = np.row_stack(grid_num // 2 * (row_even, row_odd)).astype(np.uint8) return 255 * Z.repeat(grid_size, axis=0).repeat(grid_size, axis=1) class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title='Framebuffer post-processing', keys='interactive', size=(512, 512)) # Build cube data # -------------------------------------- vertices, indices, _ = create_cube() vertices = VertexBuffer(vertices) self.indices = IndexBuffer(indices) # Build program # -------------------------------------- view = translate((0, 0, -7)) self.phi, self.theta = 60, 20 model = rotate(self.theta, (0, 0, 1)).dot(rotate(self.phi, (0, 1, 0))) self.cube = Program(cube_vertex, cube_fragment) self.cube.bind(vertices) self.cube["texture"] = checkerboard() self.cube["texture"].interpolation = 'linear' self.cube['model'] = model self.cube['view'] = view color = Texture2D((512, 512, 3), interpolation='linear') self.framebuffer = FrameBuffer(color, RenderBuffer((512, 512))) self.quad = Program(quad_vertex, quad_fragment, count=4) self.quad['texcoord'] = [(0, 0), (0, 1), (1, 0), (1, 1)] self.quad['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] self.quad['texture'] = color # OpenGL and Timer initalization # -------------------------------------- set_state(clear_color=(.3, .3, .35, 1), depth_test=True) self.timer = app.Timer('auto', connect=self.on_timer, start=True) self._set_projection(self.physical_size) self.show() def on_draw(self, event): with self.framebuffer: set_viewport(0, 0, 512, 512) clear(color=True, depth=True) set_state(depth_test=True) self.cube.draw('triangles', self.indices) set_viewport(0, 0, *self.physical_size) clear(color=True) set_state(depth_test=False) self.quad.draw('triangle_strip') def on_resize(self, event): self._set_projection(event.physical_size) def _set_projection(self, size): width, height = size set_viewport(0, 0, width, height) projection = perspective(30.0, width / float(height), 2.0, 10.0) self.cube['projection'] = projection def on_timer(self, event): self.theta += .5 self.phi += .5 model = rotate(self.theta, (0, 0, 1)).dot(rotate(self.phi, (0, 1, 0))) self.cube['model'] = model self.update() if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/outlined_cube.py0000644000175000017510000000672314507104216020257 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 # ----------------------------------------------------------------------------- """ Show a rotating cube with an outline ==================================== """ import numpy as np from vispy import gloo, app from vispy.gloo import Program, VertexBuffer, IndexBuffer from vispy.util.transforms import perspective, translate, rotate from vispy.geometry import create_cube vertex = """ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform vec4 u_color; attribute vec3 position; attribute vec2 texcoord; attribute vec3 normal; attribute vec4 color; varying vec4 v_color; void main() { v_color = u_color * color; gl_Position = u_projection * u_view * u_model * vec4(position,1.0); } """ fragment = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Rotating cube', keys='interactive') self.timer = app.Timer('auto', self.on_timer) # Build cube data V, I, outline = create_cube() vertices = VertexBuffer(V) self.faces = IndexBuffer(I) self.outline = IndexBuffer(outline) # Build program # -------------------------------------- self.program = Program(vertex, fragment) self.program.bind(vertices) # Build view, model, projection & normal # -------------------------------------- view = translate((0, 0, -5)) model = np.eye(4, dtype=np.float32) self.program['u_model'] = model self.program['u_view'] = view self.phi, self.theta = 0, 0 self.activate_zoom() # OpenGL initialization # -------------------------------------- gloo.set_state(clear_color=(0.30, 0.30, 0.35, 1.00), depth_test=True, polygon_offset=(1, 1), line_width=0.75, blend_func=('src_alpha', 'one_minus_src_alpha')) self.timer.start() self.show() def on_draw(self, event): gloo.clear(color=True, depth=True) # Filled cube gloo.set_state(blend=False, depth_test=True, polygon_offset_fill=True) self.program['u_color'] = 1, 1, 1, 1 self.program.draw('triangles', self.faces) # Outlined cube gloo.set_state(blend=True, depth_mask=False, polygon_offset_fill=False) self.program['u_color'] = 0, 0, 0, 1 self.program.draw('lines', self.outline) gloo.set_state(depth_mask=True) def on_resize(self, event): self.activate_zoom() def activate_zoom(self): gloo.set_viewport(0, 0, *self.physical_size) projection = perspective(45.0, self.size[0] / float(self.size[1]), 2.0, 10.0) self.program['u_projection'] = projection def on_timer(self, event): self.theta += .5 self.phi += .5 self.program['u_model'] = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.update() if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/gloo/multi_texture.py0000755000175000017510000000416414507104216020350 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2 """ Use multiple textures ===================== We create two textures. One that shows a red, green and blue band in the horizontal direction and one that does the same in the vertical direction. In the fragment shader the colors from both textures are added. """ import numpy as np from vispy import gloo from vispy import app # Images to be displayed W, H = 30, 30 im1 = np.zeros((W, H, 3), np.float32) im2 = np.zeros((W, H, 3), np.float32) im1[:10, :, 0] = 1.0 im1[10:20, :, 1] = 1.0 im1[20:, :, 2] = 1.0 im2[:, :10, 0] = 1.0 im2[:, 10:20, 1] = 1.0 im1[:, 20:, 2] = 1.0 # A simple texture quad data = np.zeros(4, dtype=[('a_position', np.float32, 2), ('a_texcoord', np.float32, 2)]) data['a_position'] = np.array([[-1, -1], [+1, -1], [-1, +1], [+1, +1]]) data['a_texcoord'] = np.array([[1, 0], [1, 1.2], [0, 0], [0, 1.2]]) VERT_SHADER = """ attribute vec2 a_position; attribute vec2 a_texcoord; varying vec2 v_texcoord; void main (void) { v_texcoord = a_texcoord; gl_Position = vec4(a_position, 0.0, 1.0); } """ FRAG_SHADER = """ uniform sampler2D u_tex1; uniform sampler2D u_tex2; varying vec2 v_texcoord; void main() { vec3 clr1 = texture2D(u_tex1, v_texcoord).rgb; vec3 clr2 = texture2D(u_tex2, v_texcoord).rgb; gl_FragColor.rgb = clr1 + clr2; gl_FragColor.a = 1.0; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(500, 500), keys='interactive') self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) self.program['u_tex1'] = gloo.Texture2D(im1, interpolation='linear') self.program['u_tex2'] = gloo.Texture2D(im2, interpolation='linear') self.program.bind(gloo.VertexBuffer(data)) gloo.set_clear_color('white') self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) def on_draw(self, event): gloo.clear(color=True, depth=True) self.program.draw('triangle_strip') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/lighted_cube.py0000644000175000017510000001175214507104216020052 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 # ----------------------------------------------------------------------------- """ Show a rotating cube with lighting ================================== """ import numpy as np from vispy import gloo, app from vispy.gloo import Program, VertexBuffer, IndexBuffer from vispy.util.transforms import perspective, translate, rotate from vispy.geometry import create_cube vertex = """ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform vec4 u_color; attribute vec3 position; attribute vec2 texcoord; attribute vec3 normal; attribute vec4 color; varying vec3 v_position; varying vec3 v_normal; varying vec4 v_color; void main() { v_normal = normal; v_position = position; v_color = color * u_color; gl_Position = u_projection * u_view * u_model * vec4(position,1.0); } """ fragment = """ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_normal; uniform vec3 u_light_intensity; uniform vec3 u_light_position; varying vec3 v_position; varying vec3 v_normal; varying vec4 v_color; void main() { // Calculate normal in world coordinates vec3 normal = normalize(u_normal * vec4(v_normal,1.0)).xyz; // Calculate the location of this fragment (pixel) in world coordinates vec3 position = vec3(u_view*u_model * vec4(v_position, 1)); // Calculate the vector from this pixels surface to the light source vec3 surfaceToLight = u_light_position - position; // Calculate the cosine of the angle of incidence (brightness) float brightness = dot(normal, surfaceToLight) / (length(surfaceToLight) * length(normal)); brightness = max(min(brightness,1.0),0.0); // Calculate final color of the pixel, based on: // 1. The angle of incidence: brightness // 2. The color/intensities of the light: light.intensities // 3. The texture and texture coord: texture(tex, fragTexCoord) gl_FragColor = v_color * brightness * vec4(u_light_intensity, 1); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Lighted cube', keys='interactive') self.timer = app.Timer('auto', self.on_timer) # Build cube data V, F, outline = create_cube() vertices = VertexBuffer(V) self.faces = IndexBuffer(F) self.outline = IndexBuffer(outline) # Build view, model, projection & normal # -------------------------------------- self.view = translate((0, 0, -5)) model = np.eye(4, dtype=np.float32) normal = np.array(np.matrix(np.dot(self.view, model)).I.T) # Build program # -------------------------------------- self.program = Program(vertex, fragment) self.program.bind(vertices) self.program["u_light_position"] = 2, 2, 2 self.program["u_light_intensity"] = 1, 1, 1 self.program["u_model"] = model self.program["u_view"] = self.view self.program["u_normal"] = normal self.phi, self.theta = 0, 0 self.activate_zoom() # OpenGL initialization # -------------------------------------- gloo.set_state(clear_color=(0.30, 0.30, 0.35, 1.00), depth_test=True, polygon_offset=(1, 1), blend_func=('src_alpha', 'one_minus_src_alpha'), line_width=0.75) self.timer.start() self.show() def on_draw(self, event): gloo.clear(color=True, depth=True) # program.draw(gl.GL_TRIANGLES, indices) # Filled cube gloo.set_state(blend=False, depth_test=True, polygon_offset_fill=True) self.program['u_color'] = 1, 1, 1, 1 self.program.draw('triangles', self.faces) # Outlined cube gloo.set_state(polygon_offset_fill=False, blend=True, depth_mask=False) self.program['u_color'] = 0, 0, 0, 1 self.program.draw('lines', self.outline) gloo.set_state(depth_mask=True) def on_resize(self, event): self.activate_zoom() def activate_zoom(self): gloo.set_viewport(0, 0, *self.physical_size) projection = perspective(45.0, self.size[0] / float(self.size[1]), 2.0, 10.0) self.program['u_projection'] = projection def on_timer(self, event): self.theta += .5 self.phi += .5 model = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) normal = np.linalg.inv(np.dot(self.view, model)).T self.program['u_model'] = model self.program['u_normal'] = normal self.update() if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/gloo/hello_fbo.py0000755000175000017510000000657114507104216017373 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 3 """ Use FrameBuffers ================ Minimal example demonstrating the use of frame buffer objects (FBO). This example blurs the output image. """ from vispy import gloo from vispy import app import numpy as np # Create vertices vPosition = np.array([[-0.8, -0.8, 0.0], [+0.7, -0.7, 0.0], [-0.7, +0.7, 0.0], [+0.8, +0.8, 0.0, ]], np.float32) vPosition_full = np.array([[-1.0, -1.0, 0.0], [+1.0, -1.0, 0.0], [-1.0, +1.0, 0.0], [+1.0, +1.0, 0.0, ]], np.float32) vTexcoord = np.array([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]], np.float32) # For initial quad VERT_SHADER1 = """ attribute vec3 a_position; void main (void) { gl_Position = vec4(a_position, 1.0); } """ FRAG_SHADER1 = """ uniform vec4 u_color; void main() { gl_FragColor = u_color; } """ # To render the result of the FBO VERT_SHADER2 = """ attribute vec3 a_position; attribute vec2 a_texcoord; varying vec2 v_texcoord; void main (void) { // Pass tex coords v_texcoord = a_texcoord; // Calculate position gl_Position = vec4(a_position.x, a_position.y, a_position.z, 1.0); } """ FRAG_SHADER2 = """ uniform sampler2D u_texture1; varying vec2 v_texcoord; const float c_zero = 0.0; const int c_sze = 5; void main() { float scalefactor = 1.0 / float(c_sze * c_sze * 4 + 1); gl_FragColor = vec4(c_zero, c_zero, c_zero, 1.0); for (int y=-c_sze; y<=c_sze; y++) { for (int x=-c_sze; x<=c_sze; x++) { vec2 step = vec2(x,y) * 0.01; vec3 color = texture2D(u_texture1, v_texcoord.st+step).rgb; gl_FragColor.rgb += color * scalefactor; } } } """ SIZE = 50 class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(560, 420)) # Create texture to render to shape = self.physical_size[1], self.physical_size[0] self._rendertex = gloo.Texture2D((shape + (3,))) # Create FBO, attach the color buffer and depth buffer self._fbo = gloo.FrameBuffer(self._rendertex, gloo.RenderBuffer(shape)) # Create program to render a shape self._program1 = gloo.Program(VERT_SHADER1, FRAG_SHADER1) self._program1['u_color'] = 0.9, 1.0, 0.4, 1 self._program1['a_position'] = gloo.VertexBuffer(vPosition) # Create program to render FBO result self._program2 = gloo.Program(VERT_SHADER2, FRAG_SHADER2) self._program2['a_position'] = gloo.VertexBuffer(vPosition) self._program2['a_texcoord'] = gloo.VertexBuffer(vTexcoord) self._program2['u_texture1'] = self._rendertex self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) def on_draw(self, event): # Draw the same scene as as in hello_quad.py, but draw it to the FBO with self._fbo: gloo.set_clear_color((0.0, 0.0, 0.5, 1)) gloo.clear(color=True, depth=True) gloo.set_viewport(0, 0, *self.physical_size) self._program1.draw('triangle_strip') # Now draw result to a full-screen quad # Init gloo.set_clear_color('white') gloo.clear(color=True, depth=True) self._program2.draw('triangle_strip') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/gpuimage.py0000644000175000017510000000635014507104216017230 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # vispy: gallery 5:45:5 """ Animate a 2D function ===================== Illustrate how to plot a 2D function (an image) y=f(x,y) on the GPU. """ from vispy import app, gloo vertex = """ attribute vec2 a_position; varying vec2 v_position; void main() { gl_Position = vec4(a_position, 0.0, 1.0); v_position = a_position; } """ fragment = """ #include "math/constants.glsl" //const float M_PI = 3.14159265358979323846; uniform float u_time; varying vec2 v_position; /********************************************************** Specify the parameters here. **********************************************************/ const float z_offset = 1.; // (z+z_offset)/z_max should be in [0,1] const float z_max = 2.; const float x_scale = 5.; // x is between -x_scale and +x_scale const float y_scale = 5.; // y is between -y_scale and +y_scale const float t_scale = 5.; // scale for the time /*********************************************************/ float f(float x, float y, float t) { // x is in [-x_scale, +x_scale] // y is in [-y_scale, +y_scale] // t is in [0, +oo) /********************************************************** Write your function below. **********************************************************/ float k = .25*cos(t); return (cos(x)+k)*(sin(y)-k); /*********************************************************/ } vec4 jet(float x) { vec3 a, b; float c; if (x < 0.34) { a = vec3(0, 0, 0.5); b = vec3(0, 0.8, 0.95); c = (x - 0.0) / (0.34 - 0.0); } else if (x < 0.64) { a = vec3(0, 0.8, 0.95); b = vec3(0.85, 1, 0.04); c = (x - 0.34) / (0.64 - 0.34); } else if (x < 0.89) { a = vec3(0.85, 1, 0.04); b = vec3(0.96, 0.7, 0); c = (x - 0.64) / (0.89 - 0.64); } else { a = vec3(0.96, 0.7, 0); b = vec3(0.5, 0, 0); c = (x - 0.89) / (1.0 - 0.89); } return vec4(mix(a, b, c), 1.0); } void main() { vec2 pos = v_position; float z = f(x_scale * pos.x, y_scale * pos.y, t_scale * u_time); gl_FragColor = jet((z + z_offset) / (z_max)); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, position=(300, 100), size=(800, 800), keys='interactive') self.program = gloo.Program(vertex, fragment) self.program['a_position'] = [(-1., -1.), (-1., +1.), (+1., -1.), (+1., +1.)] self.program['u_time'] = 0.0 self.timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_timer(self, event): self.program['u_time'] = event.elapsed self.update() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) def on_draw(self, event): self.program.draw('triangle_strip') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/gl/0000755000175000017510000000000014507104250015454 5ustar nileshnileshvispy-0.14.1/examples/gloo/gl/quad_instanced.py0000644000175000017510000001134714507104216021020 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Displaying quads using Instanced rendering ========================================== This example is a modification of examples/tutorial/gl/quad.py which uses instanced rendering to generate many copies of the same quad. """ import numpy as np from vispy import app, use from vispy.gloo import gl # we need full gl context for instanced rendering use(gl='gl+') vertex_code = """ uniform float scale; attribute vec4 color; attribute vec2 position; attribute vec2 instance_offset; varying vec4 v_color; void main() { gl_Position = vec4(scale*position + instance_offset, 0.0, 1.0); v_color = color; } """ fragment_code = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Quad (GL)', keys='interactive') def on_initialize(self, event): # Build data self.data = np.zeros(4, [("position", np.float32, 2), ("color", np.float32, 4)]) self.data['color'] = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1), (1, 1, 0, 1)] self.data['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] self.n_instances = 1000 self.instances = np.empty( self.n_instances, [("instance_offset", np.float32, 2)] ) self.instances['instance_offset'] = (np.random.rand(self.n_instances, 2) - 0.5) * 2 # Build & activate program # Request a program and shader slots from GPU program = gl.glCreateProgram() vertex = gl.glCreateShader(gl.GL_VERTEX_SHADER) fragment = gl.glCreateShader(gl.GL_FRAGMENT_SHADER) # Set shaders source gl.glShaderSource(vertex, vertex_code) gl.glShaderSource(fragment, fragment_code) # Compile shaders gl.glCompileShader(vertex) gl.glCompileShader(fragment) # Attach shader objects to the program gl.glAttachShader(program, vertex) gl.glAttachShader(program, fragment) # Build program gl.glLinkProgram(program) # Get rid of shaders (no more needed) gl.glDetachShader(program, vertex) gl.glDetachShader(program, fragment) # Make program the default program gl.glUseProgram(program) # Build buffer # Request a buffer slot from GPU buf = gl.glCreateBuffer() # Make this buffer the default one gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf) # Upload data gl.glBufferData(gl.GL_ARRAY_BUFFER, self.data, gl.GL_DYNAMIC_DRAW) # Bind attributes stride = self.data.strides[0] instance_offset = 0 loc = gl.glGetAttribLocation(program, "position") gl.glEnableVertexAttribArray(loc) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf) gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, instance_offset) instance_offset = self.data.dtype["position"].itemsize loc = gl.glGetAttribLocation(program, "color") gl.glEnableVertexAttribArray(loc) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf) gl.glVertexAttribPointer(loc, 4, gl.GL_FLOAT, False, stride, instance_offset) # instance buffer buf = gl.glCreateBuffer() gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf) gl.glBufferData(gl.GL_ARRAY_BUFFER, self.instances, gl.GL_STATIC_DRAW) stride = self.instances.strides[0] instance_offset = 0 loc = gl.glGetAttribLocation(program, "instance_offset") gl.glEnableVertexAttribArray(loc) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf) gl.glVertexAttribPointer(loc, 2, gl.GL_FLOAT, False, stride, instance_offset) # this is the magic that says "step by 1 every instance" gl.glVertexAttribDivisor(loc, 1) # Bind uniforms # -------------------------------------- loc = gl.glGetUniformLocation(program, "scale") gl.glUniform1f(loc, 0.01) def on_draw(self, event): gl.glClear(gl.GL_COLOR_BUFFER_BIT) # you need to call the instanced version of the draw call gl.glDrawArraysInstanced(gl.GL_TRIANGLE_STRIP, 0, 4, self.n_instances) def on_resize(self, event): gl.glViewport(0, 0, *event.physical_size) if __name__ == '__main__': c = Canvas() c.show() app.run() vispy-0.14.1/examples/gloo/gl/quad.py0000644000175000017510000000715314507104216016770 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 # ----------------------------------------------------------------------------- """ Drawing a simple quad ===================== """ import numpy as np from vispy import app from vispy.gloo import gl vertex_code = """ uniform float scale; attribute vec4 color; attribute vec2 position; varying vec4 v_color; void main() { gl_Position = vec4(scale*position, 0.0, 1.0); v_color = color; } """ fragment_code = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Quad (GL)', keys='interactive') def on_initialize(self, event): # Build data self.data = np.zeros(4, [("position", np.float32, 2), ("color", np.float32, 4)]) self.data['color'] = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1), (1, 1, 0, 1)] self.data['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] # Build & activate program # Request a program and shader slots from GPU program = gl.glCreateProgram() vertex = gl.glCreateShader(gl.GL_VERTEX_SHADER) fragment = gl.glCreateShader(gl.GL_FRAGMENT_SHADER) # Set shaders source gl.glShaderSource(vertex, vertex_code) gl.glShaderSource(fragment, fragment_code) # Compile shaders gl.glCompileShader(vertex) gl.glCompileShader(fragment) # Attach shader objects to the program gl.glAttachShader(program, vertex) gl.glAttachShader(program, fragment) # Build program gl.glLinkProgram(program) # Get rid of shaders (no more needed) gl.glDetachShader(program, vertex) gl.glDetachShader(program, fragment) # Make program the default program gl.glUseProgram(program) # Build buffer # Request a buffer slot from GPU buf = gl.glCreateBuffer() # Make this buffer the default one gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf) # Upload data gl.glBufferData(gl.GL_ARRAY_BUFFER, self.data, gl.GL_DYNAMIC_DRAW) # Bind attributes stride = self.data.strides[0] offset = 0 loc = gl.glGetAttribLocation(program, "position") gl.glEnableVertexAttribArray(loc) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf) gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset) offset = self.data.dtype["position"].itemsize loc = gl.glGetAttribLocation(program, "color") gl.glEnableVertexAttribArray(loc) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf) gl.glVertexAttribPointer(loc, 4, gl.GL_FLOAT, False, stride, offset) # Bind uniforms # -------------------------------------- loc = gl.glGetUniformLocation(program, "scale") gl.glUniform1f(loc, 1.0) def on_draw(self, event): gl.glClear(gl.GL_COLOR_BUFFER_BIT) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) def on_resize(self, event): gl.glViewport(0, 0, *event.physical_size) if __name__ == '__main__': c = Canvas() c.show() app.run() vispy-0.14.1/examples/gloo/gl/fireworks.py0000644000175000017510000001274314507104216020052 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Almar Klein & Nicolas P .Rougier # Date: 04/03/2014 # Topic: Fireworks ! # Keywords: oarticles, gl, sprites # ----------------------------------------------------------------------------- """ Example demonstrating simulation of fireworks using point sprites ================================================================= (adapted from the "OpenGL ES 2.0 Programming Guide") This example demonstrates a series of explosions that last one second. The visualization during the explosion is highly optimized using a Vertex Buffer Object (VBO). After each explosion, vertex data for the next explosion are calculated, such that each explostion is unique. """ import numpy as np from vispy import app from vispy.gloo import gl vertex_code = """ #version 120 uniform float time; uniform vec3 center; attribute float lifetime; attribute vec3 start; attribute vec3 end; varying float v_lifetime; void main () { if (time < lifetime) { gl_Position.xyz = start + (time * end) + center; gl_Position.w = 1.0; gl_Position.y -= 1.5 * time * time; } else { gl_Position = vec4(-1000, -1000, 0, 0); } v_lifetime = clamp(1.0 - (time / lifetime), 0.0, 1.0); gl_PointSize = (v_lifetime * v_lifetime) * 40.0; } """ fragment_code = """ #version 120 uniform vec4 color; varying float v_lifetime; void main() { float d = 1 - length(gl_PointCoord - vec2(.5,.5)) / (sqrt(2)/2); gl_FragColor = d*color; gl_FragColor.a = d; gl_FragColor.a *= v_lifetime; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(800, 600), title='GL Fireworks', keys='interactive') def on_initialize(self, event): # Build & activate program self.program = gl.glCreateProgram() vertex = gl.glCreateShader(gl.GL_VERTEX_SHADER) fragment = gl.glCreateShader(gl.GL_FRAGMENT_SHADER) gl.glShaderSource(vertex, vertex_code) gl.glShaderSource(fragment, fragment_code) gl.glCompileShader(vertex) gl.glCompileShader(fragment) gl.glAttachShader(self.program, vertex) gl.glAttachShader(self.program, fragment) gl.glLinkProgram(self.program) gl.glDetachShader(self.program, vertex) gl.glDetachShader(self.program, fragment) gl.glUseProgram(self.program) # Build vertex buffer n = 10000 self.data = np.zeros(n, dtype=[('lifetime', np.float32), ('start', np.float32, 3), ('end', np.float32, 3)]) vbuffer = gl.glCreateBuffer() gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbuffer) gl.glBufferData(gl.GL_ARRAY_BUFFER, self.data, gl.GL_DYNAMIC_DRAW) # Bind buffer attributes stride = self.data.strides[0] offset = 0 loc = gl.glGetAttribLocation(self.program, "lifetime") gl.glEnableVertexAttribArray(loc) gl.glVertexAttribPointer(loc, 1, gl.GL_FLOAT, False, stride, offset) offset = self.data.dtype["lifetime"].itemsize loc = gl.glGetAttribLocation(self.program, "start") gl.glEnableVertexAttribArray(loc) gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset) offset = self.data.dtype["start"].itemsize loc = gl.glGetAttribLocation(self.program, "end") gl.glEnableVertexAttribArray(loc) gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset) # OpenGL initalization self.elapsed_time = 0 gl.glClearColor(0, 0, 0, 1) gl.glDisable(gl.GL_DEPTH_TEST) gl.glEnable(gl.GL_BLEND) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE) gl.glEnable(34370) # gl.GL_VERTEX_PROGRAM_POINT_SIZE gl.glEnable(34913) # gl.GL_POINT_SPRITE gl.glViewport(0, 0, *self.physical_size) self.new_explosion() self.timer = app.Timer('auto', self.on_timer, start=True) def on_draw(self, event): gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) gl.glDrawArrays(gl.GL_POINTS, 0, len(self.data)) def on_resize(self, event): gl.glViewport(0, 0, *event.physical_size) def on_timer(self, event): self.elapsed_time += 1. / 60. if self.elapsed_time > 1.5: self.new_explosion() self.elapsed_time = 0.0 loc = gl.glGetUniformLocation(self.program, "time") gl.glUniform1f(loc, self.elapsed_time) self.update() def new_explosion(self): n = len(self.data) color = np.random.uniform(0.1, 0.9, 4).astype(np.float32) color[3] = 1.0 / n ** 0.08 loc = gl.glGetUniformLocation(self.program, "color") gl.glUniform4f(loc, *color) center = np.random.uniform(-0.5, 0.5, 3) loc = gl.glGetUniformLocation(self.program, "center") gl.glUniform3f(loc, *center) self.data['lifetime'] = np.random.normal(2.0, 0.5, (n,)) self.data['start'] = np.random.normal(0.0, 0.2, (n, 3)) self.data['end'] = np.random.normal(0.0, 1.2, (n, 3)) gl.glBufferData(gl.GL_ARRAY_BUFFER, self.data, gl.GL_DYNAMIC_DRAW) if __name__ == '__main__': c = Canvas() c.show() app.run() vispy-0.14.1/examples/gloo/gl/cube.py0000644000175000017510000002035014507104216016746 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 # ----------------------------------------------------------------------------- """ Drawing a rotated 3D cube ========================= """ import math import numpy as np from vispy import app from vispy.gloo import gl def checkerboard(grid_num=8, grid_size=32): row_even = grid_num // 2 * [0, 1] row_odd = grid_num // 2 * [1, 0] Z = np.row_stack(grid_num // 2 * (row_even, row_odd)).astype(np.uint8) return 255 * Z.repeat(grid_size, axis=0).repeat(grid_size, axis=1) def rotate(M, angle, x, y, z, point=None): angle = math.pi * angle / 180 c, s = math.cos(angle), math.sin(angle) n = math.sqrt(x * x + y * y + z * z) x /= n y /= n z /= n cx, cy, cz = (1 - c) * x, (1 - c) * y, (1 - c) * z R = 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=M.dtype).T M[...] = np.dot(M, R) return M def translate(M, x, y=None, z=None): y = x if y is None else y z = x if z is None else z T = np.array([[1.0, 0.0, 0.0, x], [0.0, 1.0, 0.0, y], [0.0, 0.0, 1.0, z], [0.0, 0.0, 0.0, 1.0]], dtype=M.dtype).T M[...] = np.dot(M, T) return M def frustum(left, right, bottom, top, znear, zfar): M = np.zeros((4, 4), dtype=np.float32) M[0, 0] = +2.0 * znear / (right - left) M[2, 0] = (right + left) / (right - left) M[1, 1] = +2.0 * znear / (top - bottom) M[3, 1] = (top + bottom) / (top - bottom) M[2, 2] = -(zfar + znear) / (zfar - znear) M[3, 2] = -2.0 * znear * zfar / (zfar - znear) M[2, 3] = -1.0 return M def perspective(fovy, aspect, znear, zfar): h = math.tan(fovy / 360.0 * math.pi) * znear w = h * aspect return frustum(-w, w, -h, h, znear, zfar) def makecube(): """ Generate vertices & indices for a filled cube """ vtype = [('a_position', np.float32, 3), ('a_texcoord', np.float32, 2)] 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]]) # 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_t = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] vertices = np.zeros(24, vtype) vertices['a_position'] = p[faces_p] vertices['a_texcoord'] = t[faces_t] indices = np.resize( np.array([0, 1, 2, 0, 2, 3], dtype=itype), 6 * (2 * 3)) indices += np.repeat(4 * np.arange(6), 6).astype(np.uint32) return vertices, indices cube_vertex = """ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; attribute vec3 a_position; attribute vec2 a_texcoord; varying vec2 v_texcoord; void main() { gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0); v_texcoord = a_texcoord; } """ cube_fragment = """ uniform sampler2D u_texture; varying vec2 v_texcoord; void main() { gl_FragColor = texture2D(u_texture, v_texcoord); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Rotating cube (GL version)', keys='interactive') def on_initialize(self, event): # Build & activate cube program self.cube = gl.glCreateProgram() vertex = gl.glCreateShader(gl.GL_VERTEX_SHADER) fragment = gl.glCreateShader(gl.GL_FRAGMENT_SHADER) gl.glShaderSource(vertex, cube_vertex) gl.glShaderSource(fragment, cube_fragment) gl.glCompileShader(vertex) gl.glCompileShader(fragment) gl.glAttachShader(self.cube, vertex) gl.glAttachShader(self.cube, fragment) gl.glLinkProgram(self.cube) gl.glDetachShader(self.cube, vertex) gl.glDetachShader(self.cube, fragment) gl.glUseProgram(self.cube) # Get data & build cube buffers vcube_data, self.icube_data = makecube() vcube = gl.glCreateBuffer() gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vcube) gl.glBufferData(gl.GL_ARRAY_BUFFER, vcube_data, gl.GL_STATIC_DRAW) icube = gl.glCreateBuffer() gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, icube) gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, self.icube_data, gl.GL_STATIC_DRAW) # Bind cube attributes stride = vcube_data.strides[0] offset = 0 loc = gl.glGetAttribLocation(self.cube, "a_position") gl.glEnableVertexAttribArray(loc) gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset) offset = vcube_data.dtype["a_position"].itemsize loc = gl.glGetAttribLocation(self.cube, "a_texcoord") gl.glEnableVertexAttribArray(loc) gl.glVertexAttribPointer(loc, 2, gl.GL_FLOAT, False, stride, offset) # Create & bind cube texture crate = checkerboard() texture = gl.glCreateTexture() gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR) gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR) gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE) gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE) gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_LUMINANCE, gl.GL_LUMINANCE, gl.GL_UNSIGNED_BYTE, crate.shape[:2]) gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, gl.GL_LUMINANCE, gl.GL_UNSIGNED_BYTE, crate) loc = gl.glGetUniformLocation(self.cube, "u_texture") gl.glUniform1i(loc, texture) gl.glBindTexture(gl.GL_TEXTURE_2D, 0) # Create & bind cube matrices view = np.eye(4, dtype=np.float32) model = np.eye(4, dtype=np.float32) projection = np.eye(4, dtype=np.float32) translate(view, 0, 0, -7) self.phi, self.theta = 60, 20 rotate(model, self.theta, 0, 0, 1) rotate(model, self.phi, 0, 1, 0) loc = gl.glGetUniformLocation(self.cube, "u_model") gl.glUniformMatrix4fv(loc, 1, False, model) loc = gl.glGetUniformLocation(self.cube, "u_view") gl.glUniformMatrix4fv(loc, 1, False, view) loc = gl.glGetUniformLocation(self.cube, "u_projection") gl.glUniformMatrix4fv(loc, 1, False, projection) # OpenGL initalization gl.glClearColor(0.30, 0.30, 0.35, 1.00) gl.glEnable(gl.GL_DEPTH_TEST) self._resize(*(self.size + self.physical_size)) self.timer = app.Timer('auto', self.on_timer, start=True) def on_draw(self, event): gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) gl.glDrawElements(gl.GL_TRIANGLES, self.icube_data.size, gl.GL_UNSIGNED_INT, None) def on_resize(self, event): self._resize(*(event.size + event.physical_size)) def _resize(self, width, height, physical_width, physical_height): gl.glViewport(0, 0, physical_width, physical_height) projection = perspective(35.0, width / float(height), 2.0, 10.0) loc = gl.glGetUniformLocation(self.cube, "u_projection") gl.glUniformMatrix4fv(loc, 1, False, projection) def on_timer(self, event): self.theta += .5 self.phi += .5 model = np.eye(4, dtype=np.float32) rotate(model, self.theta, 0, 0, 1) rotate(model, self.phi, 0, 1, 0) loc = gl.glGetUniformLocation(self.cube, "u_model") gl.glUniformMatrix4fv(loc, 1, False, model) self.update() if __name__ == '__main__': c = Canvas() c.show() app.run() vispy-0.14.1/examples/gloo/gl/README.rst0000644000175000017510000000034614507104216017150 0ustar nileshnileshThe low-level GL module ======================= Vispy has a low-level `gl` module which wraps `GL` calls almost to a 1-to-1 ratio. While this is intended mostly for internal use, it is possible to use this to construct programs. vispy-0.14.1/examples/gloo/geometry_shader.py0000644000175000017510000000407714507104216020617 0ustar nileshnilesh# !/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Use a Geometry Shader ===================== Simple geometry shader: Takes one point as input emits one triangle as output. NOTE: This example is currently not processed in CI. """ import numpy as np from vispy import gloo from vispy import app # geometry shaders require full OpenGL namespace provided by PyOpenGL gloo.gl.use_gl('gl+') position = np.random.normal(loc=0, scale=0.3, size=(1000, 2)).astype('float32') VERT_SHADER = """ #version 330 in vec2 a_position; void main (void) { gl_Position = vec4(a_position, 0, 1); gl_PointSize = 3.0; } """ GEOM_SHADER = """ #version 330 layout (points) in; layout (triangle_strip, max_vertices=3) out; void main(void) { vec4 p = gl_in[0].gl_Position; gl_Position = p; EmitVertex(); gl_Position = p + vec4(0.06, 0.03, 0, 0); EmitVertex(); gl_Position = p + vec4(0.03, 0.06, 0, 0); EmitVertex(); EndPrimitive(); } """ FRAG_SHADER = """ #version 330 out vec4 frag_color; void main() { frag_color = vec4(0,0,0,0.5); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(400, 400)) self.program = gloo.Program() self.program.set_shaders(vert=VERT_SHADER, geom=GEOM_SHADER, frag=FRAG_SHADER) self.program['a_position'] = gloo.VertexBuffer(position) gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_clear_color('white') self.context.set_state('translucent', cull_face=False, depth_test=False) self.show() def on_resize(self, event): gloo.set_viewport(0, 0, event.physical_size[0], event.physical_size[1]) def on_draw(self, event): self.context.clear() self.program.draw('points') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/display_shape.py0000755000175000017510000000245514507104216020264 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2 """ Draw a Quad =========== Simple example demonstrating showing a quad using a gloo Program. """ from vispy import gloo from vispy import app import numpy as np # Create vertices vPosition = np.array([[-0.8, -0.8, 0.0], [+0.7, -0.7, 0.0], [-0.7, +0.7, 0.0], [+0.8, +0.8, 0.0, ]], np.float32) VERT_SHADER = """ // simple vertex shader attribute vec3 a_position; void main (void) { gl_Position = vec4(a_position, 1.0); } """ FRAG_SHADER = """ // simple fragment shader uniform vec4 u_color; void main() { gl_FragColor = u_color; } """ class Canvas(app.Canvas): def __init__(self): super().__init__(keys='interactive') # Create program self._program = gloo.Program(VERT_SHADER, FRAG_SHADER) # Set uniform and attribute self._program['u_color'] = 0.2, 1.0, 0.4, 1 self._program['a_position'] = gloo.VertexBuffer(vPosition) gloo.set_clear_color('white') self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) def on_draw(self, event): gloo.clear() self._program.draw('triangle_strip') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/display_points.py0000755000175000017510000000472714507104216020504 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2 """ Draw 2D points ============== Simple example plotting 2D points. """ from vispy import gloo from vispy import app import numpy as np VERT_SHADER = """ attribute vec2 a_position; attribute vec3 a_color; attribute float a_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_radius; varying float v_linewidth; varying float v_antialias; void main (void) { v_radius = a_size; v_linewidth = 1.0; v_antialias = 1.0; v_fg_color = vec4(0.0,0.0,0.0,0.5); v_bg_color = vec4(a_color, 1.0); gl_Position = vec4(a_position, 0.0, 1.0); gl_PointSize = 2.0*(v_radius + v_linewidth + 1.5*v_antialias); } """ FRAG_SHADER = """ #version 120 varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_radius; varying float v_linewidth; varying float v_antialias; void main() { float size = 2.0*(v_radius + v_linewidth + 1.5*v_antialias); float t = v_linewidth/2.0-v_antialias; float r = length((gl_PointCoord.xy - vec2(0.5,0.5))*size); float d = abs(r - v_radius) - t; if( d < 0.0 ) gl_FragColor = v_fg_color; else { float alpha = d/v_antialias; alpha = exp(-alpha*alpha); if (r > v_radius) gl_FragColor = vec4(v_fg_color.rgb, alpha*v_fg_color.a); else gl_FragColor = mix(v_bg_color, v_fg_color, alpha); } } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive') ps = self.pixel_scale # Create vertices n = 10000 v_position = 0.25 * np.random.randn(n, 2).astype(np.float32) v_color = np.random.uniform(0, 1, (n, 3)).astype(np.float32) v_size = np.random.uniform(2*ps, 12*ps, (n, 1)).astype(np.float32) self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) # Set uniform and attribute self.program['a_color'] = gloo.VertexBuffer(v_color) self.program['a_position'] = gloo.VertexBuffer(v_position) self.program['a_size'] = gloo.VertexBuffer(v_size) gloo.set_state(clear_color='white', blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self.show() def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) def on_draw(self, event): gloo.clear(color=True, depth=True) self.program.draw('points') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/display_lines.py0000755000175000017510000000740314507104216020274 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2:50:5 """ Draw a bunch of lines ===================== This example demonstrates how multiple line-pieces can be drawn using one call, by discarding some fragments. Note that this example uses canvas.context.X() to call gloo functions. These functions are also available as vispy.gloo.X(), but apply explicitly to the canvas. Mouse wheel allows zooming in on the lines. Spacebar controls a timer that triggers rotation of the lines in 3D space. """ import numpy as np from vispy import gloo from vispy import app from vispy.util.transforms import perspective, translate, rotate W, H = 400, 400 # Create vertices n = 100 a_position = np.random.uniform(-1, 1, (n, 3)).astype(np.float32) a_id = np.random.randint(0, 30, (n, 1)) a_id = np.sort(a_id, axis=0).astype(np.float32) VERT_SHADER = """ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; attribute vec3 a_position; attribute float a_id; varying float v_id; void main (void) { v_id = a_id; gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0); } """ FRAG_SHADER = """ varying float v_id; void main() { float f = fract(v_id); // The second useless test is needed on OSX 10.8 if( (f > 0.0001) && (f < .9999) ) discard; else gl_FragColor = vec4(0,0,0,1); } """ class Canvas(app.Canvas): # --------------------------------- def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(W, H)) self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) # Set uniform and attribute self.program['a_id'] = gloo.VertexBuffer(a_id) self.program['a_position'] = gloo.VertexBuffer(a_position) self.translate = 5 self.view = translate((0, 0, -self.translate), dtype=np.float32) self.model = np.eye(4, dtype=np.float32) gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self.projection = perspective(45.0, self.size[0] / float(self.size[1]), 1.0, 1000.0) self.program['u_projection'] = self.projection self.program['u_model'] = self.model self.program['u_view'] = self.view self.theta = 0 self.phi = 0 self.context.set_clear_color('white') self.context.set_state('translucent') self.timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() # --------------------------------- def on_key_press(self, event): if event.text == ' ': if self.timer.running: self.timer.stop() else: self.timer.start() # --------------------------------- def on_timer(self, event): self.theta += .5 self.phi += .5 self.model = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.program['u_model'] = self.model self.update() # --------------------------------- def on_resize(self, event): gloo.set_viewport(0, 0, event.physical_size[0], event.physical_size[1]) self.projection = perspective(45.0, event.size[0] / float(event.size[1]), 1.0, 1000.0) self.program['u_projection'] = self.projection # --------------------------------- def on_mouse_wheel(self, event): self.translate += event.delta[1] self.translate = max(2, self.translate) self.view = translate((0, 0, -self.translate)) self.program['u_view'] = self.view self.update() # --------------------------------- def on_draw(self, event): self.context.clear() self.program.draw('line_strip') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/colored_quad.py0000644000175000017510000000334214507104216020071 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 1 # ----------------------------------------------------------------------------- # Copyright (c) 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 # ----------------------------------------------------------------------------- """ Show color quad Image ===================== Create a new drawing using triangle_strip """ from vispy import app, gloo from vispy.gloo import Program vertex = """ attribute vec4 color; attribute vec2 position; varying vec4 v_color; void main() { gl_Position = vec4(position, 0.0, 1.0); v_color = color; } """ fragment = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): super().__init__(size=(512, 512), title='Colored quad', keys='interactive') # Build program self.program = Program(vertex, fragment, count=4) # Set uniforms and attributes self.program['color'] = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1), (1, 1, 0, 1)] self.program['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] gloo.set_viewport(0, 0, *self.physical_size) self.show() def on_draw(self, event): gloo.clear() self.program.draw('triangle_strip') def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) c = Canvas() if __name__ == '__main__': app.run() vispy-0.14.1/examples/gloo/colored_cube_instanced.py0000644000175000017510000000637414507104216022115 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Nicolas P .Rougier, Lorenzo Gaifas # Date: 04/03/2014 # ----------------------------------------------------------------------------- """ Draw many colored cubes using instanced rendering ================================================= """ import numpy as np from vispy import app, gloo, use from vispy.gloo import Program, VertexBuffer, IndexBuffer from vispy.util.transforms import perspective, translate, rotate from vispy.geometry import create_cube use(gl='gl+') vertex = """ uniform mat4 model; uniform mat4 view; uniform mat4 projection; // per-vertex attributes attribute vec3 position; attribute vec2 texcoord; attribute vec3 normal; attribute vec4 color; // unused (it's returned by generate_cube() but we don't need it) // per-instance attributes attribute vec3 instance_shift; attribute vec3 instance_color; varying vec4 v_color; void main() { v_color = vec4(instance_color, 1); gl_Position = projection * view * model * vec4(position + instance_shift,1.0); } """ fragment = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Colored instanced cube', keys='interactive') # Build cube data V, I, _ = create_cube() vertices = VertexBuffer(V) self.indices = IndexBuffer(I) instance_shift = VertexBuffer(((np.random.rand(100, 3) - 0.5) * 50).astype(np.float32), divisor=1) instance_color = VertexBuffer(np.random.rand(5, 3).astype(np.float32), divisor=20) # Build program self.program = Program(vertex, fragment) self.program.bind(vertices) # Build view, model, projection & normal view = translate((0, 0, -100)) model = np.eye(4, dtype=np.float32) self.program['model'] = model self.program['view'] = view self.program['instance_shift'] = instance_shift self.program['instance_color'] = instance_color self.phi, self.theta = 0, 0 gloo.set_state(clear_color=(0.30, 0.30, 0.35, 1.00), depth_test=True) self.activate_zoom() self.timer = app.Timer('auto', self.on_timer, start=True) self.show() def on_draw(self, event): gloo.clear(color=True, depth=True) self.program.draw('triangles', self.indices) def on_resize(self, event): self.activate_zoom() def activate_zoom(self): gloo.set_viewport(0, 0, *self.physical_size) projection = perspective(45.0, self.size[0] / float(self.size[1]), 2.0, 200.0) self.program['projection'] = projection def on_timer(self, event): self.theta += .5 self.phi += .5 self.program['model'] = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.update() if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/gloo/colored_cube.py0000644000175000017510000000522314507104216020055 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 # ----------------------------------------------------------------------------- """ Show a rotating colored cube ============================ """ import numpy as np from vispy import app, gloo from vispy.gloo import Program, VertexBuffer, IndexBuffer from vispy.util.transforms import perspective, translate, rotate from vispy.geometry import create_cube vertex = """ uniform mat4 model; uniform mat4 view; uniform mat4 projection; attribute vec3 position; attribute vec2 texcoord; attribute vec3 normal; attribute vec4 color; varying vec4 v_color; void main() { v_color = color; gl_Position = projection * view * model * vec4(position,1.0); } """ fragment = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Colored cube', keys='interactive') # Build cube data V, I, _ = create_cube() vertices = VertexBuffer(V) self.indices = IndexBuffer(I) # Build program self.program = Program(vertex, fragment) self.program.bind(vertices) # Build view, model, projection & normal view = translate((0, 0, -5)) model = np.eye(4, dtype=np.float32) self.program['model'] = model self.program['view'] = view self.phi, self.theta = 0, 0 gloo.set_state(clear_color=(0.30, 0.30, 0.35, 1.00), depth_test=True) self.activate_zoom() self.timer = app.Timer('auto', self.on_timer, start=True) self.show() def on_draw(self, event): gloo.clear(color=True, depth=True) self.program.draw('triangles', self.indices) def on_resize(self, event): self.activate_zoom() def activate_zoom(self): gloo.set_viewport(0, 0, *self.physical_size) projection = perspective(45.0, self.size[0] / float(self.size[1]), 2.0, 10.0) self.program['projection'] = projection def on_timer(self, event): self.theta += .5 self.phi += .5 self.program['model'] = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.update() if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/gloo/animate_shape.py0000755000175000017510000000600114507104216020224 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2:20:2 """ Animate a Shape =============== Example demonstrating showing a quad using a Texture2D and VertexBuffer and a timer to control the drawing. """ import time import numpy as np from vispy import gloo from vispy import app # Create a texture im1 = np.zeros((100, 100, 3), 'float32') im1[:50, :, 0] = 1.0 im1[:, :50, 1] = 1.0 im1[50:, 50:, 2] = 1.0 # Create vertices and texture coords, combined in one array for high performance vertex_data = np.zeros(4, dtype=[('a_position', np.float32, 3), ('a_texcoord', np.float32, 2)]) vertex_data['a_position'] = np.array([[-0.8, -0.8, 0.0], [+0.7, -0.7, 0.0], [-0.7, +0.7, 0.0], [+0.8, +0.8, 0.0, ]]) vertex_data['a_texcoord'] = np.array([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]) # Create indices and an ElementBuffer for it indices = np.array([0, 1, 2, 1, 2, 3], np.uint16) indices_buffer = gloo.IndexBuffer(indices) client_indices_buffer = gloo.IndexBuffer(indices) VERT_SHADER = """ // simple vertex shader attribute vec3 a_position; attribute vec2 a_texcoord; uniform float sizeFactor; void main (void) { // Pass tex coords gl_TexCoord[0] = vec4(a_texcoord.x, a_texcoord.y, 0.0, 0.0); // Calculate position gl_Position = sizeFactor*vec4(a_position.x, a_position.y, a_position.z, 1.0/sizeFactor); } """ FRAG_SHADER = """ // simple fragment shader uniform sampler2D texture1; void main() { gl_FragColor = texture2D(texture1, gl_TexCoord[0].st); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive') # Create program self._program = gloo.Program(VERT_SHADER, FRAG_SHADER) # Create vertex buffer self._vbo = gloo.VertexBuffer(vertex_data) # Set uniforms, samplers, attributes # We create one VBO with all vertex data (array of structures) # and create two views from it for the attributes. self._program['texture1'] = gloo.Texture2D(im1) self._program.bind(self._vbo) # This does: # self._program['a_position'] = self._vbo['a_position'] # self._program['a_texcoords'] = self._vbo['a_texcoords'] gloo.set_clear_color('white') self._timer = app.Timer('auto', connect=self.update, start=True) self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) def on_draw(self, event): # Clear gloo.clear() # Draw self._program['sizeFactor'] = 0.5 + np.sin(time.time() * 3) * 0.2 # Draw (pick one!) # self._program.draw('triangle_strip') self._program.draw('triangles', indices_buffer) # self._program.draw('triangles', client_indices_buffer) # Not # recommended if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/animate_images_slice.py0000644000175000017510000001026714507104216021556 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # author: Irwin Zaid # vispy: gallery 2:40:4 """ Animate an Image ================ Use a timer to trigger updating an image. This example demonstrates a 3D Texture. The volume contains noise that is smoothed in the z-direction. Shown is one slice through that volume to give the effect of "morphing" noise. """ import numpy as np from vispy.util.transforms import ortho from vispy import gloo from vispy import app from vispy.visuals.shaders import ModularProgram # Shape of image to be displayed D, H, W = 30, 60, 90 # Modulated image img_array = np.random.uniform(0, 0.1, (D, H, W, 3)).astype(np.float32) # Depth slices are dark->light img_array[...] += np.linspace(0, 0.9, D)[:, np.newaxis, np.newaxis, np.newaxis] # Make vertical direction more green moving upward img_array[..., 1] *= np.linspace(0, 1, H)[np.newaxis, :, np.newaxis] # Make horizontal direction more red moving rightward img_array[..., 0] *= np.linspace(0, 1, W)[np.newaxis, np.newaxis, :] # A simple texture quad data = np.zeros(4, dtype=[('a_position', np.float32, 2), ('a_texcoord', np.float32, 2)]) data['a_position'] = np.array([[0, 0], [W, 0], [0, H], [W, H]]) data['a_texcoord'] = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) VERT_SHADER = """ // Uniforms uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; // Attributes attribute vec2 a_position; attribute vec2 a_texcoord; // Varyings varying vec2 v_texcoord; // Main void main (void) { v_texcoord = a_texcoord; gl_Position = u_projection * u_view * u_model * vec4(a_position,0.0,1.0); } """ FRAG_SHADER = """ uniform $sampler_type u_texture; uniform float i; varying vec2 v_texcoord; void main() { // step through gradient with i, note that slice (depth) comes last here! gl_FragColor = $sample(u_texture, vec3(v_texcoord, i)); gl_FragColor.a = 1.0; } """ class Canvas(app.Canvas): def __init__(self, emulate3d=True): app.Canvas.__init__(self, keys='interactive', size=((W*5), (H*5))) if emulate3d: tex_cls = gloo.TextureEmulated3D else: tex_cls = gloo.Texture3D self.texture = tex_cls(img_array, interpolation='nearest', wrapping='clamp_to_edge') self.program = ModularProgram(VERT_SHADER, FRAG_SHADER) self.program.frag['sampler_type'] = self.texture.glsl_sampler_type self.program.frag['sample'] = self.texture.glsl_sample self.program['u_texture'] = self.texture self.program['i'] = 0.0 self.program.bind(gloo.VertexBuffer(data)) self.view = np.eye(4, dtype=np.float32) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.program['u_model'] = self.model self.program['u_view'] = self.view self.projection = ortho(0, W, 0, H, -1, 1) self.program['u_projection'] = self.projection self.i = 0 gloo.set_clear_color('white') self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) self.projection = ortho(0, width, 0, height, -100, 100) self.program['u_projection'] = self.projection # Compute the new size of the quad r = width / float(height) R = W / float(H) if r < R: w, h = width, width / R x, y = 0, int((height - h) / 2) else: w, h = height * R, height x, y = int((width - w) / 2), 0 data['a_position'] = np.array( [[x, y], [x + w, y], [x, y + h], [x + w, y + h]]) self.program.bind(gloo.VertexBuffer(data)) def on_timer(self, event): # cycle every 2 sec self.i = (self.i + 1./120.) % 1.0 self.update() def on_draw(self, event): gloo.clear(color=True, depth=True) self.program['i'] = 1.9 * np.abs(0.5 - self.i) self.program.draw('triangle_strip') if __name__ == '__main__': # Use emulated3d to switch from an emulated 3D texture to an actual one canvas = Canvas(emulate3d=True) app.run() vispy-0.14.1/examples/gloo/animate_images.py0000644000175000017510000000573614507104216020404 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 4:30:2 """ Show an Image ============= Draw a new array of random image pixels on every draw cycle. """ import numpy as np from vispy.util.transforms import ortho from vispy import gloo from vispy import app # Image to be displayed W, H = 64, 48 img_array = np.random.uniform(0, 1, (W, H)).astype(np.float32) # A simple texture quad data = np.zeros(4, dtype=[('a_position', np.float32, 2), ('a_texcoord', np.float32, 2)]) data['a_position'] = np.array([[0, 0], [W, 0], [0, H], [W, H]]) data['a_texcoord'] = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) VERT_SHADER = """ // Uniforms uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform float u_antialias; // Attributes attribute vec2 a_position; attribute vec2 a_texcoord; // Varyings varying vec2 v_texcoord; // Main void main (void) { v_texcoord = a_texcoord; gl_Position = u_projection * u_view * u_model * vec4(a_position,0.0,1.0); } """ FRAG_SHADER = """ uniform sampler2D u_texture; varying vec2 v_texcoord; void main() { gl_FragColor = texture2D(u_texture, v_texcoord); gl_FragColor.a = 1.0; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=((W * 5), (H * 5))) self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) self.texture = gloo.Texture2D(img_array, interpolation='linear') self.program['u_texture'] = self.texture self.program.bind(gloo.VertexBuffer(data)) self.view = np.eye(4, dtype=np.float32) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.program['u_model'] = self.model self.program['u_view'] = self.view self.projection = ortho(0, W, 0, H, -1, 1) self.program['u_projection'] = self.projection gloo.set_clear_color('white') self._timer = app.Timer('auto', connect=self.update, start=True) self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) self.projection = ortho(0, width, 0, height, -100, 100) self.program['u_projection'] = self.projection # Compute thje new size of the quad r = width / float(height) R = W / float(H) if r < R: w, h = width, width / R x, y = 0, int((height - h) / 2) else: w, h = height * R, height x, y = int((width - w) / 2), 0 data['a_position'] = np.array( [[x, y], [x + w, y], [x, y + h], [x + w, y + h]]) self.program.bind(gloo.VertexBuffer(data)) def on_draw(self, event): gloo.clear(color=True, depth=True) img_array[...] = np.random.uniform(0, 1, (W, H)).astype(np.float32) self.texture.set_data(img_array) self.program.draw('triangle_strip') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/gloo/README.rst0000644000175000017510000000023214507104216016540 0ustar nileshnileshGloo ==== More example scripts are available in the VisPy repository's `example scripts directory `_. vispy-0.14.1/examples/demo/0000755000175000017510000000000014507104250015036 5ustar nileshnileshvispy-0.14.1/examples/demo/visuals/0000755000175000017510000000000014507104250016524 5ustar nileshnileshvispy-0.14.1/examples/demo/visuals/wiggly_bar.py0000644000175000017510000010304614507104216021232 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # 2016, Scott Paine # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- """ ********** Wiggly Bar ********** Usage of VisPy to numerically simulate and view a simple physics model. .. image:: http://i.imgur.com/ad0s9lB.png This is a simple example of using VisPy to simulate a system with two springs, a pivot, and a mass. The system evolves in a nonlinear fashion, according to two equations: .. image:: http://i.imgur.com/8reci4N.png In these equations, the J term is the polar moment of inertia of the rod given by: .. image:: http://i.imgur.com/94cI1TL.png The system has the option to update once every step using the `Euler `_ method or a more stable third-order `Runge-Kutta `_ method. The instability of the Euler Method becomes apparent as the time step is increased. """ from __future__ import division, print_function, absolute_import from vispy import app, visuals from vispy.visuals import transforms from vispy.io import load_data_file import sys import numpy as np import string import logging import traceback # To switch between PyQt5 and PySide2 bindings just change the from import from PyQt5 import QtCore, QtGui, QtWidgets # Provide automatic signal function selection for PyQt5/PySide2 pyqtsignal = QtCore.pyqtSignal if hasattr(QtCore, 'pyqtSignal') else QtCore.Signal logger = logging.getLogger(__name__) VALID_METHODS = ['euler', 'runge-kutta'] PARAMETERS = [('d1', 0.0, 10.0, 'double', 0.97), ('d2', 0.0, 10.0, 'double', 0.55), ('m', 0.01, 100.0, 'double', 2.0), ('M', 0.01, 100.0, 'double', 12.5), ('k1', 0.01, 75.0, 'double', 1.35), ('k2', 0.01, 75.0, 'double', 0.50), ('b', 1.0, 1000.0, 'double', 25.75), ('time step', 0.001, 1.0, 'double', 1/60), ('x', -0.25, 0.25, 'double', -0.01), ('x dot', -10.0, 10.0, 'double', -0.12), ('theta', -np.pi/5, np.pi/5, 'double', 0.005), ('theta dot', -np.pi/2, np.pi/2, 'double', 0.0), ('scale', 5, 500, 'int', 50), ('font size', 6.0, 128.0, 'double', 24.0)] CONVERSION_DICT = {'d1': 'd1', 'd2': 'd2', 'm': 'little_m', 'M': 'big_m', 'k1': 'spring_k1', 'k2': 'spring_k2', 'b': 'b', 'x': 'x', 'x dot': 'x_dot', 'theta': 'theta', 'theta dot': 'theta_dot', 'scale': 'scale', 'time step': 'dt', 'font size': 'font_size'} def make_spiral(num_points=100, num_turns=4, height=12, radius=2.0, xnot=None, ynot=None, znot=None): """ Generate a list of points corresponding to a spiral. Parameters ---------- num_points : int Number of points to map spiral over. More points means a rounder spring. num_turns : int Number of coils in the spiral height : float The height of the spiral. Keep it in whatever units the rest of the spiral is in. radius : float The radius of the coils. The spiral will end up being 2*radius wide. xnot : float Initial x-coordinate for the spiral coordinates to start at. ynot : float Initial y-coordinate for the spiral coordinates to start at. znot : float Initial z-coordinate for the spiral coordinates to start at. Returns ------- coord_list: list of tuples Coordinate list of (x, y, z) positions for the spiral Notes ----- Right now, this assumes the center is at x=0, y=0. Later, it might be good to add in stuff to change that. """ coords_list = [] znot = -4 if znot is None else znot xnot = radius if xnot is None else xnot ynot = 0 if ynot is None else ynot theta_not = np.arctan2(ynot, xnot) coords_list.append((xnot, ynot, znot)) for point in range(num_points): znot += height / num_points theta_not += 2 * np.pi * num_turns / num_points xnot = np.cos(theta_not) * radius ynot = np.sin(theta_not) * radius coords_list.append((xnot, ynot, znot)) return coords_list def make_spring(num_points=300, num_turns=4, height=12, radius=2.0, xnot=None, ynot=None, znot=None): """ Generate a list of points corresponding to a spring. Parameters ---------- num_points : int Number of points to map spring over. More points means a rounder spring. num_turns : int Number of coils in the spring height : float The height of the spring. Keep it in whatever units the rest of the spring is in. radius : float The radius of the coils. The spring will end up being 2*radius wide. xnot : float Initial x-coordinate for the spring coordinates to start at. ynot : float Initial y-coordinate for the spring coordinates to start at. znot : float Initial z-coordinate for the spring coordinates to start at. Returns ------- coord_list: list of tuples Coordinate list of (x, y, z) positions for the spring Notes ----- Right now, this assumes the center is at x=0, y=0. Later, it might be good to add in stuff to change that. Right now, the length of the "ends" is 10% of the overall length, as well as a small "turn" that is length radius / 2. In the future, maybe there could be a kwarg to set the length of the sides of the spring. For now, 10% looks good. """ coords_list = [] init_pts = num_points // 10 znot = 0 if znot is None else znot xnot = 0 if xnot is None else xnot ynot = 0 if ynot is None else ynot coords_list.append((xnot, ynot, znot)) for _ in range(init_pts): znot += height / num_points coords_list.append((xnot, ynot, znot)) hold_z = znot for i in range(init_pts // 2): small_theta = (i + 1) * np.pi / init_pts xnot = radius / 2 * (1 - np.cos(small_theta)) znot = hold_z + radius / 2 * np.sin(small_theta) coords_list.append((xnot, ynot, znot)) coords_list += make_spiral(num_points=num_points - 3 * init_pts, num_turns=num_turns, height=( height - (91 * height / num_points) - radius / 2 ), radius=radius, xnot=xnot, ynot=ynot, znot=znot) hold_z = coords_list[-1][-1] for i in range(init_pts // 2): small_theta = np.pi / 2 - (i + 1) * np.pi / init_pts xnot = radius / 2 * (1 - np.cos(small_theta)) znot = hold_z + radius / 2 * np.cos(small_theta) coords_list.append((xnot, ynot, znot)) xnot = 0.0 znot += height / num_points for _ in range(init_pts): znot += height / num_points coords_list.append((xnot, ynot, znot)) coords_list.append((0, 0, height)) return coords_list class WigglyBar(app.Canvas): def __init__(self, d1=None, d2=None, little_m=None, big_m=None, spring_k1=None, spring_k2=None, b=None, x=None, x_dot=None, theta=None, theta_dot=None, px_len=None, scale=None, pivot=False, method='Euler', dt=None, font_size=None): """ Main VisPy Canvas for simulation of physical system. Parameters ---------- d1 : float Length of rod (in meters) from pivot to upper spring. d2 : float Length of rod (in meters) from pivot to lower spring. little_m : float Mass of attached cube (in kilograms). big_m : float Mass of rod (in kilograms). spring_k1 : float Spring constant of lower spring (in N/m). spring_k2 : float Spring constant of upper spring (in N/m). b : float Coefficient of quadratic sliding friction (in kg/m). x : float Initial x-position of mass (in m). x_dot : float Initial x-velocity of mass (in m/s). theta : float Initial angle of rod, with respect to vertical (in radians). theta_dot : float Initial angular velocity of rod (in rad/s). px_len : int Length of the rod, in pixels. scale : int Scaling factor to change size of elements. pivot : bool Switch for showing/hiding pivot point. method : str Method to use for updating. dt : float Time step for simulation. font_size : float Size of font for text elements, in points. Notes ----- As of right now, the only supported methods are "euler" or "runge-kutta". These correspond to an Euler method or an order 3 Runge-Kutta method for updating x, theta, x dot, and theta dot. """ app.Canvas.__init__(self, title='Wiggly Bar', size=(800, 800), create_native=False) # Some initialization constants that won't change self.standard_length = 0.97 + 0.55 self.center = np.asarray((500, 450)) self.visuals = [] self._set_up_system( d1=d1, d2=d2, little_m=little_m, big_m=big_m, spring_k1=spring_k1, spring_k2=spring_k2, b=b, x=x, x_dot=x_dot, theta=theta, theta_dot=theta_dot, px_len=px_len, scale=scale, pivot=pivot, method=method, dt=dt, font_size=font_size ) piv_x_y_px = np.asarray(( self.pivot_loc_px * np.sin(self.theta), -1 * self.pivot_loc_px * (np.cos(self.theta)) )) # Make the spring points points = make_spring(height=self.px_len/4, radius=self.px_len/24) # Put up a text visual to display time info self.font_size = 24. if font_size is None else font_size self.text = visuals.TextVisual('0:00.00', color='white', pos=[50, 250, 0], anchor_x='left', anchor_y='bottom') self.text.font_size = self.font_size # Let's put in more text so we know what method is being used to # update this self.method_text = visuals.TextVisual( 'Method: {}'.format(self.method), color='white', pos=[50, 250, 0], anchor_x='left', anchor_y='top' ) self.method_text.font_size = 2/3 * self.font_size # Get the pivoting bar ready self.rod = visuals.BoxVisual(width=self.px_len/40, height=self.px_len/40, depth=self.px_len, color='white') self.rod.transform = transforms.MatrixTransform() self.rod.transform.scale( (self.scale, self.scale * self.rod_scale, 0.0001) ) self.rod.transform.rotate(np.rad2deg(self.theta), (0, 0, 1)) self.rod.transform.translate(self.center - piv_x_y_px) # Show the pivot point (optional) pivot_center = (self.center[0], self.center[1], -self.px_len/75) self.center_point = visuals.SphereVisual(radius=self.px_len/75, color='red') self.center_point.transform = transforms.MatrixTransform() self.center_point.transform.scale((self.scale, self.scale, 0.0001)) self.center_point.transform.translate(pivot_center) # Get the upper spring ready. self.spring_2 = visuals.TubeVisual( points, radius=self.px_len/100, color=(0.5, 0.5, 1, 1) ) self.spring_2.transform = transforms.MatrixTransform() self.spring_2.transform.rotate(90, (0, 1, 0)) self.spring_2.transform.scale((self.scale, self.scale, 0.0001)) self.spring_2.transform.translate(self.center + self.s2_loc) # Get the lower spring ready. self.spring_1 = visuals.TubeVisual( points, radius=self.px_len/100, color=(0.5, 0.5, 1, 1) ) self.spring_1.transform = transforms.MatrixTransform() self.spring_1.transform.rotate(90, (0, 1, 0)) self.spring_1.transform.scale( ( self.scale * (1.0-(self.x*self.px_per_m)/(self.scale*self.px_len/2)), self.scale, 0.0001 ) ) self.spring_1.transform.translate(self.center + self.s1_loc) # Finally, prepare the mass that is being moved self.mass = visuals.BoxVisual( width=self.px_len / 4, height=self.px_len / 8, depth=self.px_len / 4, color='white' ) self.mass.transform = transforms.MatrixTransform() self.mass.transform.scale((self.scale, self.scale, 0.0001)) self.mass.transform.translate(self.center + self.mass_loc) # Append all the visuals self.visuals.append(self.center_point) self.visuals.append(self.rod) self.visuals.append(self.spring_2) self.visuals.append(self.spring_1) self.visuals.append(self.mass) self.visuals.append(self.text) self.visuals.append(self.method_text) # Set up a timer to update the image and give a real-time rendering self._timer = app.Timer('auto', connect=self.on_timer, start=True) def on_draw(self, ev): # Stolen from previous - just clears the screen and redraws stuff self.context.set_clear_color((0, 0, 0, 1)) self.context.set_viewport(0, 0, *self.physical_size) self.context.clear() for vis in self.visuals: if vis is self.center_point and not self.show_pivot: continue else: vis.draw() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for vis in self.visuals: vis.transforms.configure(canvas=self, viewport=vp) def on_timer(self, ev): # Update x, theta, x_dot, theta_dot self.params_update(dt=self.dt, method=self.method) # Calculate change for the upper spring, relative to its starting point extra_term = self.theta - self.theta_not trig_junk = ( np.sin(self.theta_not) * (np.cos(extra_term) - 1) + np.cos(self.theta_not) * np.sin(extra_term) ) delta_x = self.d1 * self.px_per_m * trig_junk net_s2_scale = (1 - (delta_x / (self.scale * self.px_len / 4))) # Calculate change for the lower spring, relative to something # arbitrary so I didn't have horrors mathematically trig_junk_2 = np.sin(self.theta_not) - np.sin(self.theta) first_term = self.d2 * trig_junk_2 top_term = (first_term - self.x)*self.px_per_m net_s1_scale = 1 + top_term/self.s1_l_not self.s1_loc[0] = -0.5 * ( -self.x*self.px_per_m + self.s1_l_not + self.d2*self.px_per_m*(np.sin(self.theta)+np.sin(self.theta_not)) ) self.s1_loc[0] -= 0.5 * net_s1_scale * self.s1_l_not # Calculate the new pivot location - this is important because the # rotation occurs about # the center of the rod, so it has to be offset appropriately piv_x_y_px = np.asarray(( self.pivot_loc_px*np.sin(self.theta), -1 * self.pivot_loc_px*np.cos(self.theta) )) # Calculate the new mass x location, relative (again) to some # simple parameter where x=0 self.mass_loc[0] = self.x_is_0 + self.x * self.px_per_m # Figure out how much time has passed millis_passed = int(100 * (self.t % 1)) sec_passed = int(self.t % 60) min_passed = int(self.t // 60) # Apply the necessary transformations to the rod self.rod.transform.reset() self.rod.transform.scale( (self.scale, self.scale * self.rod_scale, 0.0001) ) self.rod.transform.rotate(np.rad2deg(self.theta), (0, 0, 1)) self.rod.transform.translate(self.center - piv_x_y_px) # Redraw and rescale the upper spring self.spring_2.transform.reset() self.spring_2.transform.rotate(90, (0, 1, 0)) self.spring_2.transform.scale((net_s2_scale * self.scale, self.scale, 0.0001)) self.spring_2.transform.translate(self.center + self.s2_loc + np.asarray([delta_x, 0])) # Redraw and rescale the lower spring # (the hardest part to get, mathematically) self.spring_1.transform.reset() self.spring_1.transform.rotate(90, (0, 1, 0)) self.spring_1.transform.scale((net_s1_scale * self.scale, self.scale, 0.0001)) self.spring_1.transform.translate(self.center + self.s1_loc) # Redrew and rescale the mass self.mass.transform.reset() self.mass.transform.scale((self.scale, self.scale, 0.0001)) self.mass.transform.translate(self.center + self.mass_loc) # Update the timer with how long it's been self.text.text = '{:0>2d}:{:0>2d}.{:0>2d}'.format(min_passed, sec_passed, millis_passed) # Trigger all of the drawing and updating self.update() def params_update(self, dt, method='euler'): # Uses either Euler method or Runge-Kutta, # depending on your input to "method" if method.lower() == 'euler': self._euler_update(dt) elif method.lower() == 'runge-kutta': self._runge_kutta_update(dt) def _euler_update(self, dt): """Update system using Euler's method (equivalent to order 1 Runge-Kutta Method). """ # Calculate the second derivative of x x_dd_t1 = -self.b * self.x_dot * np.abs(self.x_dot) x_dd_t2 = -self.spring_k1 * (self.x + self.d2 * self.theta) x_dot_dot = (x_dd_t1 + x_dd_t2) / self.little_m # Calculate the second derivative of theta term1 = -self.spring_k1 * self.d2 * self.x term2 = ( -self.theta * (self.spring_k1*(self.d2**2) + self.spring_k2*(self.d1**2)) ) theta_dot_dot = (term1 + term2) / self.j_term # Update everything appropriately self.t += dt self.x += dt * self.x_dot self.theta += dt * self.theta_dot self.x_dot += dt * x_dot_dot self.theta_dot += dt * theta_dot_dot def _runge_kutta_update(self, dt): """Update using order 3 Runge-Kutta Method. """ info_vector = np.asarray( [self.x_dot, self.theta_dot, self.x, self.theta] ).copy() t1a = -self.b * info_vector[0] * np.abs(info_vector[0]) t1b = -self.spring_k1*(info_vector[2] + self.d2*info_vector[3]) t2a = -self.spring_k1*self.d2*info_vector[2] t2b = -info_vector[3] * ( self.spring_k1*(self.d2 ** 2) + self.spring_k2*(self.d1 ** 2) ) k1 = [ (t1a + t1b)/self.little_m, (t2a + t2b)/self.j_term, info_vector[0], info_vector[1] ] k1 = np.asarray(k1) * dt updated_est = info_vector + 0.5 * k1 t1a = -self.b * updated_est[0] * np.abs(updated_est[0]) t1b = -self.spring_k1 * (updated_est[2] + self.d2 * updated_est[3]) t2a = -self.spring_k1 * self.d2 * updated_est[2] t2b = -updated_est[3] * ( self.spring_k1 * (self.d2 ** 2) + self.spring_k2 * (self.d1 ** 2) ) k2 = [ (t1a + t1b)/self.little_m, (t2a + t2b)/self.j_term, updated_est[0], updated_est[1] ] k2 = np.asarray(k2) * dt updated_est = info_vector - k1 + 2 * k2 t1a = -self.b * updated_est[0] * np.abs(updated_est[0]) t1b = -self.spring_k1 * (updated_est[2] + self.d2 * updated_est[3]) t2a = -self.spring_k1 * self.d2 * updated_est[2] t2b = -updated_est[3] * ( self.spring_k1 * (self.d2 ** 2) + self.spring_k2 * (self.d1 ** 2) ) k3 = [ (t1a + t1b)/self.little_m, (t2a + t2b)/self.j_term, updated_est[0], updated_est[1] ] k3 = np.asarray(k3) * dt final_est = info_vector + (1/6)*(k1 + 4*k2 + k3) self.x_dot, self.theta_dot, self.x, self.theta = final_est.copy() self.t += dt def reset_parms(self, d1=None, d2=None, little_m=None, big_m=None, spring_k1=None, spring_k2=None, b=None, x=None, x_dot=None, theta=None, theta_dot=None, px_len=None, scale=None, pivot=False, method='Euler', dt=None, font_size=None): """ Reset system with a new set of paramters. Parameters ---------- d1 : float Length of rod (in meters) from pivot to upper spring. d2 : float Length of rod (in meters) from pivot to lower spring. little_m : float Mass of attached cube (in kilograms). big_m : float Mass of rod (in kilograms). spring_k1 : float Spring constant of lower spring (in N/m). spring_k2 : float Spring constant of upper spring (in N/m). b : float Coefficient of quadratic sliding friction (in kg/m). x : float Initial x-position of mass (in m). x_dot : float Initial x-velocity of mass (in m/s). theta : float Initial angle of rod, with respect to vertical (in radians). theta_dot : float Initial angular velocity of rod (in rad/s). px_len : int Length of the rod, in pixels. scale : int Scaling factor to change size of elements. pivot : bool Switch for showing/hiding pivot point. method : str Method to use for updating. dt : float Time step for simulation. font_size : float Size of font for text elements, in points. Notes ----- Since the time is reset, the system is reset as well by calling this method. """ self._set_up_system( d1=d1, d2=d2, little_m=little_m, big_m=big_m, spring_k1=spring_k1, spring_k2=spring_k2, b=b, x=x, x_dot=x_dot, theta=theta, theta_dot=theta_dot, px_len=px_len, scale=scale, pivot=pivot, method=method, dt=dt, font_size=font_size ) def _set_up_system(self, d1=None, d2=None, little_m=None, big_m=None, spring_k1=None, spring_k2=None, b=None, x=None, x_dot=None, theta=None, theta_dot=None, px_len=None, scale=None, pivot=False, method='Euler', dt=None, font_size=None): """Initialize constants for the system that will be used later. """ self.method = (string.capwords(method, '-') if method.lower() in VALID_METHODS else 'Euler') self.font_size = font_size try: self.method_text.text = 'Method: {}'.format(self.method) self.method_text.font_size = 2 / 3 * self.font_size self.text.font_size = self.font_size except AttributeError: # Running in __init__, so self.method_text isn't established yet. pass self.show_pivot = pivot # Initialize constants for the system self.t = 0 self.dt = 1 / 60 if dt is None else dt self.d1 = 0.97 if d1 is None else d1 self.d2 = 0.55 if d2 is None else d2 self.little_m = 2.0 if little_m is None else little_m self.big_m = 12.5 if big_m is None else big_m self.spring_k1 = 1.35 if spring_k1 is None else spring_k1 self.spring_k2 = 0.5 if spring_k2 is None else spring_k2 self.b = 25.75 if b is None else b self.j_term = ( (1 / 3) * self.big_m * (self.d1 ** 3 + self.d2 ** 3) / (self.d1 + self.d2) ) self.x = -0.010 if x is None else x self.x_dot = -0.12 if x_dot is None else x_dot self.theta = 0.005 if theta is None else theta self.theta_dot = 0.0 if theta_dot is None else theta_dot self.theta_not = self.theta # I'll need this later # Initialize constants for display self.px_len = 10 if px_len is None else px_len self.scale = 50 if scale is None else scale self.px_per_m = self.scale * self.px_len / (0.97 + 0.55) self.rod_scale = (self.d1 + self.d2) / self.standard_length # Set up stuff for establishing a pivot point to rotate about self.pivot_loc = (self.d2 - self.d1) / 2 self.pivot_loc_px = self.pivot_loc * self.px_per_m # Set up positioning info for the springs and mass, as well as some # constants for use later # NOTE: Springs are not like boxes. Their center of rotation is at one # end of the spring, unlike the box where it is in the middle. # The location and scaling is set to reflect this. This means # there's a little bit of x- and y-translation needed to properly # center them. self.s2_loc = np.asarray( [self.d1 * self.px_per_m * np.sin(self.theta), -self.d1 * self.px_per_m * np.cos( self.theta)] ) self.s1_l_not = self.px_len / 4 * self.scale self.x_is_0 = ( -self.d2 * self.px_per_m * np.sin(self.theta_not) - 1.5 * self.s1_l_not ) self.s1_loc = np.asarray( [self.x_is_0 + 0.5 * self.s1_l_not + self.x * self.px_per_m, self.d2 * self.px_per_m * np.cos(self.theta)] ) self.mass_loc = np.asarray( [self.x_is_0 + self.x * self.px_per_m, self.d2 * self.px_per_m * np.cos(self.theta)] ) class Paramlist(object): def __init__(self, parameters): """Container for object parameters. Based on methods from ../gloo/primitive_mesh_viewer_qt. """ self.parameters = parameters self.props = dict() self.props['pivot'] = False self.props['method'] = 'Euler' for nameV, minV, maxV, typeV, iniV in parameters: nameV = CONVERSION_DICT[nameV] self.props[nameV] = iniV class SetupWidget(QtWidgets.QWidget): changed_parameter_sig = pyqtsignal(Paramlist) def __init__(self, parent=None): """Widget for holding all the parameter options in neat lists. Based on methods from ../gloo/primitive_mesh_viewer_qt. """ super(SetupWidget, self).__init__(parent) # Create the parameter list from the default parameters given here self.param = Paramlist(PARAMETERS) # Checkbox for whether or not the pivot point is visible self.pivot_chk = QtWidgets.QCheckBox(u"Show pivot point") self.pivot_chk.setChecked(self.param.props['pivot']) self.pivot_chk.toggled.connect(self.update_parameters) # A drop-down menu for selecting which method to use for updating self.method_list = ['Euler', 'Runge-Kutta'] self.method_options = QtWidgets.QComboBox() self.method_options.addItems(self.method_list) self.method_options.setCurrentIndex( self.method_list.index((self.param.props['method'])) ) self.method_options.currentIndexChanged.connect( self.update_parameters ) # Separate the different parameters into groupboxes, # so there's a clean visual appearance self.parameter_groupbox = QtWidgets.QGroupBox(u"System Parameters") self.conditions_groupbox = QtWidgets.QGroupBox(u"Initial Conditions") self.display_groupbox = QtWidgets.QGroupBox(u"Display Parameters") self.groupbox_list = [self.parameter_groupbox, self.conditions_groupbox, self.display_groupbox] self.splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical) # Get ready to create all the spinboxes with appropriate labels plist = [] self.psets = [] # important_positions is used to separate the # parameters into their appropriate groupboxes important_positions = [0, ] param_boxes_layout = [QtWidgets.QGridLayout(), QtWidgets.QGridLayout(), QtWidgets.QGridLayout()] for nameV, minV, maxV, typeV, iniV in self.param.parameters: # Create Labels for each element plist.append(QtWidgets.QLabel(nameV)) if nameV == 'x' or nameV == 'scale': # 'x' is the start of the 'Initial Conditions' groupbox, # 'scale' is the start of the 'Display Parameters' groupbox important_positions.append(len(plist) - 1) # Create Spinboxes based on type - doubles get a DoubleSpinBox, # ints get regular SpinBox. # Step sizes are the same for every parameter except font size. if typeV == 'double': self.psets.append(QtWidgets.QDoubleSpinBox()) self.psets[-1].setDecimals(3) if nameV == 'font size': self.psets[-1].setSingleStep(1.0) else: self.psets[-1].setSingleStep(0.01) elif typeV == 'int': self.psets.append(QtWidgets.QSpinBox()) # Set min, max, and initial values self.psets[-1].setMaximum(maxV) self.psets[-1].setMinimum(minV) self.psets[-1].setValue(iniV) pidx = -1 for pos in range(len(plist)): if pos in important_positions: pidx += 1 param_boxes_layout[pidx].addWidget(plist[pos], pos + pidx, 0) param_boxes_layout[pidx].addWidget(self.psets[pos], pos + pidx, 1) self.psets[pos].valueChanged.connect(self.update_parameters) param_boxes_layout[0].addWidget(QtWidgets.QLabel('Method: '), 8, 0) param_boxes_layout[0].addWidget(self.method_options, 8, 1) param_boxes_layout[-1].addWidget(self.pivot_chk, 2, 0, 3, 0) for groupbox, layout in zip(self.groupbox_list, param_boxes_layout): groupbox.setLayout(layout) for groupbox in self.groupbox_list: self.splitter.addWidget(groupbox) vbox = QtWidgets.QVBoxLayout() hbox = QtWidgets.QHBoxLayout() hbox.addWidget(self.splitter) hbox.addStretch(5) vbox.addLayout(hbox) vbox.addStretch(1) self.setLayout(vbox) def update_parameters(self, option): """When the system parameters change, get the state and emit it.""" self.param.props['pivot'] = self.pivot_chk.isChecked() self.param.props['method'] = self.method_list[ self.method_options.currentIndex() ] keys = map(lambda x: x[0], self.param.parameters) for pos, nameV in enumerate(keys): self.param.props[CONVERSION_DICT[nameV]] = self.psets[pos].value() self.changed_parameter_sig.emit(self.param) class MainWindow(QtWidgets.QMainWindow): def __init__(self, param=None): """Main Window for holding the Vispy Canvas and the parameter control menu. """ QtWidgets.QMainWindow.__init__(self) self.resize(1067, 800) icon = load_data_file('wiggly_bar/spring.ico') self.setWindowIcon(QtGui.QIcon(icon)) self.setWindowTitle('Nonlinear Physical Model Simulation') self.parameter_object = SetupWidget(self) self.parameter_object.param = (param if param is not None else self.parameter_object.param) self.parameter_object.changed_parameter_sig.connect(self.update_view) self.view_box = WigglyBar(**self.parameter_object.param.props) self.view_box.create_native() self.view_box.native.setParent(self) splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal) splitter.addWidget(self.parameter_object) splitter.addWidget(self.view_box.native) self.setCentralWidget(splitter) def update_view(self, param): """Update the VisPy canvas when the parameters change. """ self.view_box.reset_parms(**param.props) def uncaught_exceptions(ex_type, ex_value, ex_traceback): lines = traceback.format_exception(ex_type, ex_value, ex_traceback) msg = ''.join(lines) logger.error('Uncaught Exception\n%s', msg) def main(): sys.excepthook = uncaught_exceptions logging.basicConfig(level=logging.INFO) logging.getLogger().setLevel(logging.INFO) appQt = QtWidgets.QApplication(sys.argv) win = MainWindow() win.show() appQt.exec_() if __name__ == '__main__': main() vispy-0.14.1/examples/demo/scene/0000755000175000017510000000000014507104250016133 5ustar nileshnileshvispy-0.14.1/examples/demo/scene/scrolling_plots.py0000644000175000017510000000252614507104216021731 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # vispy: gallery 30 """ Show 10,000 realtime scrolling plots """ from vispy import app, scene import numpy as np canvas = scene.SceneCanvas(keys='interactive', show=True, size=(1024, 768)) grid = canvas.central_widget.add_grid() view = grid.add_view(0, 1) view.camera = scene.MagnifyCamera(mag=1, size_factor=0.5, radius_ratio=0.6) # Add axes yax = scene.AxisWidget(orientation='left') yax.stretch = (0.05, 1) grid.add_widget(yax, 0, 0) yax.link_view(view) xax = scene.AxisWidget(orientation='bottom') xax.stretch = (1, 0.05) grid.add_widget(xax, 1, 1) xax.link_view(view) N = 4900 M = 2000 cols = int(N**0.5) view.camera.rect = (0, 0, cols, N/cols) lines = scene.ScrollingLines(n_lines=N, line_size=M, columns=cols, dx=0.8/M, cell_size=(1, 8), parent=view.scene) lines.transform = scene.STTransform(scale=(1, 1/8.)) def update(ev): m = 50 data = np.random.normal(size=(N, m), scale=0.3) data[data > 1] += 4 lines.roll_data(data) canvas.context.flush() # prevent memory leak when minimized timer = app.Timer(connect=update, interval=0) timer.start() if __name__ == '__main__': import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/demo/scene/picking.py0000644000175000017510000000562614507104216020144 0ustar nileshnileshimport numpy as np import vispy.plot as vp from vispy.color import get_colormap # load example data from vispy.io import load_data_file data = np.load(load_data_file('electrophys/iv_curve.npz'))['arr_0'] data *= 1000 # V -> mV dt = 1e-4 # this data is sampled at 10 kHz # create figure with plot fig = vp.Fig() plt = fig[0, 0] plt._configure_2d() plt.title.text = 'Current Clamp Recording' plt.ylabel.text = 'Membrane Potential (mV)' plt.xlabel.text = 'Time (ms)' selected = None # plot data cmap = get_colormap('hsl') colors = cmap.map(np.linspace(0.1, 0.9, data.shape[0])) t = np.arange(data.shape[1]) * (dt * 1000) for i, y in enumerate(data): line = plt.plot((t, y), color=colors[i]) line.interactive = True line.unfreeze() # make it so we can add a new property to the instance line.data_index = i line.freeze() # Build visuals used for cursor cursor_text = vp.Text("", pos=(0, 0), anchor_x='left', anchor_y='center', font_size=8, parent=plt.view.scene) cursor_line = vp.Line(parent=plt.view.scene) cursor_symbol = vp.Markers(pos=np.array([[0, 0]]), parent=plt.view.scene) cursor_line.visible = False cursor_symbol.visible = False cursor_line.order = 10 cursor_symbol.order = 11 cursor_text.order = 10 @fig.connect def on_mouse_press(event): global selected, fig if event.handled or event.button != 1: return if selected is not None: selected.set_data(width=1) selected = None for v in fig.visuals_at(event.pos): if isinstance(v, vp.LinePlot): selected = v break if selected is not None: selected.set_data(width=3) update_cursor(event.pos) @fig.connect def on_mouse_move(event): update_cursor(event.pos) def update_cursor(pos): global selected, cursor, data, dt, plt if selected is None: cursor_text.visible = False cursor_line.visible = False cursor_symbol.visible = False else: # get data for the selected trace trace = data[selected.data_index] # map the mouse position to data coordinates tr = fig.scene.node_transform(selected) pos = tr.map(pos) # get interpolated y coordinate x = min(max(pos[0], t[0]), t[-2]) ind = x / (dt * 1000) i = int(np.floor(ind)) s = ind - i y = trace[i] * (1 - s) + trace[i + 1] * s # update cursor cursor_text.text = "x=%0.2f ms, y=%0.2f mV" % (x, y) offset = np.diff(tr.map([[0, 0], [10, 0]]), axis=0)[0, 0] cursor_text.pos = x + offset, y rect = plt.view.camera.rect cursor_line.set_data(np.array([[x, rect.bottom], [x, rect.top]])) cursor_symbol.set_data(pos=np.array([[x, y]]), symbol='+', face_color='b') cursor_text.visible = True cursor_line.visible = True cursor_symbol.visible = True if __name__ == '__main__': fig.app.run() vispy-0.14.1/examples/demo/scene/oscilloscope.py0000644000175000017510000002375614507104216021222 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ An oscilloscope, spectrum analyzer, and spectrogram. This demo uses pyaudio to record data from the microphone. If pyaudio is not available, then a signal will be generated instead. """ from __future__ import division import threading import atexit import numpy as np from vispy import app, scene, gloo, visuals from vispy.util.filter import gaussian_filter try: import pyaudio class MicrophoneRecorder(object): def __init__(self, rate=44100, chunksize=1024): self.rate = rate self.chunksize = chunksize self.p = pyaudio.PyAudio() self.stream = self.p.open(format=pyaudio.paInt16, channels=1, rate=self.rate, input=True, frames_per_buffer=self.chunksize, stream_callback=self.new_frame) self.lock = threading.Lock() self.stop = False self.frames = [] atexit.register(self.close) def new_frame(self, data, frame_count, time_info, status): data = np.fromstring(data, 'int16') with self.lock: self.frames.append(data) if self.stop: return None, pyaudio.paComplete return None, pyaudio.paContinue def get_frames(self): with self.lock: frames = self.frames self.frames = [] return frames def start(self): self.stream.start_stream() def close(self): with self.lock: self.stop = True self.stream.close() self.p.terminate() except ImportError: class MicrophoneRecorder(object): def __init__(self): self.chunksize = 1024 self.rate = rate = 44100 t = np.linspace(0, 10, rate*10) self.data = (np.sin(t * 10.) * 0.3).astype('float32') self.data += np.sin((t + 0.3) * 20.) * 0.15 self.data += gaussian_filter(np.random.normal(size=self.data.shape) * 0.2, (0.4, 8)) self.data += gaussian_filter(np.random.normal(size=self.data.shape) * 0.005, (0, 1)) self.data += np.sin(t * 1760 * np.pi) # 880 Hz self.data = (self.data * 2**10 - 2**9).astype('int16') self.ptr = 0 def get_frames(self): if self.ptr + 1024 > len(self.data): end = 1024 - (len(self.data) - self.ptr) frame = np.concatenate((self.data[self.ptr:], self.data[:end])) else: frame = self.data[self.ptr:self.ptr+1024] self.ptr = (self.ptr + 1024) % (len(self.data) - 1024) return [frame] def start(self): pass class Oscilloscope(scene.ScrollingLines): """A set of lines that are temporally aligned on a trigger. Data is added in chunks to the oscilloscope, and each new chunk creates a new line to draw. Older lines are slowly faded out until they are removed. Parameters ---------- n_lines : int The maximum number of lines to draw. line_size : int The number of samples in each line. dx : float The x spacing between adjacent samples in a line. color : tuple The base color to use when drawing lines. Older lines are faded by decreasing their alpha value. trigger : tuple A set of parameters (level, height, width) that determine how triggers are detected. parent : Node An optional parent scenegraph node. """ def __init__(self, n_lines=100, line_size=1024, dx=1e-4, color=(20, 255, 50), trigger=(0, 0.002, 1e-4), parent=None): self._trigger = trigger # trigger_level, trigger_height, trigger_width # lateral positioning for trigger self.pos_offset = np.zeros((n_lines, 3), dtype=np.float32) # color array to fade out older plots self.color = np.empty((n_lines, 4), dtype=np.ubyte) self.color[:, :3] = [list(color)] self.color[:, 3] = 0 self._dim_speed = 0.01 ** (1 / n_lines) self.frames = [] # running list of recently received frames self.plot_ptr = 0 scene.ScrollingLines.__init__(self, n_lines=n_lines, line_size=line_size, dx=dx, color=self.color, pos_offset=self.pos_offset, parent=parent) self.set_gl_state('additive', line_width=2) def new_frame(self, data): self.frames.append(data) # see if we can discard older frames while len(self.frames) > 10: self.frames.pop(0) if self._trigger is None: dx = 0 else: # search for next trigger th = int(self._trigger[1]) # trigger window height tw = int(self._trigger[2] / self._dx) # trigger window width thresh = self._trigger[0] trig = np.argwhere((data[tw:] > thresh + th) & (data[:-tw] < thresh - th)) if len(trig) > 0: m = np.argmin(np.abs(trig - len(data) / 2)) i = trig[m, 0] y1 = data[i] y2 = data[min(i + tw * 2, len(data) - 1)] s = y2 / (y2 - y1) i = i + tw * 2 * (1-s) dx = i * self._dx else: # default trigger at center of trace # (optionally we could skip plotting instead, or place this # after the most recent trace) dx = self._dx * len(data) / 2. # if a trigger was found, add new data to the plot self.plot(data, -dx) def plot(self, data, dx=0): self.set_data(self.plot_ptr, data) np.multiply(self.color[..., 3], 0.98, out=self.color[..., 3], casting='unsafe') self.color[self.plot_ptr, 3] = 50 self.set_color(self.color) self.pos_offset[self.plot_ptr] = (dx, 0, 0) self.set_pos_offset(self.pos_offset) self.plot_ptr = (self.plot_ptr + 1) % self._data_shape[0] rolling_tex = """ float rolling_texture(vec2 pos) { if( pos.x < 0 || pos.x > 1 || pos.y < 0 || pos.y > 1 ) { return 0.0f; } vec2 uv = vec2(mod(pos.x+$shift, 1), pos.y); return texture2D($texture, uv).r; } """ cmap = """ vec4 colormap(float x) { x = x - 1e4; return vec4(x/5e6, x/2e5, x/1e4, 1); } """ class ScrollingImage(scene.Image): def __init__(self, shape, parent): self._shape = shape self._color_fn = visuals.shaders.Function(rolling_tex) self._ctex = gloo.Texture2D(np.zeros(shape+(1,), dtype='float32'), format='luminance', internalformat='r32f') self._color_fn['texture'] = self._ctex self._color_fn['shift'] = 0 self.ptr = 0 scene.Image.__init__(self, method='impostor', parent=parent) # self.set_gl_state('additive', cull_face=False) self.shared_program.frag['get_data'] = self._color_fn cfun = visuals.shaders.Function(cmap) self.shared_program.frag['color_transform'] = cfun @property def size(self): return self._shape def roll(self, data): data = data.reshape(data.shape[0], 1, 1) self._ctex[:, self.ptr] = data self._color_fn['shift'] = (self.ptr+1) / self._shape[1] self.ptr = (self.ptr + 1) % self._shape[1] self.update() def _prepare_draw(self, view): if self._need_vertex_update: self._build_vertex_data() if view._need_method_update: self._update_method(view) mic = MicrophoneRecorder() n_fft_frames = 8 fft_samples = mic.chunksize * n_fft_frames win = scene.SceneCanvas(keys='interactive', show=True, fullscreen=True) grid = win.central_widget.add_grid() view3 = grid.add_view(row=0, col=0, col_span=2, camera='panzoom', border_color='grey') image = ScrollingImage((1 + fft_samples // 2, 4000), parent=view3.scene) image.transform = scene.LogTransform((0, 10, 0)) # view3.camera.rect = (0, 0, image.size[1], np.log10(image.size[0])) view3.camera.rect = (3493.32, 1.85943, 605.554, 1.41858) view1 = grid.add_view(row=1, col=0, camera='panzoom', border_color='grey') view1.camera.rect = (-0.01, -0.6, 0.02, 1.2) gridlines = scene.GridLines(color=(1, 1, 1, 0.5), parent=view1.scene) scope = Oscilloscope(line_size=mic.chunksize, dx=1.0/mic.rate, parent=view1.scene) view2 = grid.add_view(row=1, col=1, camera='panzoom', border_color='grey') view2.camera.rect = (0.5, -0.5e6, np.log10(mic.rate/2), 5e6) lognode = scene.Node(parent=view2.scene) lognode.transform = scene.LogTransform((10, 0, 0)) gridlines2 = scene.GridLines(color=(1, 1, 1, 1), parent=lognode) spectrum = Oscilloscope(line_size=1 + fft_samples // 2, n_lines=10, dx=mic.rate/fft_samples, trigger=None, parent=lognode) mic.start() window = np.hanning(fft_samples) fft_frames = [] def update(ev): global fft_frames, scope, spectrum, mic data = mic.get_frames() for frame in data: # import scipy.ndimage as ndi # frame -= ndi.gaussian_filter(frame, 50) # frame -= frame.mean() scope.new_frame(frame) fft_frames.append(frame) if len(fft_frames) >= n_fft_frames: cframes = np.concatenate(fft_frames) * window fft = np.abs(np.fft.rfft(cframes)).astype('float32') fft_frames.pop(0) spectrum.new_frame(fft) image.roll(fft) timer = app.Timer(interval='auto', connect=update) timer.start() if __name__ == '__main__': app.run() vispy-0.14.1/examples/demo/scene/klein.py0000644000175000017510000000270514507104216017615 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Demonstration of the Klein bottle rendering using Mesh. """ import sys from vispy import app, scene from vispy.geometry.parametric import surface def klein(u, v): from math import pi, cos, sin if u < pi: x = 3 * cos(u) * (1 + sin(u)) + \ (2 * (1 - cos(u) / 2)) * cos(u) * cos(v) z = -8 * sin(u) - 2 * (1 - cos(u) / 2) * sin(u) * cos(v) else: x = 3 * cos(u) * (1 + sin(u)) + (2 * (1 - cos(u) / 2)) * cos(v + pi) z = -8 * sin(u) y = -2 * (1 - cos(u) / 2) * sin(v) return x/5, y/5, z/5 # Prepare canvas canvas = scene.SceneCanvas(keys='interactive', size=(800, 600), show=True) # Set up a viewbox to display the image with interactive pan/zoom view = canvas.central_widget.add_view() # Add camera view.camera = scene.cameras.ArcballCamera(parent=view.scene) # Add mesh vertices, indices = surface(klein, urepeat=3) indices = indices.reshape(len(indices)//3, 3) mesh = scene.visuals.Mesh(vertices=vertices['position'], faces=indices, color='white', parent=view.scene, shading='smooth') if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/demo/scene/force_directed_graph.py0000644000175000017510000000632514507104216022637 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Plot clusters of data points and a graph of connections """ from vispy import app, scene, color import numpy as np # Initialize arrays for position, color, edges, and types for each point in # the graph. npts = 400 nedges = 900 ngroups = 7 np.random.seed(127396) pos = np.empty((npts, 2), dtype='float32') colors = np.empty((npts, 3), dtype='float32') edges = np.empty((nedges, 2), dtype='uint32') types = np.empty(npts, dtype=int) # Assign random starting positions pos[:] = np.random.normal(size=pos.shape, scale=4.) # Assign each point to a group grpsize = npts // ngroups ptr = 0 typ = 0 while ptr < npts: size = np.random.random() * grpsize + grpsize // 2 types[int(ptr):int(ptr+size)] = typ typ += 1 ptr = ptr + size # Randomly select connections, with higher connection probability between # points in the same group conn = [] connset = set() while len(conn) < nedges: i, j = np.random.randint(npts, size=2) if i == j: continue p = 0.7 if types[i] == types[j] else 0.01 if np.random.random() < p: if (i, j) in connset: continue connset.add((i, j)) connset.add((j, i)) conn.append([i, j]) edges[:] = conn # Assign colors to each point based on its type cmap = color.get_colormap('cubehelix') typ_colors = np.array([cmap.map(x)[0, :3] for x in np.linspace(0.2, 0.8, typ)]) colors[:] = typ_colors[types] # Add some RGB noise and clip colors *= 1.1 ** np.random.normal(size=colors.shape) colors = np.clip(colors, 0, 1) # Display the data canvas = scene.SceneCanvas(keys='interactive', show=True) view = canvas.central_widget.add_view() view.camera = 'panzoom' view.camera.aspect = 1 lines = scene.Line(pos=pos, connect=edges, antialias=False, method='gl', color=(1, 1, 1, 0.2), parent=view.scene) markers = scene.Markers(pos=pos, face_color=colors, symbol='o', parent=view.scene) view.camera.set_range() i = 1 def update(ev): global pos, edges, lines, markers, view, force, dist, i dx = np.empty((npts, npts, 2), dtype='float32') dx[:] = pos[:, np.newaxis, :] dx -= pos[np.newaxis, :, :] dist = (dx**2).sum(axis=2)**0.5 dist[dist == 0] = 1. ndx = dx / dist[..., np.newaxis] force = np.zeros((npts, npts, 2), dtype='float32') # all points push away from each other force -= 0.1 * ndx / dist[..., np.newaxis]**2 # connected points pull toward each other # pulsed force helps to settle faster: s = 0.1 # s = 0.05 * 5 ** (np.sin(i/20.) / (i/100.)) # s = 0.05 + 1 * 0.99 ** i mask = np.zeros((npts, npts, 1), dtype='float32') mask[edges[:, 0], edges[:, 1]] = s mask[edges[:, 1], edges[:, 0]] = s force += dx * dist[..., np.newaxis] * mask # points do not exert force on themselves force[np.arange(npts), np.arange(npts)] = 0 force = force.sum(axis=0) pos += np.clip(force, -3, 3) * 0.09 lines.set_data(pos=pos) markers.set_data(pos=pos, face_color=colors) i += 1 timer = app.Timer(interval=0, connect=update, start=True) if __name__ == '__main__': app.run() vispy-0.14.1/examples/demo/scene/flow_lines.py0000644000175000017510000001502114507104216020647 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Show vector field flow """ from __future__ import division from vispy import app, scene, visuals, gloo from vispy.util import ptime import numpy as np class VectorFieldVisual(visuals.Visual): vertex = """ uniform sampler2D field; attribute vec2 index; uniform vec2 shape; uniform vec2 field_shape; uniform float spacing; varying float dist; // distance along path for this vertex varying vec2 ij; uniform sampler2D offset; uniform float seg_len; uniform int n_iter; // iterations to integrate along field per vertex uniform vec2 attractor; varying vec4 base_color; uniform sampler2D color; void main() { // distance along one line dist = index.y * seg_len; vec2 local; ij = vec2(mod(index.x, shape.x), floor(index.x / shape.x)); // *off* is a random offset to the starting location, which prevents // the appearance of combs in the field vec2 off = texture2D(offset, ij / shape).xy - 0.5; local = spacing * (ij + off); vec2 uv; vec2 dir; vec2 da; int index_y = int(index.y); for( int i=0; i 1: return (0, 0) return (0, self._field_shape[axis]) def update_time(self, ev): t = ptime.time() self._time += t - self._last_time self._last_time = t self.shared_program['time'] = self._time self.update() VectorField = scene.visuals.create_visual_node(VectorFieldVisual) def fn(y, x): dx = x-50 dy = y-30 hyp = (dx**2 + dy**2)**0.5 + 0.01 return np.array([100 * dy / hyp**1.7, -100 * dx / hyp**1.8]) field = np.fromfunction(fn, (100, 100)).transpose(1, 2, 0).astype('float32') field[..., 0] += 10 * np.cos(np.linspace(0, 2 * 3.1415, 100)) color = np.zeros((100, 100, 4), dtype='float32') color[..., :2] = (field + 5) / 10. color[..., 2] = 0.5 color[..., 3] = 0.5 canvas = scene.SceneCanvas(keys='interactive', show=True) view = canvas.central_widget.add_view(camera='panzoom') vfield = VectorField(field[..., :2], spacing=0.5, segments=30, seg_len=0.05, parent=view.scene, color=color) view.camera.set_range() @canvas.connect def on_mouse_move(event): if 3 in event.buttons: tr = canvas.scene.node_transform(vfield) vfield.shared_program['attractor'] = tr.map(event.pos)[:2] if __name__ == '__main__': app.run() vispy-0.14.1/examples/demo/plot/0000755000175000017510000000000014507104250016014 5ustar nileshnileshvispy-0.14.1/examples/demo/plot/plot.py0000644000175000017510000000142514507104216017350 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Plot data with different styles """ import numpy as np from vispy import plot as vp from vispy.io import load_data_file data = np.load(load_data_file('electrophys/iv_curve.npz'))['arr_0'] time = np.arange(0, data.shape[1], 1e-4) fig = vp.Fig(size=(800, 800), show=False) x = np.linspace(0, 10, 20) y = np.cos(x) line = fig[0, 0].plot((x, y), symbol='o', width=3, title='I/V Curve', xlabel='Current (pA)', ylabel='Membrane Potential (mV)') grid = vp.visuals.GridLines(color=(0, 0, 0, 0.5)) grid.set_gl_state('translucent') fig[0, 0].view.add(grid) if __name__ == '__main__': fig.show(run=True) vispy-0.14.1/examples/demo/gloo/0000755000175000017510000000000014507104250015776 5ustar nileshnileshvispy-0.14.1/examples/demo/gloo/voronoi.py0000644000175000017510000000635514507104216020056 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # vispy: testskip - because this example sometimes sets inactive attributes """Computing a Voronoi diagram on the GPU. Shows how to use uniform arrays. Original version by Xavier Olive (xoolive). """ import numpy as np from vispy import app from vispy import gloo # Voronoi shaders. VS_voronoi = """ attribute vec2 a_position; void main() { gl_Position = vec4(a_position, 0., 1.); } """ FS_voronoi = """ uniform vec2 u_seeds[32]; uniform vec3 u_colors[32]; uniform vec2 u_screen; void main() { float dist = distance(u_screen * u_seeds[0], gl_FragCoord.xy); vec3 color = u_colors[0]; for (int i = 1; i < 32; i++) { float current = distance(u_screen * u_seeds[i], gl_FragCoord.xy); if (current < dist) { color = u_colors[i]; dist = current; } } gl_FragColor = vec4(color, 1.0); } """ # Seed point shaders. VS_seeds = """ attribute vec2 a_position; uniform float u_ps; void main() { gl_Position = vec4(2. * a_position - 1., 0., 1.); gl_PointSize = 10. * u_ps; } """ FS_seeds = """ varying vec3 v_color; void main() { gl_FragColor = vec4(1., 1., 1., 1.); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(600, 600), title='Voronoi diagram', keys='interactive') self.ps = self.pixel_scale self.seeds = np.random.uniform(0, 1.0 * self.ps, size=(32, 2)).astype(np.float32) self.colors = np.random.uniform(0.3, 0.8, size=(32, 3)).astype(np.float32) self.idx = 0 # Set Voronoi program. self.program_v = gloo.Program(VS_voronoi, FS_voronoi) self.program_v['a_position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] # HACK: work-around a bug related to uniform arrays until # issue #345 is solved. for i in range(32): self.program_v['u_seeds[%d]' % i] = self.seeds[i, :] self.program_v['u_colors[%d]' % i] = self.colors[i, :] # Set seed points program. self.program_s = gloo.Program(VS_seeds, FS_seeds) self.program_s['a_position'] = self.seeds self.program_s['u_ps'] = self.ps self.activate_zoom() self.show() def on_draw(self, event): gloo.clear() self.program_v.draw('triangle_strip') self.program_s.draw('points') def on_resize(self, event): self.activate_zoom() def activate_zoom(self): self.width, self.height = self.size gloo.set_viewport(0, 0, *self.physical_size) self.program_v['u_screen'] = self.physical_size def on_mouse_move(self, event): x, y = event.pos x, y = x / float(self.width), 1 - y / float(self.height) self.program_v['u_seeds[%d]' % self.idx] = x * self.ps, y * self.ps # TODO: just update the first line in the VBO instead of uploading the # whole array of seed points. self.seeds[self.idx, :] = x, y self.program_s['a_position'].set_data(self.seeds) self.update() def on_mouse_press(self, event): self.idx = (self.idx + 1) % 32 if __name__ == "__main__": c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/unstructured_2d.py0000644000175000017510000001630514507104216021513 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Per Rosengren # Date: 18/03/2014 # Abstract: Unstructured2D canvas example # Keywords: unstructured delaunay colormap # Require: scipy # ---------------------------------------------------------------------------- """Unstructured2D canvas example. Takes unstructured 2D locations, with corresponding 1 or 2 dimensional scalar "values". Plots the values looked up from colormaps and interpolated between the locations. """ import sys import numpy as np import scipy.spatial from vispy import gloo, app from vispy.util.transforms import ortho class Canvas(app.Canvas): def __init__(self, x=None, y=None, u=None, v=None, colormap=None, data_lim=None, dir_x_right=True, dir_y_top=True, **kwargs): app.Canvas.__init__(self, **kwargs) self.create_shader(colormap) self.create_mesh(x, y, u, v) self.program.bind(self.vbo) if data_lim is not None: self._data_lim = data_lim else: self._data_lim = [[x.min(), x.max()], [y.min(), y.max()]] self._dir_x_right = dir_x_right self._dir_y_top = dir_y_top self.activate_zoom() self.show() def create_shader(self, colormap): if len(colormap.shape) == 2: args = dict( n_dims="1", tex_t="float", texture2D_arg="vec2(v_texcoord, 0.)") else: args = dict( n_dims="2", tex_t="vec2", texture2D_arg="v_texcoord") vertex = """ uniform mat4 projection; uniform sampler2D texture; attribute vec2 position; attribute {tex_t} texcoord; varying {tex_t} v_texcoord; void main() {{ gl_Position = projection * vec4(position, 0.0, 1.0); v_texcoord = texcoord; }} """.format(**args) fragment = """ uniform sampler2D texture; varying {tex_t} v_texcoord; void main() {{ gl_FragColor = texture2D(texture, {texture2D_arg}); }} """.format(**args) self.program = gloo.Program(vertex, fragment) if len(colormap.shape) == 2: self.program['texture'] = np.ascontiguousarray( colormap[None, :, :]) else: self.program['texture'] = colormap self.program['texture'].interpolation = 'linear' self.projection = np.eye(4, dtype=np.float32) def create_mesh(self, x, y, u, v): tri = scipy.spatial.Delaunay(np.column_stack([x, y])) edges = tri.simplices.astype(np.uint32) uv = [] for c in [u, v]: if c is not None: c = c.astype('f4') c = .5 + .5 * c / np.abs(c).max() uv.append(c) data = np.column_stack( [ x.astype('f4'), y.astype('f4') ] + uv ).view(dtype=[('position', 'f4', (2,)), ('texcoord', 'f4', (2 if v is not None else 1,)), ]) self.vbo = gloo.VertexBuffer(data) self.index = gloo.IndexBuffer(edges) gloo.set_state(blend=True, clear_color='white', blend_func=('src_alpha', 'one_minus_src_alpha')) def on_draw(self, event): gloo.clear() self.program.draw('triangles', self.index) def on_resize(self, event): self.activate_zoom() def activate_zoom(self): width, heigh = self.size gloo.set_viewport(0, 0, *self.physical_size) data_width = self._data_lim[0][1] - self._data_lim[0][0] data_height = self._data_lim[1][1] - self._data_lim[1][0] data_aspect = data_width / float(data_height) frame_aspect = width / float(height) if frame_aspect >= data_aspect: padding = (frame_aspect * data_height - data_width) / 2. frame_lim = [ [self._data_lim[0][0] - padding, self._data_lim[0][1] + padding], [self._data_lim[1][0], self._data_lim[1][1]]] else: padding = (data_width / frame_aspect - data_height) / 2. frame_lim = [ [self._data_lim[0][0], self._data_lim[0][1]], [self._data_lim[1][0] - padding, self._data_lim[1][1] + padding]] args_ortho = frame_lim[0][::(1 if self._dir_x_right else -1)] args_ortho += frame_lim[1][::(1 if self._dir_y_top else -1)] args_ortho += -1000, 1000 self.projection = ortho(*args_ortho) self.program['projection'] = self.projection def create_colormap2d_hsv(size=512): import matplotlib.colors import math u, v = np.meshgrid(np.linspace(-1, 1, size), np.linspace(-1, 1, size)) hsv = np.ones((size, size, 3), dtype=np.float32) hsv[:, :, 0] = (np.arctan2(u, v) / (2 * math.pi) + .5) hsv[:, :, 1] = np.minimum(1., np.sqrt(u ** 2 + v ** 2)) rgb = matplotlib.colors.hsv_to_rgb(hsv) return rgb def create_colormap2d_4dirs(size=512): rgb = np.ones((size, size, 3), dtype=np.float32) hs = size // 2 u, v = np.meshgrid(np.linspace(1, 0, hs), np.linspace(1, 0, hs)) rgb[:hs, :hs, 0] = 1. rgb[:hs, :hs, 1] = 1. - v + u / 2. rgb[:hs, :hs, 2] = 1. - np.maximum(u, v) u = u[:, ::-1] rgb[:hs, hs:, 0] = 1. - u + v rgb[:hs, hs:, 1] = 1. - np.maximum(u, v) rgb[:hs, hs:, 2] = 1. - v + u v = v[::-1, :] rgb[hs:, hs:, 0] = 1. - np.maximum(u, v) rgb[hs:, hs:, 1] = 1. - u + v rgb[hs:, hs:, 2] = 1. - v + u u = u[:, ::-1] rgb[hs:, :hs, 0] = 1. - v + u / 2. rgb[hs:, :hs, 1] = 1. rgb[hs:, :hs, 2] = 1. - np.maximum(u, v) rgb = np.minimum(1., rgb) return rgb def create_colormap1d_hot(size=512): rgb = np.ones((size, 3), dtype=np.float32) hs = size // 2 u = np.linspace(1, 0, hs) rgb[:hs, 0] = 1 - u rgb[:hs, 1] = 1 - u u = u[::-1] rgb[hs:, 1] = 1 - u rgb[hs:, 2] = 1 - u return rgb if __name__ == '__main__': loc = np.random.random_sample(size=(100, 2)) np.random.shuffle(loc) vec = np.empty_like(loc) vec[:, 0] = np.cos(loc[:, 0] * 10) vec[:, 1] = np.cos(loc[:, 1] * 13) width = 500 height = 500 c1 = Canvas(title="Unstructured 2D - 2D colormap", size=(width, height), position=(0, 40), x=loc[:, 0], y=loc[:, 1], u=vec[:, 0], v=vec[:, 1], colormap=create_colormap2d_4dirs(size=128), keys='interactive') c2 = Canvas(title="Unstructured 2D - 1D colormap", size=(width, height), position=(width + 20, 40), x=loc[:, 0], y=loc[:, 1], u=vec[:, 0], colormap=create_colormap1d_hot(size=128), keys='interactive') if sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/demo/gloo/two_qt_widgets.py0000644000175000017510000000222514507104216021416 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip """ Example demonstrating the use of two GLCanvases in one QtApp. """ from PyQt5 import QtCore, QtWidgets import sys from fireworks import Canvas as FireCanvas from rain import Canvas as RainCanvas class MainWindow(QtWidgets.QMainWindow): def __init__(self): QtWidgets.QMainWindow.__init__(self) self.resize(1000, 500) self.setWindowTitle('vispy example ...') self.splitter_h = QtWidgets.QSplitter(QtCore.Qt.Horizontal) # Central Widget splitter1 = QtWidgets.QSplitter(QtCore.Qt.Horizontal) self.rain_canvas = RainCanvas() self.rain_canvas.create_native() self.rain_canvas.native.setParent(self) self.fireworks_canvas = FireCanvas() self.fireworks_canvas.create_native() self.fireworks_canvas.native.setParent(self) splitter1.addWidget(self.fireworks_canvas.native) splitter1.addWidget(self.rain_canvas.native) self.setCentralWidget(splitter1) if __name__ == '__main__': appQt = QtWidgets.QApplication(sys.argv) win = MainWindow() win.show() appQt.exec_() vispy-0.14.1/examples/demo/gloo/terrain.py0000644000175000017510000001511614507104216020022 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Terrain generation using diamond-square alogrithm and Scipy for Delaunay triangulation """ from vispy import gloo from vispy import app from vispy.util.transforms import perspective, translate, rotate import numpy as np from scipy.spatial import Delaunay # Arrays for storing generated points and triangles points = [] triangles = [] height = 0.0 def generate_terrain(r_min, r_max, c_min, c_max, disp): """Recursively generates terrain using diamond-square algorithm and stores the vertices in points """ a = points[r_min, c_min, 2] b = points[r_min, c_max, 2] c = points[r_max, c_min, 2] d = points[r_max, c_max, 2] r_mid = (r_min + r_max) // 2 c_mid = (c_min + c_max) // 2 e = (a+b+c+d)/4 + np.random.uniform(0, disp) points[r_mid, c_mid, 2] = e points[r_min, c_mid, 2] = (a + b + e)/3 + np.random.uniform(0, disp) points[r_max, c_mid, 2] = (c + d + e)/3 + np.random.uniform(0, disp) points[r_mid, c_min, 2] = (a + c + e)/3 + np.random.uniform(0, disp) points[r_mid, c_max, 2] = (b + d + e)/3 + np.random.uniform(0, disp) new_disp = disp * (2 ** (-0.5)) if (r_mid - r_min > 1 or c_mid - c_min > 1): generate_terrain(r_min, r_mid, c_min, c_mid, new_disp) if (r_max - r_mid > 1 or c_mid - c_min > 1): generate_terrain(r_mid, r_max, c_min, c_mid, new_disp) if (r_mid - r_min > 1 or c_max - c_mid > 1): generate_terrain(r_min, r_mid, c_mid, c_max, new_disp) if (r_max - r_mid > 1 or c_max - c_mid > 1): generate_terrain(r_mid, r_max, c_mid, c_max, new_disp) def generate_points(length=3): """Generates points via recursive function and generate triangles using Scipy Delaunay triangulation Parameters ---------- length : int (2 ** length + 1 by 2 ** length + 1) number of points is generated """ print("Points are being generated...") global points, triangles, height size = 2**(length) + 1 points = np.indices((size, size, 1)).T[0].transpose((1, 0, 2)) points = points.astype(np.float32) generate_terrain(0, size-1, 0, size-1, length) height = length points = np.resize(points, (size*size, 3)) points2 = np.delete(points, 2, 1) tri = Delaunay(points2) triangles = points[tri.simplices] triangles = np.vstack(triangles) print("Points successfully generated.") VERT_SHADER = """ uniform float u_height; uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; attribute vec3 a_position; varying vec4 v_color; void main (void) { gl_Position = u_projection * u_view * u_model * vec4(a_position, 1.0); v_color = vec4(0.0, a_position[2] * a_position[2] / (u_height * u_height * u_height), 0.1, 1.0); } """ FRAG_SHADER = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive') self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) # Sets the view to an appropriate position over the terrain self.default_view = np.array([[0.8, 0.2, -0.48, 0], [-0.5, 0.3, -0.78, 0], [-0.01, 0.9, -0.3, 0], [-4.5, -21.5, -7.4, 1]], dtype=np.float32) self.view = self.default_view self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.translate = [0, 0, 0] self.rotate = [0, 0, 0] self.program['u_height'] = height self.program['u_model'] = self.model self.program['u_view'] = self.view self.program['a_position'] = gloo.VertexBuffer(triangles) self.activate_zoom() gloo.set_state(clear_color='black', depth_test=True) self.show() def on_key_press(self, event): """Controls - a(A) - move left d(D) - move right w(W) - move up s(S) - move down x/X - rotate about x-axis cw/anti-cw y/Y - rotate about y-axis cw/anti-cw z/Z - rotate about z-axis cw/anti-cw space - reset view p(P) - print current view i(I) - zoom in o(O) - zoom out """ self.translate = [0, 0, 0] self.rotate = [0, 0, 0] if(event.text == 'p' or event.text == 'P'): print(self.view) elif(event.text == 'd' or event.text == 'D'): self.translate[0] = 0.3 elif(event.text == 'a' or event.text == 'A'): self.translate[0] = -0.3 elif(event.text == 'w' or event.text == 'W'): self.translate[1] = 0.3 elif(event.text == 's' or event.text == 'S'): self.translate[1] = -0.3 elif(event.text == 'o' or event.text == 'O'): self.translate[2] = 0.3 elif(event.text == 'i' or event.text == 'I'): self.translate[2] = -0.3 elif(event.text == 'x'): self.rotate = [1, 0, 0] elif(event.text == 'X'): self.rotate = [-1, 0, 0] elif(event.text == 'y'): self.rotate = [0, 1, 0] elif(event.text == 'Y'): self.rotate = [0, -1, 0] elif(event.text == 'z'): self.rotate = [0, 0, 1] elif(event.text == 'Z'): self.rotate = [0, 0, -1] elif(event.text == ' '): self.view = self.default_view self.view = self.view.dot( translate(-np.array(self.translate)).dot( rotate(self.rotate[0], (1, 0, 0)).dot( rotate(self.rotate[1], (0, 1, 0)).dot( rotate(self.rotate[2], (0, 0, 1)))))) self.program['u_view'] = self.view self.update() def on_resize(self, event): self.activate_zoom() def activate_zoom(self): gloo.set_viewport(0, 0, *self.physical_size) self.projection = perspective(60.0, self.size[0] / float(self.size[1]), 1.0, 100.0) self.program['u_projection'] = self.projection def on_draw(self, event): # Clear gloo.clear(color=True, depth=True) # Draw self.program.draw('triangles') generate_points(8) if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/spacy.py0000644000175000017510000001157114507104216017476 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # 2014, Almar Klein # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- """ Visualization of traveling through space. """ import time import numpy as np from vispy import gloo from vispy import app from vispy.util.transforms import perspective vertex = """ #version 120 uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform float u_time_offset; uniform float u_pixel_scale; attribute vec3 a_position; attribute float a_offset; varying float v_pointsize; void main (void) { vec3 pos = a_position; pos.z = pos.z - a_offset - u_time_offset; vec4 v_eye_position = u_view * u_model * vec4(pos, 1.0); gl_Position = u_projection * v_eye_position; // stackoverflow.com/questions/8608844/... // ... resizing-point-sprites-based-on-distance-from-the-camera float radius = 1; vec4 corner = vec4(radius, radius, v_eye_position.z, v_eye_position.w); vec4 proj_corner = u_projection * corner; gl_PointSize = 100.0 * u_pixel_scale * proj_corner.x / proj_corner.w; v_pointsize = gl_PointSize; } """ fragment = """ #version 120 varying float v_pointsize; void main() { float x = 2.0*gl_PointCoord.x - 1.0; float y = 2.0*gl_PointCoord.y - 1.0; float a = 0.9 - (x*x + y*y); a = a * min(1.0, v_pointsize/1.5); gl_FragColor = vec4(1.0, 1.0, 1.0, a); } """ N = 100000 # Number of stars SIZE = 100 SPEED = 4.0 # time in seconds to go through one block NBLOCKS = 10 class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title='Spacy', keys='interactive', size=(800, 600)) self.program = gloo.Program(vertex, fragment) self.view = np.eye(4, dtype=np.float32) self.model = np.eye(4, dtype=np.float32) self.activate_zoom() self.timer = app.Timer('auto', connect=self.update, start=True) # Set uniforms (some are set later) self.program['u_model'] = self.model self.program['u_view'] = self.view self.program['u_pixel_scale'] = self.pixel_scale # Set attributes self.program['a_position'] = np.zeros((N, 3), np.float32) self.program['a_offset'] = np.zeros((N, 1), np.float32) # Init self._timeout = 0 self._active_block = 0 for i in range(NBLOCKS): self._generate_stars() self._timeout = time.time() + SPEED gloo.set_state(clear_color='black', depth_test=False, blend=True, blend_equation='func_add', blend_func=('src_alpha', 'one_minus_src_alpha')) self.show() def on_key_press(self, event): if event.text == ' ': if self.timer.running: self.timer.stop() else: self.timer.start() def on_resize(self, event): self.activate_zoom() def activate_zoom(self): width, height = self.size gloo.set_viewport(0, 0, *self.physical_size) far = SIZE*(NBLOCKS-2) self.projection = perspective(25.0, width / float(height), 1.0, far) self.program['u_projection'] = self.projection def on_draw(self, event): # Set time offset. Factor runs from 1 to 0 # the time offset goes from 0 to size factor = (self._timeout - time.time()) / SPEED self.program['u_time_offset'] = -(1-factor) * SIZE # Draw gloo.clear() self.program.draw('points') # Build new starts if the first block is fully behind us if factor < 0: self._generate_stars() def on_close(self, event): self.timer.stop() def _generate_stars(self): # Get number of stars in each block blocksize = N // NBLOCKS # Update active block self._active_block += 1 if self._active_block >= NBLOCKS: self._active_block = 0 # Create new position data for the active block pos = np.zeros((blocksize, 3), 'float32') pos[:, :2] = np.random.normal(0.0, SIZE/2., (blocksize, 2)) # x-y pos[:, 2] = np.random.uniform(0, SIZE, (blocksize,)) # z start_index = self._active_block * blocksize self.program['a_position'].set_subdata(pos, offset=start_index) # print(start_index) # Set offsets - active block gets offset 0 for i in range(NBLOCKS): val = i - self._active_block if val < 0: val += NBLOCKS values = np.ones((blocksize, 1), 'float32') * val * SIZE start_index = i*blocksize self.program['a_offset'].set_subdata(values, offset=start_index) # Reset timer self._timeout += SPEED if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/skybox.py0000644000175000017510000001245214507104216017675 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Demonstration of cube mapping with trackball interaction. """ import numpy as np from math import cos, sin, pi from vispy import app, gloo from vispy.gloo import gl from vispy.io import read_png, load_data_file from vispy.util.transforms import perspective def lookAt(eye, target, up=[0, 0, 1]): """Computes matrix to put eye looking at target point.""" eye = np.asarray(eye).astype(np.float32) target = np.asarray(target).astype(np.float32) up = np.asarray(up).astype(np.float32) vforward = eye - target vforward /= np.linalg.norm(vforward) vright = np.cross(up, vforward) vright /= np.linalg.norm(vright) vup = np.cross(vforward, vright) view = np.r_[vright, -np.dot(vright, eye), vup, -np.dot(vup, eye), vforward, -np.dot(vforward, eye), [0, 0, 0, 1]].reshape(4, 4, order='F') return view def getView(azimuth, elevation, distance, target=[0, 0, 0]): """Computes view matrix based on angle, distance and target.""" x = distance * sin(elevation) * sin(azimuth) y = distance * sin(-elevation) * cos(azimuth) z = distance * cos(elevation) return lookAt([x, y, z], target) vertex_shader = """ attribute vec3 a_position; attribute vec3 a_texcoord; varying vec3 v_texcoord; uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; void main() { v_texcoord = a_texcoord; gl_Position = u_projection * u_view * u_model * vec4(a_position, 1.0); } """ fragment_shader = """ uniform samplerCube a_texture; varying vec3 v_texcoord; void main() { gl_FragColor = textureCube(a_texture, v_texcoord); } """ vertices = 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]]).astype(np.float32) faces = np.array([vertices[i] for i in [0, 1, 2, 3, 0, 3, 4, 5, 0, 5, 6, 1, 6, 7, 2, 1, 7, 4, 3, 2, 4, 7, 6, 5]]) indices = np.resize(np.array([0, 1, 2, 0, 2, 3], dtype=np.uint32), 36) indices += np.repeat(4 * np.arange(6, dtype=np.uint32), 6) texture = np.zeros((6, 1024, 1024, 3), dtype=np.float32) texture[2] = read_png(load_data_file("skybox/sky-left.png"))/255. texture[3] = read_png(load_data_file("skybox/sky-right.png"))/255. texture[0] = read_png(load_data_file("skybox/sky-front.png"))/255. texture[1] = read_png(load_data_file("skybox/sky-back.png"))/255. texture[4] = read_png(load_data_file("skybox/sky-up.png"))/255. texture[5] = read_png(load_data_file("skybox/sky-down.png"))/255. class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(1024, 1024), title='Skybox example', keys='interactive') self.cubeSize = 10 self.pressed = False self.azimuth = pi / 2.0 self.elevation = pi / 2.0 self.distanceMin = 1 self.distanceMax = 50 self.distance = 30 self.sensitivity = 5.0 self.view = getView(self.azimuth, self.elevation, self.distance) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.program = gloo.Program(vertex_shader, fragment_shader, count=24) self.program['a_position'] = faces*self.cubeSize self.program['a_texcoord'] = faces self.program['a_texture'] = gloo.TextureCube(texture, interpolation='linear') self.program['u_model'] = self.model self.program['u_view'] = self.view gloo.set_viewport(0, 0, *self.physical_size) self.projection = perspective(60.0, self.size[0] / float(self.size[1]), 1.0, 100.0) self.program['u_projection'] = self.projection gl.glEnable(gl.GL_DEPTH_TEST) gloo.set_clear_color('black') self.show() def on_draw(self, event): gloo.clear(color=True, depth=True) self.program.draw(gl.GL_TRIANGLES, gloo.IndexBuffer(indices)) def on_mouse_wheel(self, event): self.distance = self.distance - event.delta[1] self.distance = min(max(self.distance, self.distanceMin), self.distanceMax) self.program['u_view'] = getView(self.azimuth, self.elevation, self.distance) self.update() def on_mouse_press(self, event): self.pressed = True self.mousex = event.pos[0] self.mousey = event.pos[1] def on_mouse_release(self, event): self.pressed = False def on_mouse_move(self, event): if self.pressed: dazimuth = (event.pos[0] - self.mousex) * (2*pi) / self.size[0] delevation = (event.pos[1] - self.mousey) * (2*pi) / self.size[1] self.mousex = event.pos[0] self.mousey = event.pos[1] self.azimuth = (self.azimuth - dazimuth/self.sensitivity) self.elevation = (self.elevation - delevation/self.sensitivity) self.program['u_view'] = getView(self.azimuth, self.elevation, self.distance) self.update() if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/signals.py0000644000175000017510000000730414507104216020016 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Multiple digital signals. """ from vispy import gloo from vispy import app import numpy as np import math m = 20 n = 25000 x = np.tile(np.linspace(-1., 1., n), m) y = .1 * np.random.randn(m, n) y += np.arange(m).reshape((-1, 1)) data = np.zeros(n*m, dtype=[ ('a_position', np.float32, 2), ('a_color', np.float32, 3), ('a_index', np.float32), ]) data['a_position'] = np.zeros((n*m, 2), dtype=np.float32) data['a_position'][:, 0] = x data['a_position'][:, 1] = .9*(y.ravel()/y.max()*2-1) data['a_color'] = np.repeat(np.random.uniform(size=(m, 3), low=.5, high=.9), n, axis=0) data['a_index'] = np.repeat(np.arange(m), n) VERT_SHADER = """ #version 120 attribute vec2 a_position; attribute float a_index; varying float v_index; attribute vec3 a_color; varying vec3 v_color; uniform vec2 u_pan; uniform vec2 u_scale; void main() { vec2 position_tr = u_scale * (a_position + u_pan); gl_Position = vec4(position_tr, 0.0, 1.0); v_color = a_color; v_index = a_index; } """ FRAG_SHADER = """ #version 120 varying vec3 v_color; varying float v_index; void main() { gl_FragColor = vec4(v_color, 1.0); if ((fract(v_index) > .00001) && (fract(v_index) < .99999)) gl_FragColor.a = 0.; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive') self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) self.program.bind(gloo.VertexBuffer(data)) self.program['u_pan'] = (0., 0.) self.program['u_scale'] = (1., 1.) gloo.set_viewport(0, 0, *self.physical_size) gloo.set_state(clear_color=(1, 1, 1, 1), blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self.show() def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) def on_draw(self, event): gloo.clear(color=(0.0, 0.0, 0.0, 1.0)) self.program.draw('line_strip') def _normalize(self, x_y): x, y = x_y w, h = float(self.size[0]), float(self.size[1]) return x/(w/2.)-1., y/(h/2.)-1. def on_mouse_move(self, event): if event.is_dragging: x0, y0 = self._normalize(event.press_event.pos) x1, y1 = self._normalize(event.last_event.pos) x, y = self._normalize(event.pos) dx, dy = x - x1, -(y - y1) button = event.press_event.button pan_x, pan_y = self.program['u_pan'] scale_x, scale_y = self.program['u_scale'] if button == 1: self.program['u_pan'] = (pan_x+dx/scale_x, pan_y+dy/scale_y) elif button == 2: scale_x_new, scale_y_new = (scale_x * math.exp(2.5*dx), scale_y * math.exp(2.5*dy)) self.program['u_scale'] = (scale_x_new, scale_y_new) self.program['u_pan'] = (pan_x - x0 * (1./scale_x - 1./scale_x_new), pan_y + y0 * (1./scale_y - 1./scale_y_new)) self.update() def on_mouse_wheel(self, event): dx = np.sign(event.delta[1])*.05 scale_x, scale_y = self.program['u_scale'] scale_x_new, scale_y_new = (scale_x * math.exp(2.5*dx), scale_y * math.exp(2.5*dx)) self.program['u_scale'] = (scale_x_new, scale_y_new) self.update() if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/realtime_signals.py0000644000175000017510000001073714507104216021704 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Multiple real-time digital signals with GLSL-based clipping. """ from vispy import gloo from vispy import app import numpy as np import math # Number of cols and rows in the table. nrows = 16 ncols = 20 # Number of signals. m = nrows*ncols # Number of samples per signal. n = 1000 # Various signal amplitudes. amplitudes = .1 + .2 * np.random.rand(m, 1).astype(np.float32) # Generate the signals as a (m, n) array. y = amplitudes * np.random.randn(m, n).astype(np.float32) # Color of each vertex (TODO: make it more efficient by using a GLSL-based # color map and the index). color = np.repeat(np.random.uniform(size=(m, 3), low=.5, high=.9), n, axis=0).astype(np.float32) # Signal 2D index of each vertex (row and col) and x-index (sample index # within each signal). index = np.c_[np.repeat(np.repeat(np.arange(ncols), nrows), n), np.repeat(np.tile(np.arange(nrows), ncols), n), np.tile(np.arange(n), m)].astype(np.float32) VERT_SHADER = """ #version 120 // y coordinate of the position. attribute float a_position; // row, col, and time index. attribute vec3 a_index; varying vec3 v_index; // 2D scaling factor (zooming). uniform vec2 u_scale; // Size of the table. uniform vec2 u_size; // Number of samples per signal. uniform float u_n; // Color. attribute vec3 a_color; varying vec4 v_color; // Varying variables used for clipping in the fragment shader. varying vec2 v_position; varying vec4 v_ab; void main() { float nrows = u_size.x; float ncols = u_size.y; // Compute the x coordinate from the time index. float x = -1 + 2*a_index.z / (u_n-1); vec2 position = vec2(x - (1 - 1 / u_scale.x), a_position); // Find the affine transformation for the subplots. vec2 a = vec2(1./ncols, 1./nrows)*.9; vec2 b = vec2(-1 + 2*(a_index.x+.5) / ncols, -1 + 2*(a_index.y+.5) / nrows); // Apply the static subplot transformation + scaling. gl_Position = vec4(a*u_scale*position+b, 0.0, 1.0); v_color = vec4(a_color, 1.); v_index = a_index; // For clipping test in the fragment shader. v_position = gl_Position.xy; v_ab = vec4(a, b); } """ FRAG_SHADER = """ #version 120 varying vec4 v_color; varying vec3 v_index; varying vec2 v_position; varying vec4 v_ab; void main() { gl_FragColor = v_color; // Discard the fragments between the signals (emulate glMultiDrawArrays). if ((fract(v_index.x) > 0.) || (fract(v_index.y) > 0.)) discard; // Clipping test. vec2 test = abs((v_position.xy-v_ab.zw)/v_ab.xy); if ((test.x > 1) || (test.y > 1)) discard; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title='Use your wheel to zoom!', keys='interactive') self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) self.program['a_position'] = y.reshape(-1, 1) self.program['a_color'] = color self.program['a_index'] = index self.program['u_scale'] = (1., 1.) self.program['u_size'] = (nrows, ncols) self.program['u_n'] = n gloo.set_viewport(0, 0, *self.physical_size) self._timer = app.Timer('auto', connect=self.on_timer, start=True) gloo.set_state(clear_color='black', blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self.show() def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) def on_mouse_wheel(self, event): dx = np.sign(event.delta[1]) * .05 scale_x, scale_y = self.program['u_scale'] scale_x_new, scale_y_new = (scale_x * math.exp(2.5*dx), scale_y * math.exp(0.0*dx)) self.program['u_scale'] = (max(1, scale_x_new), max(1, scale_y_new)) self.update() def on_timer(self, event): """Add some data at the end of each signal (real-time signals).""" k = 10 y[:, :-k] = y[:, k:] y[:, -k:] = amplitudes * np.random.randn(m, k) self.program['a_position'].set_data(y.ravel().astype(np.float32)) self.update() self.context.flush() # prevent memory leak when minimized def on_draw(self, event): gloo.clear() self.program.draw('line_strip') if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/raytracing.py0000644000175000017510000001601214507104216020515 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 300 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ GPU-based ray tracing example. GLSL port of the following Python example: https://gist.github.com/rossant/6046463 https://pbs.twimg.com/media/BPpbJTiCIAEoEPl.png TODO: * Once uniform structs are supported, refactor the code to encapsulate objects (spheres, planes, lights) in structures. * Customizable engine with an arbitrary number of objects. """ from math import cos from vispy import app, gloo vertex = """ #version 120 attribute vec2 a_position; varying vec2 v_position; void main() { gl_Position = vec4(a_position, 0.0, 1.0); v_position = a_position; } """ fragment = """ #version 120 const float M_PI = 3.14159265358979323846; const float INFINITY = 1000000000.; const int PLANE = 1; const int SPHERE_0 = 2; const int SPHERE_1 = 3; uniform float u_aspect_ratio; varying vec2 v_position; uniform vec3 sphere_position_0; uniform float sphere_radius_0; uniform vec3 sphere_color_0; uniform vec3 sphere_position_1; uniform float sphere_radius_1; uniform vec3 sphere_color_1; uniform vec3 plane_position; uniform vec3 plane_normal; uniform float light_intensity; uniform vec2 light_specular; uniform vec3 light_position; uniform vec3 light_color; uniform float ambient; uniform vec3 O; float intersect_sphere(vec3 O, vec3 D, vec3 S, float R) { float a = dot(D, D); vec3 OS = O - S; float b = 2. * dot(D, OS); float c = dot(OS, OS) - R * R; float disc = b * b - 4. * a * c; if (disc > 0.) { float distSqrt = sqrt(disc); float q = (-b - distSqrt) / 2.0; if (b >= 0.) { q = (-b + distSqrt) / 2.0; } float t0 = q / a; float t1 = c / q; t0 = min(t0, t1); t1 = max(t0, t1); if (t1 >= 0.) { if (t0 < 0.) { return t1; } else { return t0; } } } return INFINITY; } float intersect_plane(vec3 O, vec3 D, vec3 P, vec3 N) { float denom = dot(D, N); if (abs(denom) < 1e-6) { return INFINITY; } float d = dot(P - O, N) / denom; if (d < 0.) { return INFINITY; } return d; } vec3 run(float x, float y) { vec3 Q = vec3(x, y, 0.); vec3 D = normalize(Q - O); int depth = 0; float t_plane, t0, t1; vec3 rayO = O; vec3 rayD = D; vec3 col = vec3(0.0, 0.0, 0.0); vec3 col_ray; float reflection = 1.; int object_index; vec3 object_color; vec3 object_normal; float object_reflection; vec3 M; vec3 N, toL, toO; while (depth < 5) { /* start trace_ray */ t_plane = intersect_plane(rayO, rayD, plane_position, plane_normal); t0 = intersect_sphere(rayO, rayD, sphere_position_0, sphere_radius_0); t1 = intersect_sphere(rayO, rayD, sphere_position_1, sphere_radius_1); if (t_plane < min(t0, t1)) { // Plane. M = rayO + rayD * t_plane; object_normal = plane_normal; // Plane texture. if (mod(int(2*M.x), 2) == mod(int(2*M.z), 2)) { object_color = vec3(1., 1., 1.); } else { object_color = vec3(0., 0., 0.); } object_reflection = .25; object_index = PLANE; } else if (t0 < t1) { // Sphere 0. M = rayO + rayD * t0; object_normal = normalize(M - sphere_position_0); object_color = sphere_color_0; object_reflection = .5; object_index = SPHERE_0; } else if (t1 < t0) { // Sphere 1. M = rayO + rayD * t1; object_normal = normalize(M - sphere_position_1); object_color = sphere_color_1; object_reflection = .5; object_index = SPHERE_1; } else { break; } N = object_normal; toL = normalize(light_position - M); toO = normalize(O - M); // Shadow of the spheres on the plane. if (object_index == PLANE) { t0 = intersect_sphere(M + N * .0001, toL, sphere_position_0, sphere_radius_0); t1 = intersect_sphere(M + N * .0001, toL, sphere_position_1, sphere_radius_1); if (min(t0, t1) < INFINITY) { break; } } col_ray = vec3(ambient, ambient, ambient); col_ray += light_intensity * max(dot(N, toL), 0.) * object_color; col_ray += light_specular.x * light_color * pow(max(dot(N, normalize(toL + toO)), 0.), light_specular.y); /* end trace_ray */ rayO = M + N * .0001; rayD = normalize(rayD - 2. * dot(rayD, N) * N); col += reflection * col_ray; reflection *= object_reflection; depth++; } return clamp(col, 0., 1.); } void main() { vec2 pos = v_position; gl_FragColor = vec4(run(pos.x*u_aspect_ratio, pos.y), 1.); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, position=(300, 100), size=(800, 600), keys='interactive') self.program = gloo.Program(vertex, fragment) self.program['a_position'] = [(-1., -1.), (-1., +1.), (+1., -1.), (+1., +1.)] self.program['sphere_position_0'] = (.75, .1, 1.) self.program['sphere_radius_0'] = .6 self.program['sphere_color_0'] = (0., 0., 1.) self.program['sphere_position_1'] = (-.75, .1, 2.25) self.program['sphere_radius_1'] = .6 self.program['sphere_color_1'] = (.5, .223, .5) self.program['plane_position'] = (0., -.5, 0.) self.program['plane_normal'] = (0., 1., 0.) self.program['light_intensity'] = 1. self.program['light_specular'] = (1., 50.) self.program['light_position'] = (5., 5., -10.) self.program['light_color'] = (1., 1., 1.) self.program['ambient'] = .05 self.program['O'] = (0., 0., -1.) self.activate_zoom() self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_timer(self, event): t = event.elapsed self.program['sphere_position_0'] = (+.75, .1, 2.0 + 1.0 * cos(4*t)) self.program['sphere_position_1'] = (-.75, .1, 2.0 - 1.0 * cos(4*t)) self.update() def on_resize(self, event): self.activate_zoom() def activate_zoom(self): width, height = self.size gloo.set_viewport(0, 0, *self.physical_size) self.program['u_aspect_ratio'] = width/float(height) def on_draw(self, event): self.program.draw('triangle_strip') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/rain.py0000755000175000017510000000776414507104216017324 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Nicolas P .Rougier # Date: 06/03/2014 # Abstract: Water ripple effect following mouse # Keywords: antialias, water, mouse # ----------------------------------------------------------------------------- import numpy as np from vispy import gloo, app from vispy.gloo import Program, VertexBuffer from vispy.util.transforms import ortho vertex = """ #version 120 uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform float u_linewidth; uniform float u_antialias; attribute vec3 a_position; attribute vec4 a_fg_color; attribute float a_size; varying vec4 v_fg_color; varying float v_size; void main (void) { v_size = a_size; v_fg_color = a_fg_color; if( a_fg_color.a > 0.0) { gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0); gl_PointSize = v_size + u_linewidth + 2.*1.5*u_antialias; } else { gl_Position = u_projection * u_view * u_model * vec4(-1.,-1.,0.,1.); gl_PointSize = 0.0; } } """ fragment = """ #version 120 uniform float u_linewidth; uniform float u_antialias; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_size; float disc(vec2 P, float size) { return length((P.xy - vec2(0.5,0.5))*size); } void main() { if( v_fg_color.a <= 0.0) discard; float actual_size = v_size + u_linewidth + 2*1.5*u_antialias; float t = u_linewidth/2.0 - u_antialias; float r = disc(gl_PointCoord, actual_size); float d = abs(r - v_size/2.0) - t; if( d < 0.0 ) { gl_FragColor = v_fg_color; } else if( abs(d) > 2.5*u_antialias ) { discard; } else { d /= u_antialias; gl_FragColor = vec4(v_fg_color.rgb, exp(-d*d)*v_fg_color.a); } } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title='Rain [Move mouse]', size=(512, 512), keys='interactive') # Build data # -------------------------------------- n = 500 self.data = np.zeros(n, [('a_position', np.float32, 2), ('a_fg_color', np.float32, 4), ('a_size', np.float32)]) self.index = 0 self.program = Program(vertex, fragment) self.vdata = VertexBuffer(self.data) self.program.bind(self.vdata) self.program['u_antialias'] = 1.00 self.program['u_linewidth'] = 1.00 self.program['u_model'] = np.eye(4, dtype=np.float32) self.program['u_view'] = np.eye(4, dtype=np.float32) self.activate_zoom() gloo.set_clear_color('white') gloo.set_state(blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self.timer = app.Timer('auto', self.on_timer, start=True) self.show() def on_draw(self, event): gloo.clear() self.program.draw('points') def on_resize(self, event): self.activate_zoom() def activate_zoom(self): gloo.set_viewport(0, 0, *self.physical_size) projection = ortho(0, self.size[0], 0, self.size[1], -1, +1) self.program['u_projection'] = projection def on_timer(self, event): self.data['a_fg_color'][..., 3] -= 0.01 self.data['a_size'] += 1.0 self.vdata.set_data(self.data) self.update() def on_mouse_move(self, event): x, y = event.pos h = self.size[1] self.data['a_position'][self.index] = x, h - y self.data['a_size'][self.index] = 5 self.data['a_fg_color'][self.index] = 0, 0, 0, 1 self.index = (self.index + 1) % 500 if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/quiver.py0000755000175000017510000000450014507104216017667 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. # Distributed under the (new) BSD License. # ----------------------------------------------------------------------------- from vispy import app, gloo vertex = """ attribute vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); } """ fragment = """ #include "math/constants.glsl" #include "arrows/arrows.glsl" #include "antialias/antialias.glsl" uniform vec2 iResolution; uniform vec2 iMouse; void main() { const float M_PI = 3.14159265358979323846; const float SQRT_2 = 1.4142135623730951; const float linewidth = 3.0; const float antialias = 1.0; const float rows = 32.0; const float cols = 32.0; float body = min(iResolution.x/cols, iResolution.y/rows) / SQRT_2; vec2 texcoord = gl_FragCoord.xy; vec2 size = iResolution.xy / vec2(cols,rows); vec2 center = (floor(texcoord/size) + vec2(0.5,0.5)) * size; texcoord -= center; float theta = M_PI-atan(center.y-iMouse.y, center.x-iMouse.x); float cos_theta = cos(theta); float sin_theta = sin(theta); texcoord = vec2(cos_theta*texcoord.x - sin_theta*texcoord.y, sin_theta*texcoord.x + cos_theta*texcoord.y); float d = arrow_stealth(texcoord, body, 0.25*body, linewidth, antialias); gl_FragColor = filled(d, linewidth, antialias, vec4(0,0,0,1)); } """ canvas = app.Canvas(size=(2*512, 2*512), keys='interactive') canvas.context.set_state(blend=True, blend_func=('src_alpha', 'one_minus_src_alpha'), blend_equation='func_add') @canvas.connect def on_draw(event): gloo.clear('white') program.draw('triangle_strip') @canvas.connect def on_resize(event): program["iResolution"] = event.size gloo.set_viewport(0, 0, event.size[0], event.size[1]) @canvas.connect def on_mouse_move(event): x, y = event.pos program["iMouse"] = x, canvas.size[1] - y canvas.update() program = gloo.Program(vertex, fragment, count=4) dx, dy = 1, 1 program['position'] = (-dx, -dy), (-dx, +dy), (+dx, -dy), (+dx, +dy) program["iResolution"] = (2 * 512, 2 * 512) program["iMouse"] = (0., 0.) if __name__ == '__main__': canvas.show() app.run() vispy-0.14.1/examples/demo/gloo/primitive_mesh_viewer_qt.py0000644000175000017510000003250014507104216023463 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Abstract: show mesh primitive # Keywords: cone, arrow, sphere, cylinder, qt # ----------------------------------------------------------------------------- """ Test the fps capability of Vispy with meshdata primitive """ try: from sip import setapi setapi("QVariant", 2) setapi("QString", 2) except ImportError: pass # To switch between PyQt5 and PySide2 bindings just change the from import from PyQt5 import QtCore, QtWidgets import sys import numpy as np from vispy import app, gloo from vispy.util.transforms import perspective, translate, rotate from vispy.geometry import meshdata as md from vispy.geometry import generation as gen # Provide automatic signal function selection for PyQt5/PySide2 pyqtsignal = QtCore.pyqtSignal if hasattr(QtCore, 'pyqtSignal') else QtCore.Signal OBJECT = {'sphere': [('rows', 3, 1000, 'int', 3), ('cols', 3, 1000, 'int', 3), ('radius', 0.1, 10, 'double', 1.0)], 'cylinder': [('rows', 4, 1000, 'int', 4), ('cols', 4, 1000, 'int', 4), ('radius', 0.1, 10, 'double', 1.0), ('radius Top.', 0.1, 10, 'double', 1.0), ('length', 0.1, 10, 'double', 1.0)], 'cone': [('cols', 3, 1000, 'int', 3), ('radius', 0.1, 10, 'double', 1.0), ('length', 0.1, 10, 'double', 1.0)], 'arrow': [('rows', 4, 1000, 'int', 4), ('cols', 4, 1000, 'int', 4), ('radius', 0.01, 10, 'double', 0.1), ('length', 0.1, 10, 'double', 1.0), ('cone_radius', 0.1, 10, 'double', 0.2), ('cone_length', 0.0, 10., 'double', 0.3)]} vert = """ // Uniforms // ------------------------------------ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform vec4 u_color; // Attributes // ------------------------------------ attribute vec3 a_position; attribute vec3 a_normal; attribute vec4 a_color; // Varying // ------------------------------------ varying vec4 v_color; void main() { v_color = a_color * u_color; gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0); } """ frag = """ // Varying // ------------------------------------ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ DEFAULT_COLOR = (0, 1, 1, 1) # ----------------------------------------------------------------------------- class MyMeshData(md.MeshData): """ Add to Meshdata class the capability to export good data for gloo """ def __init__(self, vertices=None, faces=None, edges=None, vertex_colors=None, face_colors=None): md.MeshData.__init__(self, vertices=None, faces=None, edges=None, vertex_colors=None, face_colors=None) def get_glTriangles(self): """ Build vertices for a colored mesh. V is the vertices I1 is the indices for a filled mesh (use with GL_TRIANGLES) I2 is the indices for an outline mesh (use with GL_LINES) """ vtype = [('a_position', np.float32, 3), ('a_normal', np.float32, 3), ('a_color', np.float32, 4)] vertices = self.get_vertices() normals = self.get_vertex_normals() faces = np.uint32(self.get_faces()) edges = np.uint32(self.get_edges().reshape((-1))) colors = self.get_vertex_colors() nbrVerts = vertices.shape[0] V = np.zeros(nbrVerts, dtype=vtype) V[:]['a_position'] = vertices V[:]['a_normal'] = normals V[:]['a_color'] = colors return V, faces.reshape((-1)), edges.reshape((-1)) # ----------------------------------------------------------------------------- class ObjectParam(object): """ OBJECT parameter test """ def __init__(self, name, list_param): self.name = name self.list_param = list_param self.props = {} self.props['visible'] = True for nameV, minV, maxV, typeV, iniV in list_param: self.props[nameV] = iniV # ----------------------------------------------------------------------------- class ObjectWidget(QtWidgets.QWidget): """ Widget for editing OBJECT parameters """ signal_objet_changed = pyqtsignal(ObjectParam, name='objectChanged') def __init__(self, parent=None, param=None): super(ObjectWidget, self).__init__(parent) if param is None: self.param = ObjectParam('sphere', OBJECT['sphere']) else: self.param = param self.gb_c = QtWidgets.QGroupBox(u"Hide/Show %s" % self.param.name) self.gb_c.setCheckable(True) self.gb_c.setChecked(self.param.props['visible']) self.gb_c.toggled.connect(self.update_param) lL = [] self.sp = [] gb_c_lay = QtWidgets.QGridLayout() for nameV, minV, maxV, typeV, iniV in self.param.list_param: lL.append(QtWidgets.QLabel(nameV, self.gb_c)) if typeV == 'double': self.sp.append(QtWidgets.QDoubleSpinBox(self.gb_c)) self.sp[-1].setDecimals(2) self.sp[-1].setSingleStep(0.1) self.sp[-1].setLocale(QtCore.QLocale(QtCore.QLocale.English)) elif typeV == 'int': self.sp.append(QtWidgets.QSpinBox(self.gb_c)) self.sp[-1].setMinimum(minV) self.sp[-1].setMaximum(maxV) self.sp[-1].setValue(iniV) # Layout for pos in range(len(lL)): gb_c_lay.addWidget(lL[pos], pos, 0) gb_c_lay.addWidget(self.sp[pos], pos, 1) # Signal self.sp[pos].valueChanged.connect(self.update_param) self.gb_c.setLayout(gb_c_lay) vbox = QtWidgets.QVBoxLayout() hbox = QtWidgets.QHBoxLayout() hbox.addWidget(self.gb_c) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addStretch(1) self.setLayout(vbox) def update_param(self, option): """ update param and emit a signal """ self.param.props['visible'] = self.gb_c.isChecked() keys = map(lambda x: x[0], self.param.list_param) for pos, nameV in enumerate(keys): self.param.props[nameV] = self.sp[pos].value() # emit signal self.signal_objet_changed.emit(self.param) # ----------------------------------------------------------------------------- class Canvas(app.Canvas): def __init__(self,): app.Canvas.__init__(self) self.size = 800, 600 # fovy, zfar params self.fovy = 45.0 self.zfar = 10.0 width, height = self.size self.aspect = width / float(height) self.program = gloo.Program(vert, frag) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.view = translate((0, 0, -5.0)) self.program['u_model'] = self.model self.program['u_view'] = self.view self.theta = 0 self.phi = 0 self.visible = True self._timer = app.Timer(1.0 / 60) self._timer.connect(self.on_timer) self._timer.start() # --------------------------------- gloo.set_clear_color((1, 1, 1, 1)) gloo.set_state('opaque') gloo.set_polygon_offset(1, 1) # --------------------------------- def on_timer(self, event): self.theta += .5 self.phi += .5 self.model = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.program['u_model'] = self.model self.update() # --------------------------------- def on_resize(self, event): width, height = event.size self.size = event.size gloo.set_viewport(0, 0, width, height) self.aspect = width / float(height) self.projection = perspective(self.fovy, width / float(height), 1.0, self.zfar) self.program['u_projection'] = self.projection # --------------------------------- def on_draw(self, event): gloo.clear() if self.visible: # Filled mesh gloo.set_state(blend=False, depth_test=True, polygon_offset_fill=True) self.program['u_color'] = 1, 1, 1, 1 self.program.draw('triangles', self.filled_buf) # Outline gloo.set_state(blend=True, depth_test=True, polygon_offset_fill=False) gloo.set_depth_mask(False) self.program['u_color'] = 0, 0, 0, 1 self.program.draw('lines', self.outline_buf) gloo.set_depth_mask(True) # --------------------------------- def set_data(self, vertices, filled, outline): self.filled_buf = gloo.IndexBuffer(filled) self.outline_buf = gloo.IndexBuffer(outline) self.vertices_buff = gloo.VertexBuffer(vertices) self.program.bind(self.vertices_buff) self.update() # ----------------------------------------------------------------------------- class MainWindow(QtWidgets.QMainWindow): def __init__(self): QtWidgets.QMainWindow.__init__(self) self.resize(700, 500) self.setWindowTitle('vispy example ...') self.list_object = QtWidgets.QListWidget() self.list_object.setAlternatingRowColors(True) self.list_object.itemSelectionChanged.connect(self.list_objectChanged) self.list_object.addItems(list(OBJECT.keys())) self.props_widget = ObjectWidget(self) self.props_widget.signal_objet_changed.connect(self.update_view) self.splitter_v = QtWidgets.QSplitter(QtCore.Qt.Vertical) self.splitter_v.addWidget(self.list_object) self.splitter_v.addWidget(self.props_widget) self.canvas = Canvas() self.canvas.create_native() self.canvas.native.setParent(self) self.canvas.measure_fps(0.1, self.show_fps) # Central Widget splitter1 = QtWidgets.QSplitter(QtCore.Qt.Horizontal) splitter1.addWidget(self.splitter_v) splitter1.addWidget(self.canvas.native) self.setCentralWidget(splitter1) # FPS message in statusbar: self.status = self.statusBar() self.status_label = QtWidgets.QLabel('...') self.status.addWidget(self.status_label) self.mesh = MyMeshData() self.update_view(self.props_widget.param) def list_objectChanged(self): row = self.list_object.currentIndex().row() name = self.list_object.currentIndex().data() if row != -1: self.props_widget.deleteLater() self.props_widget = ObjectWidget(self, param=ObjectParam(name, OBJECT[name])) self.splitter_v.addWidget(self.props_widget) self.props_widget.signal_objet_changed.connect(self.update_view) self.update_view(self.props_widget.param) def show_fps(self, fps): nbr_tri = self.mesh.n_faces msg = "FPS - %0.2f and nbr Tri %s " % (float(fps), int(nbr_tri)) # NOTE: We can't use showMessage in PyQt5 because it causes # a draw event loop (show_fps for every drawing event, # showMessage causes a drawing event, and so on). self.status_label.setText(msg) def update_view(self, param): cols = param.props['cols'] radius = param.props['radius'] if param.name == 'sphere': rows = param.props['rows'] mesh = gen.create_sphere(cols, rows, radius=radius) elif param.name == 'cone': length = param.props['length'] mesh = gen.create_cone(cols, radius=radius, length=length) elif param.name == 'cylinder': rows = param.props['rows'] length = param.props['length'] radius2 = param.props['radius Top.'] mesh = gen.create_cylinder(rows, cols, radius=[radius, radius2], length=length) elif param.name == 'arrow': length = param.props['length'] rows = param.props['rows'] cone_radius = param.props['cone_radius'] cone_length = param.props['cone_length'] mesh = gen.create_arrow(rows, cols, radius=radius, length=length, cone_radius=cone_radius, cone_length=cone_length) else: return self.canvas.visible = param.props['visible'] self.mesh.set_vertices(mesh.get_vertices()) self.mesh.set_faces(mesh.get_faces()) colors = np.tile(DEFAULT_COLOR, (self.mesh.n_vertices, 1)) self.mesh.set_vertex_colors(colors) vertices, filled, outline = self.mesh.get_glTriangles() self.canvas.set_data(vertices, filled, outline) # Start Qt event loop unless running in interactive mode. if __name__ == '__main__': appQt = QtWidgets.QApplication(sys.argv) win = MainWindow() win.show() appQt.exec_() vispy-0.14.1/examples/demo/gloo/offscreen.py0000644000175000017510000001021114507104216020317 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Demonstrate how to do offscreen rendering. Possible use cases: * GPGPU without CUDA or OpenCL * creation of scripted animations * remote and Web backends The method consists of: 1. Not showing the canvas (show=False). 2. Rendering to an FBO. 3. Manually triggering a rendering pass with self.update(). 4. Retrieving the scene with _screenshot(). 5. Closing the app after the first rendering pass (if that's the intended scenario). """ from vispy import gloo from vispy import app from vispy.util.ptime import time from vispy.gloo.util import _screenshot # WARNING: doesn't work with Qt4 (update() does not call on_draw()??) app.use_app('glfw') vertex = """ attribute vec2 position; void main() { gl_Position = vec4(position, 0, 1.0); } """ fragment = """ uniform vec2 resolution; uniform vec2 center; uniform float scale; uniform int iter; // Jet color scheme vec4 color_scheme(float x) { vec3 a, b; float c; if (x < 0.34) { a = vec3(0, 0, 0.5); b = vec3(0, 0.8, 0.95); c = (x - 0.0) / (0.34 - 0.0); } else if (x < 0.64) { a = vec3(0, 0.8, 0.95); b = vec3(0.85, 1, 0.04); c = (x - 0.34) / (0.64 - 0.34); } else if (x < 0.89) { a = vec3(0.85, 1, 0.04); b = vec3(0.96, 0.7, 0); c = (x - 0.64) / (0.89 - 0.64); } else { a = vec3(0.96, 0.7, 0); b = vec3(0.5, 0, 0); c = (x - 0.89) / (1.0 - 0.89); } return vec4(mix(a, b, c), 1.0); } void main() { vec2 z, c; // Recover coordinates from pixel coordinates c.x = (gl_FragCoord.x / resolution.x - 0.5) * scale + center.x; c.y = (gl_FragCoord.y / resolution.y - 0.5) * scale + center.y; // Main Mandelbrot computation int i; z = c; for(i = 0; i < iter; i++) { float x = (z.x * z.x - z.y * z.y) + c.x; float y = (z.y * z.x + z.x * z.y) + c.y; if((x * x + y * y) > 4.0) break; z.x = x; z.y = y; } // Convert iterations to color float color = 1.0 - float(i) / float(iter); gl_FragColor = color_scheme(color); } """ class Canvas(app.Canvas): def __init__(self, size=(600, 600)): # We hide the canvas upon creation. app.Canvas.__init__(self, show=False, size=size) self._t0 = time() # Texture where we render the scene. self._rendertex = gloo.Texture2D(shape=self.size[::-1] + (4,)) # FBO. self._fbo = gloo.FrameBuffer(self._rendertex, gloo.RenderBuffer(self.size[::-1])) # Regular program that will be rendered to the FBO. self.program = gloo.Program(vertex, fragment) self.program["position"] = [(-1, -1), (-1, 1), (1, 1), (-1, -1), (1, 1), (1, -1)] self.program["scale"] = 3 self.program["center"] = [-0.5, 0] self.program["iter"] = 300 self.program['resolution'] = self.size # We manually draw the hidden canvas. self.update() def on_draw(self, event): # Render in the FBO. with self._fbo: gloo.clear('black') gloo.set_viewport(0, 0, *self.size) self.program.draw() # Retrieve the contents of the FBO texture. self.im = _screenshot((0, 0, self.size[0], self.size[1])) self._time = time() - self._t0 # Immediately exit the application. app.quit() if __name__ == '__main__': c = Canvas() size = c.size app.run() # The rendering is done, we get the rendering output (4D NumPy array) render = c.im print('Finished in %.1fms.' % (c._time*1e3)) # Now, we display this image with matplotlib to check. import matplotlib.pyplot as plt plt.figure(figsize=(size[0]/100., size[1]/100.), dpi=100) plt.imshow(render, interpolation='none') plt.show() vispy-0.14.1/examples/demo/gloo/ndscatter.py0000644000175000017510000001033614507104216020344 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 """N-dimensional scatter plot with GPU-based projections. The projection axes evolve smoothly over time, following a path on the Lie group SO(n). """ from vispy import gloo from vispy import app from vispy.color import ColorArray from vispy.io import load_iris import numpy as np from scipy.linalg import logm VERT_SHADER = """ #version 120 attribute vec4 a_position; attribute vec3 a_color; attribute float a_size; uniform vec2 u_pan; uniform vec2 u_scale; uniform vec4 u_vec1; uniform vec4 u_vec2; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_radius; varying float v_linewidth; varying float v_antialias; void main (void) { v_radius = a_size; v_linewidth = 1.0; v_antialias = 1.0; v_fg_color = vec4(0.0,0.0,0.0,0.5); v_bg_color = vec4(a_color, 1.0); vec2 position = vec2(dot(a_position, u_vec1), dot(a_position, u_vec2)); vec2 position_tr = u_scale * (position + u_pan); gl_Position = vec4(position_tr, 0.0, 1.0); gl_PointSize = 2.0*(v_radius + v_linewidth + 1.5*v_antialias); } """ FRAG_SHADER = """ #version 120 varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_radius; varying float v_linewidth; varying float v_antialias; void main() { float size = 2.0*(v_radius + v_linewidth + 1.5*v_antialias); float t = v_linewidth/2.0-v_antialias; float r = length((gl_PointCoord.xy - vec2(0.5,0.5))*size); float d = abs(r - v_radius) - t; if( d < 0.0 ) gl_FragColor = v_fg_color; else { float alpha = d/v_antialias; alpha = exp(-alpha*alpha); if (r > v_radius) gl_FragColor = vec4(v_fg_color.rgb, alpha*v_fg_color.a); else gl_FragColor = mix(v_bg_color, v_fg_color, alpha); } } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, position=(50, 50), keys='interactive') ps = self.pixel_scale # Load the Iris dataset and normalize. iris = load_iris() position = iris['data'].astype(np.float32) n, ndim = position.shape position -= position.mean() position /= np.abs(position).max() v_position = position*.75 v_color = ColorArray(['orange', 'magenta', 'darkblue']) v_color = v_color.rgb[iris['group'], :].astype(np.float32) v_color *= np.random.uniform(.5, 1.5, (n, 3)) v_color = np.clip(v_color, 0, 1) v_size = np.random.uniform(2*ps, 12*ps, (n, 1)).astype(np.float32) self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) self.program['a_position'] = gloo.VertexBuffer(v_position) self.program['a_color'] = gloo.VertexBuffer(v_color) self.program['a_size'] = gloo.VertexBuffer(v_size) self.program['u_pan'] = (0., 0.) self.program['u_scale'] = (1., 1.) self.program['u_vec1'] = (1., 0., 0., 0.) self.program['u_vec2'] = (0., 1., 0., 0.) # Circulant matrix. circ = np.diagflat(np.ones(ndim-1), 1) circ[-1, 0] = -1 if ndim % 2 == 0 else 1 self.logcirc = logm(circ) # We will solve the equation dX/dt = log(circ) * X in real time # to compute the matrix exponential expm(t*log(circ)). self.mat = np.eye(ndim) self.dt = .001 gloo.set_state(clear_color=(1, 1, 1, 1), blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) gloo.set_viewport(0, 0, *self.physical_size) self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_timer(self, event): # We advance the numerical solver from as many dt there have been # since the last update. for t in np.arange(0., event.dt, self.dt): self.mat += self.dt * np.dot(self.logcirc, self.mat).real # We just keep the first two columns of the matrix. self.program['u_vec1'] = self.mat[:, 0].squeeze() self.program['u_vec2'] = self.mat[:, 1].squeeze() self.update() def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) def on_draw(self, event): gloo.clear() self.program.draw('points') if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/molecular_viewer.py0000644000175000017510000001351114507104216021717 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # 2014, Aurore Deschildre, Gael Goret, Cyrille Rossant, Nicolas P. Rougier. # Distributed under the terms of the new BSD License. # ----------------------------------------------------------------------------- import numpy as np from vispy import gloo from vispy import app from vispy.util.transforms import perspective, translate, rotate from vispy.io import load_data_file vertex = """ #version 120 uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform vec3 u_light_position; uniform vec3 u_light_spec_position; attribute vec3 a_position; attribute vec3 a_color; attribute float a_radius; varying vec3 v_color; varying vec4 v_eye_position; varying float v_radius; varying vec3 v_light_direction; void main (void) { v_radius = a_radius; v_color = a_color; v_eye_position = u_view * u_model * vec4(a_position,1.0); v_light_direction = normalize(u_light_position); float dist = length(v_eye_position.xyz); gl_Position = u_projection * v_eye_position; // stackoverflow.com/questions/8608844/... // ... resizing-point-sprites-based-on-distance-from-the-camera vec4 proj_corner = u_projection * vec4(a_radius, a_radius, v_eye_position.z, v_eye_position.w); // # noqa gl_PointSize = 512.0 * proj_corner.x / proj_corner.w; } """ fragment = """ #version 120 uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform vec3 u_light_position; uniform vec3 u_light_spec_position; varying vec3 v_color; varying vec4 v_eye_position; varying float v_radius; varying vec3 v_light_direction; void main() { // r^2 = (x - x0)^2 + (y - y0)^2 + (z - z0)^2 vec2 texcoord = gl_PointCoord* 2.0 - vec2(1.0); float x = texcoord.x; float y = texcoord.y; float d = 1.0 - x*x - y*y; if (d <= 0.0) discard; float z = sqrt(d); vec4 pos = v_eye_position; pos.z += v_radius*z; vec3 pos2 = pos.xyz; pos = u_projection * pos; // gl_FragDepth = 0.5*(pos.z / pos.w)+0.5; vec3 normal = vec3(x,y,z); float diffuse = clamp(dot(normal, v_light_direction), 0.0, 1.0); // Specular lighting. vec3 M = pos2.xyz; vec3 O = v_eye_position.xyz; vec3 L = u_light_spec_position; vec3 K = normalize(normalize(L - M) + normalize(O - M)); // WARNING: abs() is necessary, otherwise weird bugs may appear with some // GPU drivers... float specular = clamp(pow(abs(dot(normal, K)), 40.), 0.0, 1.0); vec3 v_light = vec3(1., 1., 1.); gl_FragColor.rgba = vec4(.15*v_color + .55*diffuse * v_color + .35*specular * v_light, 1.0); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title='Molecular viewer', keys='interactive', size=(1200, 800)) self.ps = self.pixel_scale self.translate = 40 self.program = gloo.Program(vertex, fragment) self.view = translate((0, 0, -self.translate)) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.apply_zoom() fname = load_data_file('molecular_viewer/micelle.npz') self.load_molecule(fname) self.load_data() self.theta = 0 self.phi = 0 gloo.set_state(depth_test=True, clear_color='black') self.timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def load_molecule(self, fname): molecule = np.load(fname)['molecule'] self._nAtoms = molecule.shape[0] # The x,y,z values store in one array self.coords = molecule[:, :3] # The array that will store the color and alpha scale for all the atoms self.atomsColours = molecule[:, 3:6] # The array that will store the scale for all the atoms. self.atomsScales = molecule[:, 6] def load_data(self): n = self._nAtoms data = np.zeros(n, [('a_position', np.float32, 3), ('a_color', np.float32, 3), ('a_radius', np.float32)]) data['a_position'] = self.coords data['a_color'] = self.atomsColours data['a_radius'] = self.atomsScales*self.ps self.program.bind(gloo.VertexBuffer(data)) self.program['u_model'] = self.model self.program['u_view'] = self.view self.program['u_light_position'] = 0., 0., 2. self.program['u_light_spec_position'] = -5., 5., -5. def on_key_press(self, event): if event.text == ' ': if self.timer.running: self.timer.stop() else: self.timer.start() def on_timer(self, event): self.theta += .25 self.phi += .25 self.model = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.program['u_model'] = self.model self.update() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) self.projection = perspective(25.0, width / float(height), 2.0, 100.0) self.program['u_projection'] = self.projection def apply_zoom(self): width, height = self.physical_size gloo.set_viewport(0, 0, width, height) self.projection = perspective(25.0, width / float(height), 2.0, 100.0) self.program['u_projection'] = self.projection def on_mouse_wheel(self, event): self.translate -= event.delta[1] self.translate = max(-1, self.translate) self.view = translate((0, 0, -self.translate)) self.program['u_view'] = self.view self.update() def on_draw(self, event): gloo.clear() self.program.draw('points') if __name__ == '__main__': mvc = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/mandelbrot_double.py0000644000175000017510000002132114507104216022032 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: John David Reaver # Date: 09/12/2014 # ----------------------------------------------------------------------------- """ Example demonstrating the use of emulated double-precision floating point numbers. Based off of mandelbrot.py. The shader program emulates double-precision variables using a vec2 instead of single-precision floats. Any function starting with ds_* 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. """ from __future__ import division import numpy as np from vispy import app, gloo # Shader source code # ----------------------------------------------------------------------------- vertex = """ attribute vec2 position; void main() { gl_Position = vec4(position, 0, 1.0); } """ fragment = """ #pragma optionNV(fastmath off) #pragma optionNV(fastprecision off) uniform vec2 inv_resolution_x; // Inverse resolutions uniform vec2 inv_resolution_y; uniform vec2 center_x; uniform vec2 center_y; uniform vec2 scale; uniform int iter; // Jet color scheme vec4 color_scheme(float x) { vec3 a, b; float c; if (x < 0.34) { a = vec3(0, 0, 0.5); b = vec3(0, 0.8, 0.95); c = (x - 0.0) / (0.34 - 0.0); } else if (x < 0.64) { a = vec3(0, 0.8, 0.95); b = vec3(0.85, 1, 0.04); c = (x - 0.34) / (0.64 - 0.34); } else if (x < 0.89) { a = vec3(0.85, 1, 0.04); b = vec3(0.96, 0.7, 0); c = (x - 0.64) / (0.89 - 0.64); } else { a = vec3(0.96, 0.7, 0); b = vec3(0.5, 0, 0); c = (x - 0.89) / (1.0 - 0.89); } return vec4(mix(a, b, c), 1.0); } vec2 ds_set(float a) { // Create an emulated double by storing first part of float in first half // of vec2 vec2 z; z.x = a; z.y = 0.0; return z; } vec2 ds_add (vec2 dsa, vec2 dsb) { // Add two emulated doubles. Complexity comes from carry-over. vec2 dsc; float t1, t2, e; t1 = dsa.x + dsb.x; e = t1 - dsa.x; t2 = ((dsb.x - e) + (dsa.x - (t1 - e))) + dsa.y + dsb.y; dsc.x = t1 + t2; dsc.y = t2 - (dsc.x - t1); return dsc; } vec2 ds_mul (vec2 dsa, vec2 dsb) { vec2 dsc; float c11, c21, c2, e, t1, t2; float a1, a2, b1, b2, cona, conb, split = 8193.; cona = dsa.x * split; conb = dsb.x * split; a1 = cona - (cona - dsa.x); b1 = conb - (conb - dsb.x); a2 = dsa.x - a1; b2 = dsb.x - b1; c11 = dsa.x * dsb.x; c21 = a2 * b2 + (a2 * b1 + (a1 * b2 + (a1 * b1 - c11))); c2 = dsa.x * dsb.y + dsa.y * dsb.x; t1 = c11 + c2; e = t1 - c11; t2 = dsa.y * dsb.y + ((c2 - e) + (c11 - (t1 - e))) + c21; dsc.x = t1 + t2; dsc.y = t2 - (dsc.x - t1); return dsc; } // Compare: res = -1 if a < b // = 0 if a == b // = 1 if a > b float ds_compare(vec2 dsa, vec2 dsb) { if (dsa.x < dsb.x) return -1.; else if (dsa.x == dsb.x) { if (dsa.y < dsb.y) return -1.; else if (dsa.y == dsb.y) return 0.; else return 1.; } else return 1.; } void main() { vec2 z_x, z_y, c_x, c_y, x, y, frag_x, frag_y; vec2 four = ds_set(4.0); vec2 point5 = ds_set(0.5); // Recover coordinates from pixel coordinates frag_x = ds_set(gl_FragCoord.x); frag_y = ds_set(gl_FragCoord.y); c_x = ds_add(ds_mul(frag_x, inv_resolution_x), -point5); c_x = ds_add(ds_mul(c_x, scale), center_x); c_y = ds_add(ds_mul(frag_y, inv_resolution_y), -point5); c_y = ds_add(ds_mul(c_y, scale), center_y); // Main Mandelbrot computation int i; z_x = c_x; z_y = c_y; for(i = 0; i < iter; i++) { x = ds_add(ds_add(ds_mul(z_x, z_x), -ds_mul(z_y, z_y)), c_x); y = ds_add(ds_add(ds_mul(z_y, z_x), ds_mul(z_x, z_y)), c_y); if(ds_compare(ds_add(ds_mul(x, x), ds_mul(y, y)), four) > 0.) break; z_x = x; z_y = y; } // Convert iterations to color float color = 1.0 - float(i) / float(iter); gl_FragColor = color_scheme(color); } """ # vispy Canvas # ----------------------------------------------------------------------------- class Canvas(app.Canvas): def __init__(self, *args, **kwargs): app.Canvas.__init__(self, *args, **kwargs) self.program = gloo.Program(vertex, fragment) # Draw a rectangle that takes up the whole screen. All of the work is # done in the shader. self.program["position"] = [(-1, -1), (-1, 1), (1, 1), (-1, -1), (1, 1), (1, -1)] self.scale = 3 self.program["scale"] = set_emulated_double(self.scale) self.center = [-0.5, 0] self.bounds = [-2, 2] self.translate_center(0, 0) self.iterations = self.program["iter"] = 300 self.apply_zoom() self.min_scale = 1e-12 self.max_scale = 4 gloo.set_clear_color(color='black') self.show() def on_draw(self, event): self.program.draw() def on_resize(self, event): self.apply_zoom() def apply_zoom(self): width, height = self.physical_size gloo.set_viewport(0, 0, width, height) self.program['inv_resolution_x'] = set_emulated_double(1 / width) self.program['inv_resolution_y'] = set_emulated_double(1 / height) def on_mouse_move(self, event): """Pan the view based on the change in mouse position.""" if event.is_dragging and event.buttons[0] == 1: x0, y0 = event.last_event.pos[0], event.last_event.pos[1] x1, y1 = event.pos[0], event.pos[1] X0, Y0 = self.pixel_to_coords(float(x0), float(y0)) X1, Y1 = self.pixel_to_coords(float(x1), float(y1)) self.translate_center(X1 - X0, Y1 - Y0) self.update() def translate_center(self, dx, dy): """Translates the center point, and keeps it in bounds.""" center = self.center center[0] -= dx center[1] -= dy center[0] = min(max(center[0], self.bounds[0]), self.bounds[1]) center[1] = min(max(center[1], self.bounds[0]), self.bounds[1]) self.center = center center_x = set_emulated_double(center[0]) center_y = set_emulated_double(center[1]) self.program["center_x"] = center_x self.program["center_y"] = center_y def pixel_to_coords(self, x, y): """Convert pixel coordinates to Mandelbrot set coordinates.""" rx, ry = self.size nx = (x / rx - 0.5) * self.scale + self.center[0] ny = ((ry - y) / ry - 0.5) * self.scale + self.center[1] return [nx, ny] def on_mouse_wheel(self, event): """Use the mouse wheel to zoom.""" delta = event.delta[1] if delta > 0: # Zoom in factor = 0.9 elif delta < 0: # Zoom out factor = 1 / 0.9 for _ in range(int(abs(delta))): self.zoom(factor, event.pos) self.update() def on_key_press(self, event): """Use + or - to zoom in and out. The mouse wheel can be used to zoom, but some people don't have mouse wheels :) """ if event.text == '+' or event.text == '=': self.zoom(0.9) elif event.text == '-': self.zoom(1/0.9) self.update() def zoom(self, factor, mouse_coords=None): """Factors less than zero zoom in, and greater than zero zoom out. If mouse_coords is given, the point under the mouse stays stationary while zooming. mouse_coords should come from MouseEvent.pos. """ if mouse_coords is not None: # Record the position of the mouse x, y = float(mouse_coords[0]), float(mouse_coords[1]) x0, y0 = self.pixel_to_coords(x, y) self.scale *= factor self.scale = max(min(self.scale, self.max_scale), self.min_scale) self.program["scale"] = set_emulated_double(self.scale) if mouse_coords is not None: # Translate so mouse point is stationary x1, y1 = self.pixel_to_coords(x, y) self.translate_center(x1 - x0, y1 - y0) def set_emulated_double(number): """Emulate a double using two numbers of type float32.""" double = np.array([number, 0], dtype=np.float32) # Cast number to float32 double[1] = number - double[0] # Remainder stored in second half of array return double if __name__ == '__main__': canvas = Canvas(size=(800, 800), keys='interactive') app.run() vispy-0.14.1/examples/demo/gloo/mandelbrot.py0000644000175000017510000001265714507104216020514 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: John David Reaver # Date: 04/29/2014 # ----------------------------------------------------------------------------- from vispy import app, gloo # Shader source code # ----------------------------------------------------------------------------- vertex = """ attribute vec2 position; void main() { gl_Position = vec4(position, 0, 1.0); } """ fragment = """ uniform vec2 resolution; uniform vec2 center; uniform float scale; vec3 hot(float t) { return vec3(smoothstep(0.00,0.33,t), smoothstep(0.33,0.66,t), smoothstep(0.66,1.00,t)); } void main() { const int n = 300; const float log_2 = 0.6931471805599453; vec2 c; // Recover coordinates from pixel coordinates c.x = (gl_FragCoord.x / resolution.x - 0.5) * scale + center.x; c.y = (gl_FragCoord.y / resolution.y - 0.5) * scale + center.y; float x, y, d; int i; vec2 z = c; for(i = 0; i < n; ++i) { x = (z.x*z.x - z.y*z.y) + c.x; y = (z.y*z.x + z.x*z.y) + c.y; d = x*x + y*y; if (d > 4.0) break; z = vec2(x,y); } if ( i < n ) { float nu = log(log(sqrt(d))/log_2)/log_2; float index = float(i) + 1.0 - nu; float v = pow(index/float(n),0.5); gl_FragColor = vec4(hot(v),1.0); } else { gl_FragColor = vec4(hot(0.0),1.0); } } """ # vispy Canvas # ----------------------------------------------------------------------------- class Canvas(app.Canvas): def __init__(self, *args, **kwargs): app.Canvas.__init__(self, *args, **kwargs) self.program = gloo.Program(vertex, fragment) # Draw a rectangle that takes up the whole screen. All of the work is # done in the shader. self.program["position"] = [(-1, -1), (-1, 1), (1, 1), (-1, -1), (1, 1), (1, -1)] self.scale = self.program["scale"] = 3 self.center = self.program["center"] = [-0.5, 0] self.apply_zoom() self.bounds = [-2, 2] self.min_scale = 0.00005 self.max_scale = 4 gloo.set_clear_color(color='black') self._timer = app.Timer('auto', connect=self.update, start=True) self.show() def on_draw(self, event): self.program.draw() def on_resize(self, event): self.apply_zoom() def apply_zoom(self): width, height = self.physical_size gloo.set_viewport(0, 0, width, height) self.program['resolution'] = [width, height] def on_mouse_move(self, event): """Pan the view based on the change in mouse position.""" if event.is_dragging and event.buttons[0] == 1: x0, y0 = event.last_event.pos[0], event.last_event.pos[1] x1, y1 = event.pos[0], event.pos[1] X0, Y0 = self.pixel_to_coords(float(x0), float(y0)) X1, Y1 = self.pixel_to_coords(float(x1), float(y1)) self.translate_center(X1 - X0, Y1 - Y0) def translate_center(self, dx, dy): """Translates the center point, and keeps it in bounds.""" center = self.center center[0] -= dx center[1] -= dy center[0] = min(max(center[0], self.bounds[0]), self.bounds[1]) center[1] = min(max(center[1], self.bounds[0]), self.bounds[1]) self.program["center"] = self.center = center def pixel_to_coords(self, x, y): """Convert pixel coordinates to Mandelbrot set coordinates.""" rx, ry = self.size nx = (x / rx - 0.5) * self.scale + self.center[0] ny = ((ry - y) / ry - 0.5) * self.scale + self.center[1] return [nx, ny] def on_mouse_wheel(self, event): """Use the mouse wheel to zoom.""" delta = event.delta[1] if delta > 0: # Zoom in factor = 0.9 elif delta < 0: # Zoom out factor = 1 / 0.9 for _ in range(int(abs(delta))): self.zoom(factor, event.pos) def on_key_press(self, event): """Use + or - to zoom in and out. The mouse wheel can be used to zoom, but some people don't have mouse wheels :) """ if event.text == '+' or event.text == '=': self.zoom(0.9) elif event.text == '-': self.zoom(1/0.9) def zoom(self, factor, mouse_coords=None): """Factors less than zero zoom in, and greater than zero zoom out. If mouse_coords is given, the point under the mouse stays stationary while zooming. mouse_coords should come from MouseEvent.pos. """ if mouse_coords is not None: # Record the position of the mouse x, y = float(mouse_coords[0]), float(mouse_coords[1]) x0, y0 = self.pixel_to_coords(x, y) self.scale *= factor self.scale = max(min(self.scale, self.max_scale), self.min_scale) self.program["scale"] = self.scale # Translate so the mouse point is stationary if mouse_coords is not None: x1, y1 = self.pixel_to_coords(x, y) self.translate_center(x1 - x0, y1 - y0) if __name__ == '__main__': canvas = Canvas(size=(800, 800), keys='interactive') app.run() vispy-0.14.1/examples/demo/gloo/jfa/0000755000175000017510000000000014507104250016536 5ustar nileshnileshvispy-0.14.1/examples/demo/gloo/jfa/vertex_vispy.glsl0000755000175000017510000000132614507104216022177 0ustar nileshnilesh// Jump flooding algorithm for EDT according // to Danielsson (1980) and Guodong Rong (2007). // Implementation by Stefan Gustavson 2010. // This code is in the public domain. // This code represents one iteration of the flood filling. // You need to run it multiple times with different step // lengths to perform a full distance transformation. uniform float texw; uniform float texh; uniform float step; attribute vec2 position; attribute vec2 texcoord; varying float stepu; varying float stepv; varying vec2 uv; void main( void ) { // Get the texture coordinates uv = texcoord.xy; stepu = step / texw; // Saves a division in the fragment shader stepv = step / texh; gl_Position = vec4(position.xy, 0., 1.); } vispy-0.14.1/examples/demo/gloo/jfa/vertex.glsl0000755000175000017510000000124014507104216020740 0ustar nileshnilesh// Jump flooding algorithm for EDT according // to Danielsson (1980) and Guodong Rong (2007). // Implementation by Stefan Gustavson 2010. // This code is in the public domain. // This code represents one iteration of the flood filling. // You need to run it multiple times with different step // lengths to perform a full distance transformation. uniform float texw; uniform float texh; uniform float step; varying float stepu; varying float stepv; varying vec2 uv; void main( void ) { // Get the texture coordinates uv = gl_MultiTexCoord0.xy; stepu = step / texw; // Saves a division in the fragment shader stepv = step / texh; gl_Position = ftransform(); } vispy-0.14.1/examples/demo/gloo/jfa/jfa_vispy.py0000644000175000017510000001111514507104216021103 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # vispy: testskip - because this example sets inactive attributes on Travis """ Demo of jump flooding algoritm for EDT using GLSL Author: Stefan Gustavson (stefan.gustavson@gmail.com) 2010-08-24. This code is in the public domain. Adapted to `vispy` by Eric Larson . This version is a vispy-ized translation of jfa_translate.py. """ import numpy as np from os import path as op import sys from vispy import app from vispy.gloo import (Program, FrameBuffer, VertexBuffer, Texture2D, set_viewport) from vispy.io import load_data_file, imread this_dir = op.abspath(op.dirname(__file__)) class Canvas(app.Canvas): def __init__(self): self.use_shaders = True app.Canvas.__init__(self, size=(512, 512), keys='interactive') # Note: read as bytes, then decode; py2.6 compat with open(op.join(this_dir, 'vertex_vispy.glsl'), 'rb') as fid: vert = fid.read().decode('ASCII') with open(op.join(this_dir, 'fragment_seed.glsl'), 'rb') as f: frag_seed = f.read().decode('ASCII') with open(op.join(this_dir, 'fragment_flood.glsl'), 'rb') as f: frag_flood = f.read().decode('ASCII') with open(op.join(this_dir, 'fragment_display.glsl'), 'rb') as f: frag_display = f.read().decode('ASCII') self.programs = [Program(vert, frag_seed), Program(vert, frag_flood), Program(vert, frag_display)] # Initialize variables # using two FBs slightly faster than switching on one self.fbo_to = [FrameBuffer(), FrameBuffer()] self._setup_textures('shape1.tga') vtype = np.dtype([('position', 'f4', 2), ('texcoord', 'f4', 2)]) vertices = np.zeros(4, dtype=vtype) vertices['position'] = [[-1., -1.], [-1., 1.], [1., -1.], [1., 1.]] vertices['texcoord'] = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]] vertices = VertexBuffer(vertices) for program in self.programs: program.bind(vertices) self._timer = app.Timer('auto', self.update, start=True) self.show() def _setup_textures(self, fname): data = imread(load_data_file('jfa/' + fname), format='tga')[::-1].copy() if data.ndim == 3: data = data[:, :, 0] # Travis gets 2, I get three? self.texture_size = data.shape[:2] self.orig_tex = Texture2D(data, format='luminance', wrapping='repeat', interpolation='nearest') self.comp_texs = [] data = np.zeros(self.texture_size + (4,), np.float32) for _ in range(2): tex = Texture2D(data, format='rgba', wrapping='clamp_to_edge', interpolation='nearest') self.comp_texs.append(tex) self.fbo_to[0].color_buffer = self.comp_texs[0] self.fbo_to[1].color_buffer = self.comp_texs[1] for program in self.programs[1:2]: program['texw'], program['texh'] = self.texture_size def on_draw(self, event): if self.use_shaders: last_rend = 0 self.fbo_to[last_rend].activate() set_viewport(0, 0, *self.texture_size) self.programs[0]['texture'] = self.orig_tex self.programs[0].draw('triangle_strip') self.fbo_to[last_rend].deactivate() stepsize = (np.array(self.texture_size) // 2).max() while stepsize > 0: self.programs[1]['step'] = stepsize self.programs[1]['texture'] = self.comp_texs[last_rend] last_rend = 1 if last_rend == 0 else 0 self.fbo_to[last_rend].activate() set_viewport(0, 0, *self.texture_size) self.programs[1].draw('triangle_strip') self.fbo_to[last_rend].deactivate() stepsize //= 2 self.programs[2]['texture'] = self.comp_texs[last_rend] else: self.programs[2]['texture'] = self.orig_tex set_viewport(0, 0, *self.physical_size) self.programs[2].draw('triangle_strip') def on_key_press(self, event): if event.key is not None and event.key.name in '1234': fname = "shape%s.tga" % event.key.name self._setup_textures(fname) elif event.key == 'F1': self.use_shaders = True elif event.key == 'F2': self.use_shaders = False def fun(x): c.title = 'FPS: %0.1f' % x if __name__ == '__main__': c = Canvas() c.measure_fps(callback=fun) if sys.flags.interactive != 1: c.app.run() vispy-0.14.1/examples/demo/gloo/jfa/jfa_translation.py0000644000175000017510000002402414507104216022272 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Demo of jump flooding algoritm for EDT using GLSL Author: Stefan Gustavson (stefan.gustavson@gmail.com) 2010-08-24. This code is in the public domain. Adapted to `vispy` by Eric Larson . This version is a translation of the OSX C code to Python. Two modifications were made for OpenGL ES 2.0 compatibility: 1. GL_CLAMP_TO_BORDER was changed to GL_CLAMP_TO_EDGE, with corresponding shader changes. 2. GL_RG16 was changed to GL_RGBA with corresponding shader changes (including hard-coding "texlevels" at 65536). """ import numpy as np from os import path as op from vispy.ext import glfw from vispy.io import load_data_file from OpenGL import GL as gl from OpenGL import GLU as glu from PIL import Image import time this_dir = op.abspath(op.dirname(__file__)) def createShader(vert_fname, frag_fname): """createShader - create, load, compile and link the shader object""" with open(op.join(this_dir, vert_fname), 'rb') as fid: vert = fid.read().decode('ASCII') with open(op.join(this_dir, frag_fname), 'rb') as fid: frag = fid.read().decode('ASCII') vertexShader = gl.glCreateShader(gl.GL_VERTEX_SHADER) gl.glShaderSource(vertexShader, vert) gl.glCompileShader(vertexShader) fragmentShader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER) gl.glShaderSource(fragmentShader, frag) gl.glCompileShader(fragmentShader) programObj = gl.glCreateProgram() gl.glAttachShader(programObj, vertexShader) gl.glAttachShader(programObj, fragmentShader) gl.glLinkProgram(programObj) checkGLError() return programObj def setUniformVariables(programObj, texture, texw, texh, step): """setUniformVariables - set the uniform shader variables we need""" gl.glUseProgram(programObj) location_texture = gl.glGetUniformLocation(programObj, "texture") if location_texture != -1: gl.glUniform1i(location_texture, texture) location_texw = gl.glGetUniformLocation(programObj, "texw") if location_texw != -1: gl.glUniform1f(location_texw, texw) location_texh = gl.glGetUniformLocation(programObj, "texh") if location_texh != -1: gl.glUniform1f(location_texh, texh) location_step = gl.glGetUniformLocation(programObj, "step") if(location_step != -1): gl.glUniform1f(location_step, step) gl.glUseProgram(0) checkGLError() def loadImage(filename): # adapted for Python img = Image.open(filename) w, h = img.size x = np.array(img)[::-1].tostring() assert len(x) == w * h return x, w, h def loadShapeTexture(filename, texID): """loadShapeTexture - load 8-bit shape texture data from a TGA file and set up the corresponding texture object.""" data, texw, texh = loadImage(load_data_file('jfa/' + filename)) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(gl.GL_TEXTURE_2D, texID) # Load image into texture gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_LUMINANCE, texw, texh, 0, gl.GL_LUMINANCE, gl.GL_UNSIGNED_BYTE, data) # This is the input image. We want unaltered 1-to-1 pixel values, # so specify nearest neighbor sampling to be sure. gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_REPEAT) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_REPEAT) checkGLError() return texw, texh def createBufferTexture(texID, texw, texh): """createBufferTexture - create an 8-bit texture render target""" gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(gl.GL_TEXTURE_2D, texID) black = (0., 0., 0., 0.) # The special shader used to render this texture performs a # per-pixel image processing where point sampling is required, # so specify nearest neighbor sampling. # # Also, the flood fill shader handles its own edge clamping, so # texture mode GL_REPEAT is inconsequential. "Zero outside" would # be useful, but separate edge values are deprecated in OpenGL. # gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE) gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE) gl.glTexParameterfv(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_BORDER_COLOR, black) gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, texw, texh, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, '\x00' * texw*texh*4) gl.glBindTexture(gl.GL_TEXTURE_2D, 0) checkGLError() t0 = 0.0 frames = 0 def showFPS(texw, texh): """showFPS - Calculate and report texture size and frames per second in the window title bar (updated once per second)""" global frames, t0 t = time.time() if (t - t0) > 1.: fps = frames / (t - t0) titlestr = "%sx%s texture, %.1f FPS" % (texw, texh, fps) glfw.glfwSetWindowTitle(window, titlestr) t0 = t frames = 0 frames += 1 def checkGLError(): status = gl.glGetError() if status != gl.GL_NO_ERROR: raise RuntimeError('gl error %s' % (status,)) def renderScene(programObj, width, height): """renderScene - the OpenGL commands to render our scene.""" gl.glMatrixMode(gl.GL_PROJECTION) gl.glLoadIdentity() glu.gluOrtho2D(0, width, 0, height) gl.glViewport(0, 0, width, height) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glLoadIdentity() gl.glUseProgram(programObj) # Draw one texture mapped quad in the (x,y) plane gl.glBegin(gl.GL_QUADS) gl.glTexCoord2f(0., 0.) gl.glVertex2f(0., 0.) gl.glTexCoord2f(1., 0.) gl.glVertex2f(float(width), 0.) gl.glTexCoord2f(1., 1.) gl.glVertex2f(float(width), float(height)) gl.glTexCoord2f(0., 1.) gl.glVertex2f(0., float(height)) gl.glEnd() gl.glUseProgram(0) checkGLError() useShaders = True glfw.glfwInit() window = glfw.glfwCreateWindow(512, 512) glfw.glfwShowWindow(window) glfw.glfwMakeContextCurrent(window) time.sleep(400e-3) # needed on Linux for window to show up # Load one texture with the original image # and create two textures of the same size for the iterative rendering gl.glEnable(gl.GL_TEXTURE_2D) gl.glActiveTexture(gl.GL_TEXTURE0) textureID = gl.glGenTextures(3) texw, texh = loadShapeTexture("shape1.tga", textureID[0]) createBufferTexture(textureID[1], texw, texh) createBufferTexture(textureID[2], texw, texh) fboID = gl.glGenFramebuffers(1) programObj0 = createShader("vertex.glsl", "fragment_seed.glsl") programObj1 = createShader("vertex.glsl", "fragment_flood.glsl") programObj2 = createShader("vertex.glsl", "fragment_display.glsl") glfw.glfwSwapInterval(0) running = True while running: showFPS(texw, texh) if not useShaders: gl.glBindTexture(gl.GL_TEXTURE_2D, textureID[0]) # Pass-through else: setUniformVariables(programObj0, 0, texw, texh, 0) gl.glBindTexture(gl.GL_TEXTURE_2D, textureID[0]) gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, fboID) lastRendered = 1 gl.glFramebufferTexture2D(gl.GL_DRAW_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, textureID[lastRendered], 0) renderScene(programObj0, texw, texh) stepsize = texw//2 if texw > texh else texh//2 while stepsize > 0: setUniformVariables(programObj1, 0, texw, texh, stepsize) gl.glBindTexture(gl.GL_TEXTURE_2D, textureID[lastRendered]) lastRendered = 1 if lastRendered == 2 else 2 gl.glFramebufferTexture2D(gl.GL_DRAW_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, textureID[lastRendered], 0) renderScene(programObj1, texw, texh) stepsize = stepsize // 2 gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, 0) gl.glBindTexture(gl.GL_TEXTURE_2D, textureID[lastRendered]) width, height = glfw.glfwGetWindowSize(window) height = max(height, 1) width = max(width, 1) setUniformVariables(programObj2, 0, texw, texh, 0) renderScene(programObj2, width, height) glfw.glfwSwapBuffers(window) glfw.glfwPollEvents() if glfw.glfwGetKey(window, glfw.GLFW_KEY_1) == glfw.GLFW_PRESS: texw, texh = loadShapeTexture("shape1.tga", textureID[0]) createBufferTexture(textureID[1], texw, texh) createBufferTexture(textureID[2], texw, texh) if glfw.glfwGetKey(window, glfw.GLFW_KEY_2): texw, texh = loadShapeTexture("shape2.tga", textureID[0]) createBufferTexture(textureID[1], texw, texh) createBufferTexture(textureID[2], texw, texh) if glfw.glfwGetKey(window, glfw.GLFW_KEY_3): texw, texh = loadShapeTexture("shape3.tga", textureID[0]) createBufferTexture(textureID[1], texw, texh) createBufferTexture(textureID[2], texw, texh) if glfw.glfwGetKey(window, glfw.GLFW_KEY_4): texw, texh = loadShapeTexture("shape4.tga", textureID[0]) createBufferTexture(textureID[1], texw, texh) createBufferTexture(textureID[2], texw, texh) if glfw.glfwGetKey(window, glfw.GLFW_KEY_F1): useShaders = True if glfw.glfwGetKey(window, glfw.GLFW_KEY_F2): useShaders = False # Check if the ESC key is pressed or the window has been closed running = not glfw.glfwGetKey(window, glfw.GLFW_KEY_ESCAPE) glfw.glfwTerminate() vispy-0.14.1/examples/demo/gloo/jfa/fragment_seed.glsl0000755000175000017510000000121714507104216022232 0ustar nileshnilesh// Jump flooding algorithm for EDT according // to Danielsson (1980) and Guodong Rong (2007). // Implementation by Stefan Gustavson 2010. // This code is in the public domain. // This shader initializes the distance field // in preparation for the flood filling. uniform sampler2D texture; varying float stepu; varying float stepv; varying vec2 uv; void main( void ) { float pixel = texture2D(texture, 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 ? myinfinity : myzero; } vispy-0.14.1/examples/demo/gloo/jfa/fragment_flood.glsl0000755000175000017510000001307214507104216022417 0ustar nileshnilesh// Jump flooding algorithm for EDT according // to Danielsson (1980) and Guodong Rong (2007). // Implementation by Stefan Gustavson 2010. // This code is in the public domain. // This code represents one iteration of the flood filling. // You need to run it multiple times with different step // lengths to perform a full distance transformation. uniform sampler2D texture; varying float stepu; varying float stepv; varying vec2 uv; // Helper functions to remap unsigned normalized floats [0.0,1.0] // coming from an integer texture to the range we need [-1, 1]. // The transformations are very specifically designed to map // integer texel values exactly to pixel centers, and vice versa. // (See fragment_seed.glsl for details.) 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 distance (.z) vec3 bestseed; // Closest seed so far bestseed.xy = remap(texture2D(texture, uv).rgba); bestseed.z = length(bestseed.xy); // This code depends on the texture having a CLAMP_TO_BORDER // attribute and a border color with R = 0. // The commented-out lines handle clamping to the edge explicitly // to avoid propagating incorrect vectors when looking outside // of [0,1] in u and/or v. // These explicit conditionals cause a slowdown of about 25%. // Sometimes a periodic transform with edge repeats might be // what you want. In that case, the texture wrap mode can be // set to GL_REPEAT, and the shader code can be left unchanged. stepvec = vec2(-stepu, -stepv); newvec = uv + stepvec; if ( all( bvec4( lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0)) ) ) ) { newseed.xy = remap(texture2D(texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate distance" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(-stepu, 0.0); newvec = uv + stepvec; if ( all( bvec4( lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0)) ) ) ) { newseed.xy = remap(texture2D(texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate distance" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(-stepu, stepv); newvec = uv + stepvec; if ( all( bvec4( lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0)) ) ) ) { newseed.xy = remap(texture2D(texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate distance" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(0.0, -stepv); newvec = uv + stepvec; if ( all( bvec4( lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0)) ) ) ) { newseed.xy = remap(texture2D(texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate distance" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(0.0, stepv); newvec = uv + stepvec; if ( all( bvec4( lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0)) ) ) ) { newseed.xy = remap(texture2D(texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate distance" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(stepu, -stepv); newvec = uv + stepvec; if ( all( bvec4( lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0)) ) ) ) { newseed.xy = remap(texture2D(texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate distance" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(stepu, 0.0); newvec = uv + stepvec; if ( all( bvec4( lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0)) ) ) ) { newseed.xy = remap(texture2D(texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate distance" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } stepvec = vec2(stepu, stepv); newvec = uv + stepvec; if ( all( bvec4( lessThan(newvec, vec2(1.0)), greaterThan(newvec, vec2(0.0)) ) ) ) { newseed.xy = remap(texture2D(texture, newvec).rgba); if(newseed.x > -0.99999) { // if the new seed is not "indeterminate distance" newseed.xy = newseed.xy + stepvec; newseed.z = length(newseed.xy); if(newseed.z < bestseed.z) { bestseed = newseed; } } } gl_FragColor = remap_inv(bestseed.xy); } vispy-0.14.1/examples/demo/gloo/jfa/fragment_display.glsl0000755000175000017510000000127514507104216022763 0ustar nileshnilesh// Jump flooding algorithm for EDT according // to Danielsson (1980) and Guodong Rong (2007). // Implementation by Stefan Gustavson 2010. // This code is in the public domain. // This shader displays the final distance field // visualized as an RGB image. uniform sampler2D texture; varying vec2 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 ) { vec2 distvec = remap(texture2D(texture, uv).rgba); vec2 rainbow = 0.5+0.5*(normalize(distvec)); gl_FragColor = vec4(rainbow, 1.0-length(distvec)*4.0, 1.0); } vispy-0.14.1/examples/demo/gloo/imshow_cuts.py0000644000175000017510000001227414507104216020724 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- """ Show an image using gloo, with on-mouseover cross-section visualizations. """ import numpy as np from vispy import app from vispy.gloo import set_viewport, clear, set_state, Program # Image def func(x, y): return (1-x/2+x**5+y**3)*np.exp(-x**2-y**2) x = np.linspace(-3.0, 3.0, 512).astype(np.float32) y = np.linspace(-3.0, 3.0, 512).astype(np.float32) X, Y = np.meshgrid(x, y) idxs = func(X, Y) # Image normalization vmin, vmax = idxs.min(), idxs.max() idxs = (idxs - vmin) / (vmax - vmin) # Colormaps colormaps = np.ones((16, 512, 4)).astype(np.float32) values = np.linspace(0, 1, 512)[1:-1] # Hot colormap colormaps[0, 0] = 0, 0, 1, 1 # Low values (< vmin) colormaps[0, -1] = 0, 1, 0, 1 # High values (> vmax) colormaps[0, 1:-1, 0] = np.interp(values, [0.00, 0.33, 0.66, 1.00], [0.00, 1.00, 1.00, 1.00]) colormaps[0, 1:-1, 1] = np.interp(values, [0.00, 0.33, 0.66, 1.00], [0.00, 0.00, 1.00, 1.00]) colormaps[0, 1:-1, 2] = np.interp(values, [0.00, 0.33, 0.66, 1.00], [0.00, 0.00, 0.00, 1.00]) # Grey colormap colormaps[1, 0] = 0, 0, 1, 1 # Low values (< vmin) colormaps[1, -1] = 0, 1, 0, 1 # High values (> vmax) colormaps[1, 1:-1, 0] = np.interp(values, [0.00, 1.00], [0.00, 1.00]) colormaps[1, 1:-1, 1] = np.interp(values, [0.00, 1.00], [0.00, 1.00]) colormaps[1, 1:-1, 2] = np.interp(values, [0.00, 1.00], [0.00, 1.00]) # Jet colormap # ... lines_vertex = """ attribute vec2 position; attribute vec4 color; varying vec4 v_color; void main() { gl_Position = vec4(position, 0.0, 1.0 ); v_color = color; } """ lines_fragment = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ image_vertex = """ attribute vec2 position; attribute vec2 texcoord; varying vec2 v_texcoord; void main() { gl_Position = vec4(position, 0.0, 1.0 ); v_texcoord = texcoord; } """ image_fragment = """ uniform float vmin; uniform float vmax; uniform float cmap; uniform float n_colormaps; uniform sampler2D image; uniform sampler2D colormaps; varying vec2 v_texcoord; void main() { float value = texture2D(image, v_texcoord).r; float index = (cmap+0.5) / n_colormaps; if( value < vmin ) { gl_FragColor = texture2D(colormaps, vec2(0.0,index)); } else if( value > vmax ) { gl_FragColor = texture2D(colormaps, vec2(1.0,index)); } else { value = (value-vmin)/(vmax-vmin); value = 1.0/512.0 + 510.0/512.0*value; gl_FragColor = texture2D(colormaps, vec2(value,index)); } } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), keys='interactive') self.image = Program(image_vertex, image_fragment, 4) self.image['position'] = (-1, -1), (-1, +1), (+1, -1), (+1, +1) self.image['texcoord'] = (0, 0), (0, +1), (+1, 0), (+1, +1) self.image['vmin'] = +0.0 self.image['vmax'] = +1.0 self.image['cmap'] = 0 # Colormap index to use self.image['colormaps'] = colormaps self.image['n_colormaps'] = colormaps.shape[0] self.image['image'] = idxs.astype('float32') self.image['image'].interpolation = 'linear' set_viewport(0, 0, *self.physical_size) self.lines = Program(lines_vertex, lines_fragment) self.lines["position"] = np.zeros((4+4+514+514, 2), np.float32) color = np.zeros((4+4+514+514, 4), np.float32) color[1:1+2, 3] = 0.25 color[5:5+2, 3] = 0.25 color[9:9+512, 3] = 0.5 color[523:523+512, 3] = 0.5 self.lines["color"] = color set_state(clear_color='white', blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self.show() def on_resize(self, event): set_viewport(0, 0, *event.physical_size) def on_draw(self, event): clear(color=True, depth=True) self.image.draw('triangle_strip') self.lines.draw('line_strip') def on_mouse_move(self, event): x, y = event.pos w, h = self.size # Make sure the mouse isn't outside of the viewport. x = max(0, min(x, w - 1)) y = max(0, min(y, h - 1)) yf = 1 - y/(h/2.) xf = x/(w/2.) - 1 x_norm = int((x*512)//w) y_norm = int((y*512)//h) P = np.zeros((4+4+514+514, 2), np.float32) x_baseline = P[:4] y_baseline = P[4:8] x_profile = P[8:522] y_profile = P[522:] x_baseline[...] = (-1, yf), (-1, yf), (1, yf), (1, yf) y_baseline[...] = (xf, -1), (xf, -1), (xf, 1), (xf, 1) x_profile[1:-1, 0] = np.linspace(-1, 1, 512) x_profile[1:-1, 1] = yf + 0.15 * idxs[y_norm, :] x_profile[0] = x_profile[1] x_profile[-1] = x_profile[-2] y_profile[1:-1, 0] = xf + 0.15 * idxs[:, x_norm] y_profile[1:-1, 1] = np.linspace(-1, 1, 512) y_profile[0] = y_profile[1] y_profile[-1] = y_profile[-2] self.lines["position"] = P self.update() if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/imshow.py0000644000175000017510000000737414507104216017673 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 10 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Show an image using gloo. """ import numpy as np from vispy import app from vispy.gloo import clear, set_clear_color, set_viewport, Program # Image def func(x, y): return (1-x/2+x**5+y**3)*np.exp(-x**2-y**2) x = np.linspace(-3.0, 3.0, 512).astype(np.float32) y = np.linspace(-3.0, 3.0, 512).astype(np.float32) X, Y = np.meshgrid(x, y) idxs = func(X, Y) # Image normalization vmin, vmax = idxs.min(), idxs.max() idxs = (idxs - vmin) / (vmax - vmin) # Colormaps colormaps = np.ones((16, 512, 4)).astype(np.float32) values = np.linspace(0, 1, 512)[1:-1] # Hot colormap colormaps[0, 0] = 0, 0, 1, 1 # Low values (< vmin) colormaps[0, -1] = 0, 1, 0, 1 # High values (> vmax) colormaps[0, 1:-1, 0] = np.interp(values, [0.00, 0.33, 0.66, 1.00], [0.00, 1.00, 1.00, 1.00]) colormaps[0, 1:-1, 1] = np.interp(values, [0.00, 0.33, 0.66, 1.00], [0.00, 0.00, 1.00, 1.00]) colormaps[0, 1:-1, 2] = np.interp(values, [0.00, 0.33, 0.66, 1.00], [0.00, 0.00, 0.00, 1.00]) # Grey colormap colormaps[1, 0] = 0, 0, 1, 1 # Low values (< vmin) colormaps[1, -1] = 0, 1, 0, 1 # High values (> vmax) colormaps[1, 1:-1, 0] = np.interp(values, [0.00, 1.00], [0.00, 1.00]) colormaps[1, 1:-1, 1] = np.interp(values, [0.00, 1.00], [0.00, 1.00]) colormaps[1, 1:-1, 2] = np.interp(values, [0.00, 1.00], [0.00, 1.00]) # Jet colormap # ... img_vertex = """ attribute vec2 position; attribute vec2 texcoord; varying vec2 v_texcoord; void main() { gl_Position = vec4(position, 0.0, 1.0 ); v_texcoord = texcoord; } """ img_fragment = """ uniform float vmin; uniform float vmax; uniform float cmap; uniform sampler2D image; uniform sampler2D colormaps; uniform vec2 colormaps_shape; varying vec2 v_texcoord; void main() { float value = texture2D(image, v_texcoord).r; float index = (cmap+0.5) / colormaps_shape.y; if( value < vmin ) { gl_FragColor = texture2D(colormaps, vec2(0.0,index)); } else if( value > vmax ) { gl_FragColor = texture2D(colormaps, vec2(1.0,index)); } else { value = (value-vmin)/(vmax-vmin); value = 1.0/512.0 + 510.0/512.0*value; gl_FragColor = texture2D(colormaps, vec2(value,index)); } } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), keys='interactive') self.image = Program(img_vertex, img_fragment, 4) self.image['position'] = (-1, -1), (-1, +1), (+1, -1), (+1, +1) self.image['texcoord'] = (0, 0), (0, +1), (+1, 0), (+1, +1) self.image['vmin'] = +0.1 self.image['vmax'] = +0.9 self.image['cmap'] = 0 # Colormap index to use self.image['colormaps'] = colormaps self.image['colormaps'].interpolation = 'linear' self.image['colormaps_shape'] = colormaps.shape[1], colormaps.shape[0] self.image['image'] = idxs.astype('float32') self.image['image'].interpolation = 'linear' set_clear_color('black') self.show() def on_resize(self, event): width, height = event.physical_size set_viewport(0, 0, *event.physical_size) def on_draw(self, event): clear(color=True, depth=True) self.image.draw('triangle_strip') if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/high_frequency.py0000755000175000017510000000650014507104216021356 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 20 # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier, Guillaume Bâty. All Rights Reserved. # Distributed under the (new) BSD License. # ----------------------------------------------------------------------------- # High frequency (below pixel resolution) function plot # # -> http://blog.hvidtfeldts.net/index.php/2011/07/plotting-high-frequency-fun # ctions-using-a-gpu/ # -> https://www.shadertoy.com/view/4sB3zz # ----------------------------------------------------------------------------- from vispy import gloo, app, keys VERT_SHADER = """ attribute vec2 a_position; void main (void) { gl_Position = vec4(a_position, 0.0, 1.0); } """ FRAG_SHADER = """ uniform vec2 u_resolution; uniform float u_global_time; // --- Your function here --- float function( float x ) { float d = 3.0 - 2.0*(1.0+cos(u_global_time/5.0))/2.0; return sin(pow(x,d))*sin(x); } // --- Your function here --- float sample(vec2 uv) { const int samples = 128; const float fsamples = float(samples); vec2 maxdist = vec2(0.5,1.0)/40.0; vec2 halfmaxdist = vec2(0.5) * maxdist; float stepsize = maxdist.x / fsamples; float initial_offset_x = -0.5 * fsamples * stepsize; uv.x += initial_offset_x; float hit = 0.0; for( int i=0; i (v_linewidth/2.0+v_antialias)) { discard; } else if( d < 0.0 ) { gl_FragColor = v_fg_color; } else { float alpha = d/v_antialias; alpha = exp(-alpha*alpha); if (r > 0) gl_FragColor = vec4(v_fg_color.rgb, alpha*v_fg_color.a); else gl_FragColor = mix(v_bg_color, v_fg_color, alpha); } } float marker(vec2 P, float size) { float r = length((P.xy - vec2(0.5,0.5))*size); r -= v_size/2.; return r; } """ vs = """ attribute vec3 a_position; attribute vec4 a_fg_color; attribute vec4 a_bg_color; attribute float a_size; attribute float a_linewidth; void main(){ gl_Position = vec4(a_position, 1.); } """ fs = """ void main(){ gl_FragColor = vec4(0., 0., 0., 1.); } """ class Canvas(app.Canvas): def __init__(self, **kwargs): # Initialize the canvas for real app.Canvas.__init__(self, keys='interactive', size=(512, 512), **kwargs) ps = self.pixel_scale self.position = 50, 50 n = 100 ne = 100 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), ('a_linewidth', np.float32), ]) edges = np.random.randint(size=(ne, 2), low=0, high=n).astype(np.uint32) data['a_position'] = np.hstack((.25 * np.random.randn(n, 2), np.zeros((n, 1)))) data['a_fg_color'] = 0, 0, 0, 1 color = np.random.uniform(0.5, 1., (n, 3)) data['a_bg_color'] = np.hstack((color, np.ones((n, 1)))) data['a_size'] = np.random.randint(size=n, low=8*ps, high=20*ps) data['a_linewidth'] = 1.*ps u_antialias = 1 self.vbo = gloo.VertexBuffer(data) self.index = gloo.IndexBuffer(edges) self.view = np.eye(4, dtype=np.float32) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.program = gloo.Program(vert, frag) self.program.bind(self.vbo) self.program['u_size'] = 1 self.program['u_antialias'] = u_antialias self.program['u_model'] = self.model self.program['u_view'] = self.view self.program['u_projection'] = self.projection set_viewport(0, 0, *self.physical_size) self.program_e = gloo.Program(vs, fs) self.program_e.bind(self.vbo) set_state(clear_color='white', depth_test=False, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self.show() def on_resize(self, event): set_viewport(0, 0, *event.physical_size) def on_draw(self, event): clear(color=True, depth=True) self.program_e.draw('lines', self.index) self.program.draw('points') if __name__ == '__main__': c = Canvas(title="Graph") app.run() vispy-0.14.1/examples/demo/gloo/glsl_sandbox_cube.py0000644000175000017510000001161014507104216022026 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- """ A GLSL sandbox application based on the spinning cube. Requires PySide or PyQt5. """ import numpy as np from vispy import app, gloo from vispy.io import read_mesh, load_data_file, load_crate from vispy.util.transforms import perspective, translate, rotate try: from PyQt5.QtGui import QFont from PyQt5.QtWidgets import (QWidget, QPlainTextEdit, QLabel, QPushButton, QHBoxLayout, QVBoxLayout) except ImportError: from PyQt4.QtGui import (QWidget, QPlainTextEdit, QFont, QLabel, QPushButton, QHBoxLayout, QVBoxLayout) VERT_CODE = """ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; attribute vec3 a_position; attribute vec2 a_texcoord; varying vec2 v_texcoord; void main() { v_texcoord = a_texcoord; gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0); //gl_Position = vec4(a_position,1.0); } """ FRAG_CODE = """ uniform sampler2D u_texture; varying vec2 v_texcoord; void main() { float ty = v_texcoord.y; float tx = sin(ty*50.0)*0.01 + v_texcoord.x; gl_FragColor = texture2D(u_texture, vec2(tx, ty)); } """ # Read cube data positions, faces, normals, texcoords = \ read_mesh(load_data_file('orig/cube.obj')) colors = np.random.uniform(0, 1, positions.shape).astype('float32') faces_buffer = gloo.IndexBuffer(faces.astype(np.uint16)) class Canvas(app.Canvas): def __init__(self, **kwargs): app.Canvas.__init__(self, size=(400, 400), **kwargs) self.program = gloo.Program(VERT_CODE, FRAG_CODE) # Set attributes self.program['a_position'] = gloo.VertexBuffer(positions) self.program['a_texcoord'] = gloo.VertexBuffer(texcoords) self.program['u_texture'] = gloo.Texture2D(load_crate()) # Handle transformations self.init_transforms() self.apply_zoom() gloo.set_clear_color((1, 1, 1, 1)) gloo.set_state(depth_test=True) self._timer = app.Timer('auto', connect=self.update_transforms) self._timer.start() self.show() def on_resize(self, event): self.apply_zoom() def on_draw(self, event): gloo.clear() self.program.draw('triangles', faces_buffer) def init_transforms(self): self.theta = 0 self.phi = 0 self.view = translate((0, 0, -5)) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.program['u_model'] = self.model self.program['u_view'] = self.view def update_transforms(self, event): self.theta += .5 self.phi += .5 self.model = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.program['u_model'] = self.model self.update() def apply_zoom(self): gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self.projection = perspective(45.0, self.size[0] / float(self.size[1]), 2.0, 10.0) self.program['u_projection'] = self.projection class TextField(QPlainTextEdit): def __init__(self, parent): QPlainTextEdit.__init__(self, parent) # Set font to monospaced (TypeWriter) font = QFont('') font.setStyleHint(font.TypeWriter, font.PreferDefault) font.setPointSize(8) self.setFont(font) class MainWindow(QWidget): def __init__(self): QWidget.__init__(self, None) self.setMinimumSize(600, 400) # Create two labels and a button self.vertLabel = QLabel("Vertex code", self) self.fragLabel = QLabel("Fragment code", self) self.theButton = QPushButton("Compile!", self) self.theButton.clicked.connect(self.on_compile) # Create two editors self.vertEdit = TextField(self) self.vertEdit.setPlainText(VERT_CODE) self.fragEdit = TextField(self) self.fragEdit.setPlainText(FRAG_CODE) # Create a canvas self.canvas = Canvas(parent=self) # Layout hlayout = QHBoxLayout(self) self.setLayout(hlayout) vlayout = QVBoxLayout() # hlayout.addLayout(vlayout, 1) hlayout.addWidget(self.canvas.native, 1) # vlayout.addWidget(self.vertLabel, 0) vlayout.addWidget(self.vertEdit, 1) vlayout.addWidget(self.fragLabel, 0) vlayout.addWidget(self.fragEdit, 1) vlayout.addWidget(self.theButton, 0) self.show() def on_compile(self): vert_code = str(self.vertEdit.toPlainText()) frag_code = str(self.fragEdit.toPlainText()) self.canvas.program.set_shaders(vert_code, frag_code) # Note how we do not need to reset our variables, they are # re-set automatically (by gloo) if __name__ == '__main__': app.create() m = MainWindow() app.run() vispy-0.14.1/examples/demo/gloo/game_of_life.py0000644000175000017510000001317714507104216020757 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 200 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Nicolas P .Rougier # Date: 06/03/2014 # Abstract: GPU computing using the framebuffer # Keywords: framebuffer, GPU computing, cellular automata # ----------------------------------------------------------------------------- """ Conway game of life. """ import numpy as np from vispy.gloo import (Program, FrameBuffer, RenderBuffer, clear, set_viewport, set_state) from vispy import app render_vertex = """ attribute vec2 position; attribute vec2 texcoord; varying vec2 v_texcoord; void main() { gl_Position = vec4(position, 0.0, 1.0); v_texcoord = texcoord; } """ render_fragment = """ uniform int pingpong; uniform sampler2D texture; varying vec2 v_texcoord; void main() { float v; v = texture2D(texture, v_texcoord)[pingpong]; gl_FragColor = vec4(1.0-v, 1.0-v, 1.0-v, 1.0); } """ compute_vertex = """ attribute vec2 position; attribute vec2 texcoord; varying vec2 v_texcoord; void main() { gl_Position = vec4(position, 0.0, 1.0); v_texcoord = texcoord; } """ compute_fragment = """ uniform int pingpong; uniform sampler2D texture; uniform float dx; // horizontal distance between texels uniform float dy; // vertical distance between texels varying vec2 v_texcoord; void main(void) { vec2 p = v_texcoord; float old_state, new_state, count; old_state = texture2D(texture, p)[pingpong]; count = texture2D(texture, p + vec2(-dx,-dy))[pingpong] + texture2D(texture, p + vec2( dx,-dy))[pingpong] + texture2D(texture, p + vec2(-dx, dy))[pingpong] + texture2D(texture, p + vec2( dx, dy))[pingpong] + texture2D(texture, p + vec2(-dx, 0.0))[pingpong] + texture2D(texture, p + vec2( dx, 0.0))[pingpong] + texture2D(texture, p + vec2(0.0,-dy))[pingpong] + texture2D(texture, p + vec2(0.0, dy))[pingpong]; new_state = old_state; if( old_state > 0.5 ) { // Any live cell with fewer than two live neighbours dies // as if caused by under-population. if( count < 1.5 ) new_state = 0.0; // Any live cell with two or three live neighbours // lives on to the next generation. // Any live cell with more than three live neighbours dies, // as if by overcrowding. else if( count > 3.5 ) new_state = 0.0; } else { // Any dead cell with exactly three live neighbours becomes // a live cell, as if by reproduction. if( (count > 2.5) && (count < 3.5) ) new_state = 1.0; } if( pingpong == 0) { gl_FragColor[1] = new_state; gl_FragColor[0] = old_state; } else { gl_FragColor[1] = old_state; gl_FragColor[0] = new_state; } } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title="Conway game of life", size=(512, 512), keys='interactive') # Build programs # -------------- self.comp_size = self.size size = self.comp_size + (4,) Z = np.zeros(size, dtype=np.float32) Z[...] = np.random.randint(0, 2, size) Z[:256, :256, :] = 0 gun = """ ........................O........... ......................O.O........... ............OO......OO............OO ...........O...O....OO............OO OO........O.....O...OO.............. OO........O...O.OO....O.O........... ..........O.....O.......O........... ...........O...O.................... ............OO......................""" x, y = 0, 0 for i in range(len(gun)): if gun[i] == '\n': y += 1 x = 0 elif gun[i] == 'O': Z[y, x] = 1 x += 1 self.pingpong = 1 self.compute = Program(compute_vertex, compute_fragment, 4) self.compute["texture"] = Z self.compute["position"] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] self.compute["texcoord"] = [(0, 0), (0, 1), (1, 0), (1, 1)] self.compute['dx'] = 1.0 / size[1] self.compute['dy'] = 1.0 / size[0] self.compute['pingpong'] = self.pingpong self.render = Program(render_vertex, render_fragment, 4) self.render["position"] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] self.render["texcoord"] = [(0, 0), (0, 1), (1, 0), (1, 1)] self.render["texture"] = self.compute["texture"] self.render['pingpong'] = self.pingpong self.fbo = FrameBuffer(self.compute["texture"], RenderBuffer(self.comp_size)) set_state(depth_test=False, clear_color='black') self._timer = app.Timer('auto', connect=self.update, start=True) self.show() def on_draw(self, event): with self.fbo: set_viewport(0, 0, *self.comp_size) self.compute["texture"].interpolation = 'nearest' self.compute.draw('triangle_strip') clear() set_viewport(0, 0, *self.physical_size) self.render["texture"].interpolation = 'linear' self.render.draw('triangle_strip') self.pingpong = 1 - self.pingpong self.compute["pingpong"] = self.pingpong self.render["pingpong"] = self.pingpong if __name__ == '__main__': canvas = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/galaxy.py0000644000175000017510000001346214507104216017645 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 30 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Just a very fake galaxy. Astronomers and cosmologists will kill me ! """ import numpy as np from vispy import gloo from vispy import app from vispy.util.transforms import perspective, translate, rotate # Manual galaxy creation # (did you really expect a simulation in less than 250 python lines ?) def make_arm(n, angle): R = np.linspace(10, 450 + 50 * np.random.uniform(.5, 1.), n) R += 40 * np.random.normal(0, 2., n) * np.linspace(1, .1, n) T = angle + np.linspace(0, 2.5 * np.pi, n) + \ np.pi / 6 * np.random.normal(0, .5, n) S = 8 + 2 * np.abs(np.random.normal(0, 1, n)) S *= np.linspace(1, .85, n) P = np.zeros((n, 3), dtype=np.float32) X, Y, Z = P[:, 0], P[:, 1], P[:, 2] X[...] = R * np.cos(T) Y[...] = R * np.sin(T) * 1.1 D = np.sqrt(X * X + Y * Y) Z[...] = 8 * np.random.normal(0, 2 - D / 512., n) X += (D * np.random.uniform(0, 1, n) > 250) * \ (.05 * D * np.random.uniform(-1, 1, n)) Y += (D * np.random.uniform(0, 1, n) > 250) * \ (.05 * D * np.random.uniform(-1, 1, n)) Z += (D * np.random.uniform(0, 1, n) > 250) * \ (.05 * D * np.random.uniform(-1, 1, n)) D = (D - D.min()) / (D.max() - D.min()) return P / 256, S / 2, D p = 50000 n = 3 * p # Very simple colormap cmap = np.array([[255, 124, 0], [255, 163, 76], [255, 192, 130], [255, 214, 173], [255, 232, 212], [246, 238, 237], [237, 240, 253], [217, 228, 255], [202, 219, 255], [191, 212, 255], [182, 206, 255], [174, 202, 255], [168, 198, 255], [162, 195, 255], [158, 192, 255], [155, 189, 255], [151, 187, 255], [148, 185, 255], [145, 183, 255], [143, 182, 255], [141, 181, 255], [140, 179, 255], [139, 179, 255], [137, 177, 255]], dtype=np.uint8).reshape(1, 24, 3) VERT_SHADER = """ #version 120 // Uniforms // ------------------------------------ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform float u_size; // Attributes // ------------------------------------ attribute vec3 a_position; attribute float a_size; attribute float a_dist; // Varyings // ------------------------------------ varying float v_size; varying float v_dist; void main (void) { v_size = a_size*u_size*.75; v_dist = a_dist; gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0); gl_PointSize = v_size; } """ FRAG_SHADER = """ #version 120 // Uniforms // ------------------------------------ uniform sampler2D u_colormap; // Varyings // ------------------------------------ varying float v_size; varying float v_dist; // Main // ------------------------------------ void main() { float a = 2*(length(gl_PointCoord.xy - vec2(0.5,0.5)) / sqrt(2.0)); vec3 color = texture2D(u_colormap, vec2(v_dist,.5)).rgb; gl_FragColor = vec4(color,(1-a)*.25); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 600)) ps = self.pixel_scale self.title = "A very fake galaxy [mouse scroll to zoom]" data = np.zeros(n, [('a_position', np.float32, 3), ('a_size', np.float32), ('a_dist', np.float32)]) for i in range(3): P, S, D = make_arm(p, i * 2 * np.pi / 3) data['a_dist'][(i + 0) * p:(i + 1) * p] = D data['a_position'][(i + 0) * p:(i + 1) * p] = P data['a_size'][(i + 0) * p:(i + 1) * p] = S*ps self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.theta, self.phi = 0, 0 self.translate = 5 self.view = translate((0, 0, -self.translate)) self.program.bind(gloo.VertexBuffer(data)) self.program['u_colormap'] = gloo.Texture2D(cmap) self.program['u_size'] = 5. / self.translate self.program['u_model'] = self.model self.program['u_view'] = self.view self.apply_zoom() gloo.set_state(depth_test=False, blend=True, blend_func=('src_alpha', 'one'), clear_color='black') # Start the timer upon initialization. self.timer = app.Timer('auto', connect=self.on_timer) self.timer.start() self.show() def on_key_press(self, event): if event.text == ' ': if self.timer.running: self.timer.stop() else: self.timer.start() def on_timer(self, event): self.theta += .11 self.phi += .13 self.model = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.program['u_model'] = self.model self.update() def on_resize(self, event): self.apply_zoom() def on_mouse_wheel(self, event): self.translate -= event.delta[1] self.translate = max(2, self.translate) self.view = translate((0, 0, -self.translate)) self.program['u_view'] = self.view self.program['u_size'] = 5 / self.translate self.update() def on_draw(self, event): gloo.clear() self.program.draw('points') def apply_zoom(self): gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self.projection = perspective(45.0, self.size[0] / float(self.size[1]), 1.0, 1000.0) self.program['u_projection'] = self.projection if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/galaxy/0000755000175000017510000000000014507104250017263 5ustar nileshnileshvispy-0.14.1/examples/demo/gloo/galaxy/galaxy_specrend.py0000644000175000017510000003271014507104216023012 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip """ Colour Rendering of Spectra by John Walker http://www.fourmilab.ch/ Last updated: March 9, 2003 Converted to Python by Andrew Hutchins, sometime in early 2011. This program is in the public domain. The modifications are also public domain. (AH) For complete information about the techniques employed in this program, see the World-Wide Web document: http://www.fourmilab.ch/documents/specrend/ The xyz_to_rgb() function, which was wrong in the original version of this program, was corrected by: Andrew J. S. Hamilton 21 May 1999 Andrew.Hamilton@Colorado.EDU http://casa.colorado.edu/~ajsh/ who also added the gamma correction facilities and modified constrain_rgb() to work by desaturating the colour by adding white. A program which uses these functions to plot CIE "tongue" diagrams called "ppmcie" is included in the Netpbm graphics toolkit: http://netpbm.sourceforge.net/ (The program was called cietoppm in earlier versions of Netpbm.) """ import math # A colour system is defined by the CIE x and y coordinates of # its three primary illuminants and the x and y coordinates of # the white point. GAMMA_REC709 = 0 NTSCsystem = {"name": "NTSC", "xRed": 0.67, "yRed": 0.33, "xGreen": 0.21, "yGreen": 0.71, "xBlue": 0.14, "yBlue": 0.08, "xWhite": 0.3101, "yWhite": 0.3163, "gamma": GAMMA_REC709} EBUsystem = {"name": "SUBU (PAL/SECAM)", "xRed": 0.64, "yRed": 0.33, "xGreen": 0.29, "yGreen": 0.60, "xBlue": 0.15, "yBlue": 0.06, "xWhite": 0.3127, "yWhite": 0.3291, "gamma": GAMMA_REC709} SMPTEsystem = {"name": "SMPTE", "xRed": 0.63, "yRed": 0.34, "xGreen": 0.31, "yGreen": 0.595, "xBlue": 0.155, "yBlue": 0.07, "xWhite": 0.3127, "yWhite": 0.3291, "gamma": GAMMA_REC709} HDTVsystem = {"name": "HDTV", "xRed": 0.67, "yRed": 0.33, "xGreen": 0.21, "yGreen": 0.71, "xBlue": 0.15, "yBlue": 0.06, "xWhite": 0.3127, "yWhite": 0.3291, "gamma": GAMMA_REC709} CIEsystem = {"name": "CIE", "xRed": 0.7355, "yRed": 0.2645, "xGreen": 0.2658, "yGreen": 0.7243, "xBlue": 0.1669, "yBlue": 0.0085, "xWhite": 0.3333333333, "yWhite": 0.3333333333, "gamma": GAMMA_REC709} Rec709system = {"name": "CIE REC709", "xRed": 0.64, "yRed": 0.33, "xGreen": 0.30, "yGreen": 0.60, "xBlue": 0.15, "yBlue": 0.06, "xWhite": 0.3127, "yWhite": 0.3291, "gamma": GAMMA_REC709} def upvp_to_xy(up, vp): xc = (9 * up) / ((6 * up) - (16 * vp) + 12) yc = (4 * vp) / ((6 * up) - (16 * vp) + 12) return(xc, yc) def xy_toupvp(xc, yc): up = (4 * xc) / ((-2 * xc) + (12 * yc) + 3) vp = (9 * yc) / ((-2 * xc) + (12 * yc) + 3) return(up, vp) def xyz_to_rgb(cs, xc, yc, zc): """ Given an additive tricolour system CS, defined by the CIE x and y chromaticities of its three primaries (z is derived trivially as 1-(x+y)), and a desired chromaticity (XC, YC, ZC) in CIE space, determine the contribution of each primary in a linear combination which sums to the desired chromaticity. If the requested chromaticity falls outside the Maxwell triangle (colour gamut) formed by the three primaries, one of the r, g, or b weights will be negative. Caller can use constrain_rgb() to desaturate an outside-gamut colour to the closest representation within the available gamut and/or norm_rgb to normalise the RGB components so the largest nonzero component has value 1. """ xr = cs["xRed"] yr = cs["yRed"] zr = 1 - (xr + yr) xg = cs["xGreen"] yg = cs["yGreen"] zg = 1 - (xg + yg) xb = cs["xBlue"] yb = cs["yBlue"] zb = 1 - (xb + yb) xw = cs["xWhite"] yw = cs["yWhite"] zw = 1 - (xw + yw) rx = (yg * zb) - (yb * zg) ry = (xb * zg) - (xg * zb) rz = (xg * yb) - (xb * yg) gx = (yb * zr) - (yr * zb) gy = (xr * zb) - (xb * zr) gz = (xb * yr) - (xr * yb) bx = (yr * zg) - (yg * zr) by = (xg * zr) - (xr * zg) bz = (xr * yg) - (xg * yr) rw = ((rx * xw) + (ry * yw) + (rz * zw)) / yw gw = ((gx * xw) + (gy * yw) + (gz * zw)) / yw bw = ((bx * xw) + (by * yw) + (bz * zw)) / yw rx = rx / rw ry = ry / rw rz = rz / rw gx = gx / gw gy = gy / gw gz = gz / gw bx = bx / bw by = by / bw bz = bz / bw r = (rx * xc) + (ry * yc) + (rz * zc) g = (gx * xc) + (gy * yc) + (gz * zc) b = (bx * xc) + (by * yc) + (bz * zc) return(r, g, b) def inside_gamut(r, g, b): """ Test whether a requested colour is within the gamut achievable with the primaries of the current colour system. This amounts simply to testing whether all the primary weights are non-negative. */ """ return (r >= 0) and (g >= 0) and (b >= 0) def constrain_rgb(r, g, b): """ If the requested RGB shade contains a negative weight for one of the primaries, it lies outside the colour gamut accessible from the given triple of primaries. Desaturate it by adding white, equal quantities of R, G, and B, enough to make RGB all positive. The function returns 1 if the components were modified, zero otherwise. """ # Amount of white needed is w = - min(0, *r, *g, *b) w = -min([0, r, g, b]) # I think? # Add just enough white to make r, g, b all positive. if w > 0: r += w g += w b += w return(r, g, b) def gamma_correct(cs, c): """ Transform linear RGB values to nonlinear RGB values. Rec. 709 is ITU-R Recommendation BT. 709 (1990) ``Basic Parameter Values for the HDTV Standard for the Studio and for International Programme Exchange'', formerly CCIR Rec. 709. For details see http://www.poynton.com/ColorFAQ.html http://www.poynton.com/GammaFAQ.html """ gamma = cs.gamma if gamma == GAMMA_REC709: cc = 0.018 if c < cc: c = ((1.099 * math.pow(cc, 0.45)) - 0.099) / cc else: c = (1.099 * math.pow(c, 0.45)) - 0.099 else: c = math.pow(c, 1.0 / gamma) return(c) def gamma_correct_rgb(cs, r, g, b): r = gamma_correct(cs, r) g = gamma_correct(cs, g) b = gamma_correct(cs, b) return (r, g, b) def norm_rgb(r, g, b): """ Normalise RGB components so the most intense (unless all are zero) has a value of 1. """ greatest = max([r, g, b]) if greatest > 0: r /= greatest g /= greatest b /= greatest return(r, g, b) # spec_intens is a function def spectrum_to_xyz(spec_intens, temp): """ Calculate the CIE X, Y, and Z coordinates corresponding to a light source with spectral distribution given by the function SPEC_INTENS, which is called with a series of wavelengths between 380 and 780 nm (the argument is expressed in meters), which returns emittance at that wavelength in arbitrary units. The chromaticity coordinates of the spectrum are returned in the x, y, and z arguments which respect the identity: x + y + z = 1. CIE colour matching functions xBar, yBar, and zBar for wavelengths from 380 through 780 nanometers, every 5 nanometers. For a wavelength lambda in this range:: cie_colour_match[(lambda - 380) / 5][0] = xBar cie_colour_match[(lambda - 380) / 5][1] = yBar cie_colour_match[(lambda - 380) / 5][2] = zBar AH Note 2011: This next bit is kind of irrelevant on modern hardware. Unless you are desperate for speed. In which case don't use the Python version! To save memory, this table can be declared as floats rather than doubles; (IEEE) float has enough significant bits to represent the values. It's declared as a double here to avoid warnings about "conversion between floating-point types" from certain persnickety compilers. """ cie_colour_match = [ [0.0014, 0.0000, 0.0065], [0.0022, 0.0001, 0.0105], [0.0042, 0.0001, 0.0201], [0.0076, 0.0002, 0.0362], [0.0143, 0.0004, 0.0679], [0.0232, 0.0006, 0.1102], [0.0435, 0.0012, 0.2074], [0.0776, 0.0022, 0.3713], [0.1344, 0.0040, 0.6456], [0.2148, 0.0073, 1.0391], [0.2839, 0.0116, 1.3856], [0.3285, 0.0168, 1.6230], [0.3483, 0.0230, 1.7471], [0.3481, 0.0298, 1.7826], [0.3362, 0.0380, 1.7721], [0.3187, 0.0480, 1.7441], [0.2908, 0.0600, 1.6692], [0.2511, 0.0739, 1.5281], [0.1954, 0.0910, 1.2876], [0.1421, 0.1126, 1.0419], [0.0956, 0.1390, 0.8130], [0.0580, 0.1693, 0.6162], [0.0320, 0.2080, 0.4652], [0.0147, 0.2586, 0.3533], [0.0049, 0.3230, 0.2720], [0.0024, 0.4073, 0.2123], [0.0093, 0.5030, 0.1582], [0.0291, 0.6082, 0.1117], [0.0633, 0.7100, 0.0782], [0.1096, 0.7932, 0.0573], [0.1655, 0.8620, 0.0422], [0.2257, 0.9149, 0.0298], [0.2904, 0.9540, 0.0203], [0.3597, 0.9803, 0.0134], [0.4334, 0.9950, 0.0087], [0.5121, 1.0000, 0.0057], [0.5945, 0.9950, 0.0039], [0.6784, 0.9786, 0.0027], [0.7621, 0.9520, 0.0021], [0.8425, 0.9154, 0.0018], [0.9163, 0.8700, 0.0017], [0.9786, 0.8163, 0.0014], [1.0263, 0.7570, 0.0011], [1.0567, 0.6949, 0.0010], [1.0622, 0.6310, 0.0008], [1.0456, 0.5668, 0.0006], [1.0026, 0.5030, 0.0003], [0.9384, 0.4412, 0.0002], [0.8544, 0.3810, 0.0002], [0.7514, 0.3210, 0.0001], [0.6424, 0.2650, 0.0000], [0.5419, 0.2170, 0.0000], [0.4479, 0.1750, 0.0000], [0.3608, 0.1382, 0.0000], [0.2835, 0.1070, 0.0000], [0.2187, 0.0816, 0.0000], [0.1649, 0.0610, 0.0000], [0.1212, 0.0446, 0.0000], [0.0874, 0.0320, 0.0000], [0.0636, 0.0232, 0.0000], [0.0468, 0.0170, 0.0000], [0.0329, 0.0119, 0.0000], [0.0227, 0.0082, 0.0000], [0.0158, 0.0057, 0.0000], [0.0114, 0.0041, 0.0000], [0.0081, 0.0029, 0.0000], [0.0058, 0.0021, 0.0000], [0.0041, 0.0015, 0.0000], [0.0029, 0.0010, 0.0000], [0.0020, 0.0007, 0.0000], [0.0014, 0.0005, 0.0000], [0.0010, 0.0004, 0.0000], [0.0007, 0.0002, 0.0000], [0.0005, 0.0002, 0.0000], [0.0003, 0.0001, 0.0000], [0.0002, 0.0001, 0.0000], [0.0002, 0.0001, 0.0000], [0.0001, 0.0000, 0.0000], [0.0001, 0.0000, 0.0000], [0.0001, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000]] X = 0 Y = 0 Z = 0 # lambda = 380; lambda < 780.1; i++, lambda += 5) { for i, lamb in enumerate(range(380, 780, 5)): Me = spec_intens(lamb, temp) X += Me * cie_colour_match[i][0] Y += Me * cie_colour_match[i][1] Z += Me * cie_colour_match[i][2] XYZ = (X + Y + Z) x = X / XYZ y = Y / XYZ z = Z / XYZ return(x, y, z) def bb_spectrum(wavelength, bbTemp=5000): """ Calculate, by Planck's radiation law, the emittance of a black body of temperature bbTemp at the given wavelength (in metres). */ """ wlm = wavelength * 1e-9 # Convert to metres return (3.74183e-16 * math.pow(wlm, -5.0)) / (math.exp(1.4388e-2 / (wlm * bbTemp)) - 1.0) """ Built-in test program which displays the x, y, and Z and RGB values for black body spectra from 1000 to 10000 degrees kelvin. When run, this program should produce the following output: Temperature x y z R G B ----------- ------ ------ ------ ----- ----- ----- 1000 K 0.6528 0.3444 0.0028 1.000 0.007 0.000 (Approximation) 1500 K 0.5857 0.3931 0.0212 1.000 0.126 0.000 (Approximation) 2000 K 0.5267 0.4133 0.0600 1.000 0.234 0.010 2500 K 0.4770 0.4137 0.1093 1.000 0.349 0.067 3000 K 0.4369 0.4041 0.1590 1.000 0.454 0.151 3500 K 0.4053 0.3907 0.2040 1.000 0.549 0.254 4000 K 0.3805 0.3768 0.2428 1.000 0.635 0.370 4500 K 0.3608 0.3636 0.2756 1.000 0.710 0.493 5000 K 0.3451 0.3516 0.3032 1.000 0.778 0.620 5500 K 0.3325 0.3411 0.3265 1.000 0.837 0.746 6000 K 0.3221 0.3318 0.3461 1.000 0.890 0.869 6500 K 0.3135 0.3237 0.3628 1.000 0.937 0.988 7000 K 0.3064 0.3166 0.3770 0.907 0.888 1.000 7500 K 0.3004 0.3103 0.3893 0.827 0.839 1.000 8000 K 0.2952 0.3048 0.4000 0.762 0.800 1.000 8500 K 0.2908 0.3000 0.4093 0.711 0.766 1.000 9000 K 0.2869 0.2956 0.4174 0.668 0.738 1.000 9500 K 0.2836 0.2918 0.4246 0.632 0.714 1.000 10000 K 0.2807 0.2884 0.4310 0.602 0.693 1.000 """ if __name__ == "__main__": print("Temperature x y z R G B\n") print("----------- ------ ------ ------ ----- ----- -----\n") for t in range(1000, 10000, 500): # (t = 1000; t <= 10000; t+= 500) { x, y, z = spectrum_to_xyz(bb_spectrum, t) r, g, b = xyz_to_rgb(SMPTEsystem, x, y, z) print(" %5.0f K %.4f %.4f %.4f " % (t, x, y, z)) # I omit the approximation bit here. r, g, b = constrain_rgb(r, g, b) r, g, b = norm_rgb(r, g, b) print("%.3f %.3f %.3f" % (r, g, b)) vispy-0.14.1/examples/demo/gloo/galaxy/galaxy_simulation.py0000644000175000017510000001677714507104216023412 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # A Galaxy Simulator based on the density wave theory # (c) 2012 Ingo Berg # # Simulating a Galaxy with the density wave theory # http://beltoforion.de/galaxy/galaxy_en.html # # Python version(c) 2014 Nicolas P.Rougier # ----------------------------------------------------------------------------- import math import numpy as np class Galaxy(object): """ Galaxy simulation using the density wave theory """ def __init__(self, n=20000): """ Initialize galaxy """ # Eccentricity of the innermost ellipse self._inner_eccentricity = 0.8 # Eccentricity of the outermost ellipse self._outer_eccentricity = 1.0 # Velocity at the innermost core in km/s self._center_velocity = 30 # Velocity at the core edge in km/s self._inner_velocity = 200 # Velocity at the edge of the disk in km/s self._outer_velocity = 300 # Angular offset per parsec self._angular_offset = 0.019 # Inner core radius self._core_radius = 6000 # Galaxy radius self._galaxy_radius = 15000 # The radius after which all density waves must have circular shape self._distant_radius = 0 # Distribution of stars self._star_distribution = 0.45 # Angular velocity of the density waves self._angular_velocity = 0.000001 # Number of stars self._stars_count = n # Number of dust particles self._dust_count = int(self._stars_count * 0.75) # Number of H-II regions self._h2_count = 200 # Particles dtype = [('theta', np.float32), ('velocity', np.float32), ('angle', np.float32), ('m_a', np.float32), ('m_b', np.float32), ('size', np.float32), ('type', np.float32), ('temperature', np.float32), ('brightness', np.float32), ('position', np.float32, 2)] n = self._stars_count + self._dust_count + 2*self._h2_count self._particles = np.zeros(n, dtype=dtype) i0 = 0 i1 = i0 + self._stars_count self._stars = self._particles[i0:i1] self._stars['size'] = 3. self._stars['type'] = 0 i0 = i1 i1 = i0 + self._dust_count self._dust = self._particles[i0:i1] self._dust['size'] = 64 self._dust['type'] = 1 i0 = i1 i1 = i0 + self._h2_count self._h2a = self._particles[i0:i1] self._h2a['size'] = 0 self._h2a['type'] = 2 i0 = i1 i1 = i0 + self._h2_count self._h2b = self._particles[i0:i1] self._h2b['size'] = 0 self._h2b['type'] = 3 def __len__(self): """ Number of particles """ if self._particles is not None: return len(self._particles) return 0 def __getitem__(self, key): """ x.__getitem__(y) <==> x[y] """ if self._particles is not None: return self._particles[key] return None def reset(self, rad, radCore, deltaAng, ex1, ex2, sigma, velInner, velOuter): # Initialize parameters # --------------------- self._inner_eccentricity = ex1 self._outer_eccentricity = ex2 self._inner_velocity = velInner self._outer_velocity = velOuter self._angular_offset = deltaAng self._core_radius = radCore self._galaxy_radius = rad self._distant_radius = self._galaxy_radius * 2 self.m_sigma = sigma # Initialize stars # ---------------- stars = self._stars R = np.random.normal(0, sigma, len(stars)) * self._galaxy_radius stars['m_a'] = R stars['angle'] = 90 - R * self._angular_offset stars['theta'] = np.random.uniform(0, 360, len(stars)) stars['temperature'] = np.random.uniform(3000, 9000, len(stars)) stars['brightness'] = np.random.uniform(0.05, 0.25, len(stars)) stars['velocity'] = 0.000005 for i in range(len(stars)): stars['m_b'][i] = R[i] * self.eccentricity(R[i]) # Initialize dust # --------------- dust = self._dust X = np.random.uniform(0, 2*self._galaxy_radius, len(dust)) Y = np.random.uniform(-self._galaxy_radius, self._galaxy_radius, len(dust)) R = np.sqrt(X*X+Y*Y) dust['m_a'] = R dust['angle'] = R * self._angular_offset dust['theta'] = np.random.uniform(0, 360, len(dust)) dust['velocity'] = 0.000005 dust['temperature'] = 6000 + R/4 dust['brightness'] = np.random.uniform(0.01, 0.02) for i in range(len(dust)): dust['m_b'][i] = R[i] * self.eccentricity(R[i]) # Initialise H-II # --------------- h2a, h2b = self._h2a, self._h2b X = np.random.uniform(-self._galaxy_radius, self._galaxy_radius, len(h2a)) Y = np.random.uniform(-self._galaxy_radius, self._galaxy_radius, len(h2a)) R = np.sqrt(X*X+Y*Y) h2a['m_a'] = R h2b['m_a'] = R + 1000 h2a['angle'] = R * self._angular_offset h2b['angle'] = h2a['angle'] h2a['theta'] = np.random.uniform(0, 360, len(h2a)) h2b['theta'] = h2a['theta'] h2a['velocity'] = 0.000005 h2b['velocity'] = 0.000005 h2a['temperature'] = np.random.uniform(3000, 9000, len(h2a)) h2b['temperature'] = h2a['temperature'] h2a['brightness'] = np.random.uniform(0.005, 0.010, len(h2a)) h2b['brightness'] = h2a['brightness'] for i in range(len(h2a)): h2a['m_b'][i] = R[i] * self.eccentricity(R[i]) h2b['m_b'] = h2a['m_b'] def update(self, timestep=100000): """ Update simulation """ self._particles['theta'] += self._particles['velocity'] * timestep P = self._particles a, b = P['m_a'], P['m_b'] theta, beta = P['theta'], -P['angle'] alpha = theta * math.pi / 180.0 cos_alpha = np.cos(alpha) sin_alpha = np.sin(alpha) cos_beta = np.cos(beta) sin_beta = np.sin(beta) P['position'][:, 0] = a*cos_alpha*cos_beta - b*sin_alpha*sin_beta P['position'][:, 1] = a*cos_alpha*sin_beta + b*sin_alpha*cos_beta D = np.sqrt(((self._h2a['position'] - self._h2b['position'])**2).sum(axis=1)) S = np.maximum(1, ((1000-D)/10) - 50) self._h2a['size'] = 2.0*S self._h2b['size'] = S/6.0 def eccentricity(self, r): # Core region of the galaxy. Innermost part is round # eccentricity increasing linear to the border of the core. if r < self._core_radius: return 1 + (r / self._core_radius) * (self._inner_eccentricity-1) elif r > self._core_radius and r <= self._galaxy_radius: a = self._galaxy_radius - self._core_radius b = self._outer_eccentricity - self._inner_eccentricity return self._inner_eccentricity + (r - self._core_radius) / a * b # Eccentricity is slowly reduced to 1. elif r > self._galaxy_radius and r < self._distant_radius: a = self._distant_radius - self._galaxy_radius b = 1 - self._outer_eccentricity return self._outer_eccentricity + (r - self._galaxy_radius) / a * b else: return 1 vispy-0.14.1/examples/demo/gloo/galaxy/galaxy.py0000755000175000017510000001363314507104216021135 0ustar nileshnilesh#!/usr/bin/env python import numpy as np import sys from vispy.util.transforms import perspective from vispy.util import transforms from vispy import gloo from vispy import app from vispy import io import galaxy_specrend from galaxy_simulation import Galaxy VERT_SHADER = """ #version 120 uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; //sampler that maps [0, n] -> color according to blackbody law uniform sampler1D u_colormap; //index to sample the colormap at attribute float a_color_index; //size of the star attribute float a_size; //type //type 0 - stars //type 1 - dust //type 2 - h2a objects //type 3 - h2a objects attribute float a_type; attribute vec2 a_position; //brightness of the star attribute float a_brightness; varying vec3 v_color; void main (void) { gl_Position = u_projection * u_view * u_model * vec4(a_position, 0.0,1.0); //find base color according to physics from our sampler vec3 base_color = texture1D(u_colormap, a_color_index).rgb; //scale it down according to brightness v_color = base_color * a_brightness; if (a_size > 2.0) { gl_PointSize = a_size; } else { gl_PointSize = 0.0; } if (a_type == 2) { v_color *= vec3(2, 1, 1); } else if (a_type == 3) { v_color = vec3(.9); } } """ FRAG_SHADER = """ #version 120 //star texture uniform sampler2D u_texture; //predicted color from black body varying vec3 v_color; void main() { //amount of intensity from the grayscale star float star_tex_intensity = texture2D(u_texture, gl_PointCoord).r; gl_FragColor = vec4(v_color * star_tex_intensity, 0.8); } """ galaxy = Galaxy(10000) galaxy.reset(13000, 4000, 0.0004, 0.90, 0.90, 0.5, 200, 300) # coldest and hottest temperatures of out galaxy t0, t1 = 200.0, 10000.0 # total number of discrete colors between t0 and t1 n = 1000 dt = (t1 - t0) / n # maps [0, n) -> colors # generate a linear interpolation of temperatures # then map the temperatures to colors using black body # color predictions colors = np.zeros(n, dtype=(np.float32, 3)) for i in range(n): temperature = t0 + i * dt x, y, z = galaxy_specrend.spectrum_to_xyz(galaxy_specrend.bb_spectrum, temperature) r, g, b = galaxy_specrend.xyz_to_rgb(galaxy_specrend.SMPTEsystem, x, y, z) r = min((max(r, 0), 1)) g = min((max(g, 0), 1)) b = min((max(b, 0), 1)) colors[i] = galaxy_specrend.norm_rgb(r, g, b) # load the PNG that we use to blend the star with # to provide a circular look to each star. def load_galaxy_star_image(): fname = io.load_data_file('galaxy/star-particle.png') raw_image = io.read_png(fname) return raw_image class Canvas(app.Canvas): def __init__(self): # setup initial width, height app.Canvas.__init__(self, keys='interactive', size=(800, 600)) # create a new shader program self.program = gloo.Program(VERT_SHADER, FRAG_SHADER, count=len(galaxy)) # load the star texture self.texture = gloo.Texture2D(load_galaxy_star_image(), interpolation='linear') self.program['u_texture'] = self.texture # construct the model, view and projection matrices self.view = transforms.translate((0, 0, -5)) self.program['u_view'] = self.view self.model = np.eye(4, dtype=np.float32) self.program['u_model'] = self.model self.program['u_colormap'] = colors w, h = self.size self.projection = perspective(45.0, w / float(h), 1.0, 1000.0) self.program['u_projection'] = self.projection # start the galaxy to some decent point in the future galaxy.update(100000) data = self.__create_galaxy_vertex_data() # setup the VBO once the galaxy vertex data has been setup # bind the VBO for the first time self.data_vbo = gloo.VertexBuffer(data) self.program.bind(self.data_vbo) # setup blending gloo.set_state(clear_color=(0.0, 0.0, 0.03, 1.0), depth_test=False, blend=True, blend_func=('src_alpha', 'one')) self._timer = app.Timer('auto', connect=self.update, start=True) def __create_galaxy_vertex_data(self): data = np.zeros(len(galaxy), dtype=[('a_size', np.float32), ('a_position', np.float32, 2), ('a_color_index', np.float32), ('a_brightness', np.float32), ('a_type', np.float32)]) # see shader for parameter explanations pw, ph = self.physical_size data['a_size'] = galaxy['size'] * max(pw / 800.0, ph / 800.0) data['a_position'] = galaxy['position'] / 13000.0 data['a_color_index'] = (galaxy['temperature'] - t0) / (t1 - t0) data['a_brightness'] = galaxy['brightness'] data['a_type'] = galaxy['type'] return data def on_resize(self, event): # setup the new viewport gloo.set_viewport(0, 0, *event.physical_size) # recompute the projection matrix w, h = event.size self.projection = perspective(45.0, w / float(h), 1.0, 1000.0) self.program['u_projection'] = self.projection def on_draw(self, event): # update the galaxy galaxy.update(50000) # in years ! # recreate the numpy array that will be sent as the VBO data data = self.__create_galaxy_vertex_data() # update the VBO self.data_vbo.set_data(data) # bind the VBO to the GL context self.program.bind(self.data_vbo) # clear the screen and render gloo.clear(color=True, depth=True) self.program.draw('points') if __name__ == '__main__': c = Canvas() c.show() if sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/demo/gloo/fireworks.py0000644000175000017510000000770414507104216020375 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 20 """ Example demonstrating simulation of fireworks using point sprites. (adapted from the "OpenGL ES 2.0 Programming Guide") This example demonstrates a series of explosions that last one second. The visualization during the explosion is highly optimized using a Vertex Buffer Object (VBO). After each explosion, vertex data for the next explosion are calculated, such that each explostion is unique. """ import time import numpy as np from vispy import gloo, app # import vispy # vispy.use('pyside', 'es2') # Create a texture radius = 32 im1 = np.random.normal( 0.8, 0.3, (radius * 2 + 1, radius * 2 + 1)).astype(np.float32) # Mask it with a disk L = np.linspace(-radius, radius, 2 * radius + 1) (X, Y) = np.meshgrid(L, L) im1 *= np.array((X ** 2 + Y ** 2) <= radius * radius, dtype='float32') # Set number of particles, you should be able to scale this to 100000 N = 10000 # Create vertex data container data = np.zeros(N, [('a_lifetime', np.float32), ('a_startPosition', np.float32, 3), ('a_endPosition', np.float32, 3)]) VERT_SHADER = """ uniform float u_time; uniform vec3 u_centerPosition; attribute float a_lifetime; attribute vec3 a_startPosition; attribute vec3 a_endPosition; varying float v_lifetime; void main () { if (u_time <= a_lifetime) { gl_Position.xyz = a_startPosition + (u_time * a_endPosition); gl_Position.xyz += u_centerPosition; gl_Position.y -= 1.0 * u_time * u_time; gl_Position.w = 1.0; } else gl_Position = vec4(-1000, -1000, 0, 0); v_lifetime = 1.0 - (u_time / a_lifetime); v_lifetime = clamp(v_lifetime, 0.0, 1.0); gl_PointSize = (v_lifetime * v_lifetime) * 40.0; } """ # Deliberately add precision qualifiers to test automatic GLSL code conversion FRAG_SHADER = """ #version 120 precision highp float; uniform sampler2D texture1; uniform vec4 u_color; varying float v_lifetime; uniform highp sampler2D s_texture; void main() { highp vec4 texColor; texColor = texture2D(s_texture, gl_PointCoord); gl_FragColor = vec4(u_color) * texColor; gl_FragColor.a *= v_lifetime; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 600)) # Create program self._program = gloo.Program(VERT_SHADER, FRAG_SHADER) self._program.bind(gloo.VertexBuffer(data)) self._program['s_texture'] = gloo.Texture2D(im1) # Create first explosion self._new_explosion() # Enable blending gloo.set_state(blend=True, clear_color='black', blend_func=('src_alpha', 'one')) gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self._timer = app.Timer('auto', connect=self.update, start=True) self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) def on_draw(self, event): # Clear gloo.clear() # Draw self._program['u_time'] = time.time() - self._starttime self._program.draw('points') # New explosion? if time.time() - self._starttime > 1.5: self._new_explosion() def _new_explosion(self): # New centerpos centerpos = np.random.uniform(-0.5, 0.5, (3,)) self._program['u_centerPosition'] = centerpos # New color, scale alpha with N alpha = 1.0 / N ** 0.08 color = np.random.uniform(0.1, 0.9, (3,)) self._program['u_color'] = tuple(color) + (alpha,) # Create new vertex data data['a_lifetime'] = np.random.normal(2.0, 0.5, (N,)) data['a_startPosition'] = np.random.normal(0.0, 0.2, (N, 3)) data['a_endPosition'] = np.random.normal(0.0, 1.2, (N, 3)) # Set time to zero self._starttime = time.time() if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/donut.py0000644000175000017510000001313514507104216017506 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) 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 # ----------------------------------------------------------------------------- """ Mesmerizing donut """ import numpy as np from vispy import gloo from vispy import app from vispy.util.transforms import perspective, translate, rotate vert = """ #version 120 uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform float u_linewidth; uniform float u_antialias; uniform float u_size; uniform float u_clock; attribute vec2 a_position; attribute vec4 a_fg_color; attribute vec4 a_bg_color; attribute float a_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_size; varying float v_linewidth; varying float v_antialias; void main (void) { v_size = a_size * u_size; v_linewidth = u_linewidth; v_antialias = u_antialias; v_fg_color = a_fg_color; v_bg_color = a_bg_color; float x0 = 0.5; float z0 = 0.0; float theta = a_position.x + u_clock; float x1 = x0*cos(theta) + z0*sin(theta) - 1.0; float y1 = 0.0; float z1 = z0*cos(theta) - x0*sin(theta); float phi = a_position.y; float x2 = x1*cos(phi) + y1*sin(phi); float y2 = y1*cos(phi) - x1*sin(phi); float z2 = z1; gl_Position = u_projection * u_view * u_model * vec4(x2,y2,z2,1.); gl_PointSize = v_size + 2.*(v_linewidth + 1.5*v_antialias); } """ frag = """ #version 120 varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_size; varying float v_linewidth; varying float v_antialias; void main() { float size = v_size +2*(v_linewidth + 1.5*v_antialias); float t = v_linewidth/2.0-v_antialias; float r = length((gl_PointCoord.xy - vec2(0.5,0.5))*size) - v_size/2.; float d = abs(r) - t; if( r > (v_linewidth/2.0+v_antialias)) { discard; } else if( d < 0.0 ) { gl_FragColor = v_fg_color; } else { float alpha = d/v_antialias; alpha = exp(-alpha*alpha); if (r > 0) gl_FragColor = vec4(v_fg_color.rgb, alpha*v_fg_color.a); else gl_FragColor = mix(v_bg_color, v_fg_color, alpha); } } """ # ------------------------------------------------------------ Canvas class --- class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 800)) ps = self.pixel_scale self.title = "D'oh! A big donut" # Create vertices n, p = 50, 40 data = np.zeros(p * n, [('a_position', np.float32, 2), ('a_bg_color', np.float32, 4), ('a_fg_color', np.float32, 4), ('a_size', np.float32)]) data['a_position'][:, 0] = np.resize(np.linspace( 0, 2 * np.pi, n), p * n) data['a_position'][:, 1] = np.repeat(np.linspace(0, 2 * np.pi, p), n) data['a_bg_color'] = np.random.uniform(0.75, 1.00, (n * p, 4)) data['a_bg_color'][:, 3] = 1 data['a_fg_color'] = 0, 0, 0, 1 # data['a_size'] = np.random.uniform(8*ps, 8*ps, n * p) data['a_size'] = 8.0*ps u_linewidth = 1.0*ps u_antialias = 1.0 self.translate = 5 self.program = gloo.Program(vert, frag) self.view = translate((0, 0, -self.translate)) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.program.bind(gloo.VertexBuffer(data)) self.program['u_linewidth'] = u_linewidth self.program['u_antialias'] = u_antialias self.program['u_model'] = self.model self.program['u_view'] = self.view self.program['u_size'] = 5 / self.translate self.apply_zoom() self.theta = 0 self.phi = 0 self.clock = 0 self.stop_rotation = False gloo.set_state('translucent', clear_color='white') self.program['u_clock'] = 0.0 self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_key_press(self, event): if event.text == ' ': self.stop_rotation = not self.stop_rotation def on_timer(self, event): if not self.stop_rotation: self.theta += .5 self.phi += .5 self.model = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.program['u_model'] = self.model self.clock += np.pi / 1000 self.program['u_clock'] = self.clock self.update() def on_resize(self, event): self.apply_zoom() def on_mouse_wheel(self, event): self.translate -= event.delta[1] self.translate = max(2, self.translate) self.view = translate((0, 0, -self.translate)) self.program['u_view'] = self.view self.program['u_size'] = 5 / self.translate self.update() def on_draw(self, event): gloo.clear() self.program.draw('points') def apply_zoom(self): gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self.projection = perspective(45.0, self.size[0] / float(self.size[1]), 1.0, 1000.0) self.program['u_projection'] = self.projection if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/cloud.py0000755000175000017510000001677714507104216017505 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 1 """ Demonstrating a cloud of points. """ import numpy as np from vispy import gloo from vispy import app from vispy.util.transforms import perspective, translate, rotate vert = """ #version 120 // Uniforms // ------------------------------------ uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform float u_linewidth; uniform float u_antialias; uniform float u_size; // Attributes // ------------------------------------ attribute vec3 a_position; attribute vec4 a_fg_color; attribute vec4 a_bg_color; attribute float a_size; // Varyings // ------------------------------------ varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_size; varying float v_linewidth; varying float v_antialias; void main (void) { v_size = a_size * u_size; v_linewidth = u_linewidth; v_antialias = u_antialias; v_fg_color = a_fg_color; v_bg_color = a_bg_color; gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0); gl_PointSize = v_size + 2.*(v_linewidth + 1.5*v_antialias); } """ frag = """ #version 120 // Constants // ------------------------------------ // Varyings // ------------------------------------ varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_size; varying float v_linewidth; varying float v_antialias; // Functions // ------------------------------------ // ---------------- float disc(vec2 P, float size) { float r = length((P.xy - vec2(0.5,0.5))*size); r -= v_size/2.; return r; } // ---------------- float arrow_right(vec2 P, float size) { float r1 = abs(P.x -.50)*size + abs(P.y -.5)*size - v_size/2.; float r2 = abs(P.x -.25)*size + abs(P.y -.5)*size - v_size/2.; float r = max(r1,-r2); return r; } // ---------------- float ring(vec2 P, float size) { float r1 = length((P.xy - vec2(0.5,0.5))*size) - v_size/2.; float r2 = length((P.xy - vec2(0.5,0.5))*size) - v_size/4.; float r = max(r1,-r2); return r; } // ---------------- float clober(vec2 P, float size) { const float PI = 3.14159265358979323846264; const float t1 = -PI/2.; const vec2 c1 = 0.2*vec2(cos(t1),sin(t1)); const float t2 = t1+2.*PI/3.; const vec2 c2 = 0.2*vec2(cos(t2),sin(t2)); const float t3 = t2+2.*PI/3.; const vec2 c3 = 0.2*vec2(cos(t3),sin(t3)); float r1 = length((P.xy- vec2(0.5,0.5) - c1)*size); r1 -= v_size/3; float r2 = length((P.xy- vec2(0.5,0.5) - c2)*size); r2 -= v_size/3; float r3 = length((P.xy- vec2(0.5,0.5) - c3)*size); r3 -= v_size/3; float r = min(min(r1,r2),r3); return r; } // ---------------- float square(vec2 P, float size) { float r = max(abs(P.x -.5)*size, abs(P.y -.5)*size); r -= v_size/2.; return r; } // ---------------- float diamond(vec2 P, float size) { float r = abs(P.x -.5)*size + abs(P.y -.5)*size; r -= v_size/2.; return r; } // ---------------- float vbar(vec2 P, float size) { float r1 = max(abs(P.x -.75)*size, abs(P.x -.25)*size); float r3 = max(abs(P.x -.5)*size, abs(P.y -.5)*size); float r = max(r1,r3); r -= v_size/2.; return r; } // ---------------- float hbar(vec2 P, float size) { float r2 = max(abs(P.y -.75)*size, abs(P.y -.25)*size); float r3 = max(abs(P.x -.5)*size, abs(P.y -.5)*size); float r = max(r2,r3); r -= v_size/2.; return r; } // ---------------- float cross(vec2 P, float size) { float r1 = max(abs(P.x -.75)*size, abs(P.x -.25)*size); float r2 = max(abs(P.y -.75)*size, abs(P.y -.25)*size); float r3 = max(abs(P.x -.5)*size, abs(P.y -.5)*size); float r = max(min(r1,r2),r3); r -= v_size/2.; return r; } // Main // ------------------------------------ void main() { float size = v_size +2.0*(v_linewidth + 1.5*v_antialias); float t = v_linewidth/2.0-v_antialias; float r = disc(gl_PointCoord, size); // float r = square(gl_PointCoord, size); // float r = ring(gl_PointCoord, size); // float r = arrow_right(gl_PointCoord, size); // float r = diamond(gl_PointCoord, size); // float r = cross(gl_PointCoord, size); // float r = clober(gl_PointCoord, size); // float r = hbar(gl_PointCoord, size); // float r = vbar(gl_PointCoord, size); float d = abs(r) - t; if( r > (v_linewidth/2.0+v_antialias)) { discard; } else if( d < 0.0 ) { gl_FragColor = v_fg_color; } else { float alpha = d/v_antialias; alpha = exp(-alpha*alpha); if (r > 0.) gl_FragColor = vec4(v_fg_color.rgb, alpha*v_fg_color.a); else gl_FragColor = mix(v_bg_color, v_fg_color, alpha); } } """ # ------------------------------------------------------------ Canvas class --- class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 600)) ps = self.pixel_scale # Create vertices n = 1000000 data = np.zeros(n, [('a_position', np.float32, 3), ('a_bg_color', np.float32, 4), ('a_fg_color', np.float32, 4), ('a_size', np.float32)]) data['a_position'] = 0.45 * np.random.randn(n, 3) data['a_bg_color'] = np.random.uniform(0.85, 1.00, (n, 4)) data['a_fg_color'] = 0, 0, 0, 1 data['a_size'] = np.random.uniform(5*ps, 10*ps, n) u_linewidth = 1.0 u_antialias = 1.0 self.translate = 5 self.program = gloo.Program(vert, frag) self.view = translate((0, 0, -self.translate)) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.apply_zoom() self.program.bind(gloo.VertexBuffer(data)) self.program['u_linewidth'] = u_linewidth self.program['u_antialias'] = u_antialias self.program['u_model'] = self.model self.program['u_view'] = self.view self.program['u_size'] = 5 / self.translate self.theta = 0 self.phi = 0 gloo.set_state('translucent', clear_color='white') self.timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_key_press(self, event): if event.text == ' ': if self.timer.running: self.timer.stop() else: self.timer.start() def on_timer(self, event): self.theta += .5 self.phi += .5 self.model = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.program['u_model'] = self.model self.update() def on_resize(self, event): self.apply_zoom() def on_mouse_wheel(self, event): self.translate -= event.delta[1] self.translate = max(2, self.translate) self.view = translate((0, 0, -self.translate)) self.program['u_view'] = self.view self.program['u_size'] = 5 / self.translate self.update() def on_draw(self, event): gloo.clear() self.program.draw('points') def apply_zoom(self): gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self.projection = perspective(45.0, self.size[0] / float(self.size[1]), 1.0, 1000.0) self.program['u_projection'] = self.projection if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/camera.py0000644000175000017510000000426414507104216017610 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """Display a live webcam feed. Require OpenCV (Python 2 only). """ try: import cv2 except Exception: raise ImportError("You need OpenCV for this example.") import numpy as np from vispy import app from vispy import gloo vertex = """ attribute vec2 position; attribute vec2 texcoord; varying vec2 v_texcoord; void main() { gl_Position = vec4(position, 0.0, 1.0); v_texcoord = texcoord; } """ fragment = """ uniform sampler2D texture; varying vec2 v_texcoord; void main() { gl_FragColor = texture2D(texture, v_texcoord); // HACK: the image is in BGR instead of RGB. float temp = gl_FragColor.r; gl_FragColor.r = gl_FragColor.b; gl_FragColor.b = temp; } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(640, 480), keys='interactive') self.program = gloo.Program(vertex, fragment, count=4) self.program['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] self.program['texcoord'] = [(1, 1), (1, 0), (0, 1), (0, 0)] self.program['texture'] = np.zeros((480, 640, 3)).astype(np.uint8) width, height = self.physical_size gloo.set_viewport(0, 0, width, height) self.cap = cv2.VideoCapture(0) if not self.cap.isOpened(): raise Exception("There's no available camera.") self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) def on_draw(self, event): gloo.clear('black') _, im = self.cap.read() self.program['texture'][...] = im self.program.draw('triangle_strip') def on_timer(self, event): self.update() c = Canvas() app.run() c.cap.release() vispy-0.14.1/examples/demo/gloo/brain.py0000644000175000017510000001073014507104216017446 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 2 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ 3D brain mesh viewer. """ from timeit import default_timer import numpy as np from vispy import gloo from vispy import app from vispy.util.transforms import perspective, translate, rotate from vispy.io import load_data_file brain = np.load(load_data_file('brain/brain.npz', force_download='2014-09-04')) data = brain['vertex_buffer'] faces = brain['index_buffer'] VERT_SHADER = """ #version 120 uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform vec4 u_color; attribute vec3 a_position; attribute vec3 a_normal; attribute vec4 a_color; varying vec3 v_position; varying vec3 v_normal; varying vec4 v_color; void main() { v_normal = a_normal; v_position = a_position; v_color = a_color * u_color; gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0); } """ FRAG_SHADER = """ #version 120 uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_normal; uniform vec3 u_light_intensity; uniform vec3 u_light_position; varying vec3 v_position; varying vec3 v_normal; varying vec4 v_color; void main() { // Calculate normal in world coordinates vec3 normal = normalize(u_normal * vec4(v_normal,1.0)).xyz; // Calculate the location of this fragment (pixel) in world coordinates vec3 position = vec3(u_view*u_model * vec4(v_position, 1)); // Calculate the vector from this pixels surface to the light source vec3 surfaceToLight = u_light_position - position; // Calculate the cosine of the angle of incidence (brightness) float brightness = dot(normal, surfaceToLight) / (length(surfaceToLight) * length(normal)); brightness = max(min(brightness,1.0),0.0); // Calculate final color of the pixel, based on: // 1. The angle of incidence: brightness // 2. The color/intensities of the light: light.intensities // 3. The texture and texture coord: texture(tex, fragTexCoord) // Specular lighting. vec3 surfaceToCamera = vec3(0.0, 0.0, 1.0) - position; vec3 K = normalize(normalize(surfaceToLight) + normalize(surfaceToCamera)); float specular = clamp(pow(abs(dot(normal, K)), 40.), 0.0, 1.0); gl_FragColor = v_color * brightness * vec4(u_light_intensity, 1); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive') self.size = 800, 600 self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) self.theta, self.phi = -80, 180 self.translate = 3 self.faces = gloo.IndexBuffer(faces) self.program.bind(gloo.VertexBuffer(data)) self.program['u_color'] = 1, 1, 1, 1 self.program['u_light_position'] = (1., 1., 1.) self.program['u_light_intensity'] = (1., 1., 1.) self.apply_zoom() gloo.set_state(blend=False, depth_test=True, polygon_offset_fill=True) self._t0 = default_timer() self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.update_matrices() def update_matrices(self): self.view = translate((0, 0, -self.translate)) self.model = np.dot(rotate(self.theta, (1, 0, 0)), rotate(self.phi, (0, 1, 0))) self.projection = np.eye(4, dtype=np.float32) self.program['u_model'] = self.model self.program['u_view'] = self.view self.program['u_normal'] = np.linalg.inv(np.dot(self.view, self.model)).T def on_timer(self, event): elapsed = default_timer() - self._t0 self.phi = 180 + elapsed * 50. self.update_matrices() self.update() def on_resize(self, event): self.apply_zoom() def on_mouse_wheel(self, event): self.translate += -event.delta[1]/5. self.translate = max(2, self.translate) self.update_matrices() self.update() def on_draw(self, event): gloo.clear() self.program.draw('triangles', indices=self.faces) def apply_zoom(self): gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self.projection = perspective(45.0, self.size[0] / float(self.size[1]), 1.0, 20.0) self.program['u_projection'] = self.projection if __name__ == '__main__': c = Canvas() c.show() app.run() vispy-0.14.1/examples/demo/gloo/boids.py0000755000175000017510000001316414507104216017462 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: gallery 30 """ Demonstration of boids simulation. Boids is an artificial life program, developed by Craig Reynolds in 1986, which simulates the flocking behaviour of birds. Based on code from glumpy by Nicolas Rougier. """ import time import numpy as np from scipy.spatial import cKDTree from vispy import gloo from vispy import app VERT_SHADER = """ #version 120 attribute vec3 position; attribute vec4 color; attribute float size; varying vec4 v_color; void main (void) { gl_Position = vec4(position, 1.0); v_color = color; gl_PointSize = size; } """ FRAG_SHADER = """ #version 120 varying vec4 v_color; void main() { float x = 2.0*gl_PointCoord.x - 1.0; float y = 2.0*gl_PointCoord.y - 1.0; float a = 1.0 - (x*x + y*y); gl_FragColor = vec4(v_color.rgb, a*v_color.a); } """ class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive') ps = self.pixel_scale # Create boids n = 1000 size_type = ('size', 'f4', 1*ps) if ps > 1 else ('size', 'f4') self.particles = np.zeros(2 + n, [('position', 'f4', 3), ('position_1', 'f4', 3), ('position_2', 'f4', 3), ('velocity', 'f4', 3), ('color', 'f4', 4), size_type]) self.boids = self.particles[2:] self.target = self.particles[0] self.predator = self.particles[1] self.boids['position'] = np.random.uniform(-0.25, +0.25, (n, 3)) self.boids['velocity'] = np.random.uniform(-0.00, +0.00, (n, 3)) self.boids['size'] = 4*ps self.boids['color'] = 1, 1, 1, 1 self.target['size'] = 16*ps self.target['color'][:] = 1, 1, 0, 1 self.predator['size'] = 16*ps self.predator['color'][:] = 1, 0, 0, 1 self.target['position'][:] = 0.25, 0.0, 0 # Time self._t = time.time() self._pos = 0.0, 0.0 self._button = None width, height = self.physical_size gloo.set_viewport(0, 0, width, height) # Create program self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) # Create vertex buffers self.vbo_position = gloo.VertexBuffer(self.particles['position'] .copy()) self.vbo_color = gloo.VertexBuffer(self.particles['color'].copy()) self.vbo_size = gloo.VertexBuffer(self.particles['size'].copy()) # Bind vertex buffers self.program['color'] = self.vbo_color self.program['size'] = self.vbo_size self.program['position'] = self.vbo_position gloo.set_state(clear_color=(0, 0, 0, 1), blend=True, blend_func=('src_alpha', 'one')) self._timer = app.Timer('auto', connect=self.update, start=True) self.show() def on_resize(self, event): width, height = event.physical_size gloo.set_viewport(0, 0, width, height) def on_mouse_press(self, event): self._button = event.button self.on_mouse_move(event) def on_mouse_release(self, event): self._button = None self.on_mouse_move(event) def on_mouse_move(self, event): if not self._button: return w, h = self.size x, y = event.pos sx = 2 * x / float(w) - 1.0 sy = - (2 * y / float(h) - 1.0) if self._button == 1: self.target['position'][:] = sx, sy, 0 elif self._button == 2: self.predator['position'][:] = sx, sy, 0 def on_draw(self, event): gloo.clear() # Draw self.program.draw('points') # Next iteration self._t = self.iteration(time.time() - self._t) def iteration(self, dt): t = self._t t += 0.5 * dt # self.target[...] = np.array([np.sin(t),np.sin(2*t),np.cos(3*t)])*.1 t += 0.5 * dt # self.predator[...] = np.array([np.sin(t),np.sin(2*t),np.cos(3*t)])*.2 self.boids['position_2'] = self.boids['position_1'] self.boids['position_1'] = self.boids['position'] n = len(self.boids) P = self.boids['position'] V = self.boids['velocity'] # Cohesion: steer to move toward the average position of local # flockmates C = -(P - P.sum(axis=0) / n) # Alignment: steer towards the average heading of local flockmates A = -(V - V.sum(axis=0) / n) # Repulsion: steer to avoid crowding local flockmates D, idxs = cKDTree(P).query(P, 5) M = np.repeat(D < 0.05, 3, axis=1).reshape(n, 5, 3) Z = np.repeat(P, 5, axis=0).reshape(n, 5, 3) R = -((P[idxs] - Z) * M).sum(axis=1) # Target : Follow target T = self.target['position'] - P # Predator : Move away from predator dP = P - self.predator['position'] D = np.maximum(0, 0.3 - np.sqrt(dP[:, 0] ** 2 + dP[:, 1] ** 2 + dP[:, 2] ** 2)) D = np.repeat(D, 3, axis=0).reshape(n, 3) dP *= D # self.boids['velocity'] += 0.0005*C + 0.01*A + 0.01*R + # 0.0005*T + 0.0025*dP self.boids['velocity'] += 0.0005 * C + 0.01 * \ A + 0.01 * R + 0.0005 * T + 0.025 * dP self.boids['position'] += self.boids['velocity'] self.vbo_position.set_data(self.particles['position'].copy()) return t if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/demo/gloo/atom.py0000644000175000017510000001222514507104216017314 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- # Author: Nicolas P .Rougier # Date: 06/03/2014 # Abstract: Fake electrons orbiting # Keywords: Sprites, atom, particles # ----------------------------------------------------------------------------- import numpy as np from vispy import gloo from vispy import app from vispy.util.transforms import perspective, translate, rotate # Create vertices n, p = 100, 150 data = np.zeros(p * n, [('a_position', np.float32, 2), ('a_color', np.float32, 4), ('a_rotation', np.float32, 4)]) trail = .5 * np.pi data['a_position'][:, 0] = np.resize(np.linspace(0, trail, n), p * n) data['a_position'][:, 0] += np.repeat(np.random.uniform(0, 2 * np.pi, p), n) data['a_position'][:, 1] = np.repeat(np.linspace(0, 2 * np.pi, p), n) data['a_color'] = 1, 1, 1, 1 data['a_color'] = np.repeat( np.random.uniform(0.75, 1.00, (p, 4)).astype(np.float32), n, axis=0) data['a_color'][:, 3] = np.resize(np.linspace(0, 1, n), p * n) data['a_rotation'] = np.repeat( np.random.uniform(0, 2 * np.pi, (p, 4)).astype(np.float32), n, axis=0) vert = """ #version 120 uniform mat4 u_model; uniform mat4 u_view; uniform mat4 u_projection; uniform float u_size; uniform float u_clock; attribute vec2 a_position; attribute vec4 a_color; attribute vec4 a_rotation; varying vec4 v_color; mat4 build_rotation(vec3 axis, float angle) { axis = normalize(axis); float s = sin(angle); float c = cos(angle); float oc = 1.0 - c; return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, 0.0, 0.0, 0.0, 1.0); } void main (void) { v_color = a_color; float x0 = 1.5; float z0 = 0.0; float theta = a_position.x + u_clock; float x1 = x0*cos(theta) + z0*sin(theta); float y1 = 0.0; float z1 = (z0*cos(theta) - x0*sin(theta))/2.0; mat4 R = build_rotation(a_rotation.xyz, a_rotation.w); gl_Position = u_projection * u_view * u_model * R * vec4(x1,y1,z1,1); gl_PointSize = 8.0 * u_size * sqrt(v_color.a); } """ frag = """ #version 120 varying vec4 v_color; varying float v_size; void main() { float d = 2*(length(gl_PointCoord.xy - vec2(0.5,0.5))); gl_FragColor = vec4(v_color.rgb, v_color.a*(1-d)); } """ # ------------------------------------------------------------ Canvas class --- class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 800)) self.title = "Atom [zoom with mouse scroll]" self.translate = 6.5 self.program = gloo.Program(vert, frag) self.view = translate((0, 0, -self.translate)) self.model = np.eye(4, dtype=np.float32) self.projection = np.eye(4, dtype=np.float32) self.apply_zoom() self.program.bind(gloo.VertexBuffer(data)) self.program['u_model'] = self.model self.program['u_view'] = self.view self.program['u_size'] = 5 / self.translate self.theta = 0 self.phi = 0 self.clock = 0 self.stop_rotation = False gloo.set_state('translucent', depth_test=False) self.program['u_clock'] = 0.0 self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_key_press(self, event): if event.text == ' ': self.stop_rotation = not self.stop_rotation def on_timer(self, event): if not self.stop_rotation: self.theta += .05 self.phi += .05 self.model = np.dot(rotate(self.theta, (0, 0, 1)), rotate(self.phi, (0, 1, 0))) self.program['u_model'] = self.model self.clock += np.pi / 100 self.program['u_clock'] = self.clock self.update() def on_resize(self, event): self.apply_zoom() def on_mouse_wheel(self, event): self.translate += event.delta[1] self.translate = max(2, self.translate) self.view = translate((0, 0, -self.translate)) self.program['u_view'] = self.view self.program['u_size'] = 5 / self.translate self.update() def on_draw(self, event): gloo.clear('black') self.program.draw('points') def apply_zoom(self): width, height = self.physical_size gloo.set_viewport(0, 0, width, height) self.projection = perspective(45.0, width / float(height), 1.0, 1000.0) self.program['u_projection'] = self.projection if __name__ == '__main__': c = Canvas() app.run() vispy-0.14.1/examples/collections/0000755000175000017510000000000014507104250016430 5ustar nileshnileshvispy-0.14.1/examples/collections/triangle_collection.py0000755000175000017510000000326614507104216023036 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip import numpy as np from vispy import app, gloo from vispy.geometry import Triangulation from vispy.visuals.collections import PathCollection, TriangleCollection canvas = app.Canvas(size=(800, 800), show=True, keys='interactive') gloo.set_viewport(0, 0, canvas.size[0], canvas.size[1]) gloo.set_state("translucent", depth_test=False) def triangulate(P): n = len(P) S = np.repeat(np.arange(n + 1), 2)[1:-1] S[-2:] = n - 1, 0 S = S.reshape(len(S) // 2, 2) T = Triangulation(P[:, :2], S) T.triangulate() points = T.pts triangles = T.tris.ravel() P = np.zeros((len(points), 3), dtype=np.float32) P[:, :2] = points return P, triangles def star(inner=0.5, outer=1.0, n=5): R = np.array([inner, outer] * n) T = np.linspace(0, 2 * np.pi, 2 * n, endpoint=False) P = np.zeros((2 * n, 3)) P[:, 0] = R * np.cos(T) P[:, 1] = R * np.sin(T) return P paths = PathCollection("agg", color='shared') triangles = TriangleCollection("raw", color='shared') P0 = star() P1, tris = triangulate(P0) n = 1000 for i in range(n): c = i / float(n) x, y = np.random.uniform(-1, +1, 2) s = 25 / 800.0 triangles.append(P1 * s + (x, y, i / 1000.), tris, color=(1, 0, 0, .5)) paths.append( P0 * s + (x, y, (i - 1) / 1000.), closed=True, color=(0, 0, 0, .5)) paths["linewidth"] = 1.0 paths['viewport'] = 0, 0, 800, 800 @canvas.connect def on_draw(e): gloo.clear('white') triangles.draw() paths.draw() @canvas.connect def on_resize(event): width, height = event.size gloo.set_viewport(0, 0, width, height) paths['viewport'] = 0, 0, width, height app.run() vispy-0.14.1/examples/collections/tiger.py0000755000175000017510000000462614507104216020131 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) 2014, Nicolas P. Rougier. All Rights Reserved. # Distributed under the (new) BSD License. # ----------------------------------------------------------------------------- from vispy import app, gloo from vispy.util import load_data_file from vispy.util.svg import Document from vispy.visuals.collections import PathCollection, PolygonCollection from vispy.visuals.transforms import PanZoomTransform path = load_data_file('tiger/tiger.svg') tiger = Document(path) width, height = int(tiger.viewport.width), int(tiger.viewport.height) canvas = app.Canvas(size=(width, height), show=True, keys='interactive') gloo.set_viewport(0, 0, width, height) gloo.set_state("translucent", depth_test=True) panzoom = PanZoomTransform(canvas, aspect=1.0) paths = PathCollection( "agg+", linewidth='shared', color="shared", transform=panzoom) polys = PolygonCollection("agg", transform=panzoom) paths.update.connect(canvas.update) z = 0 for path in tiger.paths: for vertices, closed in path.vertices: vertices = 2 * (vertices / (width, height, 1)) - 1 vertices[:, 1] = -vertices[:, 1] if len(vertices) < 3: continue if path.style.stroke is not None: vertices[:, 2] = z - 0.5 / 1000. if path.style.stroke_width: stroke_width = path.style.stroke_width.value else: stroke_width = 2.0 paths.append(vertices, closed=closed, color=path.style.stroke.rgba, linewidth=stroke_width) if path.style.fill is not None: if path.style.stroke is None: vertices[:, 2] = z - 0.25 / 1000. paths.append(vertices, closed=closed, color=path.style.fill.rgba, linewidth=1) vertices[:, 2] = z polys.append(vertices, color=path.style.fill.rgba) z -= 1 / 1000. paths["linewidth"] = 1.0 paths['viewport'] = 0, 0, 800, 800 @canvas.connect def on_draw(e): gloo.clear('white') polys.draw() paths.draw() @canvas.connect def on_resize(e): width, height = e.size[0], e.size[1] gloo.set_viewport(0, 0, width, height) paths['viewport'] = 0, 0, width, height if __name__ == '__main__': canvas.show() app.run() vispy-0.14.1/examples/collections/segment_collection.py0000755000175000017510000000177114507104216022672 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip import numpy as np from vispy import app, gloo from vispy.visuals.collections import SegmentCollection c = app.Canvas(size=(1200, 600), show=True, keys='interactive') gloo.set_viewport(0, 0, c.size[0], c.size[1]) gloo.set_state("translucent", depth_test=False) segments = SegmentCollection("agg", linewidth="local") n = 100 P0 = np.dstack( (np.linspace(100, 1100, n), np.ones(n) * 50, np.zeros(n))).reshape(n, 3) P0 = 2 * (P0 / (1200, 600, 1)) - 1 P1 = np.dstack( (np.linspace(110, 1110, n), np.ones(n) * 550, np.zeros(n))).reshape(n, 3) P1 = 2 * (P1 / (1200, 600, 1)) - 1 segments.append(P0, P1, linewidth=np.linspace(1, 8, n)) segments['antialias'] = 1 segments['viewport'] = 0, 0, 1200, 600 @c.connect def on_draw(e): gloo.clear('white') segments.draw() @c.connect def on_resize(e): width, height = e.size[0], e.size[1] gloo.set_viewport(0, 0, width, height) segments['viewport'] = 0, 0, width, height app.run() vispy-0.14.1/examples/collections/polygon_collection.py0000755000175000017510000000240614507104216022713 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip import numpy as np from vispy import app, gloo from vispy.visuals.collections import PathCollection, PolygonCollection canvas = app.Canvas(size=(800, 800), show=True, keys='interactive') gloo.set_viewport(0, 0, canvas.size[0], canvas.size[1]) gloo.set_state("translucent", depth_test=False) def star(inner=0.5, outer=1.0, n=5): R = np.array([inner, outer] * n) T = np.linspace(0, 2 * np.pi, 2 * n, endpoint=False) P = np.zeros((2 * n, 3)) P[:, 0] = R * np.cos(T) P[:, 1] = R * np.sin(T) return P paths = PathCollection("agg", color='shared') polys = PolygonCollection("raw", color='shared') P = star() n = 100 for i in range(n): c = i / float(n) x, y = np.random.uniform(-1, +1, 2) s = 100 / 800.0 polys.append(P * s + (x, y, i / 1000.), color=(1, 0, 0, .5)) paths.append( P * s + (x, y, (i - 1) / 1000.), closed=True, color=(0, 0, 0, .5)) paths["linewidth"] = 1.0 paths['viewport'] = 0, 0, 800, 800 @canvas.connect def on_draw(e): gloo.clear('white') polys.draw() paths.draw() @canvas.connect def on_resize(event): width, height = event.size gloo.set_viewport(0, 0, width, height) paths['viewport'] = 0, 0, width, height app.run() vispy-0.14.1/examples/collections/point_collection.py0000755000175000017510000000163614507104216022361 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip import sys import numpy as np from vispy import app, gloo from vispy.visuals.collections import PointCollection from vispy.visuals.transforms import PanZoomTransform canvas = app.Canvas(size=(800, 600), show=True, keys='interactive') gloo.set_viewport(0, 0, canvas.size[0], canvas.size[1]) gloo.set_state("translucent", depth_test=False) panzoom = PanZoomTransform(canvas) points = PointCollection("agg", color="shared", transform=panzoom) points.append(np.random.normal(0.0, 0.5, (10000, 3)), itemsize=5000) points["color"] = (1, 0, 0, 1), (0, 0, 1, 1) points.update.connect(canvas.update) @canvas.connect def on_draw(event): gloo.clear('white') points.draw() @canvas.connect def on_resize(event): width, height = event.size gloo.set_viewport(0, 0, width, height) if __name__ == '__main__' and sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/collections/path_collection.py0000755000175000017510000000225514507104216022162 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip import numpy as np from vispy import app, gloo from vispy.visuals.collections import PathCollection c = app.Canvas(size=(800, 800), show=True, keys='interactive') gloo.set_viewport(0, 0, c.size[0], c.size[1]) gloo.set_state("translucent", depth_test=False) def star(inner=0.5, outer=1.0, n=5): R = np.array([inner, outer] * n) T = np.linspace(0, 2 * np.pi, 2 * n, endpoint=False) P = np.zeros((2 * n, 3)) P[:, 0] = R * np.cos(T) P[:, 1] = R * np.sin(T) return P n = 2500 S = star(n=5) P = np.tile(S.ravel(), n).reshape(n, len(S), 3) P *= np.random.uniform(5, 10, n)[:, np.newaxis, np.newaxis] P[:, :, :2] += np.random.uniform(0, 800, (n, 2))[:, np.newaxis, :] P = P.reshape(n * len(S), 3) P = 2 * (P / (800, 800, 1)) - 1 paths = PathCollection(mode="agg") paths.append(P, closed=True, itemsize=len(S)) paths["linewidth"] = 1.0 paths['viewport'] = 0, 0, 800, 800 @c.connect def on_draw(e): gloo.clear('white') paths.draw() @c.connect def on_resize(e): width, height = e.size[0], e.size[1] gloo.set_viewport(0, 0, width, height) paths['viewport'] = 0, 0, width, height app.run() vispy-0.14.1/examples/collections/chloropleth.py0000755000175000017510000000412514507104216021334 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip import json import numpy as np from vispy import app, gloo from vispy.util import load_data_file from vispy.visuals.collections import PathCollection, PolygonCollection from vispy.visuals.transforms import PanZoomTransform path = load_data_file('uscounties/uscounties.geojson') with open(path, 'r') as f: geo = json.load(f) def unique_rows(data): v = data.view(data.dtype.descr * data.shape[1]) _, idx = np.unique(v, return_index=True) return data[np.sort(idx)] def add(P, color): P = np.array(P) if len(P) < 2: return P = np.array(P) / 20.0 + (5, -2) p = np.zeros((len(P), 3)) p[:, :2] = P p = unique_rows(p) if len(p) > 1: paths.append(p, closed=True) if len(p) > 2: polys.append(p, color=color) # Create the canvas. canvas = app.Canvas(size=(800, 800), keys='interactive') gloo.set_viewport(0, 0, canvas.size[0], canvas.size[1]) gloo.set_state("translucent", depth_test=False) panzoom = PanZoomTransform(canvas, aspect=1) paths = PathCollection(mode="agg+", color="global", transform=panzoom) polys = PolygonCollection("raw", color="local", transform=panzoom) paths.update.connect(canvas.update) for feature in geo["features"]: if feature["geometry"]["type"] == 'Polygon': path = feature["geometry"]["coordinates"] rgba = np.random.uniform(0.5, .8, 4) rgba[3] = 1 add(path[0], color=rgba) elif feature["geometry"]["type"] == 'MultiPolygon': coordinates = feature["geometry"]["coordinates"] for path in coordinates: rgba = np.random.uniform(0.5, .8, 4) rgba[3] = 1 add(path[0], color=rgba) paths["color"] = 0, 0, 0, 1 paths["linewidth"] = 1.0 paths['viewport'] = 0, 0, 800, 800 @canvas.connect def on_draw(e): gloo.clear('white') polys.draw() paths.draw() @canvas.connect def on_resize(event): width, height = event.size gloo.set_viewport(0, 0, width, height) paths['viewport'] = 0, 0, width, height if __name__ == '__main__': canvas.show() app.run() vispy-0.14.1/examples/benchmark/0000755000175000017510000000000014507104250016044 5ustar nileshnileshvispy-0.14.1/examples/benchmark/simple_vispy.py0000755000175000017510000000150214507104216021144 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- import sys from vispy import app from vispy.gloo import clear # app.use_app('pyqt5') # or pyside, glut, pyglet, sdl2, etc. canvas = app.Canvas(size=(512, 512), title="Do nothing benchmark (vispy)", keys='interactive') @canvas.connect def on_draw(event): clear(color=True, depth=True) canvas.update() # Draw frames as fast as possible if __name__ == '__main__': canvas.show() canvas.measure_fps() if sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/benchmark/simple_glut.py0000755000175000017510000000254214507104216020752 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from vispy.gloo import gl def on_display(): gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) glut.glutSwapBuffers() def on_keyboard(key, x, y): if key == '\033': sys.exit() def on_idle(): global t, t0, frames t = glut.glutGet(glut.GLUT_ELAPSED_TIME) frames = frames + 1 elapsed = (t - t0) / 1000.0 if elapsed > 2.5: print("FPS : %.2f (%d frames in %.2f second)" % (frames / elapsed, frames, elapsed)) t0, frames = t, 0 glut.glutPostRedisplay() if __name__ == '__main__': import sys import OpenGL.GLUT as glut glut.glutInit(sys.argv) glut.glutInitDisplayMode( glut.GLUT_DOUBLE | glut.GLUT_RGB | glut.GLUT_DEPTH) glut.glutInitWindowSize(512, 512) glut.glutCreateWindow("Do nothing benchmark (GLUT)") glut.glutDisplayFunc(on_display) glut.glutKeyboardFunc(on_keyboard) t0, frames, t = glut.glutGet(glut.GLUT_ELAPSED_TIME), 0, 0 glut.glutIdleFunc(on_idle) glut.glutMainLoop() vispy-0.14.1/examples/benchmark/scene_test_2.py0000644000175000017510000001357414507104216021007 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Compare an optimal plot grid implementation to the same functionality provided by scenegraph. Use --vispy-cprofile to see an overview of time spent in all functions. Use util.profiler and --vispy-profile=ClassName.method_name for more directed profiling measurements. """ from __future__ import division import numpy as np from vispy import gloo, app, scene, visuals from vispy.util.profiler import Profiler class GridCanvas(app.Canvas): def __init__(self, cells, **kwargs): m, n = (10, 10) self.grid_size = (m, n) self.cells = cells super(GridCanvas, self).__init__(keys='interactive', show=True, **kwargs) def on_initialize(self, event): self.context.set_state(clear_color='black', blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) def on_mouse_move(self, event): if event.is_dragging and not event.modifiers: dx = (event.pos - event.last_event.pos) * [1, -1] i, j = event.press_event.pos / self.size m, n = len(self.cells), len(self.cells[0]) cell = self.cells[int(i*m)][n - 1 - int(j*n)] if event.press_event.button == 1: offset = (np.array(cell.offset) + (dx / (np.array(self.size) / [m, n])) * (2 / np.array(cell.scale))) cell.set_transform(offset, cell.scale) else: cell.set_transform(cell.offset, cell.scale * 1.05 ** dx) self.update() def on_draw(self, event): prof = Profiler() # noqa self.context.clear() M = len(self.cells) N = len(self.cells[0]) w, h = self.size for i in range(M): for j in range(N): self.context.set_viewport(w*i/M, h*j/N, w/M, h/N) self.cells[i][j].draw() vert = """ attribute vec2 pos; uniform vec2 offset; uniform vec2 scale; void main() { gl_Position = vec4((pos + offset) * scale, 0, 1); } """ frag = """ void main() { gl_FragColor = vec4(1, 1, 1, 0.5); } """ class Line(object): def __init__(self, data, offset, scale): self.data = gloo.VertexBuffer(data) self.program = gloo.Program(vert, frag) self.program['pos'] = self.data self.set_transform(offset, scale) def set_transform(self, offset, scale): self.offset = offset self.scale = scale self.program['offset'] = self.offset self.program['scale'] = self.scale def draw(self): self.program.draw('line_strip') scales = np.array((1.9 / 100., 2. / 10.)) class VisualCanvas(app.Canvas): def __init__(self, vis, **kwargs): super(VisualCanvas, self).__init__(keys='interactive', show=True, **kwargs) m, n = (10, 10) self.grid_size = (m, n) self.visuals = vis def on_initialize(self, event): self.context.set_state(clear_color='black', blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) def on_mouse_move(self, event): if event.is_dragging and not event.modifiers: dx = np.array(event.pos - event.last_event.pos) x, y = event.press_event.pos / self.size m, n = self.grid_size i, j = int(x*m), n - 1 - int(y*n) v = self.visuals[i][j] tr = v.transform if event.press_event.button == 1: tr.translate = np.array(tr.translate)[:2] + \ dx * scales * (1, -1) else: tr.scale = tr.scale[:2] * 1.05 ** (dx * (1, -1)) self.update() def on_draw(self, event): prof = Profiler() # noqa self.context.clear() M, N = self.grid_size w, h = self.size for i in range(M): for j in range(N): self.context.set_viewport(w*i/M, h*j/N, w/M, h/N) self.visuals[i][j].draw() if __name__ == '__main__': M, N = (10, 10) data = np.empty((10000, 2), dtype=np.float32) data[:, 0] = np.linspace(0, 100, data.shape[0]) data[:, 1] = np.random.normal(size=data.shape[0]) # Optimized version cells = [] for i in range(M): row = [] cells.append(row) for j in range(N): row.append(Line(data, offset=(-50, 0), scale=scales)) gcanvas = GridCanvas(cells, position=(400, 300), size=(800, 600), title="GridCanvas") # Visual version vlines = [] for i in range(M): row = [] vlines.append(row) for j in range(N): v = visuals.LineVisual(pos=data, color='w', method='gl') v.transform = visuals.transforms.STTransform( translate=(-1, 0), scale=scales) row.append(v) vcanvas = VisualCanvas(vlines, position=(400, 300), size=(800, 600), title="VisualCanvas") # Scenegraph version scanvas = scene.SceneCanvas(show=True, keys='interactive', title="SceneCanvas") scanvas.size = 800, 600 grid = scanvas.central_widget.add_grid(margin=0) lines = [] for i in range(10): lines.append([]) for j in range(10): vb = grid.add_view(camera='panzoom', row=i, col=j) vb.camera.set_range([0, 100], [-5, 5], margin=0) line = scene.visuals.Line(pos=data, color='w', method='gl') vb.add(line) scanvas.show() import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/benchmark/scene_test_1.py0000644000175000017510000002505314507104216021001 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Compare an optimal pan/zoom implementation to the same functionality provided by scenegraph. Use --vispy-cprofile to see an overview of time spent in all functions. Use util.profiler and --vispy-profile=ClassName.method_name for more directed profiling measurements. """ import numpy as np import math from vispy import gloo, app, scene from vispy.visuals import Visual from vispy.visuals.shaders import Function, Variable from vispy.visuals.transforms import TransformSystem, BaseTransform from vispy.util.profiler import Profiler class PanZoomTransform(BaseTransform): glsl_map = """ vec4 pz_transform_map(vec4 pos) { return vec4($zoom * (pos.xy + $pan), 0., 1.); } """ glsl_imap = """ vec4 pz_transform_imap(vec2 pos) { return vec4(pos / $zoom - $pan, 0, 1); } """ Linear = True Orthogonal = True NonScaling = False Isometric = False def __init__(self): super(PanZoomTransform, self).__init__() self._pan = None self._zoom = None @property def pan(self): if isinstance(self._pan, Variable): return np.array(self._pan.value, dtype=np.float32) else: raise NotImplementedError() @pan.setter def pan(self, value): if isinstance(value, Variable): self._pan = value self._shader_map['pan'] = self._pan elif isinstance(self._pan, Variable): self._pan.value = value else: raise NotImplementedError() @property def zoom(self): if isinstance(self._zoom, Variable): return np.array(self._zoom.value, dtype=np.float32) else: raise NotImplementedError() @zoom.setter def zoom(self, value): if isinstance(value, Variable): self._zoom = value self._shader_map['zoom'] = self._zoom elif isinstance(self._zoom, Variable): self._zoom.value = value else: raise NotImplementedError() def map(self, coords): if not isinstance(coords, np.ndarray): coords = np.array(coords) return self.zoom[None, :] * (coords + self.pan[None, :]) def imap(self, coords): if not isinstance(coords, np.ndarray): coords = np.array(coords) return (coords / self.zoom[None, :]) - self.pan[None, :] class PanZoomCanvas(app.Canvas): def __init__(self, **kwargs): super(PanZoomCanvas, self).__init__(keys='interactive', **kwargs) self._visuals = [] self._pz = PanZoomTransform() self._pz.pan = Variable('uniform vec2 u_pan', (0, 0)) self._pz.zoom = Variable('uniform vec2 u_zoom', (1, 1)) self.width, self.height = self.size self.context.set_viewport(0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_state(clear_color='black', blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self._tr = TransformSystem(self) self.show() def on_resize(self, event): self.width, self.height = event.size self.context.set_viewport(0, 0, event.physical_size[0], event.physical_size[1]) def _normalize(self, x_y): x, y = x_y w, h = float(self.width), float(self.height) return x/(w/2.)-1., y/(h/2.)-1. def bounds(self): pan_x, pan_y = self._pz.pan zoom_x, zoom_y = self._pz.zoom xmin = -1 / zoom_x - pan_x xmax = +1 / zoom_x - pan_x ymin = -1 / zoom_y - pan_y ymax = +1 / zoom_y - pan_y return (xmin, ymin, xmax, ymax) def on_mouse_move(self, event): if event.is_dragging and not event.modifiers: x0, y0 = self._normalize(event.press_event.pos) x1, y1 = self._normalize(event.last_event.pos) x, y = self._normalize(event.pos) dx, dy = x - x1, -(y - y1) button = event.press_event.button pan_x, pan_y = self._pz.pan zoom_x, zoom_y = self._pz.zoom if button == 1: self._pz.pan = (pan_x + dx/zoom_x, pan_y + dy/zoom_y) elif button == 2: zoom_x_new, zoom_y_new = (zoom_x * math.exp(2.5 * dx), zoom_y * math.exp(2.5 * dy)) self._pz.zoom = (zoom_x_new, zoom_y_new) self._pz.pan = (pan_x - x0 * (1./zoom_x - 1./zoom_x_new), pan_y + y0 * (1./zoom_y - 1./zoom_y_new)) self.update() def on_mouse_wheel(self, event): prof = Profiler() # noqa if not event.modifiers: dx = np.sign(event.delta[1])*.05 x0, y0 = self._normalize(event.pos) pan_x, pan_y = self._pz.pan zoom_x, zoom_y = self._pz.zoom zoom_x_new, zoom_y_new = (zoom_x * math.exp(2.5 * dx), zoom_y * math.exp(2.5 * dx)) self._pz.zoom = (zoom_x_new, zoom_y_new) self._pz.pan = (pan_x - x0 * (1./zoom_x - 1./zoom_x_new), pan_y + y0 * (1./zoom_y - 1./zoom_y_new)) self.update() def on_key_press(self, event): if event.key == 'R': self._pz.zoom = (1., 1.) self._pz.pan = (0., 0.) self.update() def add_visual(self, name, value): value.shared_program.vert['transform'] = self._pz value.events.update.connect(self.update) self._visuals.append(value) def __setattr__(self, name, value): if isinstance(value, Visual): self.add_visual(name, value) super(PanZoomCanvas, self).__setattr__(name, value) @property def visuals(self): return self._visuals def on_draw(self, event): prof = Profiler() self.context.clear() for visual in self.visuals: visual.draw() prof('draw visual') X_TRANSFORM = """ float get_x(float x_index) { // 'x_index' is between 0 and nsamples. return -1. + 2. * x_index / (float($nsamples) - 1.); } """ Y_TRANSFORM = """ float get_y(float y_index, float sample) { // 'y_index' is between 0 and nsignals. float a = float($scale) / float($nsignals); float b = -1. + 2. * (y_index + .5) / float($nsignals); return a * sample + b; } """ DISCRETE_CMAP = """ vec3 get_color(float index) { float x = (index + .5) / float($ncolors); return texture2D($colormap, vec2(x, .5)).rgb; } """ class SignalsVisual(Visual): VERTEX_SHADER = """ attribute float a_position; attribute vec2 a_index; varying vec2 v_index; uniform float u_nsignals; uniform float u_nsamples; void main() { vec4 position = vec4($get_x(a_index.y), $get_y(a_index.x, a_position), 0., 1.); gl_Position = $transform(position); v_index = a_index; } """ FRAGMENT_SHADER = """ varying vec2 v_index; void main() { gl_FragColor = vec4($get_color(v_index.x), 1.); // Discard vertices between two signals. if ((fract(v_index.x) > 0.)) discard; } """ def __init__(self, data): Visual.__init__(self, self.VERTEX_SHADER, self.FRAGMENT_SHADER) nsignals, nsamples = data.shape # nsamples, nsignals = data.shape self._data = data a_index = np.c_[np.repeat(np.arange(nsignals), nsamples), np.tile(np.arange(nsamples), nsignals) ].astype(np.float32) # Doesn't seem to work nor to be very efficient. # indices = nsignals * np.arange(nsamples) # indices = indices[None, :] + np.arange(nsignals)[:, None] # indices = indices.flatten().astype(np.uint32) # self._ibuffer = gloo.IndexBuffer(indices) self._buffer = gloo.VertexBuffer(data.reshape(-1, 1)) self.shared_program['a_position'] = self._buffer self.shared_program['a_index'] = a_index x_transform = Function(X_TRANSFORM) x_transform['nsamples'] = nsamples self.shared_program.vert['get_x'] = x_transform y_transform = Function(Y_TRANSFORM) y_transform['scale'] = Variable('uniform float u_signal_scale', 1.) y_transform['nsignals'] = nsignals self.shared_program.vert['get_y'] = y_transform self._y_transform = y_transform colormap = Function(DISCRETE_CMAP) rng = np.random.RandomState(0) cmap = rng.uniform(size=(1, nsignals, 3), low=.5, high=.9).astype(np.float32) tex = gloo.Texture2D((cmap * 255).astype(np.uint8)) colormap['colormap'] = Variable('uniform sampler2D u_colormap', tex) colormap['ncolors'] = nsignals self.shared_program.frag['get_color'] = colormap self._draw_mode = 'line_strip' self.set_gl_state('translucent', depth_test=False) @property def data(self): return self._data @data.setter def data(self, value): self._data = value self._buffer.set_subdata(value.reshape(-1, 1)) self.update() @property def signal_scale(self): return self._y_transform['scale'].value @signal_scale.setter def signal_scale(self, value): self._y_transform['scale'].value = value self.update() def _prepare_draw(self, view=None): """This method is called immediately before each draw. The *view* argument indicates which view is about to be drawn. """ pass Signals = scene.visuals.create_visual_node(SignalsVisual) if __name__ == '__main__': data = np.random.normal(size=(128, 1000)).astype(np.float32) pzcanvas = PanZoomCanvas(position=(400, 300), size=(800, 600), title="PanZoomCanvas", vsync=False) visual = SignalsVisual(data) pzcanvas.add_visual('signal', visual) scanvas = scene.SceneCanvas(show=True, keys='interactive', title="SceneCanvas", vsync=False) view = scanvas.central_widget.add_view('panzoom') svisual = Signals(data, parent=view.scene) view.camera.set_range([-0.9, 0.9], [-0.9, 0.9]) import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/0000755000175000017510000000000014507104250015356 5ustar nileshnileshvispy-0.14.1/examples/basics/visuals/0000755000175000017510000000000014507104250017044 5ustar nileshnileshvispy-0.14.1/examples/basics/visuals/windbarb_quiver.py0000644000175000017510000000560014507104216022604 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Display Windbarbs. """ import itertools import numpy as np from vispy import app from vispy.visuals import WindbarbVisual from vispy.visuals.transforms import NullTransform class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title="Windbarb plot", keys="interactive", size=(830, 430)) self.windbarb_length = 25 self.grid_spacing = 50 self.grid_coords = None self.line_vertices = None self.last_mouse = (0, 0) self.generate_grid() direction_vectors = (self.grid_coords - self.last_mouse).astype( np.float32) direction_vectors[:] /= 10 direction_vectors[:, 1] *= -1 self.visual = WindbarbVisual(pos=self.grid_coords, wind=direction_vectors, trig=False, edge_color='black', face_color='black', size=self.windbarb_length) self.visual.events.update.connect(lambda evt: self.update()) self.visual.transform = NullTransform() self.show() def generate_grid(self): n = self.grid_spacing / 2. num_cols = int(self.physical_size[0] / n * 2) num_rows = int(self.physical_size[1] / n * 2) coords = [] # Generate grid for i, j in itertools.product(range(num_rows), range(num_cols)): x = n + (n * 2 * j) y = n + (n * 2 * i) coords.append((x, y)) self.grid_coords = np.array(coords) def on_resize(self, event): self.generate_grid() self.rotate_arrows(np.array(self.last_mouse)) vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.visual.transforms.configure(canvas=self, viewport=vp) def rotate_arrows(self, point_towards): direction_vectors = (self.grid_coords - point_towards).astype( np.float32) direction_vectors[:] /= 10 direction_vectors[:, 1] *= -1 self.visual.set_data( pos=self.grid_coords, wind=direction_vectors, size=self.windbarb_length, ) def on_mouse_move(self, event): self.last_mouse = event.pos self.rotate_arrows(np.array(event.pos)) def on_draw(self, event): self.context.clear(color='white') self.visual.draw() if __name__ == '__main__': canvas = Canvas() canvas.measure_fps() app.run() vispy-0.14.1/examples/basics/visuals/visual_filters.py0000644000175000017510000000650314507104216022457 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 1 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Demonstration of Line visual with arbitrary transforms. Several Line visuals are displayed that all have the same vertex position information, but different transformations. """ import numpy as np from vispy import app, visuals from vispy.visuals.transforms import STTransform from vispy.visuals.filters import Clipper, Alpha, ColorFilter from vispy.visuals.shaders import Function from vispy.geometry import Rect # vertex positions of data to draw N = 400 pos = np.zeros((N, 2), dtype=np.float32) pos[:, 0] = np.linspace(0, 350, N) pos[:, 1] = np.random.normal(size=N, scale=20, loc=0) class Canvas(app.Canvas): def __init__(self): # Define several Line visuals that use the same position data self.lines = [visuals.LineVisual(pos=pos) for i in range(6)] self.lines[0].transform = STTransform(translate=(0, 50)) # Clipping filter (requires update when window is resized) self.lines[1].transform = STTransform(translate=(400, 50)) self.clipper = Clipper() self.lines[1].attach(self.clipper) # Opacity filter self.lines[2].transform = STTransform(translate=(0, 150)) self.lines[2].attach(Alpha(0.4)) # Color filter (for anaglyph stereo) self.lines[3].transform = STTransform(translate=(400, 150)) self.lines[3].attach(ColorFilter([1, 0, 0, 1])) # A custom filter class Hatching(object): def __init__(self): self.shader = Function(""" void screen_filter() { float f = gl_FragCoord.x * 0.4 + gl_FragCoord.y; f = mod(f, 20.0); if( f < 5.0 ) { discard; } if( f < 20.0 ) { gl_FragColor.g = gl_FragColor.g + 0.05 * (20.0-f); } } """) def _attach(self, visual): visual._get_hook('frag', 'post').add(self.shader()) self.lines[4].transform = STTransform(translate=(0, 250)) self.lines[4].attach(Hatching()) # Mixing filters self.lines[5].transform = STTransform(translate=(400, 250)) self.lines[5].attach(ColorFilter([1, 0, 0, 1])) self.lines[5].attach(Hatching()) app.Canvas.__init__(self, keys='interactive', size=(800, 800)) self.show(True) def on_draw(self, ev): self.context.clear('black', depth=True) for line in self.lines: line.draw() def on_resize(self, event): vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for line in self.lines: line.transforms.configure(canvas=self, viewport=vp) # Need to update clipping boundaries if the window resizes. tr = self.lines[1].transforms.get_transform('visual', 'framebuffer') self.clipper.bounds = tr.map(Rect(100, -20, 200, 40)) if __name__ == '__main__': win = Canvas() import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/tube.py0000644000175000017510000000436514507104216020367 0ustar nileshnilesh""" Demonstration of Tube """ import sys from vispy import scene from vispy.geometry.torusknot import TorusKnot from colorsys import hsv_to_rgb import numpy as np canvas = scene.SceneCanvas(keys='interactive') view = canvas.central_widget.add_view() points1 = TorusKnot(5, 3).first_component[:-1] points1[:, 0] -= 20. points1[:, 2] -= 15. points2 = points1.copy() points2[:, 2] += 30. points3 = points1.copy() points3[:, 0] += 41. points3[:, 2] += 30 points4 = points1.copy() points4[:, 0] += 41. points5 = points1.copy() points5[:, 0] += 20.4 points5[:, 2] += 15 colors = np.linspace(0, 1, len(points1)) colors = np.array([hsv_to_rgb(c, 1, 1) for c in colors]) vertex_colors = np.random.random(8 * len(points1)) vertex_colors = np.array([hsv_to_rgb(c, 1, 1) for c in vertex_colors]) l1 = scene.visuals.Tube(points1, shading='flat', color=colors, # this is overridden by # the vertex_colors argument vertex_colors=vertex_colors, tube_points=8) l2 = scene.visuals.Tube(points2, color=['red', 'green', 'blue'], shading='smooth', tube_points=8) l3 = scene.visuals.Tube(points3, color=colors, shading='flat', tube_points=8, closed=True) l4 = scene.visuals.Tube(points4, color=colors, shading='smooth', tube_points=8, mode='lines') # generate sine wave radii radii = np.sin(2 * np.pi * 440 * np.arange(points5.shape[0]) / 44000) radii = (radii + 1.5) / 2 l5 = scene.visuals.Tube(points5, radius=radii, color='white', shading='smooth', closed=True, tube_points=8) view.add(l1) view.add(l2) view.add(l3) view.add(l4) view.add(l5) view.camera = scene.TurntableCamera() # tube does not expose its limits yet view.camera.set_range((-20, 20), (-20, 20), (-20, 20)) canvas.show() if __name__ == '__main__': if sys.flags.interactive != 1: canvas.app.run() vispy-0.14.1/examples/basics/visuals/text_visual.py0000644000175000017510000001402114507104216021765 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- from vispy import app, gloo, visuals import numpy as np class Canvas(app.Canvas): """ This is an interactive example of how to display text on app.Canvas. The text rendering will respond to standard C escape sequences. - Scroll for changing text font size - Press left and right arrows for changing text anchor positions - Press up and down arrows for changing text content Standard C escape sequences: - \a -> alert (beep, bell) - \b -> Backspace - \f -> Formfeed - \n -> Newline - \r -> Carriage Return - \t -> Horizontal Tab - \v -> Vertical Tab - \\ -> Backslash - \' -> Single quotation mark - \" -> Double quotation mark """ def __init__(self): app.Canvas.__init__(self, title='Glyphs', keys='interactive') self.font_size = 9. # Create a cross eye for easy see if text anchor positions are good l_pos = np.array([ [-1.0, 0.0], [1.0, 0.0], [0.0, 0.0], [0.0, 1.0], [0.0, -1.0], ]) self.cross_eye_line = visuals.LineVisual(pos=l_pos, color=(1.0, 0.0, 0.0, 1), method='gl') big_test_string = 'This is the big test string!\n' big_test_string += 'It includes all of the escape sequences known\n' big_test_string += 'to man:\n\n' big_test_string += '\t-\t\\n\n' big_test_string += '\t-\t\\v\n' big_test_string += '\t-\t\\t\n' big_test_string += '\t-\tetc..\v' big_test_string += 'So \bif \fthis \rlooks correct, somebody did a \n' big_test_string += 'decent job and deserves a beer ' big_test_string += 'and a digital salute\a! ;)' big_test_string += '\vThe end!' self.string_alternatives = [ '', 'Hello (scroll/arrows to change text properties)|\a|how are u', 'Hello (scroll/arrows to change text properties)|\b|how are u', 'Hello (scroll/arrows to change text properties)|\f|how are u', 'Hello (scroll/arrows to change text properties)|\n|how are u', 'Hello (scroll/arrows to change text properties)|\r|how are u', 'Hello (scroll/arrows to change text properties)|\t|how are u', 'Hello (scroll/arrows to change text properties)|\v|how are u', 'Hello (scroll/arrows to change text properties)|\\|how are u', 'Hello (scroll/arrows to change text properties)|\'|how are u', 'Hello (scroll/arrows to change text properties)|\"|how are u', 'Hello (scroll/arrows to change text properties)|?|how are u', big_test_string, ] self.str_ind = 0 # anchor_x , anchor_y self.anchor_variants = [ ['top', 'left'], ['center', 'left'], ['bottom', 'left'], ['top', 'center'], ['center', 'center'], ['bottom', 'center'], ['top', 'right'], ['center', 'right'], ['bottom', 'right'], ] self.anchor_ind = 0 self.text = visuals.TextVisual('', bold=True, pos=(0., 0.)) self.update_text() def on_draw(self, event): gloo.clear(color='white') gloo.set_viewport(0, 0, *self.physical_size) self.cross_eye_line.draw() self.text.draw() def on_mouse_wheel(self, event): """Use the mouse wheel to zoom.""" self.font_size *= 1.25 if event.delta[1] > 0 else 0.8 self.font_size = max(min(self.font_size, 160.), 6.) self.update_text() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.text.transforms.configure(canvas=self, viewport=vp) self.update_text() def on_key_release(self, event): if 'Down' in str(event.key): if (self.str_ind == 0): self.str_ind = len(self.string_alternatives) - 1 else: self.str_ind -= 1 if 'Up' in str(event.key): if (self.str_ind == len(self.string_alternatives) - 1): self.str_ind = 0 else: self.str_ind += 1 if 'Left' in str(event.key): if (self.anchor_ind == 0): self.anchor_ind = len(self.anchor_variants) - 1 else: self.anchor_ind -= 1 if 'Right' in str(event.key): if (self.anchor_ind == len(self.anchor_variants) - 1): self.anchor_ind = 0 else: self.anchor_ind += 1 if event.key == 'b': self.text.bold = not self.text.bold if event.key == 'i': self.text.italic = not self.text.italic self.update_text() def update_text(self): pre_text = '%s pt, ' % round(self.font_size, 1) post_text = "\n(anchor_x = " + self.anchor_variants[self.anchor_ind][1] post_text += ", anchor_y = " post_text += self.anchor_variants[self.anchor_ind][0] + ")" new_txt = pre_text + self.string_alternatives[self.str_ind] + post_text self.text.text = new_txt anchor_x = self.anchor_variants[self.anchor_ind][1] anchor_y = self.anchor_variants[self.anchor_ind][0] self.text.anchors = (anchor_x, anchor_y) self.text.font_size = self.font_size self.text.pos = self.size[0] // 2, self.size[1] // 2 self.update() if __name__ == '__main__': c = Canvas() c.show() c.app.run() vispy-0.14.1/examples/basics/visuals/text_scatter.py0000644000175000017510000000464714507104216022144 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Demonstration of ability to scatter text parts and assign different colors. """ import sys from vispy import app, gloo, visuals import numpy as np class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 800)) self.n_iterations = 0 self.text_parts = [ 'This', 'is', 'a', 'multicolored', 'scattered', 'text'] self.x = 100 + np.arange(len(self.text_parts))*100 self.y = 400 + (np.sin(2 * np.pi * (self.x/self.x[-1]))*100) self.text_positions = np.c_[self.x, self.y] color = np.ones((len(self.text_parts), 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, len(self.text_parts)) color[:, 1] = color[::-1, 0] self.colors = color self.text = visuals.TextVisual(self.text_parts, bold=True, pos=self.text_positions, color=self.colors) def on_draw(self, event): gloo.clear(color='white') gloo.set_viewport(0, 0, *self.physical_size) self.text.draw() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.text.transforms.configure(canvas=self, viewport=vp) if __name__ == '__main__': c = Canvas() c.show() def update(ev): x = c.text.pos[:, 0] y = 400 + (np.sin(2 * np.pi * ((x/x[-1]))+(c.n_iterations/5))*100) color = np.ones((len(c.text_parts), 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, len(c.text_parts)) color[:, 1] = color[::-1, 0] color = np.roll(color, c.n_iterations // len(color), axis=0) c.text.pos = np.c_[x, y] c.text.color = color c.update() c.n_iterations += 1 timer = app.Timer() timer.connect(update) timer.start(0.1) if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/rotating_box.py0000644000175000017510000000335114507104216022121 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Demonstration of Cube """ import sys from vispy import app, gloo from vispy.visuals import BoxVisual, transforms class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, 'Cube', keys='interactive', size=(400, 400)) self.cube = BoxVisual(1.0, 0.5, 0.25, color='red', edge_color="k") self.theta = 0 self.phi = 0 # Create a TransformSystem that will tell the visual how to draw self.cube_transform = transforms.MatrixTransform() self.cube.transform = self.cube_transform self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.cube.transforms.configure(canvas=self, viewport=vp) def on_draw(self, event): gloo.set_viewport(0, 0, *self.physical_size) gloo.clear('white', depth=True) self.cube.draw() def on_timer(self, event): self.theta += .5 self.phi += .5 self.cube_transform.reset() self.cube_transform.rotate(self.theta, (0, 0, 1)) self.cube_transform.rotate(self.phi, (0, 1, 0)) self.cube_transform.scale((100, 100, 0.001)) self.cube_transform.translate((200, 200)) self.update() if __name__ == '__main__': win = Canvas() win.show() if sys.flags.interactive != 1: win.app.run() vispy-0.14.1/examples/basics/visuals/rescalingmarkers.py0000644000175000017510000000372614507104216022764 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Display markers at different sizes and line thicknessess. """ import numpy as np import vispy.scene as scene from vispy.scene import visuals from vispy import app n = 500 pos = np.zeros((n, 2)) colors = np.ones((n, 4), dtype=np.float32) radius, theta, dtheta = 1.0, 0.0, 5.5 / 180.0 * np.pi for i in range(500): theta += dtheta x = radius * np.cos(theta) y = radius * np.sin(theta) r = 10.1 - i * 0.02 radius -= 0.45 pos[i] = x/512.+.5, 1.-(y/512.+.5) pos *= 512 class Canvas(scene.SceneCanvas): def __init__(self): scene.SceneCanvas.__init__( self, keys='interactive', size=(512, 512), title="Marker demo [press space to change marker]", bgcolor='white' ) self.unfreeze() self.index = 0 self.markers = visuals.Markers(scaling=False) self.markers.set_data(pos, face_color=(0, 1, 0)) self.markers.symbol = self.markers.symbols[self.index] self.text = visuals.Text(self.markers.symbols[self.index], pos=(80, 15), font_size=14, color='black', parent=self.scene) self.freeze() def on_key_press(self, event): if event.text == ' ': self.index = (self.index + 1) % (len(self.markers.symbols)) self.markers.symbol = self.markers.symbols[self.index] self.text.text = self.markers.symbols[self.index] self.update() canvas = Canvas() grid = canvas.central_widget.add_grid() vb1 = grid.add_view(row=0, col=0) vb1.add(canvas.markers) if __name__ == '__main__': canvas.show() app.run() vispy-0.14.1/examples/basics/visuals/polygon_visual.py0000644000175000017510000000703714507104216022501 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Demonstration of Polygon and subclasses """ import sys import numpy as np from vispy import app, gloo, visuals from vispy.visuals import transforms # vertex positions of polygon data to draw pos = np.array([[0, 0, 0], [0.25, 0.22, 0], [0.25, 0.5, 0], [0, 0.5, 0], [-0.25, 0.25, 0]]) pos = np.array([[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]]) theta = np.linspace(0, 2*np.pi, 11) pos = np.hstack([np.cos(theta)[:, np.newaxis], np.sin(theta)[:, np.newaxis]]) pos[::2] *= 0.4 pos[-1] = pos[0] class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 800)) global pos self.visuals = [] polygon = visuals.PolygonVisual(pos=pos, color=(0.8, .2, 0, 1), border_color=(1, 1, 1, 1), border_width=3) polygon.transform = transforms.STTransform(scale=(200, 200), translate=(600, 600)) self.visuals.append(polygon) ellipse = visuals.EllipseVisual(center=(0, 0, 0), radius=(100, 150), color=(0.2, 0.2, 0.8, 1), border_color=(1, 1, 1, 1), border_width=3, start_angle=180., span_angle=150.) ellipse.transform = transforms.STTransform(scale=(0.9, 1.5), translate=(200, 300)) self.visuals.append(ellipse) rect = visuals.RectangleVisual(center=(600, 200, 0), height=200., width=300., radius=[30., 30., 0., 0.], color=(0.5, 0.5, 0.2, 1), border_width=3, border_color='white') rect.transform = transforms.NullTransform() self.visuals.append(rect) rpolygon = visuals.RegularPolygonVisual(center=(200., 600., 0), radius=160, color=(0.2, 0.8, 0.2, 1), border_color=(1, 1, 1, 1), border_width=3, sides=6) rpolygon.transform = transforms.NullTransform() self.visuals.append(rpolygon) self.show() def on_resize(self, event): vp = (0, 0, self.physical_size[0], self.physical_size[1]) for visual in self.visuals: visual.transforms.configure(canvas=self, viewport=vp) def on_draw(self, ev): gloo.set_clear_color((0, 0, 0, 1)) gloo.set_viewport(0, 0, *self.physical_size) gloo.clear() for vis in self.visuals: vis.draw() if __name__ == '__main__': win = Canvas() if sys.flags.interactive != 1: win.app.run() vispy-0.14.1/examples/basics/visuals/plane.py0000644000175000017510000000212314507104216020515 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Simple demonstration of the plane geometry. """ import sys from vispy import scene, geometry canvas = scene.SceneCanvas(keys='interactive', size=(800, 600), show=True) view = canvas.central_widget.add_view() vertices, faces, outline = geometry.create_plane(width=2, height=4, width_segments=4, height_segments=8, direction='+y') plane = scene.visuals.Plane(width=2, height=4, width_segments=4, height_segments=8, direction='+y', vertex_colors=vertices['color'], edge_color='k', parent=view.scene) camera = scene.cameras.TurntableCamera(fov=45, azimuth=-45, parent=view.scene) view.camera = camera if __name__ == '__main__' and sys.flags.interactive == 0: canvas.app.run() vispy-0.14.1/examples/basics/visuals/networkx_layout.py0000644000175000017510000000162414507104216022701 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. from vispy import scene from vispy.visuals.graphs import layouts import networkx as nx g = nx.erdos_renyi_graph(100, .2) layout = layouts.get_layout("networkx_layout", graph=g, layout="spring") # create canvas canvas = scene.SceneCanvas(title='Simple NetworkX Graph', size=( 600, 600), bgcolor='black', show=True) view = canvas.central_widget.add_view('panzoom') visual = scene.visuals.Graph( layout.adj, layout=layout, line_color=(1, 1, 1, .5), arrow_type="stealth", arrow_size=30, node_symbol="disc", node_size=20, face_color=(1, 0, 0, 1), border_width=0.0, animate=False, directed=False, parent=view.scene) @canvas.events.draw.connect def on_draw(event): if not visual.animate_layout(): canvas.update() canvas.show() vispy-0.14.1/examples/basics/visuals/mesh.py0000644000175000017510000001077114507104216020362 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Simple demonstration of Mesh visual. """ import numpy as np from vispy import app, gloo, visuals from vispy.geometry import create_sphere from vispy.visuals.transforms import (STTransform, MatrixTransform, ChainTransform) from vispy.visuals.filters import ShadingFilter class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 550)) self.meshes = [] self.rotation = MatrixTransform() # Generate some data to work with global mdata mdata = create_sphere(20, 40, 1.0) # Mesh with pre-indexed vertices, uniform color self.meshes.append(visuals.MeshVisual(meshdata=mdata, color='b')) # Mesh with pre-indexed vertices, per-face color # Because vertices are pre-indexed, we get a different color # every time a vertex is visited, resulting in sharp color # differences between edges. rng = np.random.RandomState(0) verts = mdata.get_vertices(indexed='faces') nf = verts.size//9 fcolor = np.ones((nf, 3, 4), dtype=np.float32) fcolor[..., 0] = np.linspace(1, 0, nf)[:, np.newaxis] fcolor[..., 1] = rng.randn(nf, 1) fcolor[..., 2] = np.linspace(0, 1, nf)[:, np.newaxis] mesh = visuals.MeshVisual(vertices=verts, face_colors=fcolor) self.meshes.append(mesh) # Mesh with unindexed vertices, per-vertex color # Because vertices are unindexed, we get the same color # every time a vertex is visited, resulting in no color differences # between edges. verts = mdata.get_vertices() faces = mdata.get_faces() nv = verts.size//3 vcolor = np.ones((nv, 4), dtype=np.float32) vcolor[:, 0] = np.linspace(1, 0, nv) vcolor[:, 1] = rng.randn(nv) vcolor[:, 2] = np.linspace(0, 1, nv) self.meshes.append(visuals.MeshVisual(verts, faces, vcolor)) self.meshes.append(visuals.MeshVisual(verts, faces, vcolor)) flat_shading = ShadingFilter(shading='flat', shininess=100) self.meshes[-1].attach(flat_shading) self.meshes.append(visuals.MeshVisual(verts, faces, vcolor)) smooth_shading = ShadingFilter(shading='smooth', shininess=100) self.meshes[-1].attach(smooth_shading) # Mesh with color indexed into a colormap verts = mdata.get_vertices(None) faces = mdata.get_faces() values = rng.randn(len(verts)) mesh = visuals.MeshVisual(vertices=verts, faces=faces, vertex_values=values) mesh.clim = [-1, 1] mesh.cmap = 'viridis' smooth_shading = ShadingFilter(shading='smooth', shininess=100) mesh.attach(smooth_shading) self.meshes.append(mesh) # Lay out meshes in a grid grid = (3, 3) s = 300. / max(grid) for i, mesh in enumerate(self.meshes): x = 800. * (i % grid[0]) / grid[0] + 400. / grid[0] - 2 y = 800. * (i // grid[1]) / grid[1] + 400. / grid[1] + 2 transform = ChainTransform([STTransform(translate=(x, y), scale=(s, s, s)), self.rotation]) mesh.transform = transform mesh.transforms.scene_transform = STTransform(scale=(1, 1, 0.01)) self.show() self.timer = app.Timer(connect=self.rotate) self.timer.start(0.016) def rotate(self, event): # rotate with an irrational amount over each axis so there is no # periodicity self.rotation.rotate(0.2 ** 0.5, (1, 0, 0)) self.rotation.rotate(0.3 ** 0.5, (0, 1, 0)) self.rotation.rotate(0.5 ** 0.5, (0, 0, 1)) self.update() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for mesh in self.meshes: mesh.transforms.configure(canvas=self, viewport=vp) def on_draw(self, ev): gloo.set_viewport(0, 0, *self.physical_size) gloo.clear(color='black', depth=True) for mesh in self.meshes: mesh.draw() if __name__ == '__main__': win = Canvas() import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/markers.py0000644000175000017510000000474314507104216021074 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """Display markers at different sizes and line thicknesses. Keyboard options: * spacebar: Cycle through possible marker symbols. * "s": Switch between "fixed" marker scaling (initial setting) and "scene" scaling. """ import numpy as np from vispy import app, visuals from vispy.visuals.transforms import STTransform n = 500 pos = np.zeros((n, 2)) colors = np.ones((n, 4), dtype=np.float32) radius, theta, dtheta = 1.0, 0.0, 5.5 / 180.0 * np.pi for i in range(500): theta += dtheta x = 256 + radius * np.cos(theta) y = 256 + radius * np.sin(theta) r = 10.1 - i * 0.02 radius -= 0.45 pos[i] = x, y colors[i] = (i/500, 1.0-i/500, 0, 1) class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(512, 512), title="Marker demo [press space to change marker]") self.index = 0 self.markers = visuals.MarkersVisual() self.markers.set_data(pos, face_color=colors) self.markers.symbol = self.markers.symbols[self.index] self.markers.transform = STTransform() self.show() def on_draw(self, event): self.context.clear(color='white') self.markers.draw() def on_mouse_wheel(self, event): """Use the mouse wheel to zoom.""" self.markers.transform.zoom((1.25**event.delta[1],)*2, center=event.pos) self.update() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.markers.transforms.configure(viewport=vp, canvas=self) def on_key_press(self, event): if event.text == ' ': self.index = (self.index + 1) % (len(self.markers.symbols)) self.markers.symbol = self.markers.symbols[self.index] self.update() elif event.text == 's': self.markers.scaling = "fixed" if self.markers.scaling != "fixed" else "scene" self.update() if __name__ == '__main__': print(__doc__) canvas = Canvas() app.run() vispy-0.14.1/examples/basics/visuals/line_transform.py0000644000175000017510000000622514507104216022447 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 1 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Demonstration of Line visual with arbitrary transforms. Several Line visuals are displayed that all have the same vertex position information, but different transformations. """ import numpy as np from vispy import app, gloo, visuals from vispy.visuals.transforms import (STTransform, LogTransform, MatrixTransform, PolarTransform) # vertex positions of data to draw N = 200 pos = np.zeros((N, 2), dtype=np.float32) pos[:, 0] = np.linspace(-350, 350, N) pos[:, 1] = np.random.normal(size=N, scale=50, loc=0) # One array of colors color = np.ones((N, 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, N) color[:, 1] = color[::-1, 0] class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 800)) # Define several Line visuals that use the same position data # but have different colors and transformations colors = [color, (1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1), (1, 1, 0, 1), (1, 1, 1, 1)] self.lines = [visuals.LineVisual(pos=pos, color=colors[i]) for i in range(6)] center = STTransform(translate=(400, 400)) self.lines[0].transform = center self.lines[1].transform = (center * STTransform(scale=(1, 0.1, 1))) self.lines[2].transform = (center * STTransform(translate=(200, 200, 0)) * STTransform(scale=(0.3, 0.5, 1))) self.lines[3].transform = (center * STTransform(translate=(-200, -200, 0), scale=(200, 1)) * LogTransform(base=(10, 0, 0)) * STTransform(translate=(1, 0, 0))) self.lines[4].transform = MatrixTransform() self.lines[4].transform.rotate(45, (0, 0, 1)) self.lines[4].transform.scale((0.3, 0.3, 1)) self.lines[4].transform.translate((200, 200, 0)) self.lines[5].transform = (STTransform(translate=(200, 600, 0), scale=(5, 5)) * PolarTransform() * LogTransform(base=(2, 0, 0)) * STTransform(scale=(0.01, 0.1), translate=(4, 20))) self.show() def on_draw(self, ev): gloo.clear('black', depth=True) for line in self.lines: line.draw() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for line in self.lines: line.transforms.configure(canvas=self, viewport=vp) if __name__ == '__main__': win = Canvas() import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/line_prototype.py0000644000175000017510000002334314507104216022501 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 10 # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. import sys import numpy as np from vispy import app, gloo, visuals from vispy.visuals.filters import Clipper, ColorFilter from vispy.visuals.shaders import MultiProgram from vispy.visuals.collections import PointCollection from vispy.visuals.transforms import STTransform from vispy.scene import SceneCanvas from vispy.scene.visuals import create_visual_node class LineVisual(visuals.Visual): """Example of a very simple GL-line visual. This shows the minimal set of methods that need to be reimplemented to make a new visual class. """ def __init__(self, pos=None, color=(1, 1, 1, 1)): vcode = """ attribute vec2 a_pos; void main() { gl_Position = $transform(vec4(a_pos, 0., 1.)); gl_PointSize = 10.; } """ fcode = """ void main() { gl_FragColor = $color; } """ visuals.Visual.__init__(self, vcode=vcode, fcode=fcode) self.pos_buf = gloo.VertexBuffer() # The Visual superclass contains a MultiProgram, which is an object # that behaves like a normal shader program (you can assign shader # code, upload values, set template variables, etc.) but internally # manages multiple ModularProgram instances, one per view. # The MultiProgram is accessed via the `shared_program` property, so # the following modifications to the program will be applied to all # views: self.shared_program['a_pos'] = self.pos_buf self.shared_program.frag['color'] = color self._need_upload = False # Visual keeps track of draw mode, index buffer, and GL state. These # are shared between all views. self._draw_mode = 'line_strip' self.set_gl_state('translucent', depth_test=False) if pos is not None: self.set_data(pos) def set_data(self, pos): self._pos = pos self._need_upload = True def _prepare_transforms(self, view=None): view.view_program.vert['transform'] = view.transforms.get_transform() def _prepare_draw(self, view=None): """This method is called immediately before each draw. The *view* argument indicates which view is about to be drawn. """ if self._need_upload: # Note that pos_buf is shared between all views, so we have no need # to use the *view* argument in this example. This will be true # for most visuals. self.pos_buf.set_data(self._pos) self._need_upload = False class PointVisual(LineVisual): """Another simple visual class. Due to the simplicity of these example classes, it was only necessary to subclass from LineVisual and set the draw mode to 'points'. A more fully-featured PointVisual class might not follow this approach. """ def __init__(self, pos=None, color=(1, 1, 1, 1)): LineVisual.__init__(self, pos, color) self._draw_mode = 'points' class PlotLineVisual(visuals.CompoundVisual): """An example compound visual that draws lines and points. To the user, the compound visual behaves exactly like a normal visual--it has a transform system, draw() and bounds() methods, etc. Internally, the compound visual automatically manages proxying these transforms and methods to its sub-visuals. """ def __init__(self, pos=None, line_color=(1, 1, 1, 1), point_color=(1, 1, 1, 1)): self._line = LineVisual(pos, color=line_color) self._point = PointVisual(pos, color=point_color) visuals.CompoundVisual.__init__(self, [self._line, self._point]) class PointCollectionVisual(visuals.Visual): """Thin wrapper around a point collection. Note: This is currently broken! """ def __init__(self): prog = MultiProgram(vcode='', fcode='') self.points = PointCollection("agg", color="shared", program=prog) visuals.Visual.__init__(self, program=prog) def _prepare_draw(self, view): if self.points._need_update: self.points._update() self._draw_mode = self.points._mode self._index_buffer = self.points._indices_buffer def append(self, *args, **kwargs): self.points.append(*args, **kwargs) def _prepare_transforms(self, view=None): pass @property def color(self): return self.points['color'] @color.setter def color(self, c): self.points['color'] = c class PanZoomTransform(STTransform): def __init__(self, canvas=None, aspect=None, **kwargs): self._aspect = aspect self.attach(canvas) STTransform.__init__(self, **kwargs) def attach(self, canvas): """ Attach this tranform to a canvas """ self._canvas = canvas canvas.events.mouse_wheel.connect(self.on_mouse_wheel) canvas.events.mouse_move.connect(self.on_mouse_move) def on_mouse_move(self, event): if event.is_dragging: dxy = event.pos - event.last_event.pos button = event.press_event.button if button == 1: self.move(dxy) elif button == 2: center = 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) def on_mouse_wheel(self, event): self.zoom(np.exp(event.delta * (0.01, -0.01)), event.pos) canvas = app.Canvas(keys='interactive', size=(900, 600), show=True, title="Visual Canvas") pos = np.random.normal(size=(1000, 2), loc=0, scale=50).astype('float32') pos[0] = [0, 0] # Make a line visual line = LineVisual(pos=pos) line.transforms.canvas = canvas line.transform = STTransform(scale=(2, 1), translate=(20, 20)) panzoom = PanZoomTransform(canvas) line.transforms.scene_transform = panzoom panzoom.changed.connect(lambda ev: canvas.update()) # Attach color filter to all views (current and future) of the visual line.attach(ColorFilter((1, 1, 0.5, 0.7))) # Attach a clipper just to this view. The Clipper filter requires a # transform that maps from the framebuffer coordinate system to the # clipping coordinates. tr = line.transforms.get_transform('framebuffer', 'canvas') line.attach(Clipper((20, 20, 260, 260), transform=tr), view=line) # Make a view of the line that will draw its shadow shadow = line.view() shadow.transforms.canvas = canvas shadow.transform = STTransform(scale=(2, 1), translate=(25, 25)) shadow.transforms.scene_transform = panzoom shadow.attach(ColorFilter((0, 0, 0, 0.6)), view=shadow) tr = shadow.transforms.get_transform('framebuffer', 'canvas') shadow.attach(Clipper((20, 20, 260, 260), transform=tr), view=shadow) # And make a second view of the line with different clipping bounds view = line.view() view.transforms.canvas = canvas view.transform = STTransform(scale=(2, 0.5), translate=(450, 150)) tr = view.transforms.get_transform('framebuffer', 'canvas') view.attach(Clipper((320, 20, 260, 260), transform=tr), view=view) # Make a compound visual plot = PlotLineVisual(pos, (0.5, 1, 0.5, 0.2), (0.5, 1, 1, 0.3)) plot.transforms.canvas = canvas plot.transform = STTransform(translate=(80, 450), scale=(1.5, 1)) tr = plot.transforms.get_transform('framebuffer', 'canvas') plot.attach(Clipper((20, 320, 260, 260), transform=tr), view=plot) # And make a view on the compound view2 = plot.view() view2.transforms.canvas = canvas view2.transform = STTransform(scale=(1.5, 1), translate=(450, 400)) tr = view2.transforms.get_transform('framebuffer', 'canvas') view2.attach(Clipper((320, 320, 260, 260), transform=tr), view=view2) # And a shadow for the view shadow2 = plot.view() shadow2.transforms.canvas = canvas shadow2.transform = STTransform(scale=(1.5, 1), translate=(455, 405)) shadow2.attach(ColorFilter((0, 0, 0, 0.6)), view=shadow2) tr = shadow2.transforms.get_transform('framebuffer', 'canvas') shadow2.attach(Clipper((320, 320, 260, 260), transform=tr), view=shadow2) # Example of a collection visual collection = PointCollectionVisual() collection.transforms.canvas = canvas collection.transform = STTransform(translate=(750, 150)) collection.append(np.random.normal(loc=0, scale=20, size=(10000, 3)), itemsize=5000) collection.color = (1, 0.5, 0.5, 1), (0.5, 0.5, 1, 1) shadow3 = collection.view() shadow3.transforms.canvas = canvas shadow3.transform = STTransform(scale=(1, 1), translate=(752, 152)) shadow3.attach(ColorFilter((0, 0, 0, 0.6)), view=shadow3) # tr = shadow3.transforms.get_transform('framebuffer', 'canvas') # shadow3.attach(Clipper((320, 320, 260, 260), transform=tr), view=shadow2) order = [shadow, line, view, plot, shadow2, view2, shadow3, collection] @canvas.connect def on_draw(event): canvas.context.clear((0.3, 0.3, 0.3, 1.0)) for v in order: v.draw() def on_resize(event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, canvas.physical_size[0], canvas.physical_size[1]) canvas.context.set_viewport(*vp) for v in order: v.transforms.configure(canvas=canvas, viewport=vp) canvas.events.resize.connect(on_resize) on_resize(None) Line = create_visual_node(LineVisual) canvas2 = SceneCanvas(keys='interactive', title='Scene Canvas', show=True) v = canvas2.central_widget.add_view(margin=10) v.border_color = (1, 1, 1, 1) v.bgcolor = (0.3, 0.3, 0.3, 1) v.camera = 'panzoom' line2 = Line(pos, parent=v.scene) def mouse(ev): print(ev) v.events.mouse_press.connect(mouse) if __name__ == '__main__': if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/line_plot_axes.py0000644000175000017510000000407214507104216022430 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Demonstration of LinePlot visual with axes. """ import numpy as np import sys from vispy import gloo, app, visuals # size of canvas and graph canvas_size = (1000, 800) graph_size = (900, 700) # vertex positions of data to draw N = 30 margin_x = (canvas_size[0]-graph_size[0]) / 2. margin_y = (canvas_size[1]-graph_size[1]) / 2. pos_xax = np.array([[margin_x, canvas_size[1]-margin_y], [canvas_size[0]-margin_x, canvas_size[1]-margin_y]]) pos_yax = np.array([[margin_x, canvas_size[1]-margin_y], [margin_x, margin_y]]) pos = np.zeros((N, 2), dtype=np.float32) pos[:, 0] = np.linspace(margin_x, canvas_size[0]-margin_x, N) pos[:, 1] = np.clip(np.random.normal(size=N, scale=100, loc=canvas_size[1] / 2.), margin_y, canvas_size[0]-margin_y) class Canvas(app.Canvas): def __init__(self): self.line = visuals.LinePlotVisual(pos, color='w', edge_color='w', face_color=(0.2, 0.2, 1)) self.axis_x = visuals.AxisVisual(pos_xax, (0, 100), (0., 1.)) self.axis_y = visuals.AxisVisual(pos_yax, (5, 7.5), (-1., 0.)) app.Canvas.__init__(self, keys='interactive', size=canvas_size, show=True) def on_draw(self, event): gloo.clear('black') self.axis_x.draw() self.axis_y.draw() self.line.draw() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.axis_x.transforms.configure(canvas=self, viewport=vp) self.axis_y.transforms.configure(canvas=self, viewport=vp) self.line.transforms.configure(canvas=self, viewport=vp) if __name__ == '__main__': win = Canvas() if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/line_plot3d.py0000644000175000017510000000152314507104216021635 0ustar nileshnilesh# pyline: disable=no-member """ plot3d using existing visuals : LinePlotVisual """ import numpy as np import sys from vispy import app, visuals, scene # build visuals Plot3D = scene.visuals.create_visual_node(visuals.LinePlotVisual) # build canvas canvas = scene.SceneCanvas(keys='interactive', title='plot3d', show=True) # Add a ViewBox to let the user zoom/rotate view = canvas.central_widget.add_view() view.camera = 'turntable' view.camera.fov = 45 view.camera.distance = 6 # prepare data N = 60 x = np.sin(np.linspace(-2, 2, N)*np.pi) y = np.cos(np.linspace(-2, 2, N)*np.pi) z = np.linspace(-2, 2, N) # plot pos = np.c_[x, y, z] Plot3D(pos, width=2.0, color='red', edge_color='w', symbol='o', face_color=(0.2, 0.2, 1, 0.8), parent=view.scene) if __name__ == '__main__': if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/line_plot.py0000644000175000017510000000244514507104216021412 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip (KNOWNFAIL) # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Simple demonstration of LinePlot visual. """ import numpy as np import sys from vispy import gloo, app, visuals # vertex positions of data to draw N = 20 pos = np.zeros((N, 2), dtype=np.float32) pos[:, 0] = np.linspace(10, 790, N) pos[:, 1] = np.random.normal(size=N, scale=100, loc=400) class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 800)) self.line = visuals.LinePlotVisual(pos, color='w', edge_color='w', symbol='o', face_color=(0.2, 0.2, 1)) self.show() def on_draw(self, event): gloo.clear('black') self.line.draw() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.line.transforms.configure(canvas=self, viewport=vp) if __name__ == '__main__': win = Canvas() if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/line_draw.py0000644000175000017510000001532514507104216021372 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip (KNOWNFAIL) # Copyright (c) 2015, Felix Schill. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Simple demonstration of mouse drawing and editing of a line plot. This demo extends the Line visual from scene adding mouse events that allow modification and creation of line points with the mouse. Vispy takes care of coordinate transforms from screen to ViewBox - the demo works on different zoom levels. """ import numpy as np from vispy import app, scene class EditLineVisual(scene.visuals.Line): """ Mouse editing extension to the Line visual. This class adds mouse picking for line points, mouse_move handling for dragging existing points, and adding new points when clicking into empty space. """ def __init__(self, *args, **kwargs): scene.visuals.Line.__init__(self, *args, **kwargs) self.unfreeze() # initialize point markers self.markers = scene.visuals.Markers(parent=self) self.marker_colors = np.ones((len(self.pos), 4), dtype=np.float32) self.markers.set_data(pos=self.pos, symbol="s", edge_color="red", size=6) self.selected_point = None self.selected_index = -1 # snap grid size self.gridsize = 10 self.freeze() def on_draw(self, event): # draw line and markers scene.visuals.Line.draw(self) self.markers.draw() def print_mouse_event(self, event, what): """ print mouse events for debugging purposes """ print('%s - pos: %r, button: %s, delta: %r' % (what, event.pos, event.button, event.delta)) def select_point(self, pos_scene, radius=5): """ Get line point close to mouse pointer and its index Parameters ---------- event : the mouse event being processed radius : scalar max. distance in pixels between mouse and line point to be accepted return: (numpy.array, int) picked point and index of the point in the pos array """ # project mouse radius from screen coordinates to document coordinates mouse_radius = 6 # print("Mouse radius in document units: ", mouse_radius) # find first point within mouse_radius index = 0 for p in self.pos: if np.linalg.norm(pos_scene[:3] - p) < mouse_radius: # print p, index # point found, return point and its index return p, index index += 1 # no point found, return None return None, -1 def update_markers(self, selected_index=-1, highlight_color=(1, 0, 0, 1)): """ update marker colors, and highlight a marker with a given color """ self.marker_colors.fill(1) # default shape (non-highlighted) shape = "o" size = 6 if 0 <= selected_index < len(self.marker_colors): self.marker_colors[selected_index] = highlight_color # if there is a highlighted marker, # change all marker shapes to a square shape = "s" size = 8 self.markers.set_data(pos=self.pos, symbol=shape, edge_color='red', size=size, face_color=self.marker_colors) def on_mouse_press(self, pos_scene): # pos_scene = event.pos[:3] # find closest point to mouse and select it self.selected_point, self.selected_index = self.select_point(pos_scene) # if no point was clicked add a new one if self.selected_point is None: print("adding point", len(self.pos)) self._pos = np.append(self.pos, [pos_scene[:3]], axis=0) self.set_data(pos=self.pos) self.marker_colors = np.ones((len(self.pos), 4), dtype=np.float32) self.selected_point = self.pos[-1] self.selected_index = len(self.pos) - 1 # update markers and highlights self.update_markers(self.selected_index) def on_mouse_release(self, event): self.print_mouse_event(event, 'Mouse release') self.selected_point = None self.update_markers() def on_mouse_move(self, pos_scene): if self.selected_point is not None: # update selected point to new position given by mouse self.selected_point[0] = round(pos_scene[0] / self.gridsize) \ * self.gridsize self.selected_point[1] = round(pos_scene[1] / self.gridsize) \ * self.gridsize self.set_data(pos=self.pos) self.update_markers(self.selected_index) def highlight_markers(self, pos_scene): # if no button is pressed, just highlight the marker that would be # selected on click hl_point, hl_index = self.select_point(pos_scene) self.update_markers(hl_index, highlight_color=(0.5, 0.5, 1.0, 1.0)) self.update() class Canvas(scene.SceneCanvas): """ A simple test canvas for testing the EditLineVisual """ def __init__(self): scene.SceneCanvas.__init__(self, keys='interactive', size=(800, 800)) # Create some initial points n = 7 self.unfreeze() self.pos = np.zeros((n, 3), dtype=np.float32) self.pos[:, 0] = np.linspace(-50, 50, n) self.pos[:, 1] = np.random.normal(size=n, scale=10, loc=0) # create new editable line self.line = EditLineVisual(pos=self.pos, color='w', width=3, antialias=True, method='gl') self.view = self.central_widget.add_view() self.view.camera = scene.PanZoomCamera(rect=(-100, -100, 200, 200), aspect=1.0) # the left mouse button pan has to be disabled in the camera, as it # interferes with dragging line points # Proposed change in camera: make mouse buttons configurable self.view.camera._viewbox.events.mouse_move.disconnect( self.view.camera.viewbox_mouse_event) self.view.add(self.line) self.show() self.selected_point = None scene.visuals.GridLines(parent=self.view.scene) self.freeze() def on_mouse_press(self, event): # self.line.print_mouse_event(event, 'Mouse press') tr = self.scene.node_transform(self.line) pos = tr.map(event.pos) self.line.on_mouse_press(pos) def on_mouse_move(self, event): # left mouse button tr = self.scene.node_transform(self.line) pos = tr.map(event.pos) if event.button == 1: self.line.on_mouse_move(pos) else: self.line.highlight_markers(pos) if __name__ == '__main__': win = Canvas() app.run() vispy-0.14.1/examples/basics/visuals/line.py0000644000175000017510000000747314507104216020362 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Demonstration of various features of Line visual. """ from __future__ import division import sys import numpy as np from vispy import app, gloo, visuals from vispy.visuals.transforms import STTransform, NullTransform # vertex positions of data to draw N = 200 pos = np.zeros((N, 2), dtype=np.float32) pos[:, 0] = np.linspace(10, 390, N) pos[:, 1] = np.random.normal(size=N, scale=20, loc=0) # color array color = np.ones((N, 4), dtype=np.float32) color[:, 0] = np.linspace(0, 1, N) color[:, 1] = color[::-1, 0] # connection array connect = np.empty((N-1, 2), np.int32) connect[:, 0] = np.arange(N-1) connect[:, 1] = connect[:, 0] + 1 connect[N//2, 1] = N//2 # put a break in the middle class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 800)) # Create several visuals demonstrating different features of Line self.lines = [ # agg-method lines: # per-vertex color visuals.LineVisual(pos=pos, color=color, method='agg'), # solid visuals.LineVisual(pos=pos, color=(0, 0.5, 0.3, 1), method='agg'), # wide visuals.LineVisual(pos=pos, color=color, width=5, method='agg'), # GL-method lines: visuals.LineVisual(pos=pos, color=color, method='gl'), visuals.LineVisual(pos=pos, color=(0, 0.5, 0.3, 1), method='gl'), visuals.LineVisual(pos=pos, color=color, width=5, method='gl'), # GL-method: "connect" not available in AGG method yet # only connect alternate vert pairs visuals.LineVisual(pos=pos, color=(0, 0.5, 0.3, 1), connect='segments', method='gl'), # connect specific pairs visuals.LineVisual(pos=pos, color=(0, 0.5, 0.3, 1), connect=connect, method='gl'), ] counts = [0, 0] for i, line in enumerate(self.lines): # arrange lines in a grid tidx = (line.method == 'agg') x = 400 * tidx y = 140 * (counts[tidx] + 1) counts[tidx] += 1 line.transform = STTransform(translate=[x, y]) # redraw the canvas if any visuals request an update line.events.update.connect(lambda evt: self.update()) self.texts = [visuals.TextVisual('GL', bold=True, font_size=24, color='w', pos=(200, 40)), visuals.TextVisual('Agg', bold=True, font_size=24, color='w', pos=(600, 40))] for text in self.texts: text.transform = NullTransform() self.visuals = self.lines + self.texts self.show() def on_draw(self, event): gloo.clear('black') for visual in self.visuals: visual.draw() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for visual in self.visuals: visual.transforms.configure(canvas=self, viewport=vp) if __name__ == '__main__': win = Canvas() def update(ev): pos[:, 1] = np.random.normal(size=N, scale=30, loc=0) for line in win.lines: line.set_data(pos) timer = app.Timer() timer.connect(update) timer.start(1.0) if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/image_visual.py0000644000175000017510000000364714507104216022077 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Simple demonstration of ImageVisual. """ import numpy as np import vispy.app from vispy import gloo from vispy import visuals from vispy.visuals.transforms import STTransform class Canvas(vispy.app.Canvas): def __init__(self): vispy.app.Canvas.__init__(self, keys='interactive', size=(800, 800)) self.image = visuals.ImageVisual(get_image(), method='subdivide') # scale and center image in canvas s = 700. / max(self.image.size) t = 0.5 * (700. - (self.image.size[0] * s)) + 50 self.image.transform = STTransform(scale=(s, s), translate=(t, 50)) self.show() def on_draw(self, ev): gloo.clear(color='black', depth=True) self.image.draw() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.image.transforms.configure(canvas=self, viewport=vp) def get_image(): """Load an image from the demo-data repository if possible. Otherwise, just return a randomly generated image. """ from vispy.io import load_data_file, read_png try: return read_png(load_data_file('mona_lisa/mona_lisa_sm.png')) except Exception as exc: # fall back to random image print("Error loading demo image data: %r" % exc) # generate random image image = np.random.normal(size=(100, 100, 3)) image[20:80, 20:80] += 3. image[50] += 3. image[:, 50] += 3. image = ((image - image.min()) * (253. / (image.max() - image.min()))).astype(np.ubyte) return image if __name__ == '__main__': win = Canvas() import sys if sys.flags.interactive != 1: vispy.app.run() vispy-0.14.1/examples/basics/visuals/image_transforms.py0000644000175000017510000001044714507104216022766 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # vispy: gallery 2 """ Simple demonstration of ImageVisual. """ import numpy as np import vispy.app from vispy import gloo from vispy import visuals from vispy.visuals.transforms import (MatrixTransform, STTransform, arg_to_array, LogTransform, PolarTransform, BaseTransform) from image_visual import get_image class Canvas(vispy.app.Canvas): def __init__(self): vispy.app.Canvas.__init__(self, keys='interactive', size=(800, 800)) # Create 4 copies of an image to be displayed with different transforms image = get_image() self.images = [visuals.ImageVisual(image, method='impostor') for i in range(4)] # Transform all images to a standard size / location (because # get_image() might return unexpected sizes) s = 100. / max(self.images[0].size) tx = 0.5 * (100 - (self.images[0].size[0] * s)) ty = 0.5 * (100 - (self.images[0].size[1] * s)) base_tr = STTransform(scale=(s, s), translate=(tx, ty)) self.images[0].transform = (STTransform(scale=(30, 30), translate=(600, 600)) * SineTransform() * STTransform(scale=(0.1, 0.1), translate=(-5, -5)) * base_tr) tr = MatrixTransform() tr.rotate(40, (0, 0, 1)) tr.rotate(30, (1, 0, 0)) tr.translate((0, -20, -60)) p = MatrixTransform() p.set_perspective(0.5, 1, 0.1, 1000) tr = p * tr tr1 = (STTransform(translate=(200, 600)) * tr * STTransform(translate=(-50, -50)) * base_tr) self.images[1].transform = tr1 tr2 = (STTransform(scale=(3, -100), translate=(200, 50)) * LogTransform((0, 2, 0)) * STTransform(scale=(1, -0.01), translate=(-50, 1.1)) * base_tr) self.images[2].transform = tr2 tr3 = (STTransform(scale=(400, 400), translate=(570, 400)) * PolarTransform() * STTransform(scale=(np.pi/150, -0.005), translate=(-3.3*np.pi/4., 0.7)) * base_tr) self.images[3].transform = tr3 text = visuals.TextVisual( text=['logarithmic', 'polar', 'perspective', 'custom (sine)'], pos=[(100, 20), (500, 20), (100, 410), (500, 410)], color='k', font_size=16) self.visuals = self.images + [text] self.show() def on_draw(self, ev): gloo.clear(color='w', depth=True) for vis in self.visuals: vis.draw() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for vis in self.visuals: vis.transforms.configure(canvas=self, viewport=vp) # A simple custom Transform class SineTransform(BaseTransform): """ Add sine wave to y-value for wavy effect. """ glsl_map = """ vec4 sineTransform(vec4 pos) { return vec4(pos.x, pos.y + sin(pos.x), pos.z, 1); }""" glsl_imap = """ vec4 sineTransform(vec4 pos) { return vec4(pos.x, pos.y - sin(pos.x), pos.z, 1); }""" Linear = False @arg_to_array def map(self, coords): ret = coords.copy() ret[..., 1] += np.sin(ret[..., 0]) return ret @arg_to_array def imap(self, coords): ret = coords.copy() ret[..., 1] -= np.sin(ret[..., 0]) return ret def inverse(self): return InvSineTransform() class InvSineTransform(BaseTransform): glsl_map = SineTransform.glsl_imap glsl_imap = SineTransform.glsl_map Linear = False map = SineTransform.imap imap = SineTransform.map def inverse(self): return SineTransform() if __name__ == '__main__': win = Canvas() import sys if sys.flags.interactive != 1: vispy.app.run() vispy-0.14.1/examples/basics/visuals/grid_mesh.py0000644000175000017510000000266114507104216021366 0ustar nileshnilesh""" Demonstration of a mesh constructed in a grid about x,y,z coordinates. """ import sys from vispy import scene from vispy.color import get_colormap import numpy as np from scipy.special import sph_harm canvas = scene.SceneCanvas(keys='interactive') view = canvas.central_widget.add_view() thetas, phis = np.meshgrid(np.linspace(0, np.pi, 100), np.linspace(0, 2*np.pi, 150)) shape = thetas.shape linear_shape = thetas.shape[0] * thetas.shape[1] cm = get_colormap('hot') for harm_degree in range(3): for harm_order in range(harm_degree + 1): harmonic_values = sph_harm(harm_order, harm_degree, phis, thetas).real rs = 1. + 0.4*harmonic_values xs = rs * np.sin(thetas) * np.cos(phis) ys = rs * np.sin(thetas) * np.sin(phis) zs = rs * np.cos(thetas) xs -= 4.5 zs -= 4.5 xs += 2.5 * harm_degree zs += 2.5 * harm_order v_min = np.min(harmonic_values) v_max = np.max(harmonic_values) scale = 1. / np.max(np.abs([v_min, v_max])) harmonic_values *= 0.5*scale harmonic_values += 0.5 colors = cm[harmonic_values.reshape(linear_shape)].rgba.reshape( (shape[0], shape[1], 4)) mesh = scene.visuals.GridMesh(xs, ys, zs, colors=colors) view.add(mesh) view.camera = 'turntable' canvas.show() if __name__ == '__main__': if sys.flags.interactive != 1: canvas.app.run() vispy-0.14.1/examples/basics/visuals/graph.py0000644000175000017510000000340514507104216020523 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This example demonstrates how to visualise a NetworkX graph using the GraphVisual. """ import sys import networkx as nx from vispy import app, visuals from vispy.visuals.graphs import layouts from vispy.visuals.transforms import STTransform class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title="Simple NetworkX Graph", keys="interactive", size=(600, 600)) graph = nx.adjacency_matrix( nx.fast_gnp_random_graph(500, 0.005, directed=True)) layout = layouts.get_layout('force_directed', iterations=100) self.visual = visuals.GraphVisual( graph, layout=layout, line_color='black', arrow_type="stealth", arrow_size=30, node_symbol="disc", node_size=20, face_color=(1, 0, 0, 0.5), border_width=0.0, animate=True, directed=False) self.visual.transform = STTransform(self.visual_size, (20, 20)) self.show() @property def visual_size(self): return self.physical_size[0] - 40, self.physical_size[1] - 40 def on_resize(self, event): self.visual.transform.scale = self.visual_size vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.visual.transforms.configure(canvas=self, viewport=vp) def on_draw(self, event): self.context.clear('white') self.visual.draw() if not self.visual.animate_layout(): self.update() if __name__ == '__main__': win = Canvas() if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/dynamic_polygon.py0000644000175000017510000001035114507104216022613 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Demonstration of Polygon and subclasses """ import sys import numpy as np from vispy import app, visuals from vispy.visuals import transforms # vertex positions of polygon data to draw pos = np.array([[0, 0, 0], [0.25, 0.22, 0], [0.25, 0.5, 0], [0, 0.5, 0], [-0.25, 0.25, 0]]) pos = np.array([[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]]) theta = np.linspace(0, 2*np.pi, 11) pos = np.hstack([np.cos(theta)[:, np.newaxis], np.sin(theta)[:, np.newaxis]]) pos[::2] *= 0.4 pos[-1] = pos[0] class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 800)) global pos self.visuals = [] polygon = visuals.PolygonVisual(pos=pos, color=(0.8, .2, 0, 1), border_color=(1, 1, 1, 1)) polygon.transform = transforms.STTransform(scale=(200, 200), translate=(600, 600)) self.visuals.append(polygon) ellipse = visuals.EllipseVisual(center=(0, 0, 0), radius=(100, 100), color=(0.2, 0.2, 0.8, 1), border_color=(1, 1, 1, 1), start_angle=180., span_angle=150.) ellipse.transform = transforms.STTransform(scale=(0.9, 1.5), translate=(200, 200)) self.visuals.append(ellipse) rect = visuals.RectangleVisual(center=(600, 200, 0), height=200., width=300., radius=[30., 30., 0., 0.], color=(0.5, 0.5, 0.2, 1), border_color='white') rect.transform = transforms.NullTransform() self.visuals.append(rect) rpolygon = visuals.RegularPolygonVisual(center=(200., 600., 0), radius=160, color=(0.2, 0.8, 0.2, 1), border_color=(1, 1, 1, 1), sides=6) rpolygon.transform = transforms.NullTransform() self.visuals.append(rpolygon) self._timer = app.Timer('auto', connect=self.on_timer, start=True) self.show() def on_draw(self, ev): self.context.set_clear_color((0, 0, 0, 1)) self.context.set_viewport(0, 0, *self.physical_size) self.context.clear() for vis in self.visuals: vis.draw() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for vis in self.visuals: vis.transforms.configure(canvas=self, viewport=vp) def on_timer(self, event): polygon, ellipse, rect, rpolygon = self.visuals r = ellipse.radius ellipse.radius = r[0], r[1] + np.sin(event.elapsed * 10) ellipse.span_angle = (ellipse.span_angle + 100. * event.dt) % 360 c = (0.3 * (0.5 + np.sin(event.elapsed * 2 + 0)), 0.3 * (0.5 + np.sin(event.elapsed * 2 + np.pi * 2./3.)), 0.3 * (0.5 + np.sin(event.elapsed * 2 + np.pi * 4./3.))) polygon.color = c polygon.border_color = (.8, .8, .8, 0.5 + (0.5 * np.sin(event.elapsed*10))) rpolygon.radius = 100 + 10 * np.sin(event.elapsed * 3.1) rpolygon.sides = int(20 + 17 * np.sin(event.elapsed)) self.update() if __name__ == '__main__': win = Canvas() if sys.flags.interactive != 1: win.app.run() vispy-0.14.1/examples/basics/visuals/custom_visual.py0000644000175000017510000001042714507104216022321 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 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 from vispy import app from vispy.gloo import VertexBuffer from vispy.visuals import Visual from vispy.visuals.transforms import STTransform class MarkerVisual(Visual): # My full vertex shader, with just a `transform` hook. VERTEX_SHADER = """ #version 120 attribute vec2 a_position; attribute vec3 a_color; attribute float a_size; varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_radius; varying float v_linewidth; varying float v_antialias; void main (void) { v_radius = a_size; v_linewidth = 1.0; v_antialias = 1.0; v_fg_color = vec4(0.0,0.0,0.0,0.5); v_bg_color = vec4(a_color, 1.0); gl_Position = $transform(vec4(a_position,0,1)); gl_PointSize = 2.0*(v_radius + v_linewidth + 1.5*v_antialias); } """ FRAGMENT_SHADER = """ #version 120 varying vec4 v_fg_color; varying vec4 v_bg_color; varying float v_radius; varying float v_linewidth; varying float v_antialias; void main() { float size = 2.0*(v_radius + v_linewidth + 1.5*v_antialias); float t = v_linewidth/2.0-v_antialias; float r = length((gl_PointCoord.xy - vec2(0.5,0.5))*size); float d = abs(r - v_radius) - t; if( d < 0.0 ) gl_FragColor = v_fg_color; else { float alpha = d/v_antialias; alpha = exp(-alpha*alpha); if (r > v_radius) gl_FragColor = vec4(v_fg_color.rgb, alpha*v_fg_color.a); else gl_FragColor = mix(v_bg_color, v_fg_color, alpha); } } """ def __init__(self, pos=None, color=None, size=None): Visual.__init__(self, self.VERTEX_SHADER, self.FRAGMENT_SHADER) self._pos = pos self._color = color self._size = size self.set_gl_state(blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) self._draw_mode = 'points' def _prepare_transforms(self, view=None): view.view_program.vert['transform'] = view.transforms.get_transform() def _prepare_draw(self, view): # attributes / uniforms are not available until program is built self.shared_program['a_position'] = VertexBuffer(self._pos) self.shared_program['a_color'] = VertexBuffer(self._color) self.shared_program['a_size'] = VertexBuffer(self._size) class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive') ps = self.pixel_scale n = 10000 pos = 0.25 * np.random.randn(n, 2).astype(np.float32) color = np.random.uniform(0, 1, (n, 3)).astype(np.float32) size = np.random.uniform(2*ps, 12*ps, (n, 1)).astype(np.float32) self.points = MarkerVisual(pos=pos, color=color, size=size) w, h = self.size self.points.transform = STTransform(scale=(w / 2., h / 2.), translate=(w / 2., h / 2.)) def on_mouse_move(self, event): if event.is_dragging: dxy = event.pos - event.last_event.pos button = event.press_event.button if button == 1: self.points.transform.move(dxy) elif button == 2: center = event.press_event.pos self.points.transform.zoom(np.exp(dxy * (0.01, -0.01)), center) self.update() def on_resize(self, event): vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.points.transforms.configure(canvas=self, viewport=vp) def on_draw(self, event): self.context.clear('white') self.points.draw() if __name__ == '__main__': c = Canvas() c.show() app.run() vispy-0.14.1/examples/basics/visuals/colorbar_visual_types.py0000644000175000017510000000562214507104216024037 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """Using Colorbars with the Canvas to demo different types""" from vispy import app from vispy import gloo from vispy.visuals import ColorBarVisual from vispy.color import get_colormap colormap = get_colormap("viridis") def style_colorbar(colorbar): colorbar.label.font_size = 18 colorbar.label.color = "black" colorbar.clim = (0, 10) colorbar.ticks[0].font_size = 14 colorbar.ticks[1].font_size = 14 colorbar.ticks[0].color = "black" colorbar.ticks[1].color = "black" colorbar.border_width = 1 colorbar.border_color = "black" return colorbar def get_left_orientation_bar(): pos = 50, 300 size = 400, 10 colorbar = ColorBarVisual(pos=pos, size=size, label="orientation left", cmap=colormap, orientation="left") return style_colorbar(colorbar) def get_right_orientation_bar(): pos = 200, 300 size = 400, 10 colorbar = ColorBarVisual(pos=pos, size=size, label="orientation right", cmap=colormap, orientation="right") return style_colorbar(colorbar) def get_top_orientation_bar(): pos = 600, 400 size = 300, 10 colorbar = ColorBarVisual(pos=pos, size=size, label="orientation top", cmap=colormap, orientation="top") return style_colorbar(colorbar) def get_bottom_orientation_bar(): pos = 600, 150 size = 300, 10 colorbar = ColorBarVisual(pos=pos, size=size, label="orientation bottom", cmap=colormap, orientation="bottom") return style_colorbar(colorbar) class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(800, 600), keys="interactive") self.bars = [] self.bars.append(get_left_orientation_bar()) self.bars.append(get_right_orientation_bar()) self.bars.append(get_top_orientation_bar()) self.bars.append(get_bottom_orientation_bar()) self.show() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for bar in self.bars: bar.transforms.configure(canvas=self, viewport=vp) def on_draw(self, event): # clear the color buffer gloo.clear(color="white") for bar in self.bars: bar.draw() if __name__ == '__main__': win = Canvas() app.run() vispy-0.14.1/examples/basics/visuals/colorbar_visual.py0000644000175000017510000001076514507104216022617 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: gallery 30 # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """Using Colorbars with the Canvas with the Mandlebrot set""" from vispy import app from vispy import gloo from vispy.visuals.transforms import STTransform from vispy.visuals import ColorBarVisual, ImageVisual from vispy.color import Color, get_colormap import numpy as np ESCAPE_MAGNITUDE = 2 MIN_MAGNITUDE = 0.002 MAX_ITERATIONS = 50 colormap = get_colormap("hot") def get_num_escape_turns(x, y): """Returns the number of iterations it took to escape as normalized values. Parameters ---------- x: float the x coordinates of the point y: float the y coordinates of the point Returns ------- float: [0, 1] * 0 if it took 0 iterations to escape * 1 if did not escape in MAX_ITERATIONS iterations * a linearly interpolated number between 0 and 1 if the point took anywhere between 0 to MAX_ITERATIONS to escape """ c = complex(x, y) z = complex(x, y) num_iterations = 0 while (MIN_MAGNITUDE < np.absolute(z) < ESCAPE_MAGNITUDE and num_iterations < MAX_ITERATIONS): z = (z ** 2) + c num_iterations += 1 return float(num_iterations) / float(MAX_ITERATIONS) def get_mandlebrot_escape_values(width, height): """Constructs the Mandlebro set for a grid of dimensions (width, height) Parameters ---------- width: int width of the resulting grid height: int height of the resulting grid Returns ------- A grid of floating point values containing the output of get_num_escape_turns function for each point """ x_vals = np.linspace(-3, 2, width) y_vals = np.linspace(-1.5, 1.5, height) grid = np.meshgrid(x_vals, y_vals) v_get_num_escape_turns = np.vectorize(get_num_escape_turns) return v_get_num_escape_turns(*grid).astype(np.float32) def get_vertical_bar(pos, size): """ Constructs the vertical bar that represents the color values for the Mandlebrot set Returns ------- A vispy.visual.ColorBarVisual object that represents the data of the Mandlebrot set """ vertical = ColorBarVisual(pos=pos, size=size, label="iterations to escape", cmap=colormap, orientation="left") vertical.label.font_size = 15 vertical.label.color = "white" vertical.clim = (0, MAX_ITERATIONS) vertical.ticks[0].font_size = 10 vertical.ticks[1].font_size = 10 vertical.ticks[0].color = "white" vertical.ticks[1].color = "white" vertical.border_width = 1 vertical.border_color = Color("#ababab") return vertical class Canvas(app.Canvas): def __init__(self): # dimensions of generated image img_dim = np.array([700, 500]) # position of colorbar colorbar_pos = np.array([100, 300]) # size of the colorbar, measured as (major, minor) colorbar_size = np.array([400, 20]) # position of the generated image image_pos = np.array([200, 80]) app.Canvas.__init__(self, size=(800, 600), keys="interactive") img_data = get_mandlebrot_escape_values(img_dim[0], img_dim[1]) self.image = ImageVisual(img_data, cmap=colormap) self.image.transform = \ STTransform(scale=1.1, translate=image_pos) self.vertical_bar = get_vertical_bar(colorbar_pos, colorbar_size) self.show() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.image.transforms.configure(canvas=self, viewport=vp) self.vertical_bar.transforms.configure(canvas=self, viewport=vp) def on_draw(self, event): # clear the color buffer gloo.clear(color=colormap[0.0]) self.image.draw() # render the horizontal and vertical bar # with the TransformSystem we had created before # self.horizontal_bar.draw(self.colorbar_transform_sys) self.vertical_bar.draw() if __name__ == '__main__': win = Canvas() app.run() vispy-0.14.1/examples/basics/visuals/box.py0000644000175000017510000000413314507104216020211 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Simple demonstration of Box visual. """ from vispy import app, gloo, visuals from vispy.geometry import create_box from vispy.visuals.transforms import MatrixTransform class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, keys='interactive', size=(800, 550)) vertices, faces, outline = create_box(width=1, height=1, depth=1, width_segments=4, height_segments=8, depth_segments=16) self.box = visuals.BoxVisual(width=1, height=1, depth=1, width_segments=4, height_segments=8, depth_segments=16, vertex_colors=vertices['color'], edge_color='b') self.theta = 0 self.phi = 0 self.transform = MatrixTransform() self.box.transform = self.transform self.show() self.timer = app.Timer(connect=self.rotate) self.timer.start(0.016) def rotate(self, event): self.theta += .5 self.phi += .5 self.transform.reset() self.transform.rotate(self.theta, (0, 0, 1)) self.transform.rotate(self.phi, (0, 1, 0)) self.transform.scale((100, 100, 0.001)) self.transform.translate((200, 200)) self.update() def on_resize(self, event): # Set canvas viewport and reconfigure visual transforms to match. vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.box.transforms.configure(canvas=self, viewport=vp) def on_draw(self, ev): gloo.clear(color='white', depth=True) self.box.draw() if __name__ == '__main__': win = Canvas() import sys if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/bezier.py0000644000175000017510000000472514507104216020710 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ This example demonstrates how to draw curved lines (bezier). """ import sys from vispy import app, gloo, visuals from vispy.geometry import curves from vispy.visuals.transforms import STTransform, NullTransform class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title='Bezier lines example', keys='interactive', size=(400, 750)) self.lines = [ visuals.LineVisual(curves.curve4_bezier( (10, 0), (50, -190), (350, 190), (390, 0) ), color='w', width=2, method='agg'), visuals.LineVisual(curves.curve4_bezier( (10, 0), (190, -190), (210, 190), (390, 0) ), color='w', width=2, method='agg'), visuals.LineVisual(curves.curve3_bezier( (10, 0), (30, 200), (390, 0) ), color='w', width=2, method='agg') ] # Translate each line visual downwards for i, line in enumerate(self.lines): x = 0 y = 200 * (i + 1) line.transform = STTransform(translate=[x, y]) self.texts = [ visuals.TextVisual('Third order curve', bold=True, color='w', font_size=14, pos=(200, 75)), visuals.TextVisual('Quadratic curve', bold=True, color='w', font_size=14, pos=(200, 525)), ] for text in self.texts: text.transform = NullTransform() self.visuals = self.lines + self.texts self.show() def on_draw(self, event): gloo.clear('black') gloo.set_viewport(0, 0, *self.physical_size) for visual in self.visuals: visual.draw() def on_resize(self, event): vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for visual in self.visuals: visual.transforms.configure(canvas=self, viewport=vp) if __name__ == '__main__': win = Canvas() if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/axially_symmetric_surfaces.py0000644000175000017510000000725614507104216025064 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ * Demonstrates the usage of SurfacePlot() using meshes created in spherical and cylindrical coordinates to generate surfaces with rotational symmetry. * Meshes can be generated in xy, yz or zx axes. See create_cylinder(). * Also this example demonstrates the creation of surfaces with discontinuity See create_circular_hole() """ import sys import numpy as np from vispy import scene from vispy.scene.visuals import SurfacePlot from vispy.scene.visuals import XYZAxis def create_cylinder(radius, length, center=(0.0, 0.0, 0.0)): """ Creates the data of a cylinder oriented along z axis whose center, radius and length are given as inputs Based on the example given at: https://stackoverflow.com/a/49311446/2602319 """ z = np.linspace(0, length, 100) theta = np.linspace(0, 2 * np.pi, 100) theta_grid, z_grid = np.meshgrid(theta, z) x_grid = radius * np.cos(theta_grid) + center[0] y_grid = radius * np.sin(theta_grid) + center[1] z_grid = z_grid + center[2] return x_grid, y_grid, z_grid def create_paraboloid(a, b, c, radius=1.0, center=(0.0, 0.0, 0.0)): """ Creates the data of a paraboloid whose center, radius and a, b, c parameters are given as inputs """ # Generate the grid in cylindrical coordinates r = np.linspace(0, radius, 100) theta = np.linspace(0, 2 * np.pi, 100) R, THETA = np.meshgrid(r, theta) # Convert to rectangular coordinates x_grid, y_grid = R * np.cos(THETA), R * np.sin(THETA) z_grid = c * ((x_grid / a) ** 2 + (y_grid / b) ** 2) # Elliptic paraboloid return x_grid + center[0], y_grid + center[1], z_grid + center[2] def create_sphere(radius=1.0, center=(0.0, 0.0, 0.0)): """ Creates the data of a sphere whose center, and radius are given as inputs """ # Generate the grid in spherical coordinates # Names of the spherical coordinate axes according to ISO convention theta = np.linspace(0, np.pi, 50) phi = np.linspace(0, 2 * np.pi, 50) PHI, THETA = np.meshgrid(phi, theta) RHO = radius # Size of the sphere # Convert to cartesian coordinates x_grid = (RHO * np.sin(THETA) * np.cos(PHI)) + center[0] y_grid = (RHO * np.sin(THETA) * np.sin(PHI)) + center[1] z_grid = (RHO * np.cos(THETA)) + center[2] return x_grid, y_grid, z_grid def create_circular_hole(x_grid, y_grid, hole_radius=0.5, center=(0.0, 0.0)): X = np.where((x_grid - center[0]) ** 2 + (y_grid - center[1]) ** 2 <= hole_radius ** 2, np.NAN, x_grid) Y = np.where((x_grid - center[0]) ** 2 + (y_grid - center[1]) ** 2 <= hole_radius ** 2, np.NAN, y_grid) return X, Y # Prepare canvas canvas = scene.SceneCanvas(keys='interactive', size=(800, 600), show=True) view = canvas.central_widget.add_view() camera = scene.cameras.TurntableCamera(fov=60, elevation=30, azimuth=50, parent=view.scene) camera.set_range((-4, 4), (-4, 4), (-4, 4)) view.camera = camera # Add a 3D axis XYZAxis(parent=view.scene) x_grid, y_grid, z_grid = create_paraboloid(6, 3, 3, radius=2, center=(0, 0, 3)) x_grid, y_grid = create_circular_hole(x_grid, y_grid, hole_radius=0.5, center=(0.0, 0.0)) SurfacePlot(x_grid, y_grid, z_grid, color="aquamarine", parent=view.scene) x_grid, y_grid, z_grid = create_cylinder(1, 3, center=(0, 0, 0)) SurfacePlot(x_grid, y_grid, z_grid, color="lightgreen", parent=view.scene) x_grid, y_grid, z_grid = create_sphere(0.5, center=(0, 0, 4)) SurfacePlot(x_grid, y_grid, z_grid, color='lightseagreen', parent=view.scene) if __name__ == '__main__' and sys.flags.interactive == 0: canvas.app.run() vispy-0.14.1/examples/basics/visuals/arrows_quiver.py0000755000175000017510000000530514507104216022336 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ This example shows how to use the `ArrowVisual` for a quiver plot """ from __future__ import division import sys import itertools import numpy as np from vispy import app, gloo, visuals from vispy.visuals.transforms import NullTransform class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title="Quiver plot", keys="interactive", size=(830, 430)) self.arrow_length = 20 self.grid_coords = None self.line_vertices = None self.last_mouse = (0, 0) self.generate_grid() self.visual = visuals.ArrowVisual( color='white', connect='segments', arrow_size=8 ) self.visual.events.update.connect(lambda evt: self.update()) self.visual.transform = NullTransform() self.show() def generate_grid(self): num_cols = int(self.physical_size[0] / 50) num_rows = int(self.physical_size[1] / 50) coords = [] # Generate grid for i, j in itertools.product(range(num_rows), range(num_cols)): x = 25 + (50 * j) y = 25 + (50 * i) coords.append((x, y)) self.grid_coords = np.array(coords) def on_resize(self, event): self.generate_grid() self.rotate_arrows(np.array(self.last_mouse)) vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.visual.transforms.configure(canvas=self, viewport=vp) def rotate_arrows(self, point_towards): direction_vectors = (self.grid_coords - point_towards).astype( np.float32) norms = np.sqrt(np.sum(direction_vectors**2, axis=-1)) direction_vectors[:, 0] /= norms direction_vectors[:, 1] /= norms vertices = np.repeat(self.grid_coords, 2, axis=0) vertices[::2] = vertices[::2] + ((0.5 * self.arrow_length) * direction_vectors) vertices[1::2] = vertices[1::2] - ((0.5 * self.arrow_length) * direction_vectors) self.visual.set_data( pos=vertices, arrows=vertices.reshape((len(vertices)//2, 4)), ) def on_mouse_move(self, event): self.last_mouse = event.pos self.rotate_arrows(np.array(event.pos)) def on_draw(self, event): gloo.clear('black') self.visual.draw() if __name__ == '__main__': win = Canvas() if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/arrows.py0000644000175000017510000000621414507104216020740 0ustar nileshnilesh#!/usr/bin/env python # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ This example demonstrates how to draw lines with one or more arrow heads. """ import sys import numpy as np from vispy import app, gloo, visuals from vispy.geometry import curves from vispy.visuals.transforms import STTransform class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, title='Arrows example', keys='interactive', size=(1050, 650)) line1 = curves.curve4_bezier( (10.0, 0.0), (50, -190), (350, 190), (390, 0.0) ) arrows1 = np.array([ line1[-2], line1[-1] ]).reshape((1, 4)) line2 = curves.curve4_bezier( (10.0, 0.0), (190, -190), (210, 190), (390, 0.0) ) arrows2 = np.array([ line2[1], line2[0], line2[-2], line2[-1] ]).reshape((2, 4)) line3 = curves.curve3_bezier( (10.0, 0.0), (50, 190), (390, 0.0) ) arrows3 = np.array([ line3[-2], line3[-1] ]).reshape((1, 4)) arrow_types = ["curved", "stealth", "inhibitor_round", "angle_60"] self.lines = [] for i, arrow_type in enumerate(arrow_types): arrows = [ visuals.ArrowVisual(line1, color='w', width=6, method='agg', arrows=arrows1, arrow_type=arrow_type, arrow_size=30.0), visuals.ArrowVisual(line2, color='w', width=2, method='agg', arrows=arrows2, arrow_type=arrow_type, arrow_size=5.0), visuals.ArrowVisual(line3, color='w', width=4, method='agg', arrows=arrows3, arrow_type=arrow_type, arrow_size=10.0) ] # Translate each line visual downwards for j, visual in enumerate(arrows): x = 50 + (i * 250) y = 100 + (200 * j) visual.transform = STTransform(translate=[x, y], scale=(0.5, 1.0)) visual.events.update.connect(lambda event: self.update()) self.lines.extend(arrows) self.show() def on_draw(self, event): gloo.clear('black') for visual in self.lines: visual.draw() def on_resize(self, event): vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) for visual in self.lines: visual.transforms.configure(canvas=self, viewport=vp) if __name__ == '__main__': win = Canvas() if sys.flags.interactive != 1: app.run() vispy-0.14.1/examples/basics/visuals/arcball.py0000644000175000017510000000460214507104216021022 0ustar nileshnilesh# -*- coding: utf-8 -*- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. """ Demonstration of how to interact with visuals, here with simple arcball-style control. """ import sys import numpy as np from vispy import app from vispy.visuals import BoxVisual, transforms from vispy.util.quaternion import Quaternion class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, 'Cube', keys='interactive', size=(400, 400)) self.cube = BoxVisual(1.0, 0.5, 0.25, color='red', edge_color='black') self.cube.transform = transforms.MatrixTransform() self.cube.transform.scale((100, 100, 0.001)) self.cube.transform.translate((200, 200)) self.quaternion = Quaternion() self.show() def on_resize(self, event): vp = (0, 0, self.physical_size[0], self.physical_size[1]) self.context.set_viewport(*vp) self.cube.transforms.configure(canvas=self, viewport=vp) def on_draw(self, event): self.context.clear('white') self.cube.draw() def on_mouse_move(self, event): if event.button == 1 and event.last_event is not None: x0, y0 = event.last_event.pos x1, y1 = event.pos w, h = self.size self.quaternion = (self.quaternion * Quaternion(*_arcball(x0, y0, w, h)) * Quaternion(*_arcball(x1, y1, w, h))) self.cube.transform.matrix = self.quaternion.get_matrix() self.cube.transform.scale((100, 100, 0.001)) self.cube.transform.translate((200, 200)) self.update() def _arcball(x, y, w, h): """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 """ 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)) if __name__ == '__main__': win = Canvas() win.show() if sys.flags.interactive != 1: win.app.run() vispy-0.14.1/examples/basics/scene/0000755000175000017510000000000014507104250016453 5ustar nileshnileshvispy-0.14.1/examples/basics/scene/stereo.py0000644000175000017510000000345714507104216020341 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Example demonstrating stereo vision in a scene has an anisotropic aspect ratio. This example can be used to test that the cameras behave correctly with nested translated/rotated cameras. NOTE: This example is currently broken! """ import numpy as np from vispy import app, scene, io # Read volume vol1 = np.load(io.load_data_file('volume/stent.npz'))['arr_0'] # Prepare canvas canvas = scene.SceneCanvas(keys='interactive') canvas.size = 800, 600 canvas.show() canvas.measure_fps() # Set up a viewbox to display the image with interactive pan/zoom # Create two ViewBoxes, place side-by-side vb1 = scene.widgets.ViewBox(border_color='yellow', parent=canvas.scene) vb2 = scene.widgets.ViewBox(border_color='blue', parent=canvas.scene) # This is temporarily needed because fragment clipping method is not yet # compatible with multiple parenting. vb1.clip_method = 'viewport' vb2.clip_method = 'viewport' scenes = vb1.scene, vb2.scene # grid = canvas.central_widget.add_grid() grid.padding = 6 grid.add_widget(vb1, 0, 0) grid.add_widget(vb2, 0, 1) # Create the volume visuals, only one is visible volume1 = scene.visuals.Volume(vol1, parent=scenes, threshold=0.5) # Create cameras. The second is a child of the first, thus inheriting # its transform. cam1 = scene.cameras.TurntableCamera(parent=scenes, fov=60) cam2 = scene.cameras.PerspectiveCamera(parent=cam1, fov=60) # cam2.transform.translate((+10, 0, 0)) vb1.camera = cam1 vb2.camera = cam2 if __name__ == '__main__': app.run() vispy-0.14.1/examples/basics/scene/shared_context.py0000644000175000017510000000275614507104216022053 0ustar nileshnilesh# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ This example demonstrates the use of multiple canvases with visuals shared between them. """ import sys import numpy as np from vispy import app, scene from vispy.util.filter import gaussian_filter # NOTE: Does not work with PyQt5 currently canvas1 = scene.SceneCanvas(keys='interactive', show=True) view1 = canvas1.central_widget.add_view() view1.camera = scene.TurntableCamera(fov=60) canvas2 = scene.SceneCanvas(keys='interactive', show=True, shared=canvas1.context) view2 = canvas2.central_widget.add_view() view2.camera = 'panzoom' # Simple surface plot example # x, y values are not specified, so assumed to be 0:50 z = gaussian_filter(np.random.normal(size=(50, 50)), (1, 1)) * 10 p1 = scene.visuals.SurfacePlot(z=z, color=(0.5, 0.5, 1, 1), shading='smooth') p1.transform = scene.transforms.MatrixTransform() p1.transform.scale([1/49., 1/49., 0.02]) p1.transform.translate([-0.5, -0.5, 0]) view1.add(p1) view2.add(p1) # Add a 3D axis to keep us oriented axis = scene.visuals.XYZAxis(parent=view1.scene) canvas = canvas1 # allow running this example in our test suite if __name__ == '__main__': if sys.flags.interactive == 0: app.run() vispy-0.14.1/examples/basics/scene/modular_shaders/0000755000175000017510000000000014507104250021627 5ustar nileshnileshvispy-0.14.1/examples/basics/scene/modular_shaders/sandbox.py0000644000175000017510000003443014507104216023645 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ Sandbox for experimenting with vispy.visuals.shaders """ from PyQt5 import QtCore from PyQt5.QtWidgets import * # noqa import sys import traceback from editor import Editor, HAVE_QSCI presets = [ ('Introduction', ''' """ ------ Shader Composition Sandbox ------- Instructions: 1) Edit this code; it is immediately executed after every change. Exceptions will be displayed on the right side. 2) Assign strings to VERTEX and FRAGMENT variables (see below) and they will appear in the windows to the right. 3) Select presets from the list above to see a few examples. """ from vispy.visuals.shaders import ModularProgram vertex_shader = "void main() {}" fragment_shader = "void main() {}" program = ModularProgram(vertex_shader, fragment_shader) # obligatory: these variables are used to fill the text fields on the right. program._compile() VERTEX = program.vert_code FRAGMENT = program.frag_code '''), ('Simple hook', ''' """ In this example we define a 'hook' in the vertex shader: a function prototype with no definition. By leaving this function undefined, any new function definition may be concatenated to the shader. """ from vispy.visuals.shaders import ModularProgram, Function # The hook is called 'input_position', and is used to provide the # value for gl_Position. vertex_shader = """ vec4 input_position(); void main() { gl_Position = input_position(); } """ fragment_shader = """ void main() { } """ # ModularProgram parses the shader code for function prototypes # and registers each as a hook. program = ModularProgram(vertex_shader, fragment_shader) # Now we make a new function definition and attach it to the program. func = Function(""" vec4 input_position() { return vec4(0,0,0,0); } """) program['input_position'] = func # obligatory: these variables are used to fill the text fields on the right. program._compile() VERTEX = program.vert_code FRAGMENT = program.frag_code '''), ('Anonymous functions', ''' """ Functions may optionally be defined with '$' in front of the function name. This indicates that the function is anonymous (has no name) and thus may be assigned any new name in the program. The major benefit to using anonymous functions is that the modular shader system is free to rename functions that would otherwise conflict with each other. In this example, an anonymous function is assigned to a hook. When it is compiled into the complete program, it is renamed to match the hook. """ from vispy.visuals.shaders import ModularProgram, Function vertex_shader = """ vec4 input_position(); void main() { gl_Position = input_position(); } """ fragment_shader = """ void main() { } """ program = ModularProgram(vertex_shader, fragment_shader) # Now we make a new function definition and attach it to the program. # Note that this function is anonymous (name begins with '$') and does not # have the correct name to be attached to the input_position hook. func = Function(""" vec4 $my_function() { return vec4(0,0,0,0); } """) program['input_position'] = func # obligatory: these variables are used to fill the text fields on the right. program._compile() VERTEX = program.vert_code FRAGMENT = program.frag_code '''), ('Program variables', ''' """ Many Functions need to define their own program variables (uniform/attribute/varying) in order to operate correctly. However, with many independent functions added to a ModularProgram, it is likely that two Functions might try to define variables of the same name. To solve this, Functions may use $anonymous_variables that will be assigned to a real program variable at compile time. In the next example, we will see how ModularProgram resolves name conflicts. """ from vispy.visuals.shaders import ModularProgram, Function import numpy as np vertex_shader = """ vec4 transform_position(vec4); attribute vec4 position_a; void main() { gl_Position = transform_position(position_a); } """ fragment_shader = """ void main() { } """ program = ModularProgram(vertex_shader, fragment_shader) # Define a function to do a matrix transform. # The variable $matrix will be substituted with a uniquely-named program # variable when the function is compiled. func = Function(""" vec4 $matrix_transform(vec4 pos) { return $matrix * pos; } """) # The definition for 'matrix' must indicate the variable type and data type. func['matrix'] = ('uniform', 'mat4', np.eye(4)) program.set_hook('transform_position', func) # obligatory: these variables are used to fill the text fields on the right. program._compile() VERTEX = program.vert_code FRAGMENT = program.frag_code '''), ('Resolving name conflicts', ''' """ When anonymous functions and variables have conflicting names, the ModularProgram will generate unique names by appending _N to the end of the name. This example demonstrates dynamic naming of a program variable. """ from vispy.visuals.shaders import ModularProgram, Function import numpy as np vertex_shader = """ vec4 projection(vec4); vec4 modelview(vec4); attribute vec4 position_a; void main() { gl_Position = projection(modelview(position_a)); } """ fragment_shader = """ void main() { } """ program = ModularProgram(vertex_shader, fragment_shader) # Define two identical functions projection = Function(""" vec4 $matrix_transform(vec4 pos) { return $matrix * pos; } """) projection['matrix'] = ('uniform', 'mat4', np.eye(4)) modelview = Function(""" vec4 $matrix_transform(vec4 pos) { return $matrix * pos; } """) modelview['matrix'] = ('uniform', 'mat4', np.eye(4)) program.set_hook('projection', projection) program.set_hook('modelview', modelview) # obligatory: these variables are used to fill the text fields on the right. program._compile() VERTEX = program.vert_code FRAGMENT = program.frag_code '''), ('Function chaining', ''' """ Function chains are another essential component of shader composition, allowing a list of functions to be executed in order. """ from vispy.visuals.shaders import ModularProgram, Function, FunctionChain # Added a new hook to allow any number of functions to be executed # after gl_Position is set. vertex_shader = """ void vert_post_hook(); attribute vec4 position_a; void main() { gl_Position = position_a; vert_post_hook(); } """ fragment_shader = """ void main() { } """ program = ModularProgram(vertex_shader, fragment_shader) # Add a function to flatten the z-position of the vertex flatten = Function(""" void flatten_func() { gl_Position.z = 0; } """) # Add another function that copies an attribute to a varying # for use in the fragment shader read_color_attr = Function(""" void $read_color_attr() { $output = $input; } """) # ..and set two new program variables: # (note that no value is needed for varyings) read_color_attr['output'] = ('varying', 'vec4') read_color_attr['input'] = ('attribute', 'vec4', 'color_a') # Now create a chain that calls both functions in sequence post_chain = FunctionChain('vert_post_hook', [flatten, read_color_attr]) program.set_hook('vert_post_hook', post_chain) # obligatory: these variables are used to fill the text fields on the right. program._compile() VERTEX = program.vert_code FRAGMENT = program.frag_code '''), ('Function composition', ''' """ Chains may also be used to generate a function composition where the return value of each function call supplies the input to the next argument. Thus, the original input is transformed in a series steps. This is most commonly used for passing vertex positions through a composition of transform functions. """ from vispy.visuals.shaders import ModularProgram, Function, FunctionChain vertex_shader = """ vec4 transform_chain(vec4); attribute vec4 position_a; void main() { gl_Position = transform_chain(position_a); } """ fragment_shader = """ void main() { } """ program = ModularProgram(vertex_shader, fragment_shader) flatten = Function(""" vec4 flatten_func(vec4 pos) { pos.z = 0; pos.w = 1; return pos; } """) # Define a scaling function scale = Function(""" vec4 $scale_vertex(vec4 pos) { return pos * vec4($scale, 1); } """) scale['scale'] = ('uniform', 'vec3', (2, 1, 1)) # Assigning a list of both functions to a program hook will gemerate a # composition of functions: program['transform_chain'] = [flatten, scale] # Internally, this creates a FunctionChain: # transform = FunctionChain('transform_chain', [flatten, scale]) # obligatory: these variables are used to fill the text fields on the right. program._compile() VERTEX = program.vert_code FRAGMENT = program.frag_code '''), ('Fragment shaders', ''' """ Although the prior examples focused on vertex shaders, these concepts apply equally well for fragment shaders. However: fragment shaders have one limitation that makes them very different--they lack attributes. In order to supply attribute data to a fragment shader, we will need to introduce some supporting code to the vertex shader. """ from vispy.visuals.shaders import (ModularProgram, Function, FunctionChain) from vispy.gloo import VertexBuffer import numpy as np # we require a void hook in the vertex shader that can be used # to attach supporting code for the fragment shader. vertex_shader = """ void vert_post_hook(); attribute vec4 position_a void main() { gl_Position = position_a; vert_post_hook(); } """ # add a hook to the fragment shader to allow arbitrary color input fragment_shader = """ vec4 fragment_color(); void main() { gl_FragColor = fragment_color(); } """ program = ModularProgram(vertex_shader, fragment_shader) # First, define a simple fragment color function and bind it to a varying # input: frag_func = Function("vec4 $frag_color_input() { return $f_input; }") frag_func['f_input'] = ('varying', 'vec4') # Attach to the program program['fragment_color'] = frag_func # Next, we need a vertex shader function that will supply input # to the varying. vert_func = Function("void $vert_color_input() { $v_output = $v_input; }") colors = VertexBuffer(np.array([[1,1,1,1]], dtype=np.float32)) vert_func['v_input'] = ('attribute', 'vec4', colors) # to ensure both the vertex function output and the fragment function input # are attached to the same varying, we use the following syntax: vert_func['v_output'] = frag_func['f_input'] # and attach this to the vertex shader program['vert_post_hook'] = vert_func # obligatory: these variables are used to fill the text fields on the right. program._compile() VERTEX = program.vert_code FRAGMENT = program.frag_code '''), ('Sub-hooks', ''' """ """ from vispy.visuals.shaders import (ModularProgram, Function, FunctionChain) from vispy.gloo import VertexBuffer import numpy as np vertex_shader = """ void vert_post_hook(); void main() { gl_Position = vec4(0,0,0,0); vert_post_hook(); } """ fragment_shader = """ void main() { } """ program = ModularProgram(vertex_shader, fragment_shader) # Create a function that calls another function vert_func = Function(""" void $vert_func() { $some_other_function(); } """) # Create the second function: other_func = Function(""" void $other_func() { gl_Position.w = 1; } """) # Assign other_func to the anonymous function call in vert_func: vert_func['some_other_function'] = other_func # The name assigned to other_func will be inserted in place of # the function call in vert_func program['vert_post_hook'] = vert_func # obligatory: these variables are used to fill the text fields on the right. program._compile() VERTEX = program.vert_code FRAGMENT = program.frag_code '''), ] qsci_note = """ # [[ NOTE: Install PyQt.QsciScintilla for improved code editing ]] # [[ (Debian packages: python-qscintilla2 or python3-pyqt5.qsci ]] """ if not HAVE_QSCI: presets[0] = (presets[0][0], qsci_note + presets[0][1]) app = QApplication([]) win = QMainWindow() cw = QWidget() win.setCentralWidget(cw) layout = QGridLayout() cw.setLayout(layout) editor = Editor(language='Python') vertex = Editor(language='CPP') fragment = Editor(language='CPP') for i in range(3): editor.zoomOut() vertex.zoomOut() fragment.zoomOut() hsplit = QSplitter(QtCore.Qt.Horizontal) vsplit = QSplitter(QtCore.Qt.Vertical) layout.addWidget(hsplit) hsplit.addWidget(editor) hsplit.addWidget(vsplit) vsplit.addWidget(vertex) vsplit.addWidget(fragment) menubar = win.menuBar() last_loaded = -1 def load_example(name): global last_loaded if isinstance(name, int): code = presets[name][1] editor.setText(code) last_loaded = name else: for i, preset in enumerate(presets): n, code = preset if n == name: editor.setText(code) last_loaded = i return def load_next(): global last_loaded try: load_example(last_loaded+1) except IndexError: pass def mk_load_callback(name): return lambda: load_example(name) example_menu = menubar.addMenu('Load example..') for i, preset in enumerate(presets): name = preset[0] action = example_menu.addAction("%d. %s" % (i, name), mk_load_callback(name)) next_action = menubar.addAction("Next example", load_next) win.show() win.resize(1800, 1100) hsplit.setSizes([900, 900]) load_example(0) def update(): code = editor.text() local = {} glob = {} try: exec(code, local, glob) vert = glob['VERTEX'] frag = glob['FRAGMENT'] editor.clear_marker() except Exception: vert = traceback.format_exc() frag = "" tb = sys.exc_info()[2] while tb is not None: # print(tb.tb_lineno, tb.tb_frame.f_code.co_filename) try: if tb.tb_frame.f_code.co_filename == '': editor.set_marker(tb.tb_lineno-1) except Exception: pass tb = tb.tb_next vertex.setText(vert) fragment.setText(frag) editor.textChanged.connect(update) update() if __name__ == '__main__': app.exec_() vispy-0.14.1/examples/basics/scene/modular_shaders/editor.py0000644000175000017510000001560714507104216023502 0ustar nileshnilesh# -*- coding: utf-8 -*- # vispy: testskip # ----------------------------------------------------------------------------- # Copyright (c) Vispy Development Team. All Rights Reserved. # Distributed under the (new) BSD License. See LICENSE.txt for more info. # ----------------------------------------------------------------------------- """ # QScintilla editor # # Adapted from Eli Bendersky (eliben@gmail.com) # This code is in the public domain # # API: http://pyqt.sourceforge.net/Docs/QScintilla2/classQsciScintilla.html # """ import sys import re from PyQt5.QtCore import * # noqa from PyQt5.QtWidgets import * # noqa try: from PyQt5 import Qsci from PyQt5.Qsci import QsciScintilla HAVE_QSCI = True except ImportError: HAVE_QSCI = False if not HAVE_QSCI: # backup editor in case QScintilla is not available class Editor(QPlainTextEdit): def __init__(self, parent=None, language=None): QPlainTextEdit.__init__(self, parent) def setText(self, text): self.setPlainText(text) def text(self): return str(self.toPlainText()).encode('UTF-8') def __getattr__(self, name): return lambda: None else: class Editor(QsciScintilla): ARROW_MARKER_NUM = 8 def __init__(self, parent=None, language='Python'): super(Editor, self).__init__(parent) self.setIndentationsUseTabs(False) self.setIndentationWidth(4) # Set the default font font = QFont() font.setFamily('DejaVu Sans Mono') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) self.zoomIn() # Margin 0 is used for line numbers fontmetrics = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(0, fontmetrics.width("000") + 6) self.setMarginLineNumbers(0, True) self.setMarginsBackgroundColor(QColor("#cccccc")) self._marker = None # Clickable margin 1 for showing markers # self.setMarginSensitivity(1, True) # self.connect(self, # SIGNAL('marginClicked(int, int, Qt::KeyboardModifiers)'), # self.on_margin_clicked) self.markerDefine(QsciScintilla.RightArrow, self.ARROW_MARKER_NUM) self.setMarkerBackgroundColor(QColor("#ee1111"), self.ARROW_MARKER_NUM) # Brace matching: enable for a brace immediately before or after # the current position # self.setBraceMatching(QsciScintilla.SloppyBraceMatch) # Current line visible with special background color self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#ffe4e4")) # Set Python lexer # Set style for Python comments (style number 1) to a fixed-width # courier. # lexer = getattr(Qsci, 'QsciLexer' + language)() lexer.setDefaultFont(font) self.setLexer(lexer) self.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1, 'Courier') # Don't want to see the horizontal scrollbar at all # Use raw message to Scintilla here (all messages are documented # here: http://www.scintilla.org/ScintillaDoc.html) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.setWrapMode(QsciScintilla.WrapWord) self.setEolMode(QsciScintilla.EolUnix) # not too small # self.setMinimumSize(600, 450) def set_marker(self, line): self.clear_marker() self.markerAdd(line, self.ARROW_MARKER_NUM) self._marker = line def clear_marker(self): if self._marker is not None: self.markerDelete(self._marker, self.ARROW_MARKER_NUM) # def on_margin_clicked(self, nmargin, nline, modifiers): # Toggle marker for the line the margin was clicked on # if self.markersAtLine(nline) != 0: # self.markerDelete(nline, self.ARROW_MARKER_NUM) # else: # self.markerAdd(nline, self.ARROW_MARKER_NUM) def wheelEvent(self, ev): # Use ctrl+wheel to zoom in/out if Qt.ControlModifier & ev.modifiers(): if ev.delta() > 0: self.zoomIn() else: self.zoomOut() else: return super(Editor, self).wheelEvent(ev) def keyPressEvent(self, ev): if int(Qt.ControlModifier & ev.modifiers()) > 0: if ev.key() == Qt.Key_Slash: self.comment(True) return elif ev.key() == Qt.Key_Question: self.comment(False) return elif (ev.key() == Qt.Key_Z and Qt.ShiftModifier & ev.modifiers()): self.redo() return elif ev.key() == Qt.Key_Q: sys.exit(0) return super(Editor, self).keyPressEvent(ev) def text(self): return str(super(Editor, self).text()).encode('UTF-8') def comment(self, comment=True): sel = self.getSelection()[:] text = self.text() lines = text.split('\n') if sel[0] == -1: # toggle for just this line row, col = self.getCursorPosition() line = lines[row] self.setSelection(row, 0, row, len(line)) if comment: line = '#' + line else: line = line.replace("#", "", 1) self.replaceSelectedText(line) self.setCursorPosition(row, col+(1 if col > 0 else 0)) else: block = lines[sel[0]:sel[2]] # make sure all lines have # new = [] if comment: for line in block: new.append('#' + line) else: for line in block: if line.strip() == '': new.append(line) continue if re.match(r'\s*\#', line) is None: return new.append(line.replace('#', '', 1)) self.setSelection(sel[0], 0, sel[2], 0) self.replaceSelectedText('\n'.join(new) + '\n') # shift = 1 if comment else -1 self.setSelection(sel[0], max(0, sel[1]), sel[2], sel[3]) if __name__ == "__main__": app = QApplication(sys.argv) editor = Editor() editor.show() editor.setText(open(sys.argv[0]).read()) editor.resize(800, 800) app.exec_() vispy-0.14.1/examples/README.rst0000644000175000017510000000022514507104216015602 0ustar nileshnilesh.. title:: Gallery Gallery ======= Gallery examples are split into sections based on interface. There is a section for Gloo, Scene, and Plotting. vispy-0.14.1/doc/0000755000175000017510000000000014507104250013041 5ustar nileshnileshvispy-0.14.1/doc/thirdparty.rst0000644000175000017510000000113214507104216015764 0ustar nileshnileshThird Party Projects ==================== External projects that extend VisPy's capabilities are listed below. If you would like to have your project added to this list, please let us know by either making an issue on GitHub or, even better, making a pull request with the addition to this page. Tools ----- These are standalone applications that utilize VisPy. DIVE ^^^^ | `DIVE `_ is an extendable Qt GUI designed to simplify the process for plotting pandas DataFrames in VisPy. | Key features include: 2D/3D axes, animated plots, and data filtering/selection. vispy-0.14.1/doc/roadmap.rst0000644000175000017510000001746614507104216015236 0ustar nileshnileshRoadmap ======= Where do we see VisPy going in the future? What is development focused on? What can users look forward to in upcoming VisPy major releases? We try to answer these types of "big picture" questions below. For shorter term issues and plans see the `GitHub issue tracker `_. Road to Version 1.0 ------------------- At the time of writing VisPy still doesn't have a 1.x version number despite being used by hundreds of users from graduate students to industry. We believe a 1.0 version should represent stability and feeling of completeness when it comes to interfaces, performance, and functionality. We don't think we're there yet, but we have ideas for what a 1.0 release might look like and we've described it below. Performance and Collections ^^^^^^^^^^^^^^^^^^^^^^^^^^^ One of the main reasons users come to VisPy (or so we've been told) is the performance of visualizing **a lot** of data or visualizing data that updates quickly. VisPy does a pretty good job covering users for these use cases, but it can do better. The main areas that need improvement are: 1. ``SceneCanvas`` (`#1991 `_): In other visualization spaces, a scene graph performs optimizations for when and how components are drawn. If multiple pieces of a visualization have shared logic, let's not recompile or re-communicate things to the GPU. Let's be smart. 2. **Collections of Visuals**: Along with the SceneCanvas, VisPy needs more Visuals that support defining multiple instances of the same object. VisPy has a very low-level "collections" set of objects, but these aren't accessible or usable to the higher-level Visuals/SceneCanvas interfaces. A solution to this may be a rewrite to some Visuals to allow for multiple instances or for a complete rewrite of parts of VisPy to detect when multiple of the same object are being created and combine them together for optimized drawing. 3. **Alternative Data Containers**: See 'Dask and CuPy Integration' below. 4. **Jupyter Widgets** (`#134 `_, `#1989 `_): VisPy's Jupyter widget does not perform well. It can draw most things nowadays, but eventually lags behind any updates whether they be from timers or user input. Plotting API ^^^^^^^^^^^^ The Plotting API in VisPy has been a dream in the back of the VisPy developers' minds since the beginning of development. While the plotting interfaces exist, they are not very flexible and at times don't perform very well. It can be difficult with the plotting APIs to customize all the pieces that you want to or update data after the initial creation. This sometimes requires accessing hidden (`_` prefix) attributes and going multiple levels deep into complex (compound) visuals just to change the size or style of something. This deserves a real specification and plan for how users can get the most out of these interfaces so that they stay simple but useful. Dask and CuPy Integration ^^^^^^^^^^^^^^^^^^^^^^^^^ Dask and CuPy arrays present a very interesting opportunity for VisPy to display larger data and at faster speeds than previously possible. However, VisPy doesn't currently make this easier or even possible in some cases. With Dask, VisPy should be able to re-compute or re-load data that the user provides and throw it to the GPU when it is ready. With CuPy users should be able to do all their computations on the GPU and then let VisPy visualize that data **without** ever needing to copy it back to the CPU. See `#1985 `_ and `#1986 `_ for more information and discussion on these topics. Low-level leakage ^^^^^^^^^^^^^^^^^ VisPy depends heavily on OpenGL for all of its drawing functionality. While this performs well, we've had to bring some of the low-level logic of OpenGL into higher levels of VisPy to make things work. As VisPy continues to grow we'd like to make sure that any pieces specific to OpenGL stay in the low level parts of VisPy and are accessed through defined interfaces. See the "OpenGL, Vulkan, and WebGPU" section below for why this is important. Primitive Visuals ^^^^^^^^^^^^^^^^^ Along the same lines of preventing low-level APIs leaking into higher levels, we'd like to define a set of "primitive" Visual objects. These primitives would define a set of basic functionality and that interact the closest with the low-level OpenGL layers of VisPy. Using these primitives, users should be able to easily create their own, more complex, visualizations without ever needing to know the complexities of the underlying layers. See the `VisPy Wiki `_ for our attempt at defining these types of primitives. See the "OpenGL, Vulkan, and WebGPU" section below for why this is important. Road to Version 2.0 ------------------- We'll cross this bridge when we come to it, but maybe we can start planning sooner rather than later. OpenGL, Vulkan, and WebGPU ^^^^^^^^^^^^^^^^^^^^^^^^^^ VisPy currently strives for compliance with OpenGL 2 and OpenGL ES. This was a goal of early VisPy in order to have compatibility with mobile platforms including web browsers. Over the years this has become a major burden for VisPy. We've been able to add functionality for things like Geometry shaders and allow for newer GLSL shaders, but the majority of VisPy is still limited to the features of old OpenGL. New graphics APIs like Vulkan and WebGPU are meant to provide users with more control, flexibility, and reliability. They are also more supported by industry (ex. gaming). If VisPy wants to keep up with modern technology and still provide its high level interfaces, it needs to be able to adopt new graphics APIs like these. The "low-level leakage" and "primitive visuals" described above are the first steps towards getting VisPy's source code ready for this type of flexibility. One library that VisPy is looking to as a future "graphics backend" is Datoviz (https://datoviz.org/) which depends on Vulkan. By implementing a set of primitive visuals, we hope that VisPy can provide the same visualizations but with a completely different graphics technology doing the drawing. See `#1988 `_ to track any discussion and related issues. Deprecation of "gloo" and GLIR ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In the beginning of VisPy development, the GLIR (OpenGL Intermediate Representation) was created to allow for the possibility of "remote" rendering. This would allow users to define what they wanted, but have a remote system do all the actual GPU number crunching. This is great in theory, but in practice becomes extremely difficult to maintain and preserve performance. This was extremely obvious when working with the Jupyter widget. GLIR is also very OpenGL specific. As discussed above, this can only work for so long. The one major benefit of GLIR is the ability to save the commands for a visualization and then "replay" them. This is really cool, but is almost never used as it is only possible in the javascript vispy.js library (or at least used to be). Put bluntly, VisPy isn't getting any benefit from GLIR and it will therefore likely be deprecated in coming VisPy versions. Along with GLIR, the "gloo" interface will also need to be deprecated. VisPy development will focus on higher level functionality and let other libraries like Datoviz focus on the low level. The "gloo" interfaces are extremely useful for having full control over an OpenGL 2/ES visualization, but as described above OpenGL 2 is old. Updating "gloo", or an interface like it, to work with newer versions of OpenGL would be too much work at this point. What we're really looking for is the type of control newer graphics libraries like Vulkan can provide. vispy-0.14.1/doc/resources.rst0000644000175000017510000001157514507104216015620 0ustar nileshnilesh========= Resources ========= .. container:: lead There are actually a lot of online resources, here is only a small selection. Learning ======== VisPy will eventually provide high-level facilities to let scientists create high-quality, high-performance plots without any knowledge of OpenGL. In the meantime, you can learn more about modern OpenGL in the references below. Even when VisPy is mature enough, knowing OpenGL will still let you write entirely custom interactive visualizations that fully leverage the power of GPUs. * This `page `_ contains fundamental OpenGL tutorials and notes.All example programs are written by C++ with Code::Blocks and Orwell Dev-C++, as well as makefiles for Linux and Mac. * A new OpenGL `introduction `_ that walks through the parts that are still relevant today. * `Open.gl `_ is a great walkthrough of modern OpenGL features with example code and output. Available in various ebook formats. * `Shadertoy `_ is a great resources to experiment and learn fragment shaders. * `A tutorial about VisPy `_, by Cyrille Rossant, published in the `IPython Cookbook `_ * `A tutorial about modern OpenGL and VisPy, by Nicolas Rougier `_ * A paper on the fundamentals behind VisPy: `Rossant C and Harris KD, Hardware-accelerated interactive data visualization for neuroscience in Python, Frontiers in Neuroinformatics 2013 `_ * A free online book on modern OpenGL (but not Python): `Learning Modern 3D Graphics Programming, by Jason L. McKesson `_ * `A PyOpenGL tutorial `_ * `A tutorial on OpenGL shaders `_ References ========== * `The OpenGL Registry `_ contains specifications, header files, and related documentation for OpenGL and related APIs including GLU, GLX, and WGL. * `Quick reference `_ for the built-in functions of the OpenGL ES Shading Language. * The `Graphics Codex `_ is an app for 3D graphics students, engineers, teachers, and artists. It provides consistent, correct, and easy-to-understand definitions for technical material. Blogs ===== * `Iñigo Quílez `_ * `Florian Boesch `_ * `The Little Grasshoper `_ * `John Chapman `_ Visualisation ============= :Matplotlib: `Matplotlib `_ is a python 2D plotting library which produces publication quality figures in a variety of hardcopy formats and interactive environments across platforms. matplotlib can be used in python scripts, the python and ipython shell (ala MATLAB®* or Mathematica®†), web application servers, and six graphical user interface toolkits. :Glumpy: `Glumpy `_ is the sister project of VisPy. It is also a high-performance interactive 2D/3D data visualization library. :Bokeh: `Bokeh `_ is a Python interactive visualization library that targets modern web browsers for presentation. Its goal is to provide elegant, concise construction of novel graphics in the style of D3.js, but also deliver this capability with high-performance interactivity over very large or streaming datasets. :Seaborn: `Seaborn `_ is a library for making attractive and informative statistical graphics in Python. It is built on top of matplotlib and tightly integrated with the PyData stack, including support for numpy and pandas data structures and statistical routines from scipy and statsmodels. :Kivy: `Kivy `_ is an open source Python library for rapid development of applications that make use of innovative user interfaces, such as multi-touch apps Scientific Articles =================== * | `Ten simple rules for better figures `_ | Nicolas P. Rougier, Michael Droettboom, Phil Bourne. PLOS Computational Biology (2014). * | `Shader-based Antialiased Dashed Stroked Polylines `_ | Nicolas P. Rougier. Journal of Computer Graphics Techniques, (2013) * | `Higher Quality 2D Text Rendering `_ | Nicolas P. Rougier. Journal of Computer Graphics Techniques, (2013) vispy-0.14.1/doc/raspberry.rst0000644000175000017510000000704014507104216015607 0ustar nileshnilesh====================================== Installation on Raspberry Pi 3 Model B ====================================== Distribution ============ For this installation a recent `Rasbian Stretch with Desktop `_ is used. Download the image and write it onto a fast micro SDHC (class 10) with enough capacity (at least 16GB). You might use other distributions for raspberry, but there have been only tests with Raspbian so far. Raspbian/Raspberry setup ======================== 1. Perform first time startup (language etc.) 2. start `raspi_config` at console and make sure to select the KMS driver (Full or Fake). Best experiences were obtained with Full KMS driver:: 7 Advanced Options A1 Expand Filesystem - resize root partition to use all SD space A7 GL Driver G1 (Full KMS) OpenGL desktop driver with full KMS 3. Reboot Checking OpenGL capabilities ============================ Now start `glxgears` on a console and check the framerate. You should get something around 60 FPS. To retrieve more information about the OpenGL status of the system start `glxinfo` and `glxheads` on a console and observe the output. Depending in the MESA version (this is something not reliably explored) there will be the error message:: libGL error MESA-LOADER failed to retrieve device information You can ignore this for now, but if you have any information on the source of this error and how to resolve, please let vispy-devs know. Backend requirements ==================== VisPy requires at least one toolkit for opening a window and creates an OpenGL context. This can be done using one Qt, GLFW, SDL2, Wx, or Pyglet. .. warning:: For Raspbian/Raspberry we rely on Qt4 for now! Package requirements ==================== The only mandatory requirement for VisPy is the `numpy `_ package. This is already distributed with Raspbian. Nevertheless you need to install some system packages to get VisPy compiled, installed and running: - `python3-pyqt4` - Python3 bindings for Qt4 - `python3-pyqt4-opengl` - Python3 bindings for Qt's OpenGL module - `cython3` - C-Extensions for Python3 Please use the Raspbian package manager to retrieve these packages. Installation options ==================== You have several options to install VisPy. Make sure to use the system ``python3`` at all times. We recommend to use the latest development version. **To install the latest release version**, you can do: .. code-block:: console $ pip3 install --upgrade vispy **If you want to run the latest development version**, you can clone the repository to your local machine and install with ``develop`` to enable easy updates to latest ``main``: .. code-block:: console $ git clone git://github.com/vispy/vispy.git # creates "vispy" folder $ cd vispy $ python3 setup.py develop To run the latest development version without cloning the repository, you can also use this line: .. code-block:: console $ pip3 install git+https://github.com/vispy/vispy.git Testing installation ==================== It is strongly advised to run the vispy test suite right after installation to check if everything is ok. To do this, just type: .. code-block:: python >>> import vispy >>> vispy.test() ... Please note that the test suite may be unstable on some systems. Any potential instability in the test suite does not necessarily imply instability in the working state of the provided VisPy examples. If you have feedback or questions, please use the VisPy :doc:`community` channels. vispy-0.14.1/doc/overview.rst0000644000175000017510000000102414507104216015440 0ustar nileshnileshOverview ======== If you are new to VisPy, it is recommended that you start with the :doc:`Getting Started ` documentation. This will help you determine what parts of VisPy will fit your use case and experience best. If you've already been through the getting started guides then the below pages provide various resources where you can hopefully find the answers to your questions or get the help you need. .. toctree:: :maxdepth: 1 community resources faq roadmap dev_guide/index thirdparty vispy-0.14.1/doc/org/0000755000175000017510000000000014507104250013630 5ustar nileshnileshvispy-0.14.1/doc/org/TRADEMARKS.md0000644000175000017510000001131414507104216015611 0ustar nileshnilesh# Trademarks ## Introduction This is the Organization's policy for the use of our trademarks. While our work is available under free and open source software licenses, those licenses do not include a license to use our trademarks. This policy describes how you may use our trademarks. Our goal is to strike a balance between: 1) our need to ensure that our trademarks remain reliable indicators of the quality software we release; and 2) our community members' desire to be full participants in our Organization. ## Our Trademarks This policy covers the name of the Organization and each of the Organization's projects, as well as any associated names, trademarks, service marks, logos, mascots, or similar indicators of source or origin (our "Marks"). ## In General Whenever you use our Marks, you must always do so in a way that does not mislead anyone about exactly who is the source of the software. For example, you cannot say you are distributing the "Mark" software when you're distributing a modified version of it because people will believe they are getting the same software that they can get directly from us when they aren't. You also cannot use our Marks on your website in a way that suggests that your website is an official Organization website or that we endorse your website. But, if true, you can say you like the "Mark" software, that you participate in the "Mark" community, that you are providing an unmodified version of the "Mark" software, or that you wrote a book describing how to use the "Mark" software. This fundamental requirement, that it is always clear to people what they are getting and from whom, is reflected throughout this policy. It should also serve as your guide if you are not sure about how you are using the Marks. In addition: * You may not use or register, in whole or in part, the Marks as part of your own trademark, service mark, domain name, company name, trade name, product name or service name. * Trademark law does not allow your use of names or trademarks that are too similar to ours. You therefore may not use an obvious variation of any of our Marks or any phonetic equivalent, foreign language equivalent, takeoff, or abbreviation for a similar or compatible product or service. * You agree that any goodwill generated by your use of the Marks and participation in our community inures solely to our collective benefit. ## Distribution of unmodified source code or unmodified executable code we have compiled When you redistribute an unmodified copy of our software, you are not changing the quality or nature of it. Therefore, you may retain the Marks we have placed on the software to identify your redistribution. This kind of use only applies if you are redistributing an official distribution from this Project that has not been changed in any way. ## Distribution of executable code that you have compiled, or modified code You may use any word marks, but not any Organization logos, to truthfully describe the origin of the software that you are providing, that is, that the code you are distributing is a modification of our software. You may say, for example, that "this software is derived from the source code for 'Mark' software." Of course, you can place your own trademarks or logos on versions of the software to which you have made substantive modifications, because by modifying the software, you have become the origin of that exact version. In that case, you should not use our Marks. However, you may use our Marks for the distribution of code (source or executable) on the condition that any executable is built from the official Project source code and that any modifications are limited to switching on or off features already included in the software, translations into other languages, and incorporating minor bug-fix patches. Use of our Marks on any further modification is not permitted. ## Statements about your software's relation to our software You may use the word Marks, but not the Organization's logos, to truthfully describe the relationship between your software and ours. Our Mark should be used after a verb or preposition that describes the relationship between your software and ours. So you may say, for example, "Bob's software for the 'Mark' platform" but may not say "Bob's 'Mark' software." Some other examples that may work for you are: * [Your software] uses "Mark" software * [Your software] is powered by "Mark" software * [Your software] runs on "Mark" software * [Your software] for use with "Mark" software * [Your software] for Mark software These guidelines are based on the [Model Trademark Guidelines](http://www.modeltrademarkguidelines.org), used under a [Creative Commons Attribution 3.0 Unported license](https://creativecommons.org/licenses/by/3.0/deed.en_US) vispy-0.14.1/doc/org/STEERING-COMMITTEE.md0000644000175000017510000000232214507104216016717 0ustar nileshnilesh# Steering Committee This document lists the members of the Organization's Steering Committee. Voting members may be added once approved by the Steering Committee as described in the [charter](./CHARTER.md). By adding your name to this list you are agreeing to abide by all Organization polices, including the [charter](./CHARTER.md), the [code of conduct](https://github.com/vispy/vispy/blob/main/CODE_OF_CONDUCT.md), the [trademark policy](./TRADEMARKS.md), and the [antitrust policy](./ANTITRUST.md). If you are serving on the Steering Committee because of your affiliation with another organization (designated below), you represent that you have authority to bind that organization to these policies. | **NAME** | **Handle** | **Affiliated Organization** | |-----------------|-------------|-----------------------------| | David Hoese | @djhoese | N/A | | Almar Klein | @almarklein | N/A | | Cyrille Rossant | @rossant | N/A | | Nicolas Rougier | @rougier | N/A | --- Part of MVG-0.1-beta. Made with love by GitHub. Licensed under the [CC-BY 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/). vispy-0.14.1/doc/org/CHARTER.md0000644000175000017510000001153214507104216015246 0ustar nileshnilesh# Charter for the VisPy Organization This is the organizational charter for the VisPy Organization (the "Organization"). By adding their name to the [Steering Committee.md file](./STEERING-COMMITTEE.md), Steering Committee members agree as follows. ## 1. Mission The VisPy organization's mission is to provide libraries and tools that allow for high-performance interactive 2D and 3D scientific visualizations. ## 2. Steering Committee **2.1 Purpose**. The Steering Committee will be responsible for all technical oversight, project approval and oversight, policy oversight, and trademark management for the Organization. **2.2 Composition**. The Steering Committee voting members are listed in the steering-committee.md file in the repository. Voting members may be added or removed by no less than 3/4 affirmative vote of the Steering Committee. The Steering Committee will appoint a Chair responsible for organizing Steering Committee activity. ## 3. Voting **3.1. Decision Making**. The Steering Committee will strive for all decisions to be made by consensus. While explicit agreement of the entire Steering Committee is preferred, it is not required for consensus. Rather, the Steering Committee will determine consensus based on their good faith consideration of a number of factors, including the dominant view of the Steering Committee and nature of support and objections. The Steering Committee will document evidence of consensus in accordance with these requirements. If consensus cannot be reached, the Steering Committee will make the decision by a vote. **3.2. Voting**. The Steering Committee Chair will call a vote with reasonable notice to the Steering Committee, setting out a discussion period and a separate voting period. Any discussion may be conducted in person or electronically by text, voice, or video. The discussion will be open to the public. In any vote, each voting representative will have one vote. Except as specifically noted elsewhere in this Charter, decisions by vote require a simple majority vote of all voting members. ## 4. Termination of Membership In addition to the method set out in section 2.2, the membership of a Steering Committee member will terminate if any of the following occur: **4.1 Resignation**. Written notice of resignation to the Steering Committee. **4.2 Unreachable Member**. If a member is unresponsive at its listed handle for more than three months the Steering Committee may vote to remove the member. ## 5. Trademarks Any names, trademarks, service marks, logos, mascots, or similar indicators of source or origin and the goodwill associated with them arising out of the Organization's activities or Organization projects' activities (the "Marks"), are controlled by the Organization. Steering Committee members may only use the Marks in accordance with the Organization's [trademark policy](./TRADEMARKS.md). If a Steering Committee member is terminated or removed from the Steering Committee, any rights the Steering Committee member may have in the Marks revert to the Organization. ## 6. Antitrust Policy The Steering Committee is bound by the Organization's [antitrust policy](./ANTITRUST.md). ## 7. No Confidentiality Information disclosed in connection with any of the Organization's activities, including but not limited to meetings, Contributions, and submissions, is not confidential, regardless of any markings or statements to the contrary. ## 8. Project Criteria In order to be eligible to be a Organization project, a project must: * Be approved by the Steering Committee. * Agree to follow the guidance and direction of the Steering Committee. * Use only the following outbound licenses or agreements unless otherwise approved: - For code, a license on the Open Source Initiative's list of [Popular Licenses](https://opensource.org/licenses). - For data, a license on the Open Knowledge Foundation's list of [Recommended Conformant Licenses](http://opendefinition.org/licenses/). - For specifications, a community developed and maintained specification agreement, such the [Open Web Foundation Agreements](https://www.openwebfoundation.org/the-agreements) or [Community Specification Agreement](https://github.com/CommunitySpecification/1.0). * Include and adhere to the Organization's policies, including the [trademark policy](./TRADEMARKS.md), the [antitrust policy](./ANTITRUST.md), and the [code of conduct](https://github.com/vispy/vispy/blob/main/CODE_OF_CONDUCT.md). ## 9. Amendments Amendments to this charter, the [antitrust policy](./ANTITRUST.md), the [trademark policy](./TRADEMARKS.md), or the [code of conduct](https://github.com/vispy/vispy/blob/main/CODE_OF_CONDUCT.md) may only be made with at least a 3/4 affirmative vote of the Steering Committee. --- Part of MVG-0.1-beta. Made with love by GitHub. Licensed under the [CC-BY 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/). vispy-0.14.1/doc/org/ANTITRUST.md0000644000175000017510000000220614507104216015551 0ustar nileshnilesh# Antitrust Policy Participants acknowledge that they may compete with other participants in various lines of business and that it is therefore imperative that they and their respective representatives act in a manner that does not violate any applicable antitrust laws, competition laws, or associated regulations. This Policy does not restrict any participant from engaging in other similar projects. Each participant may design, develop, manufacture, acquire or market competitive deliverables, products, and services, and conduct its business, in whatever way it chooses. No participant is obligated to announce or market any products or services. Without limiting the generality of the foregoing, participants agree not to have any discussion relating to any product pricing, methods or channels of product distribution, contracts with third-parties, division or allocation of markets, geographic territories, or customers, or any other topic that relates in any way to limiting or lessening fair competition. --- Part of MVG-0.1-beta. Made with love by GitHub. Licensed under the [CC-BY 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/). vispy-0.14.1/doc/news.rst0000644000175000017510000000225614507104216014556 0ustar nileshnilesh==== News ==== Releases -------- See the `Release Notes `_. Announcements ------------- - **PyCascades 2022 Sprint**, February 2022 - Chan Zuckerberg Initiative - Essential Open Source Software for Science (Cycle 4) Grant Awarded, Sept 2021 - **SciPy 2021 Sprint**, July 2021 - Chan Zuckerberg Initiative - Essential Open Source Software for Science (Cycle 2) Grant Awarded, May 2020 - `VisPy tutorial in the IPython Cookbook `__ - **Presentation at SciPy 2015**, July 2015 - **GSoC 2015**: two GSoC students are currently working on VisPy under the PSF umbrella - **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 `__ - **Presentation at BI forum**, Budapest, 6 November 2013 - **Presentation at Euroscipy**, Belgium, August 2013 - **EuroSciPy Sprint**, Belgium, August 2013 vispy-0.14.1/doc/installation.rst0000644000175000017510000002305214507104216016300 0ustar nileshnilesh============ Installation ============ Package requirements ==================== The only mandatory requirement for VisPy is the `numpy `_ package. Backend requirements ==================== VisPy requires at least one toolkit for opening a window and creates an OpenGL context. This can be done using any one of the following. .. list-table:: :widths: 25 25 50 :header-rows: 1 * - Backend - Status - Dependencies * - pyqt4 - Stable - pyqt4 * - pyqt5 - Stable - pyqt5 * - pyqt6 - Stable - pyqt6 * - pyside - Stable - pyside * - pyside2 - Stable - pyside2 * - pyside6 - Stable - pyside6 * - glfw - Stable - glfw (python) * - sdl2 - Stable - pysdl2 (pysdl2-dll recommended for MacOS/Windows) * - wx - Stable - wxPython * - pyglet - Stable - pyglet * - tkinter - Experimental - pyopengltk You can also use a Jupyter notebook with visualizations appearing inline with the ``jupyter_rfb`` backend (requires ``jupyter_rfb`` package). .. warning:: You only need to have one of these packages, no need to install them all! Optional dependencies ===================== VisPy has various optional dependencies to support features that may not be relevant for all users. The below is a list of dependencies that you may want to install to use the functionality mentioned. * **pillow**: Pillow (imported as PIL) is used to read image files. Some VisPy examples will use Pillow to load the image data that will then be shown by VisPy. * **triangle**: The `Triangle C package `_ is used via it's `triangle python `_ bindings. Please acknowledge and adhere to the licensing terms of both packages. Within VisPy `triangle`, if installed, is used to calculate a constrained Delaunay triangulation for `PolygonCollections`. Hardware requirements ===================== VisPy makes heavy use of the graphic card installed on your system. More precisely, VisPy uses the Graphical Processing Unit (GPU) through shaders. VisPy thus requires a fairly recent video card (~ less than 12 years old) as well as an up-to-date video driver such that vispy can access the programmable pipeline (as opposed to the fixed pipeline). To get information on your system, you can type: .. code-block:: python >>> print(vispy.sys_info()) The results of the above command and is long list of information related to your system and video driver. The OpenGL version must be at least 2.1. .. note:: On linux systems the `xrandr` command is used to determine the screen's DPI. On certain (virtual) displays it reports screen dimensions of 0mm x 0mm. In this case users may attempt to fix their screen resolution or download the `xdpyinfo` (xorg-xdpyinfo) utility as an alternative to `xrandr`. A default DPI of 96 is used otherwise. Installation options ==================== **Before installing VisPy** you should ensure a working version of python is installed on your computer, including all of the requirements included in the **Backend Requirements** section above. A simple way to install most of these requirements is to install the **Anaconda** scientific python distribution from Continuum Analytics. `Anaconda `_ will install most of the VisPy dependencies for you. If your computer is low on hard disk space, or you would like a minimal python installation, you may install the `Miniconda `_ package also from Continuum Analytics. Once Anaconda is installed, create a `conda python environment `_. Via conda --------- VisPy can be installed in a conda environment by using the package available from the `conda-forge `_ channel: .. code-block:: console conda install -c conda-forge vispy Via PyPI -------- VisPy can also be installed with ``pip`` to install it from PyPI: .. code-block:: console pip install --upgrade vispy Once the python dependencies have been installed, install the latest proprietary drivers for your computer's GPU. Generally these drivers may be downloaded from the GPU manufacturer's website. .. _dev_install: Via GitHub ---------- **If you want to run the latest development version**, you can clone the repository to your local machine and install vispy in "development" mode. This means that any changes to the cloned repository will be immediately available in the python environment: .. code-block:: console # creates "vispy" folder git clone git://github.com/vispy/vispy.git cd vispy # install the vispy package in editable/development mode pip install -e . To run the latest development version without cloning the repository, you can also use this line: .. code-block:: console pip install git+https://github.com/vispy/vispy.git Via Test PyPI ------------- The VisPy project uploads the latest development version of the package to test.pypi.org. This can be a good alternative to the above GitHub installation process if you don't have or don't want to use git. You can install these versions of the package by doing: .. code-block:: console pip install --pre -i https://test.pypi.org/simple/ vispy .. note:: The main portion of the version number is based on the last public release of VisPy so the Test PyPI package may be smaller than when the final package is released. Jupyter Notebook and Lab ------------------------ If you would like to use VisPy in a Jupyter Notebook and have the visualizations appear inline, we recommend using the "jupyter_rfb" backend. This backend depends on `jupyter_rfb library `_ which must be installed before your jupyter notebook or jupyter lab session is started. Note that the 'jupyter_rfb' library uses the "remote" jupyter kernel (the server) to do the drawing of your visualization and then sends the results to the client (the browser). This means that performance of animations and user interactions (mouse and keyboard events) will differ depending on the connection quality between server and client. .. versionchanged:: 0.8 The "jupyter_rfb" backend was added in Version 0.8 and the old "ipynb_webgl" was removed. Testing installation -------------------- It is strongly advised to run the vispy test suite right after installation to check if everything is ok. To do this, just type: .. code-block:: python >>> import vispy >>> vispy.test() ... Please note that the test suite may be unstable on some systems. Any potential instability in the test suite does not necessarily imply instability in the working state of the provided VisPy examples. Usage in an interactive console =============================== If running from a jupyter console, either the ``jupyter-qtconsole``, the ``jupyter-console``, or, the console within `Spyder `_, you may need to ensure a few other `IPython magic `_ functions are called prior to using vispy in a given kernel. Before using any VisPy code, we recommend running the following commands when starting your python kernel: .. code-block:: python >>> %gui qt >>> # your vispy code Namely, this has the effect of sharing the event loop between application and the interactive console allowing you use both simultaneously. Switchable graphics =================== If your laptop comes with switchable graphics you have to make sure to tell python to use your graphics card instead of the integrated Intel graphics. You can identify which graphics card will be used by running: .. code-block:: python >>> import vispy >>> print(vispy.sys_info()) and look for Nvidia in the ``GL version``. For example: ``GL version: '4.6.0 NVIDIA 390.25'``. Windows ------- In Windows, you should open the the Nvidia-console and add your specific python to the list of programs that should use the dedicated graphics card. Note that this setting is separate for different conda environments so make sure you have selected the one you are using VisPy with. Linux ----- On Linux with the proprietary Nvidia graphics drivers, you should run python with ``primusrun python your_script.py``. For use with a Jupyter kernel, say in Spyder or the ``jupyter-qtconsole``, make sure the kernel is started with ``primusrun``. For example: .. code-block:: bash $ primusrun spyder3 .. code-block:: bash $ primusrun jupyter-qtconsole Modifying default jupyter kernel -------------------------------- If you want the jupyter-qtconsole to always use your Nvidia graphics card, you can change the parameters in the default kernel. To find the default kernel, run .. code-block:: bash $ jupyter kernelspec list then edit the ``kernel.json`` file to include ``"primusrun",`` as the first parameter in ``argv``. For example: .. code-block:: json { "argv": [ "primusrun", "python", "-m", "ipykernel_launcher", "-f", "{connection_file}" ], "language": "python", "display_name": "Python 3" } Using a similar configuration, you could have two kernels configurations, one for the dedicated graphics card, and one for the integrated graphics. Spyder has it's own configuration and I don't know exactly how to make its console run with ``primusrun`` without running ``primusrun spyder3``. Embedded System Installation ============================ .. toctree:: :maxdepth: 1 raspberry vispy-0.14.1/doc/index.rst0000644000175000017510000000671714507104216014717 0ustar nileshnilesh.. title:: Home .. raw:: html
VisPy is a **high-performance interactive 2D/3D data visualization library** leveraging the computational power of modern **Graphics Processing Units (GPUs)** through the **OpenGL** library to display very large datasets. .. toctree:: :caption: Getting VisPy :maxdepth: 1 installation .. toctree:: :caption: Learning VisPy :maxdepth: 2 getting_started/index .. toctree:: :caption: Additional Help :maxdepth: 2 Documentation .. toctree:: :hidden: gallery/index API news Code of Conduct vispy-0.14.1/doc/governance/0000755000175000017510000000000014507104250015170 5ustar nileshnileshvispy-0.14.1/doc/governance/MAINTAINERS.md0000644000175000017510000000221314507104216017264 0ustar nileshnilesh# Maintainers This document lists the Maintainers of the Project. Maintainers may be added once approved by the existing maintainers as described in the [Governance document](./GOVERNANCE.md). By adding your name to this list you are agreeing to abide by the Project governance documents and to abide by all of the Organization's polices, including the [code of conduct](https://github.com/vispy/vispy/blob/main/CODE_OF_CONDUCT.md), [trademark policy](../org/TRADEMARKS.md), and [antitrust policy](../org/ANTITRUST.md). If you are participating because of your affiliation with another organization (designated below), you represent that you have the authority to bind that organization to these policies. | **NAME** | **Organization** | |-----------------|------------------| | Lorenzo Gaisfas | N/A | | David Hoese | N/A | | Almar Klein | N/A | | Kai Mühlbauer | N/A | | Cyrille Rossant | N/A | | Nicolas Rougier | N/A | --- Part of MVG-0.1-beta. Made with love by GitHub. Licensed under the [CC-BY 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/). vispy-0.14.1/doc/governance/GOVERNANCE.md0000644000175000017510000000676614507104216017162 0ustar nileshnilesh# Governance Policy This document provides the governance policy for the VisPy Project. Maintainers agree to this policy and to abide by all Project polices, including the [code of conduct](https://github.com/vispy/vispy/blob/main/CODE_OF_CONDUCT.md), [trademark policy](../org/TRADEMARKS.md), and [antitrust policy](../org/ANTITRUST.md) by adding their name to the [maintainers.md file](./MAINTAINERS.md). ## 1. Roles. This project may include the following roles. Additional roles may be adopted and documented by the Project. **1.1. Maintainers**. Maintainers are responsible for organizing activities around developing, maintaining, and updating the Project. Maintainers are also responsible for determining consensus. This Project may add or remove Maintainers with the approval of the current Maintainers. **1.2. Contributors**. Contributors are those that have made contributions to the Project. ## 2. Decisions. **2.1. Consensus-Based Decision Making**. Projects make decisions through consensus of the Maintainers. While explicit agreement of all Maintainers is preferred, it is not required for consensus. Rather, the Maintainers will determine consensus based on their good faith consideration of a number of factors, including the dominant view of the Contributors and nature of support and objections. The Maintainers will document evidence of consensus in accordance with these requirements. **2.2. Appeal Process**. Decisions may be appealed by opening an issue and that appeal will be considered by the Maintainers in good faith, who will respond in writing within a reasonable time. If the Maintainers deny the appeal, the appeal may be brought before the Organization Steering Committee, who will also respond in writing in a reasonable time. ## 3. How We Work. **3.1. Openness**. Participation is open to anyone who is directly and materially affected by the activity in question. There shall be no undue financial barriers to participation. **3.2. Balance**. The development process should balance the interests of Contributors and other stakeholders. Contributors from diverse interest categories shall be sought with the objective of achieving balance. **3.3. Coordination and Harmonization**. Good faith efforts shall be made to resolve potential conflicts or incompatibility between releases in this Project. **3.4. Consideration of Views and Objections**. Prompt consideration shall be given to the written views and objections of all Contributors. **3.5. Written procedures**. This governance document and other materials documenting this project's development process shall be available to any interested person. ## 4. No Confidentiality. Information disclosed in connection with any Project activity, including but not limited to meetings, contributions, and submissions, is not confidential, regardless of any markings or statements to the contrary. ## 5. Trademarks. Any names, trademarks, logos, or goodwill developed by and associated with the Project (the "Marks") are controlled by the Organization. Maintainers may only use these Marks in accordance with the Organization's trademark policy. If a Maintainer resigns or is removed, any rights the Maintainer may have in the Marks revert to the Organization. ## 6. Amendments. Amendments to this governance policy may be made by affirmative vote of 2/3 of all Maintainers, with approval by the Organization's Steering Committee. --- Part of MVG-0.1-beta. Made with love by GitHub. Licensed under the [CC-BY 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/). vispy-0.14.1/doc/getting_started/0000755000175000017510000000000014507104250016230 5ustar nileshnileshvispy-0.14.1/doc/getting_started/visuals.rst0000644000175000017510000000014214507104216020447 0ustar nileshnileshGetting Started - Visuals ========================= Coming soon... .. include:: _canvas_app.rst vispy-0.14.1/doc/getting_started/scene.rst0000644000175000017510000000013614507104216020061 0ustar nileshnileshGetting Started - Scene ======================= Coming soon... .. include:: _canvas_app.rst vispy-0.14.1/doc/getting_started/plot.rst0000644000175000017510000000014414507104216017741 0ustar nileshnileshGetting Started - Plotting ========================== Coming soon... .. include:: _canvas_app.rst vispy-0.14.1/doc/getting_started/modern-gl.rst0000644000175000017510000003022214507104216020647 0ustar nileshnilesh============= Modern OpenGL ============= OpenGL has evolved over the years and a big change occurred in 2003 with the introduction of the dynamic pipeline (OpenGL 2.0), i.e. the use of shaders that allow to have direct access to the GPU. .. image:: /_static/gl-history.png Before this version, OpenGL was using a fixed pipeline and you may still find a lot of tutorials that still use this fixed pipeline. This introduces some radical changes in the way of programming OpenGL and makes it both more difficult to program but far more powerful. Shaders ======= .. Note:: The shader language is called glsl. There are many versions that goes from 1.0 to 1.5 and subsequents version get the number of OpenGL version. Last version is 4.4 (February 2014). Shaders are pieces of program (using a C-like language) that are build onto the GPU and executed during the rendering pipeline. Depending on the nature of the shaders (there are many types depending on the version of OpenGL you're using), they will act at different stage of the rendering pipeline. To simplify this tutorial, we'll use only **vertex** and **fragment** shader as shown below: .. image:: /_static/gl-pipeline.png A vertex shader acts on vertices and is supposed to output the vertex **position** (→ ``gl_Position``) on the viewport (i.e. screen). A fragment shader acts at the fragment level and is supposed to output the **color** (→ ``gl_FragColor``) of the fragment. Hence, a minimal vertex shader is:: void main() { gl_Position = vec4(0.0,0.0,0.0,1.0); } while a minimal fragment shader would be:: void main() { gl_FragColor = vec4(0.0,0.0,0.0,1.0); } These two shaders are not very useful since the first will transform any vertex into the null vertex while the second will output the black color for any fragment. We'll see later how to make them to do more useful things. One question remains: when are those shaders executed, exactly? The vertex shader is executed for each vertex that is given to the rendering pipeline (we'll see what does that mean exactly later) and the fragment shader is executed on each fragment that is generated after the vertex stage. For example, in the simple figure above, the vertex would be called 3 times, once for each vertex (1,2 and 3) while the fragment shader would be executed 21 times, once for each fragment (pixel). Buffers ======= We explained earlier that the vertex shader act on the vertices. The question is thus where do those vertices comes from? The idea of modern GL is that vertices are stored on the GPU and needs to be uploaded (only once) to the GPU before rendering. The way to do that is to build buffers onto the CPU and to send them onto the GPU. If your data does not change, no need to upload it again. That is the big difference with the previous fixed pipeline where data were uploaded at each rendering call (only display lists were built into GPU memory). But what is the structure of a vertex? OpenGL does not assume anything about your vertex structure and you're free to use as many information you may need for each vertex. The only condition is that all vertices from a buffer have the same structure (possibly with different content). This again is a big difference with the fixed pipeline where OpenGL was doing a lot of complex rendering stuff for you (projections, lighting, normals, etc.) with an implicit fixed vertex structure. Now you're on your own... * **Good news** is that you're now free to do virtually anything you want. * **Bad news** is that you have to program everything, even the most basic things like projection and lighting. Let's take a simple example of a vertex structure where we want each vertex to hold a position and a color. The easiest way to do that in python is to use a structured array using the `numpy `_ library:: data = numpy.zeros(4, dtype = [ ("position", np.float32, 3), ("color", np.float32, 4)] ) We just created a CPU buffer with 4 vertices, each of them having a ``position`` (3 floats for x,y,z coordinates) and a ``color`` (4 floats for red, blue, green and alpha channels). Note that we explicitly chose to have 3 coordinates for ``position`` but we may have chosen to have only 2 if were to work in two-dimensions only. Same holds true for ``color``. We could have used only 3 channels (r,g,b) if we did not want to use transparency. This would save some bytes for each vertex. Of course, for 4 vertices, this does not really matter but you have to realize it **will matter** if you data size grows up to one or ten million vertices. Uniform, attribute, varying =========================== At this point in the tutorial, we know what are shaders and buffers but we still need to explain how they may be connected together. So, let's consider again our CPU buffer:: data = numpy.zeros(4, dtype = [ ("position", np.float32, 2), ("color", np.float32, 4)] ) We need to tell the vertex shader that it will have to handle vertices where a position is a tuple of 3 floats and color is a tuple of 4 floats. This is precisely what attributes are meant for. Let us change slightly our previous vertex shader:: attribute vec2 position; attribute vec4 color; void main() { gl_Position = vec4(position, 0.0, 1.0); } This vertex shader now expects a vertex to possess 2 attributes, one named ``position`` and one named ``color`` with specified types (vec3 means tuple of 3 floats and vec4 means tuple of 4 floats). It is important to note that even if we labeled the first attribute ``position``, this attribute is not yet bound to the actual ``position`` in the numpy array. We'll need to do it explicitly at some point in our program and there is no automagic that will bind the numpy array field to the right attribute, you'll have to do it yourself, but we'll see that later. The second type of information we can feed the vertex shader are the uniforms that may be considered as constant values (across all the vertices). Let's say for example we want to scale all the vertices by a constant factor ``scale``, we would thus write:: uniform float scale; attribute vec2 position; attribute vec4 color; void main() { gl_Position = vec4(position*scale, 0.0, 1.0); } Last type is the varying type that is used to pass information between the vertex stage and the fragment stage. So let us suppose (again) we want to pass the vertex color to the fragment shader, we now write:: uniform float scale; attribute vec2 position; attribute vec4 color; varying vec4 v_color; void main() { gl_Position = vec4(position*scale, 0.0, 1.0); v_color = color; } and then in the fragment shader, we write:: varying vec4 v_color; void main() { gl_FragColor = v_color; } The question is: what is the value of ``v_color`` inside the fragment shader? If you look at the figure that introduced the gl pipeline, we have 3 vertices and 21 fragments. What is the color of each individual fragment? The answer is *the interpolation of all 3 vertices color*. This interpolation is made using distance of the fragment to each individual vertex. This is a very important concept to understand. Any varying value is interpolated between the vertices that compose the elementary item (mostly, line or triangle). Transformations =============== Projection matrix ----------------- We need first to define what do we want to view, that is, we need to define a viewing volume such that any object within the volume (even partially) will be rendered while objects outside won't. On the image below, the yellow and red spheres are within the volume while the green one is not and does not appear on the projection. .. image:: /_static/ViewFrustum.png There exist many different ways to project a 3D volume onto a 2D screen but we'll only use the `perspective projection `_ (distant objects appear smaller) and the `orthographic projection `_ which is a parallel projection (distant objects have the same size as closer ones) as illustrated on the image above. Until now (previous section), we have been using implicitly an orthographic projection in the z=0 plane. .. note:: In older versions of OpenGL, these matrices were available as `glFrustum `_ and `glOrtho `_. Depending on the projection we want, we will use one of the two projection matrices below: **Perspective matrix** .. image:: /_static/frustum-matrix.png **Orthographic matrix** .. image:: /_static/ortho-matrix.png At this point, it is not necessary to understand how these matrices were built. Suffice it to say they are standard matrices in the 3D world. Both suppose the viewer (=camera) is located at position (0,0,0) and is looking in the direction (0,0,1). There exists a second form of the perspective matrix that might be easier to manipulate. Instead of specifying the right/left/top/bottom planes, we'll use field of view in the horizontal and vertical direction: **Perspective matrix** .. image:: /_static/perspective-matrix.png where ``fovy`` specifies the field of view angle, in degrees, in the y direction and ``aspect`` specifies the aspect ratio that determines the field of view in the x direction. Model and view matrices ----------------------- We are almost done with matrices. You may have guessed that the above matrix requires the viewing volume to be in the z direction. We could design our 3D scene such that all objects are withing this direction but it would not be very convenient. So instead, we'll use a view matrix that will map the the world space to camera space. This is pretty much as if we were orienting the camera at a given position and look toward a given direction. In the meantime, we can further refine the whole pipeline by providing a model matrix that will maps the object's local coordinate space into world space. For example, this will be useful for rotating an object around its center. To sum up, we need: * **Model matrix** maps from an object's local coordinate space into world space * **View matrix** maps from world space to camera space * **Projection matrix** maps from camera to screen space Learning modern OpenGL ====================== There exist a lot of resources on the web related to OpenGL. I only mention here a few of them that deals with the dynamic rendering pipeline. If you've found other resources, make sure they deal with the dynamic rendering pipeline and not the fixed one. An intro to modern OpenGL ------------------------- OpenGL has been around a long time, and from reading all the accumulated layers of documentation out there on the Internet, it's not always clear what parts are historic and what parts are still useful and supported on modern graphics hardware. It's about time for a new OpenGL `introduction that `_ walks through the parts that are still relevant today. Learning Modern 3D Graphics Programming --------------------------------------- This `book `_ is intended to teach you how to be a graphics programmer. It is not aimed at any particular graphics field; it is designed to cover most of the basics of 3D rendering. So if you want to be a game developer, a CAD program designer, do some computer visualization, or any number of things, this book can still be an asset for you. This does not mean that it covers everything there is about 3D graphics. Hardly. It tries to provide a sound foundation for your further exploration in whatever field of 3D graphics you are interested in. OpenGL ES 2.0 documentation --------------------------- `OpenGL ES 2.0 `_ is defined relative to the OpenGL 2.0 specification and emphasizes a programmable 3D graphics pipeline with the ability to create shader and program objects and the ability to write vertex and fragment shaders in the OpenGL ES Shading Language. VisPy is based on OpenGL ES 2.0 because it give access to the programmable pipeline while keeping overall complexity tractable. vispy-0.14.1/doc/getting_started/index.rst0000644000175000017510000000326414507104216020100 0ustar nileshnileshGetting Started =============== VisPy strives to provide an easy path for users to make fast interactive visualizations. To serve as many users as possible VisPy provides different interfaces for differing levels of experience. While one interface may be enough to build a simple visualization, knowing all the interfaces can provide the most flexibility for fully customizing your visualization. The below pages are meant to provide an introduction to these interfaces and help guide you into what interface might be best for your experience and the final visualization you are looking to achieve. Additionally, the :doc:`../gallery/index` can be used for inspiration. Further low-level details can be found in the :doc:`API documentation <../api/modules>` and existing examples. VisPy targets two primary 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. Users in this category can write their own visualizations with :mod:`vispy.gloo` (requires knowing OpenGL/GLSL). Another option with VisPy development is to encapsulate gloo-based visualizations into re-usable `Visual` classes. The below pages will provide an introduction of these interfaces. .. toctree:: :maxdepth: 1 modern-gl Gloo Visuals 2. **Scientists without any knowledge of OpenGL**, who are seeking a high-level, high-performance plotting toolkit. Use the :mod:`vispy.plot` and :mod:`vispy.scene` interfaces for high-level work. The below pages provide an introduction into these interfaces. .. toctree:: :maxdepth: 1 Scene Plotting vispy-0.14.1/doc/getting_started/gloo.rst0000644000175000017510000003105014507104216017723 0ustar nileshnileshGetting Started - gloo ====================== The gloo layer of VisPy is the lowest level interface and is the closest thing to OpenGL that VisPy provides. This also means it is the most complicated. While OpenGL is complicated, gloo tries to provide a simple to use object-oriented layer on top of that. The guide below will walk through the basics of using VisPy's gloo interface to create a visualization. .. include:: _canvas_app.rst Basic Script ------------ To start any gloo visualization we will need to create a ``Canvas`` object and an ``Application``. Here is the most basic working example we can create: .. code-block:: python import sys from vispy import app, gloo canvas = app.Canvas(keys='interactive') @canvas.connect def on_draw(event): gloo.set_clear_color((0.2, 0.4, 0.6, 1.0)) gloo.clear() canvas.show() if __name__ == '__main__' and sys.flags.interactive == 0: app.run() If you run the above code you should see a single window with a solid blue-ish background. Let's go through this code one chunk at a time: 1. We start out by importing the the `sys` module from the Python standard library followed by the vispy :mod:`~vispy.app` and :mod:`~vispy.gloo` modules. 2. We create a ``Canvas`` object representing the overall space where our visualization will take place. 3. We define a simple "on_draw" function telling OpenGL to fill the Canvas with a specific RGBA (Red, Green, Blue, Alpha) color. The color components are defined as floating point numbers between 0 and 1. We use the ``canvas.connect`` decorator method to attach this method to any "draw" events coming from the canvas. When using this technique the function must be named ``on_``. For more on the available events that can be connected to, see the :class:`vispy.app.canvas.Canvas` docstring. 4. We call :meth:`canvas.show() ` to display the Canvas object on the screen. VisPy will talk to the underlying GUI backend (PyQt, Wx, etc) to construct a native GUI "widget" with our OpenGL visualization inside. 5. The script ends with us running a default VisPy :class:`~vispy.app.application.Application` object. Later on we'll see how we can define the exact Application we want to use, but in this early stage we won't need to. The if statement here is a common occurrence in VisPy example scripts so that the Application is only started when the code is run as a script (instead of imported). This also helps with more advanced usage where we run this script in an interactive Python interpreter. .. note:: The above code is also available in the :ref:`examples/gloo/start.py ` script. Basic Script (Alternative) -------------------------- Another common way to structure a script like this is to subclass the ``Canvas`` class and override the necessary methods directly. This can be useful if you want to keep all parts of your visualization contained in the ``Canvas`` object instead of throughout a script. Here is what the above "connect style" script would look like as a subclass: .. code-block:: python import sys from vispy import app, gloo class MyCanvas(app.Canvas): def on_draw(self, event): gloo.set_clear_color((0.2, 0.4, 0.6, 1.0)) gloo.clear() canvas = MyCanvas(keys='interactive') canvas.show() if __name__ == '__main__' and sys.flags.interactive == 0: app.run() Create an OpenGL Program ------------------------ As mentioned earlier, the ``gloo`` interface provides a low-level object-oriented interface on top of OpenGL. If we want to do anything more complicated that a solid color, we'll need to start using these OpenGL objects. We'll start with the below code to draw a simple shape in our Canvas. As mentioned in :doc:`index`, if you aren't familiar with OpenGL then it is highly recommended that you read :doc:`modern-gl` before diving into the below code. .. code-block:: python from vispy import app, gloo from vispy.gloo import Program vertex = """ attribute vec4 color; attribute vec2 position; varying vec4 v_color; void main() { gl_Position = vec4(position, 0.0, 1.0); v_color = color; } """ fragment = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): super().__init__(size=(512, 512), title='Colored quad', keys='interactive') # Build program self.program = Program(vertex, fragment, count=4) # Set uniforms and attributes self.program['color'] = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1), (1, 1, 0, 1)] self.program['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] gloo.set_viewport(0, 0, *self.physical_size) self.show() def on_draw(self, event): gloo.clear() self.program.draw('triangle_strip') def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) if __name__ == '__main__': c = Canvas() app.run() Similar to the previous example, we've created a subclass of the ``Canvas`` object to hold on to all of the objects we create. We start by defining an OpenGL :class:`~vispy.gloo.program.Program` which expects two shaders in the simplest case: a vertex shader and a fragment shader. Now that we have the Program, we are able to start setting uniforms and attributes used by the shaders. In this example we've included the ``.show()`` call inside the ``__init__`` method so the Canvas is shown as soon as it is created. Lastly, we create two event handlers. One for the "resize" event so when the user resizes the GUI window we can update the size of the OpenGL canvas (viewport). The other handler is for "draw" where we clear the canvas, setting it to the "clear color" of white, and then tell our GL Program to draw or execute itself. When we run this example we should see something like this: .. image:: ../gallery/gloo/images/sphx_glr_colored_quad_001.png :alt: Screenshot of examples/gloo/colored_quad.py example script. :class: sphx-glr-single-img This shape was created by drawing a ``"triangle_strip"`` using the coordinates we assigned to the `position` attribute. Colors are interpolated between the 4 colors we assigned to each vertex (``color``) automatically by the GPU. Under the hood, VisPy automatically converts these positions and colors to numpy arrays. For the positions it creates a :class:`~vispy.gloo.buffer.VertexBuffer` object to store them. We could have done this ourselves by replacing this line with: .. code-block:: python self._program['position'] = gloo.VertexBuffer(pos_np_arr) With this basic template, you can now start modifying the shader code, or provide different uniforms and attributes. With the right setup, you can also change the drawing method used (ex. 'points' or 'lines' instead of 'triangle_strip'). .. note:: The above code is also available in the :ref:`examples/gloo/colored_quad.py ` script. Timers ------ A common requirement of any visualization is to make changes over time. VisPy provides a generic :class:`~vispy.app.timer.Timer` class to help with this. Let's take the quad example above and make a few modifications. First, we'll create a :class:`~vispy.app.timer.Timer` in the ``Canvas.__init__`` method. We'll also define a ``clock`` instance variable to use in our shader later and then we'll start the timer. .. code-block:: python self.program['theta'] = 0.0 self.timer = app.Timer('auto', self.on_timer) self.clock = 0 self.timer.start() We first set a new uniform in our shader called ``theta`` which we'll use later on. The ``'auto'`` setting tells the timer to fire as quickly as quickly as it can (in between drawing and other GUI events). We've connected this timer to a new ``on_timer`` method which will be executed whenever the timer is triggered. .. code-block:: python def on_timer(self, event): self.clock += 0.001 * 1000.0 / 60. self.program['theta'] = self.clock self.update() In this method, we're updating our special ``clock`` counter variable, updating that ``theta`` uniform in our shader, and finally we call ``self.update()`` which tells the ``Canvas`` to start redrawing itself. .. note:: The method name ``on_timer`` is by convention, but can be named anything. Lastly, we update our vertex shader so the coordinates we provide to the OpenGL program are adjusted based on our new ``theta`` uniform. .. code-block:: python vertex = """ uniform float theta; attribute vec4 color; attribute vec2 position; varying vec4 v_color; void main() { float ct = cos(theta); float st = sin(theta); float x = 0.75* (position.x*ct - position.y*st); float y = 0.75* (position.x*st + position.y*ct); gl_Position = vec4(x, y, 0.0, 1.0); v_color = color; } """ Putting it all together our new script looks like this: .. code-block:: python from vispy import gloo, app from vispy.gloo import Program vertex = """ uniform float theta; attribute vec4 color; attribute vec2 position; varying vec4 v_color; void main() { float ct = cos(theta); float st = sin(theta); float x = 0.75* (position.x*ct - position.y*st); float y = 0.75* (position.x*st + position.y*ct); gl_Position = vec4(x, y, 0.0, 1.0); v_color = color; } """ fragment = """ varying vec4 v_color; void main() { gl_FragColor = v_color; } """ class Canvas(app.Canvas): def __init__(self): super().__init__(size=(512, 512), title='Rotating quad', keys='interactive') # Build program & data self.program = Program(vertex, fragment, count=4) self.program['color'] = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1), (1, 1, 0, 1)] self.program['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] self.program['theta'] = 0.0 gloo.set_viewport(0, 0, *self.physical_size) gloo.set_clear_color('white') self.timer = app.Timer('auto', self.on_timer) self.clock = 0 self.timer.start() self.show() def on_draw(self, event): gloo.clear() self.program.draw('triangle_strip') def on_resize(self, event): gloo.set_viewport(0, 0, *event.physical_size) def on_timer(self, event): self.clock += 0.001 * 1000.0 / 60. self.program['theta'] = self.clock self.update() if __name__ == '__main__': c = Canvas() app.run() The end result is a 2D square that rotates for every timer event. .. image:: ../gallery/gloo/images/sphx_glr_rotating_quad_001.gif :alt: Screenshot of examples/gloo/colored_quad.py example script. :class: sphx-glr-single-img .. note:: The above code is also available in the :ref:`examples/gloo/rotating_quad.py ` script. Keyboard Events --------------- So far our examples haven't included any user interaction. The easiest way to include some is to attach meaning to certain key presses. We can update our example to include a "pause" button on the animation. We add one new method to handle all keyboard events. .. code-block:: python def on_key_press(self, event): if event.text == ' ': if self.timer.running: self.timer.stop() else: self.timer.start() This method is automatically executed when the ``Canvas`` detects a key press. The ``event.text`` is used to check for the Spacebar being pressed. If the timer is running, we stop it, otherwise we start it. Other Topics ------------ That's it for the "Getting Started" topics for the gloo interface. Now it is time to get coding and exploring the other parts of the documentation. If you think something is missing here and would make for a really good "Getting Started" topic, please create an issue on GitHub. See :doc:`../community` for more information. vispy-0.14.1/doc/getting_started/_canvas_app.rst0000644000175000017510000000575314507104216021250 0ustar nileshnileshGetting System Information -------------------------- A good quick way of checking if VisPy is properly installed is to print out system information. This output includes information like what backends you have installed as well as information that can be gathered from OpenGL about your GPU. This can be very important information to provide to VisPy maintainers when filing bugs or asking questions. To get this information, you can run the following python code: .. code-block:: python import vispy print(vispy.sys_info()) .. note:: This can be done in a one-liner from the command line with:: python -c "import vispy; print(vispy.sys_info())" The output of this should look something like the below, but will ultimately depend on your environment and your machine. :: Platform: Linux-5.4.0-7642-generic-x86_64-with-debian-bullseye-sid Python: 3.7.6 | packaged by conda-forge | (default, Mar 23 2020, 23:03:20) [GCC 7.3.0] NumPy: 1.18.1 Backend: PyQt5 pyqt4: None pyqt5: ('PyQt5', '5.12.3', '5.12.5') pyside: None pyside2: None pyglet: None glfw: None sdl2: None wx: None egl: EGL 1.5 NVIDIA: OpenGL_ES OpenGL osmesa: None _test: None GL version: '4.6.0 NVIDIA 455.28' MAX_TEXTURE_SIZE: 32768 Extensions: 'GL_AMD_multi_draw_indirect ...' One important thing to look for is the "GL version". If you see an empty string here or got an error when running this command, this likely means your system's OpenGL library is not properly installed or can't be found by VisPy. You may need to upgrade or re-install your GPU drivers to fix this or OpenGL may not be compatible with your system. The Canvas and The Application ------------------------------ There are two things that are common across all of the VisPy interfaces in one way or another: 1. One ``Application`` instance 2. At least one ``Canvas`` (or subclass) instance The Application ^^^^^^^^^^^^^^^ The VisPy ``Application`` object wraps the high-level event loop logic of the VisPy backend you use (PyQt5, Wx, etc). In most cases you don't have to know too much about this, but you do need to create and run the application which we'll see below. If the application is not started, VisPy will not be able to process events and won't run properly. Note that just like with any GUI framework, calling the ``.run()`` method of the application is a blocking call. Nothing after the ``.run()`` call will be executed until the application is stopped, usually by closing your GUI window. The Canvas ^^^^^^^^^^ The ``Canvas`` object will be your main way of using and controlling VisPy. Depending on the VisPy interface you're using, you'll either be using the ``Canvas``, ``SceneCanvas``, or creating ``Figure`` objects. While these different types of canvases are built on each other, they will almost never be used in the same application. To help keep your code easy to understand it is best not to mix these classes or their subcomponents. vispy-0.14.1/doc/gallery/0000755000175000017510000000000014507104250014500 5ustar nileshnileshvispy-0.14.1/doc/gallery/index.rst0000644000175000017510000000227514507104216016351 0ustar nileshnileshGallery ======= .. toctree:: :hidden: gloo/index scene/index plotting/index VisPy has many examples and we've split them into the main interfaces that VisPy offers. There are more examples that haven't been organized into this structure that you can find in the VisPy repository's `example scripts `_. Gloo ---- Gloo is the lowest level interface offered by VisPy. It requires knowledge of OpenGL concepts like buffers, textures, and shaders. See the :doc:`Gloo Gallery ` for examples. Scene ----- The Scene or SceneCanvas API is a high-level interface that provides the most "bang for your buck". You have control over low-level "visual" elements and their properties while also having access to high-level features like cameras and transform systems. See the :doc:`Scene Gallery ` for examples. Plotting -------- The plotting API in VisPy is still experimental, but you can still do quite a bit with it. This interface is for people who want to get something plotted as quickly as possible with basic features like axes, labels, and pan/zoom controls. See the :doc:`Plotting Gallery ` for examples. vispy-0.14.1/doc/faq.rst0000644000175000017510000001545214507104216014353 0ustar nileshnileshFAQ === VisPy is a big project with a lot of features and sometimes a couple ways to do things. It sits on top of the very complex OpenGL library. We've tried to document what we could in an organized and easy to find manner, but some topics don't quite fit. This set of Frequently Asked Questions is where you'll find the answer to some of these miscellaneous questions. Why is my visualization slower when I add more Visual objects? -------------------------------------------------------------- Each Visual object in VisPy is an OpenGL Program consisting of at least a vertex shader and a fragment shader (see :doc:`getting_started/modern-gl`). In general, except for some very specific cases, OpenGL Programs can only be executed one at a time by a single OpenGL context. This means that in your VisPy visualization each Visual object you tell VisPy to draw will extend how long each update (draw) takes. When frames per second (FPS) or responsiveness are a concern, this means each Visual you add reduces the performance of your visualization. While VisPy is constantly striving to improve performance, there are things that you can do in the mean time (depending on your particular case). The most important change that you can make is to lower the number of Visual objects you have. For a lot of Visuals it is possible to combine them into one by putting a little extra work into the data you provide them. For example, instead of creating 10 separate LineVisuals, create 1 LineVisual that draws 10 lines. While this is a simple example, the same concept applies to other types of Visuals in VisPy and more complex use cases. As a last resort for the most complex cases, a custom Visual (custom shader code) may be necessary. Before writing a custom Visual, check with VisPy maintainers by asking a question on gitter or creating a question as a GitHub issue. Is VisPy multi-threaded or thread-safe? --------------------------------------- VisPy does not have any special multi-thread or multi-process handling except for the funcionality provided by the GUI framework backends that it uses. For example, PyQt5/PySide2 provide QThread objects for running code in another thread. These libraries also provide ways of transferring data safely or communicating between threads; mainly signals and slots. However, there is a limit to what operations can be performed outside the main thread. The main or GUI thread for most GUI frameworks is the **only** thread that can perform drawing operations or operations that will trigger drawing. This includes OpenGL functions. This means that calling ``self.update()`` on a VisPy ``Canvas`` or ``Visual`` object must ultimately be done in the main thread. Data that will be drawn can be created or updated in a secondary thread, but the main/GUI thread must still be the one to do the redraw. Since many Visual objects automatically call ``self.update()`` for property or data modifications this can be difficult to do in the most performant way. Updates or requests for changes to better support thread-safe data updates are welcome. How to render headless/off-screen with VisPy? --------------------------------------------- There are two strategies to render without windows with VisPy: 1. Use Xvfb that simulates an X server in memory without displaying windows. This can be used with any VisPy backend. 2. Use a backend that directly renders into memory buffers, e.g. OSMesa or EGL (`further info `_). Then, in your VisPy script, use :: image = canvas.render() import imageio imageio.imwrite("rendered.png", image) to save the rendered scene to an image file. Xvfb ^^^^ Wrap the command to launch your script with ``xfvb-run``:: xvfb-run -a python my_script.py https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml OSMesa ^^^^^^ Using the OSMesa (Off-Screen Mesa) backend:: import vispy vispy.use("osmesa") VisPy tries to detect the OSMesa shared library, but, if needed, it can be set explicitly with :: export OSMESA_LIBRARY=/usr/lib/libOSMesa.so https://mesa-docs.readthedocs.io/en/latest/osmesa.html EGL ^^^ Using the EGL backend:: import vispy vispy.use("egl") VisPy tries to detect the EGL shared library, but, if needed, it can be set explicitly with :: # Choose one, or adapt to your system. export EGL_LIBRARY=/usr/lib/libEGL.so export EGL_LIBRARY=/usr/lib/libEGL_mesa.so export EGL_LIBRARY=/usr/lib/libEGL_nvidia.so https://en.wikipedia.org/wiki/EGL_(API) How to achieve transparency with 2D objects? -------------------------------------------- With objects that lie in a 2D plane, e.g. images, it is possible to get a translucent effect (i.e. partial transparency, a see-through effect) by positioning them at different depth levels in the viewing direction and by drawing the visuals from furthest to closest with the appropriate blending mode. Here are the key steps to achieve this with two :class:`~vispy.scene.visuals.Image` visual nodes in a :class:`~vispy.scene.canvas.SceneCanvas`: 1. Set the opacity value in the alpha channel of the images:: # A white image with integer values between 0 and 255. image_data1 = np.ones((200, 300, 4), dtype='uint8') # Half translucent. image_data1[..., 3] = 128 # A blue image with float values between 0 and 1. image_data2 = np.zeros((200, 300, 4), dtype='np.float32') image_data2[..., 2] = 1.0 # Blue. # A bit more translucent. image_data2[..., 3] = 0.25 visual1 = Image(image_data1) visual2 = Image(image_data2) 2. Position the visuals at different depth levels (z-levels) in the viewing direction:: from vispy.visuals import transforms visual1.transform = transforms.STTransform(translate=(0, 0, 1)) visual2.transform = transforms.STTransform(translate=(0, 0, 2)) A higher ``z`` value means further back, assuming the viewing direction is the ``+z`` axis, e.g. the default for a :class:`~vispy.scene.cameras.PanZoomCamera`. 3. Draw the visuals from back to front by setting the draw :obj:`~vispy.scene.node.Node.order` of the nodes manually:: visual2.order = 1 # Furthest, drawn first. visual1.order = 2 # Closest, drawn second. This requires depth testing and blending enabled. An appropriate blending function is, for example, ``(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)`` with `glBlendFunc `_ in OpenGL. This is the default on the :class:`~vispy.scene.visuals.Image` visual node, but otherwise it can be set with :: visual1.set_gl_state('translucent') which is a shortcut for :: visual1.set_gl_state(depth_test=True, cull_face=False, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha')) How do I cite VisPy? -------------------- See the VisPy repository for citation information: https://github.com/vispy/vispy/blob/main/CITATION.rst vispy-0.14.1/doc/dev_guide/0000755000175000017510000000000014507104250014774 5ustar nileshnileshvispy-0.14.1/doc/dev_guide/writing_examples.rst0000644000175000017510000001115414507104216021113 0ustar nileshnileshWriting Examples ================ One of the easiest ways to learn VisPy is to use example scripts as a starting point. VisPy includes a lot of its example scripts in the :doc:`../gallery/index` which is generated by the `sphinx-gallery `_ library. While any example that shows a usage of VisPy is usage care must be taken to make it runnable by sphinx-gallery and allow :class:`VisPy's sphinx-gallery scraper ` to take screenshots of it. The below sections describe some important points of writing an example and gotchas to avoid so the examples are properly included in the gallery. Using the VisPy Scraper in other projects ----------------------------------------- It is possible for projects outside of VisPy to use the sphinx-gallery scraper. After following the sphinx-gallery configuration steps, include "vispy" as one of your ``image_scrapers`` in your ``conf.py`` configuration: .. code-block:: python sphinx_gallery_conf = { 'image_scrapers': ('vispy',), ... } It can be used alongside other scrapers like the sphinx-gallery default ``matplotlib`` scraper. The scraper follows all the same rules described below unless stated otherwise. Script Location --------------- Example scripts that are meant to be included in the gallery should be placed in one of ``examples/gloo/``, ``examples/scene/``, or ``examples/plotting/`` directories. Sphinx-gallery will only search in these directories and only at the top 2 levels (only the first sub-directories). Note this is specific to the VisPy project. Third-party libraries using the VisPy scraper are free to put examples wherever they configure sphinx-gallery to look. Canvas ------ The scraper expects examples to have one of the following defined in the global namespace: * ``canvas``: An instance of a ``Canvas``, ``SceneCanvas``, or subclass of one of these. It must be named "canvas". * ``Canvas``: A class that can be instantiated by calling ``Canvas()``. * ``fig``: A ``Figure`` instance. It must be named "fig". In the case of an example that is a larger Qt5 Application see the below section. Applications ------------ The scraper can also collect screenshots of Qt5 Applications. It will find the top-level ``QMainWindow`` or ``QWidget`` and ignore any VisPy canvases. At the end of the example should be included an ``app.run()`` call where ``app`` **must** be a VisPy ``Application`` instance. Using a QApplication instance will work for basic running of the example, but currently can cause segmentation faults when run by sphinx-gallery. VisPy's :meth:`vispy.app.application.Application.run` has special handling to not block execution when run by sphinx-gallery. Examples using other GUI frameworks are not currently supported in gallery execution. Specify frames to grab ---------------------- A special comment can be added to the top of the example script to tell the scraper what frame(s) to grab of an animation. By default (if none is specified) the scraper will draw the Canvas-like object multiple times (5) and then grab the current visualized output. This ensures that the visualization has been completely drawn. To customize a different number of draws before a screenshot is grabbed put the following comment in the first ten lines of the example script (usually the second line after the hashbang): .. code-block:: python # vispy: gallery 30 This tells the scraper to draw the Canvas-like object 30 times. To build an animation (currently stored as a gif image), you can specify a range of frames by doing: .. code-block:: python # vispy: gallery 10:50:10 This is passed to ``range`` as individual arguments (start, end, step). So the above will produce a list of ``[10, 15, 20, 25,30, 35, 40, 45]`` meaning draw the Canvas-like object 10 times and grab a screenshot, then 5 more times (15 total) and grab a screenshot, then 5 more times (20 total) and grab a screenshot, and so on. By default the scraper will not grab any frames. If the gallery comment is specified, but has no number or range specified then the first frame will be grabbed. Specify images created by example --------------------------------- Some examples may involve creating screenshots or animations. To tell the scraper to grab these files instead of trying to generate another screenshot specify the following frame specifier comment: .. code-block:: python # vispy: gallery-exports animation.gif Where ``animation.gif`` is a filename relative to the example script that is produced by running the example. It can be any image format. Multiple space-separated filenames can be provided also. vispy-0.14.1/doc/dev_guide/index.rst0000644000175000017510000000067714507104216016651 0ustar nileshnileshDeveloper's Guide ================= The following pages describe information for contributing and maintaining the VisPy library. A normal user experience should not require knowing any of this information. .. toctree:: :maxdepth: 1 contributor_guide writing_examples To become familiar with the low-level OpenGL Intermediate Representation (GLIR), it is best to start with the specification outlined in the :mod:`vispy.gloo.glir` module.vispy-0.14.1/doc/dev_guide/contributor_guide.rst0000644000175000017510000002642614507104216021271 0ustar nileshnileshContributor's Guide =================== So you're thinking about contributing to VisPy...great! Below you'll find instructions on the different ways to contribute and how to do it. You'll also find information about coding style and other best practices. Who can contribute? ------------------- VisPy accepts contributions from anyone as long as they meet our standards. While we will accept contributions from anyone, we especially value ideas and contributions from folks with diverse backgrounds and identities. There are many ways to contribute (see below) and no contribution is too small. What can be contributed? ------------------------ 1. **Bugs**: Tell us when you think you've found something wrong or when you just can't get something to work after following the instructions. 2. **Features**: Have an idea how VisPy could be improved? We'd like to hear it. Bonus points if you have ideas on how it can be implemented. 3. **Ticket Review**: Not sure how to help out, but you've become pretty familiar with the project? Help the VisPy maintainers clean out old, duplicate, or already resolved bug reports and feature requests. 4. **Documentation**: See a typo? Please correct us. Is something documented wrong or out of date? Tell us about it. Could the documentation be made less confusing if their was more detail? Let us know. Tell us what was confusing. Even better, tell us what would have made it easier to understand in the first place. See below for more info on best practices. 5. **Code**: If there is a bug that you want to fix or a feature you want to add, please let us know. See below for how we prefer you make these contributions. How can I contribute? --------------------- Almost all communication with the VisPy maintainers should be done through the main VisPy GitHub repository: https://github.com/vispy/vispy/ * Bug reports and feature requests can be submitted through the "Issues" on the repository. `This GitHub page `_ can help you create an issue if you're unfamiliar with the process. When you create an issue you'll see VisPy's template asking you for specific information. It is really important that you provide as much of this information as possible. Most importantly for bugs is providing a `Minimal Complete and Verifiable Example (MCVE) `_ * Any changes to actual code, including documentation, should be submitted as a pull request on GitHub. GitHub's documentation includes instructions on `making a pull request `_ if you're new to GitHub or to git in general. Please make sure to submit pull requests using a **new** branch (not your fork's main branch). Don't be afraid to submit a pull request with only partial fixes/features. Pull requests can always be updated after they are created. Creating them early gives maintainers a chance to provide an early review of your code if that's something you're looking for. See below for more information on writing documentation and checking your changes. No matter how you contribute, VisPy maintainers will try their best to read, research, and respond to your query as soon as possible. For code changes, automated checks and tests will run on GitHub to provide an initial "review" of your changes. What if I need help? -------------------- The best way to ask for help from VisPy maintainers is to talk to us on gitter. If you have more general VisPy questions that users may be able to help with check out the `main gitter channel `_. If you have questions specific to VisPy's design or how you should contribute to VisPy there is a gitter channel specifically for `VisPy Developers `_. Lastly, feel free to create an issue on GitHub to ask a question. If you've already created a pull request you can comment there. Development Environment ----------------------- See the installation instructions for different ways to install VisPy and its dependencies. We suggest installing VisPy :ref:`from source ` if you are planning on modifying any code. Coding Style ------------ In general, VisPy follows the PEP 8 style guidelines: https://www.python.org/dev/peps/pep-0008/ The easiest way to see if your meeting these guidelines is to code as you normally would and run ``flake8`` to check for errors (see below). Otherwise, see existing VisPy code for examples of what we expect. Checking Code Style ^^^^^^^^^^^^^^^^^^^ Code style is automatically checked by VisPy's Continuous Integration (CI). If you'd like to check the style on your own local machine you can install and run the ``flake8`` utility from the root of the vispy repository. To install: .. code-block:: bash pip install flake8 Then run the following from the root of the VisPy directory: .. code-block:: bash python make test flake This will inform you of any code style issues in the entire vispy python package directory. Documentation Style ------------------- All docstrings in VisPy's python code follow the NumPy style. You can find the full reference here: https://numpydoc.readthedocs.io/en/latest/format.html However, the simplest way to get a hang of this style is to look at the existing VisPy code. Checking Documentation Style ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Similar to code style, documentation style is tested during VisPy's automated testing when you create or edit a pull request. If you'd like to check it locally you can use the same ``flake8`` tool as for code, but with the addition of the ``flake8-docstring`` package. To install: .. code-block:: pip install flake8-docstrings Then run the following from the root of the VisPy directory: .. code-block:: bash python make test flake This will check both code style and docstring style. Adding Tests ------------ VisPy depends on self-contained tests to know that changes haven't broken any existing functionality. Our unit tests are written using the ``pytest`` library. Some parts of VisPy require extra steps to test them thoroughly, but utilities exist to help with this. For example, VisPy has multiple backends that can be used, so to be thoroughly checked tests should be run for each of these backends. Luckily, VisPy's automated tests will run every test over a series of backends for you when you make a pull request so you shouldn't normally have to worry about this in your local testing. Writing Tests ^^^^^^^^^^^^^ As mentioned, tests are written so that they can be run with pytest. In the most basic cases this means adding one or more functions or classes to modules in a ``tests`` directory. For example, tests for the vispy.plot subpackage are in the ``vispy/plot/tests/test_plot.py`` module. Note that both the module and the function should start with ``test_`` so that pytest can discover them. Tests should completely test the changes being submitted. Depending on the changes this may be as simple as calling the function or as complicated as building a full visualization with a Canvas and set of Visual objects. Looking at existing tests is a good place to start. If you have any questions you can always contact the VisPy maintainers or leave a comment on your pull request asking for assistance. For more complex tests, you may require that certain dependencies be installed or that a GUI window can be opened. In those case you can look at the various decorators in :mod:`vispy.testing`. For example, if you need to make a Canvas, your test should only run when a VisPy Application can be created. In this case the :func:`~vispy.testing._testing.requires_application` decorator can be used: .. code-block:: python from vispy.testing import requires_application @requires_application() def test_my_change(): with app.Canvas() as c: # do something with the Canvas 'c' All available decorators in the testing module start with ``requires_``. See the module documentation for more information. Running Tests ^^^^^^^^^^^^^ In the basic cases, the traditional method of calling ``pytest `` will work to run a limited set of tests: .. code-block:: pytest vispy/plot/tests/test_plot.py However, this will only run on one backend. To easily run tests on multiple backends: .. code-block:: python make test unit This runs tests in the same way that tests are run on the CI environments. Additional test commands are available including: .. code-block:: python make test nobackend To run tests without any backend selected. Or: .. code-block:: python make test full To run both nobackend and unit tests as well as "extra" tests including docstring and flake tests. Lastly: .. code-block:: python make test examples Which will attempt to run all example scripts. .. note:: Due to environment, GPU driver, or dependency differences not all tests may pass on your system. The CI environments should be considered the "one truth" for passing tests until tests are made more flexible for differences in systems. Sphinx Documentation -------------------- VisPy's documentation website is a `Sphinx `_ project stored in the "doc" directory of the repository. To generate the documentation run: .. code-block:: bash cd doc make html Repeated execution of ``make html`` will reuse information from previous runs which may be faster, but may also produce incorrect output in specific cases. To make sure, you can clean out the build directory by running ``make clean``. To view the output you can view the build folder in a browser: .. code-block:: firefox _build/html/index.html As part of the documentation generation, the sphinx-gallery project will run all of the examples to generate screenshots for the gallery pages. This can take a long time and is unnecessary if you aren't modifying the gallery or examples. To build the site without generating the gallery run: .. code-block:: make html SPHINXOPTS="-D plot_gallery=0" Jupyter Widget -------------- VisPy no longer has a jupyter widget as part of the main VisPy Python package. Instead the "jupyter_rfb" package is used through the "jupyter_rfb" backend of VisPy. Major changes to this backend will likely need changes to the jupyter_rfb library which can be found here: https://github.com/vispy/jupyter_rfb/ Updating my fork's branch to "main" ----------------------------------- The VisPy project has switched to using the branch name "main" as its primary branch. If you forked the repository before this change, you may find it confusing to work between your fork and the upstream VisPy repository. If you wish to update your fork, go to the branches page for your repository (ex. ``https://github.com//vispy/branches``) and edit/rename the "master" branch to "main". On your local system, you'll also want to point to the new name as well. GitHub provides instructions for doing this update. For convenience they've been copied below: .. code-block:: bash git branch -m master main git fetch origin git branch -u origin/main main git remote set-head origin -a If you've configured multiple "remotes" on your system, you may need to change these commands with the proper remote name. vispy-0.14.1/doc/conf.py0000644000175000017510000003034414507104216014346 0ustar nileshnilesh# -*- coding: utf-8 -*- # # vispy documentation build configuration file # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. from datetime import date import sys import os import re import warnings import vispy # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.append(os.path.abspath('ext')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = '1.6' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.imgmath', 'sphinx.ext.autosummary', 'sphinx.ext.intersphinx', 'numpydoc', 'sphinxcontrib.apidoc', 'sphinx_gallery.gen_gallery', 'myst_parser', ] # API docs apidoc_module_dir = "../vispy" apidoc_output_dir = "api" apidoc_excluded_paths = ["../vispy/ext"] apidoc_separate_modules = True # Sphinx Gallery from sphinx_gallery.sorting import FileNameSortKey # the following files are ignored from gallery processing ignore_files = ['plotting/export.py', 'gloo/geometry_shader.py', ] ignore_pattern_regex = [re.escape(os.sep) + f for f in ignore_files] ignore_pattern_regex = "|".join(ignore_pattern_regex) sphinx_gallery_conf = { 'examples_dirs': ['../examples/gloo', '../examples/scene', '../examples/plotting'], 'gallery_dirs': ['gallery/gloo', 'gallery/scene', 'gallery/plotting'], 'filename_pattern': re.escape(os.sep), 'ignore_pattern': ignore_pattern_regex, 'only_warn_on_example_error': True, 'image_scrapers': ('vispy',), 'reset_modules': tuple(), # remove default matplotlib/seaborn resetters 'first_notebook_cell': '%gui qt', # tell notebooks to use Qt backend 'within_subsection_order': FileNameSortKey, } # Let vispy.app.application:Application.run know that we are generating gallery images os.environ["_VISPY_RUNNING_GALLERY_EXAMPLES"] = "1" # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The main toctree document. master_doc = 'index' # General information about the project. project = u'VisPy' copyright = u'2013-{}, VisPy developers'.format(date.today().year) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The full version, including alpha/beta/rc tags. release = vispy.__version__ # The short X.Y version. version = '.'.join(release.split('.')[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build', 'README.rst'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'pydata_sphinx_theme' # Tell the theme where the code lives def _custom_edit_url(github_user, github_repo, github_version, doc_path, file_name, default_edit_page_url_template): """Create custom 'edit' URLs for API modules since they are dynamically generated.""" if file_name.startswith("api/"): # this is a dynamically generated API page, link to actual Python source modpath = os.sep.join(os.path.splitext(file_name[4:])[0].split(".")) if modpath == "modules": # main package listing modpath = "vispy" rel_modpath = os.path.join("..", modpath) if os.path.isdir(rel_modpath): doc_path = modpath + "/" file_name = "__init__.py" elif os.path.isfile(rel_modpath + ".py"): doc_path = os.path.dirname(modpath) file_name = os.path.basename(modpath) + ".py" else: warnings.warn(f"Not sure how to generate the API URL for: {file_name}") return default_edit_page_url_template.format(github_user=github_user, github_repo=github_repo, github_version=github_version, doc_path=doc_path, file_name=file_name) html_context = { "github_user": "vispy", "github_repo": "vispy", "github_version": "main", "doc_path": "doc", "edit_page_url_template": "{{ vispy_custom_edit_url(github_user, github_repo, github_version, doc_path, file_name, default_edit_page_url_template) }}", "default_edit_page_url_template": "https://github.com/{github_user}/{github_repo}/edit/{github_version}/{doc_path}{file_name}", "vispy_custom_edit_url": _custom_edit_url, } # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { "use_edit_page_button": True, "github_url": "https://github.com/vispy/vispy", "twitter_url": "https://twitter.com/vispyproject", "header_links_before_dropdown": 7, } # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = 'VisPy' # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = "_static/vispy-teaser-short.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = "_static/favicon.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {'documentation*': ['localtoc.html', 'searchbox.html'], # 'installation*' : ['localtoc.html', 'searchbox.html'], # 'modern-gl*' : ['localtoc.html', 'searchbox.html'], # 'api*' : ['localtoc.html', 'searchbox.html'], # # 'resources*' : ['localtoc.html', 'searchbox.html'], # } # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'vispydoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # ----------------------------------------------------------------------------- # Numpy extensions # ----------------------------------------------------------------------------- numpydoc_show_class_members = False # ----------------------------------------------------------------------------- # intersphinx # ----------------------------------------------------------------------------- _python_doc_base = "https://docs.python.org/3" intersphinx_mapping = { "python": (_python_doc_base, None), "numpy": ("https://numpy.org/doc/stable/", None), "scipy": ("https://scipy.github.io/devdocs/", None), } def setup(app): # Add custom CSS app.add_css_file('style.css') vispy-0.14.1/doc/community.rst0000644000175000017510000000430514507104216015623 0ustar nileshnilesh========= Community ========= .. toctree:: :hidden: :glob: org/* governance/* .. container:: row .. container:: col-md-1 text-center .. raw:: html .. container:: col-md-4 If you have questions related to the use of vispy, we have a `chat room `_ on gitter. .. raw:: html
.. container:: col-md-1 text-center .. raw:: html .. container:: col-md-4 If you find a problem related to this website, please submit an issue to the `vispy repository `_. .. raw:: html
.. container:: row .. container:: col-md-1 text-center .. raw:: html .. container:: col-md-4 If you have a bug to report, a feature you’d like to request or a patch you wish to submit, please `submit an issue `_. .. container:: col-md-1 text-center .. raw:: html .. container:: col-md-4 If you want to help with the development of vispy, join us on `GitHub `_ or the `Developer Gitter `_. There is also a `developer's mailing list `_. .. container:: row .. container:: col-md-1 text-center .. raw:: html .. container:: col-md-4 VisPy is both a library and an organization. If you want to know more about the organization and its structure check out our :doc:`Charter <./org/CHARTER>` and see who's on the :doc:`Steering Committee <./org/STEERING-COMMITTEE>`. .. container:: col-md-1 text-center .. raw:: html .. container:: col-md-4 The :doc:`maintainers <./governance/MAINTAINERS>` of VisPy manage the project following a simple set of expectations which you can find in the :doc:`governance <./governance/GOVERNANCE>` document. vispy-0.14.1/doc/api/0000755000175000017510000000000014507104250013612 5ustar nileshnileshvispy-0.14.1/doc/api/.gitkeep0000644000175000017510000000000014507104216015233 0ustar nileshnileshvispy-0.14.1/doc/_static/0000755000175000017510000000000014507104250014467 5ustar nileshnileshvispy-0.14.1/doc/_static/vispy-text-white.svg0000644000175000017510000051752614507104216020504 0ustar nileshnilesh image/svg+xmlVisPy vispy-0.14.1/doc/_static/vispy-text-white.png0000644000175000017510000000216214507104216020452 0ustar nileshnilesh‰PNG  IHDR?¼ —„sBIT|dˆ pHYs Ý Ý\P”tEXtSoftwarewww.inkscape.org›î<ïIDATX…ݘ[ˆ•UÇK²I­Ét¬—Š +*J™‡ì†hHP”/ùPÑE2 »ÑK(tñ)ŠÂˆÈÊʃò‚â‹EIPL&$â%Qǵé×Ã9'öìÙçŒsf’ìÃ|ûÿ_k}kí½¾½¿ïÀ(@] þ¦ö©[ÕKG#î¿P—ç'Ü `QD˜ªÓ€Ç2ÿÑ©î.JøW"âùfR¯¤~ ø Ø¿7?¿ÙûÆ »— vsªZWÆ¿0‚|fî“£O]¦ŽIí¨w‚?U°û6³9¨Ž­j3Ôµê×êêy#ÈçTНaÙHjG¯öfA×e6“ÕþÌæÝ&ï7E½ªÞÕ)¾[=VàûÔÉÍä0&"þÖeü­êÙÉx·Xg’ðwÕN¨ý=’4UýD= ì~«Ô•êCäyeDŒÍøà&õåìþÛÕÈrx1³ÙRî+Ìêm‰ãÛ™vHmIô_2}q¢]V-²6%ö¥•o«jã Úê´ßžÿc¦?][͵@_6«³“ëÛ3틈8^wb0)÷_îõSŒÕ^àEÄ÷À7?·v¡^\—hýÀc"â°1sž]u¼ÈÏíNN—gãUÀÍÑL–ŸcžúðiAÛVý¿"ãç&×wfÚ¦ˆØýÏH½?k‹?ÕVuaÆ÷ªãÓH6nûŽBKv©OZØôÞn¿>ñ;×ÊãXC¿zaUÛ”ùÍÏozz"3š©®Ê¸Õ…„?M=Y'ùnõñ&‹ß©^œù¾™Ù,°²€Ç®GÉÝÀ欮vàÆŒNËS}ç{ òD C]2Œû×€öˆØ•i¥ÖŸ¤'×êˆ86(ªúP6skx¾³ÜªuW>±™ .±ò cŸÕ£©ÎÊÏR§«— 53ê¶,îŠ,Ö-õ§XyÖkèË?«ãרí¯WÏIÆc<ɪß6TÑIü‡3ßt²»LÎÿ/.±Ø’P- İZ¾ŠeÀ^uµºXÀàà$p´‰Ø%|ô&ãt_X™~°Upîfø }$ÕC+pw}CDœl2öDÄõ#àÁLX™¥¯¢5À_~sDôŒF‚¶ G9f¾ñ|;RbÐÊGÄnõ`J&­Ïm¼ÅÀ·¸mÉõTÞ gmTvø`•7½³·ÅÀ«YüÁ»scü@å1špï 3Æ™ õÙlÓëQ[s»Ò3FB½˜ \ ÌÏ䎈8|ú³:MPß)‘Z9ë'•|Fö3нÀ=q°$þoÚØCåαÀ`+°<"~­çð7ŽÃ½-ÕÕ»IEND®B`‚vispy-0.14.1/doc/_static/vispy-teaser.svg0000644000175000017510000052653614507104216017666 0ustar nileshnilesh image/svg+xmlVisPy vispy-0.14.1/doc/_static/vispy-teaser.png0000644000175000017510000002110414507104216017630 0ustar nileshnilesh‰PNG  IHDRÞg[(~sBIT|dˆ pHYs  À½¥ÚtEXtSoftwarewww.inkscape.org›î< IDATxœíÝy”eUuøño54 ³(´‚“¢ˆ i‡(üEQCЀ⌇¨qˆDcýý¢QÇ8àÔŠ8DŒˆÁ! ¨QE2 2tKcÓcåÝï×EQõêÝ}Ï^Õ÷³ÖY²\ýîÙ÷¾zï¼{î9{ƒ$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’*™è:IEm ììÜ \ œ œ ÜÜUp’$Í'GדCÚï—t $IóÅó€ t§¶ã» S’¤ñ·p£º“Ä ý¨.‚•ºÍ»@ Ò³€7'^·x°¾l8;Ø'ñºÓ€Wðï^lYñØÀqÄs_Uwðî†ûXC<ŸX¹ñ¿Ü@<¼Ìe &w­~G?“_;Ÿc»V)Ù7»6áÎÀC€ç«Þ¿Mär.ýݳS2>Í3K‰;›6ùõ¥ýœø2žï†EÄŠðÌ5z\ñó@rç±xl¢ñÐöÀ;“ˆ»Ô&c™©½©`|3›{<L÷ƒ_ÚJà}Àî5®ißý+¹kó‘.‚â-äÎãœ.‚UZÞ©þ”ØT÷»æ&`û†bTOmAT×ÈV[™ïm=ðeb`¾y¹kÒ·éæ_’;w¬Òú6ðBì‡?%×ÔöœcT,^Äì«1m·o€/ hæ“ì/öúv"ÿZ"y‹ÆG^ˆÇ6KÆ6h_k8FõÀD1箳qlë‰ÕлV¾êý”¦µ’OÓþ‰\ügt¬jéëÀ q#sv2¾I"ûÞŽ-Ä©<œ²‹r»•ØÂ2îEÍ÷%wþ7Ðì`Ù;öcºVµôyà…ÈF•­þ5 <±¥8Õ’÷¿ªº°æ[»ø“Ñߊ^:ŸÜ¹?¡‹`§ø#rqÿØ®ƒxûh1‘7}Oâz.%2líI¬ìïÓ÷¾¼LÆ8 üßã\pÚ^”ò—Dc3çl²žÈÍ[¢DãžDšÏ¯ ž™›“¯;øÏ±T‘-x:±U®ŽÅT/41诮ÙwÆvÄbº?&êÃî ìÜe„×®!Î\\FÌ2œ¿±]ÚD°cìÄÚ™ŒÙRŠNŸ†^ü>ùÚÒ¶'_áìæ’4ià4ÊÜÕ]O¬ì=–È^µºÐq»j€éßFdã*qNËŒòæôÌîä¤ÜHwÓÍÄ—~æ}:²@ÿšì»Í•Ô»9Ô¿E,&kâ³t±èðeÀ^ ŸÏ8ÜñB~•ýÏf9Þ"òë·ÑœÐ;5¬3çð½âMy2ñÈ~˜Ö_'ÙýØT¹eoš«2Ôv[™~ü±Wñ]rçÚÕs©‡Vˆqj»™(?YWŸÞGÕºšl‡µ‹ˆµ»7p^ã2ð~4çÊ!Ç|mò˜“ôãfàÆ;þ¡¶&ÿ¦¯ ö£=“™§5¶!Ÿ–¯¯í:àn3œëñ¼ëÀyäî¯$ê׎‹—’»†ï"XàbœÚ>V¨ÿ>¼ûg&ã*ÝÖoŒåhÊ='—÷5É8'™}±æòé*Ï.|~_!ûˆ»åÞÚ‹ê‹dV†F)ð¡ŠÇ—öUæ®ÅºQ¦êÒZbª¯j­×.,!w‡tí/À™ jÛfÞïRÕ•ú4ð."j_×YQÛd»ž2%Çeàý«dœ“ æþ¹ä17÷.wz•Ý…ü†OwïÈ'¾G}Î!nßG}`Ÿ­ß:.íoG¼[²ÞK|™ŒzüÏ1«h³wKm¯ê~x2ÎkÉ//ïöÄžä®?CÃÚU”Y 0.oöoc’á¹ÃWã¸Ç<¿ªž?$®¹Z/ËNwbë™û–6ö®ØÇÖô§¸}Sm3O9³ñE0ê³Ñ_Ðü⓺þŠÜõ[Örœÿ’Œó=cèÃÀ»#ðßÉ8ÚlÿPè|Çeà=(ç$Q~u6À%Éã^Tðüªú渆µ+(³ó¤¨Í‰ Bs1±H*³õ¢rF×Ü6Ú'’×àÁÄ{që}Ü@|(ûj{â™JÕkWjÁÒ(&Èÿ,Yö±ëwý¿Ó$¦¿—:çqx›Œs’¹eÖYdõàBçWÅÝÈçxsñµ=‘ÛsXÐß þê<_¼;s&ó¥­'òþÖqWbAÖCú¹ø³šý4éßÉ]¿Ã[Šï‘Éø.¥ì³ö®Þ7&ûo»-+t¾0>ïS’qN2÷Ø%ä·>¾³ÐùUñ·ÉX7P}v¶Qw~ÊìxD¡¾Þ:¤Ÿ’í6â‡Äûˆi©¿ìÞü±Ì¾8N.sÙØŽØó<Û–®uô·2ÎÑä®Ý'[ŠïÝÉøJgêràÝ›üžÈ¶[ɪ^ã2ð¾*ç°íDSš<þÕ”[ã0ªï$cíÃJìÿo7f.ß·¸S)ùG¾-£/ØÊ¶o_ôs-<ÚŠø¢+•d¶¶–XÁ\ÊÖÄ4ÿLç7W°¯RîDdº©zí–Óütó"b¡Næ½}@áXºxëV´‹‰¶ï&ÖŠKüMOü>‰Xˆ™ù{˜$¾tK—÷ãÉ8/ñø‡&?I»‹•îEn+æ$ðÜãêžÌœþlÊ>»xñ }•j‘Ÿš|$Í.(ib:fâ‹í–ú;¶þêú¹kwDÃqe­ü´Xºxw¢ÞÝî/7ÆP¥ºÖ"à¾ÀÓ‰š£n«{zú,g6.o¶pÇi#¢FËjž[¯NƸ’žì¹7wLöKʤ¾›MvŠ`®v*q'XÇæä§çjWÑÜJº%Dõéûe_ßPYO$wí>Õp\ïMÆõÚbéjàÍ>ˆ¥rÅïMÌæœÉÌ{4Km!šjÞ&cœ$íêï“}¬ ½Šjç&c\ÖR|CíÌíaÞDdj2Gîî䧆µÿGÙ./l(ÎC Æ8“ûqÇL./k¸Ï*“K Úätó"¢EfÀÙ½xºx³™éª|©Wµ 1Eý³)ý5ñcrÞ:7Ufw!¿ÈªôLÄLöNÆÖÆ÷ãö¿N¤*C¯$ÑfkMÝ ß@¬%÷{óT6=³\üyKýŽ"[ÞìI Åsp2žÒϺxèóFÚ»Ëyñ|xçŽÝ÷w7rÛñŸÿªˆNIöõåäùUñúdl¿¦ãL[[‚&‰;ÞC[ìûK”È~H,jÂðÅÂñ6ñLp6Û³©Vòjàñ-ö=Ì£È]»ŠçýÉx^ÚP<] ¼U2¦ Ú)5û싾¼§'ã›$÷ñ1ɾÖ[›4uö£J{cÃq 5A¬(\C$°hjКÉ"†ïC­Ú6u@›tò¿4gjë­NiIKŸ êî'.a¹*M+(ÿ÷º‘î±j,kin†¨«7“ù]5ûì‹>¼oLÆ6h“ès‚X™ÞÅßá0HÆ´žf £-Üy ‘eäÄÀ{[SÁÌ`båd)Ÿ'îx›t±ð¦”E4³R|˜óØ”ú ´óHa˜ ÄB¸ª¶§ü]û£¨žÒ"UÝõ…céZffÉϳno‚tßT㫉b5UMIöùÉ×âÉ×E?éÄ€¥½g2Ó=™rwŽ“Ä3Ÿ6ìL>5ÙLí•-Å=“‰–]™ø#r×î¤ÂqdŸ7?»pSÓTó%ŒG…¬¹ôíŽw7btÝïšÖˆagr³ h.w|ö.ü˜†â™Ó¶Dï.Õ©%9½ý†v?ðgŒý-Æ=“miïGË03%l™«ýžrÓÍ›3{°amq÷Ý”®ÞÌû1I¿Ó”Žª/ïݦLF½Õ ¯H4Š““}ÿcÍ~g²4Ëï‰|6Õ¼’XÅÜ¥= kðk°-_*x¬’×!c%ð£Žc€Üœ툽À%LnÚýtâÃ<ßü6ùºk!”so¢¼ÝéÀ•Ä>ÚÅûˆ”:NH¾îY5ûIv«Òg‰<ö)µ½);<Ö¨)ÐJ)ù,¹êÒþùêdr+ &V›×õ´äëJåÝî›ï?FªZB”°<¸°hDý·=ÃH/"¾÷¶!’ûlGL#ߨwckâ9ù¯(s×ùmbz÷¾_w_b†µÔÍÞùÏë²B1Œ­Ï,­é‚ÓíV0ö.ëWöÍy䦎ê®SØœ|"¦wt5Õü„d¿S§6ßLwkHêÈN5÷±­!Ör”’-ÌP2gA¶rØÅ´ðH²w…}§)9ÏžË*9µ˜­_<eîKL7?†Ü~Ã/ÒîN€6}—Ø:—µ‘7ü¢d[›[µÉ‹ˆ÷²”O?ªªz:åfa³ÓÌŸ$àFõ}à]WðXus2WµEÁc­-x¬qw*± ²ª£köë4óÝBT¿©kWbëÚ%Àq4“iJ3{3eÞén *ÔUµ„2‹6#÷yß@ó9ÞÇÂ(7òÌ–cß§`ìç·{ßMõkx+ù”ÅÄ—IÕ>¯£u]–Ü;Ú¨Û{IÛ,WÕ¸O5o Þ~ß¹’Œ«D¶¹l­3 ô=’¾ßñŽZŒym¯¢¼Ác•¼óAæ.rkâ™dơ䲇JÙY›>ºœX [ÒÄË׉ÕôÏa<Ÿ÷Õ*âš¾©Á>Î"Ò Wu$õ­Õ™fnŰw[šÝ{8Š’ÏeSðX£x\Ác][ðXYUj¦6íóä¦ß³ÓÅN3÷ZšÛn¶”˜ ½x;QiFy«‡›d&Ém-Ú†zû¼G%^·œ2;Šx)ðèûÿkÊN_µõCb‚ø¢(û?µ÷LîD$2Ù£ÃfrÕ¯ãJª?ëß‚(Yµ¯6+›t9Õ<°ñC¹é)ÒõÄ”àQt»rܦšWÿ@» ØîB.“ÕWkô™]ißf¹Æ9mFÔj}ÍÕ6æ`Êþñ=gLã~nKqO÷pbŸå!õ?Ì_’»–O­ØÏáÉ~Úü±Ô‡¢¶óåÉX2íJbëJ3sã2ðÞFÔMÞ­™Ë0§“Fˆqz[K,´ÊX–èoøãdÙ‘˜«¿€(–Ц([\þ2ÚùñÝ‚1OÒ~ºÆ-·€&+‡Ô±¹*PUÀ¶TmLŸYu}x!V$=O¶-ÞJ»U¼ú>ðþ–X±œ)èQÒÁäây¢¯-‰¿…ª}ý"uf-¸±Bs-Q4¡Í}¥ÙZгµ¿k8Þ# Ç»‚v§Ô!þ'i±ßŒÏQýzVYÝœý ·%­O/ÄûóÉ]»ºŸ•7ÐÎ÷SÞkˆ"!W9ª lú>©Ò2™ÿ²EuŽMYKön&½šöª7|€²œ«h®&ï½É%ÑÖÎh(Öév>̦†OÓÿUïG‘»¦£.–:"yü×Õ>³jú6ð윖Œ­Nû Ígªëzà]N¤ìü(ñí>Ížn-¯$wŽûTìçÓ‰>ÖÑÁÂÑ*wRO!‚]‰)¸§¯ ¦p›r&¹Â̳يØÜý0âb)Û).K×®ýZáãM·ˆx†ü66MÕA|˜3‰*Útq—S5§÷ÑŒ6å\õy0ćy¡¬fžËÕÄ Õ‰i϶vÜ“ø,žHdeª“]«´_çŒøoo%Ò9®"J0^C¤-½‚¸¶ãâSÄš‡ª »žÅèù£·"÷cëk”s·ß0ðNàÎ õ·%›î´K¶_Qn¯í®ÄÔHé×Ó좈ÈäSû<›ö³|Õñ ª_×Q¦›“[Íü½bg6º¾ÞñNw¹ä'uÚOhæ3Ô—²€ã"s7z)£ï ÈN3×Íhת£ˆí9SOàFbJ¡dªÄÌ—ë(m9ñ†Õqñ볉øÎªÛlöþs†þ¾Åøå…>Œf>pO÷¥ÅÎltã2ð,%kdÇeÚe”ßçÀ[Í£È]¯GŒxüÌêééf·N-Obæ=Z—[=J.:h†~J¶3UŒi/6å n*®ÒÏÑ÷$~ĬŸ¡¯¯3^wºÙâô§ÎqÜ&޹Žü6ˆ:ÆmàXBì-½Šf?ß“Ä"Í’?*x«û9կ׿pÜ­ˆGNUýþ2§Õ¾?!ž?ÌtR—Ïêëû³ôSª­'ž½¼šx¨?ÓÇîÄ—Õ׈g/MÆsåfö!žÉÏ–S÷ Æ»2Ìû©~}W2< áå‰c¶–ëušqx6'ž§“fÈ~º`̼ս‚ê×ëwÌ=†d§™ÛÞ¦YÔ O ±À¡î-ýÓ†ôÑD»•xü#bsæUöšš× `?b¡ÏLw¸ƒö Êý8êÊä®ñl¹›ïŸ<Þ³‹ŸÙhÆ}àê>ÄB¿LQŠQZÝò¼ÕíH|¯V½f‡ÏqÜÌ4ó…åN«;{ƒÓ°½Žø@Ý+ÙÇ"àÜ9ú˜/í*ò•tU]¾ÌÜwo£½´†Mš w‡úîYŽ—ùe¾Šê««K™OïÀÖÀKˆ¼%?[?-ŸoΧ¨~ÍNr¼ì4ó«ÊžVwva´éà5À)Ä]JU‡ŒpüùО¸6w&¦É/áøkè. eSÞFõë<[Æš¯&Žõ…ÎiTóqàØ‚HÆñkÊ}¾JlkràÍɬ×YÉì7"™iæ:)){i+àcŒ~Î'î.ª\„ÏV8þ8¶ï3zâŠEÄ—È2FŸÂ¹ø?#œ@îzï>í8[‘[m›Ùó[Ê|x6'îRVRÿ3ö‘ñ8ðæ]DõëöŒYŽ•Ù¦ô¥ΩŽ¡Ú—×:bUí1Ì=ÅzW¢L^©®OíVFËÖr¢–fÕ»€s€»püq•ù@¿pÚ18Æ º]œ¶Þ{§Sïsve8xó^Nõëö™Ž“f>²™Óꇇ «ª^”åį˜£‰Dø3ɦòë{{Éë¹?±õâ¼Äq79¶Ç}Õ\Þ@õkóùiÇ8>qŒeÍÒHÒÀ ñLÿÔû¬Õ-àÀ›wgªÏ*ÝÄ·¨f¦™¯§™\½²ùê.“Ä>á3ˆ»’éÙg2ÏôúÜNáö ‡ï¡Þó­k˜}õî|³7¹âÔë~Vâoö´æ´ÐÞÌ­A;¤fß¼õdÆ…ƒ§ãÄÄ1f[P9/=…ØUwpº”X÷bÊ©‹ìM´óˆš¢K‰i˜ÏR&Mæç‰©ù…$“ºs¿¯½Õ w_G·Ùaá¼ä÷÷ÏöÌpT¼õd¶N­q½ˆÜ˜R5QÒØ»;ðEÊ Vh·èv“í|à–‚Ç»ø‹‘Þ•ù'S eæ1ó|·ÙoêÀ ±.$sîϯٯo}UK¾~gÊkVñµ“DÞî^h³ìÛµÄCí#ˆ³® ¢ ß|peÒÙMÓ/ûR6KÏ8ù,Õ«*¼ñJôg%¢ne‹Rt=K¡ê«Ë¦ls™Ç;˯iDõVO@<£]ÓAÿóÕO‰é›cˆ¬? ÕÕDœ*Û«–V|ÝåtSH›Ü”|ÝÊ¢Q(ãD"ñ̨¶ºñ¿«®[YÃðD­êªÐù€ãˆôp'Ðÿº¯}v5±øl)ðßÇÒUïB—5\«¼§³ êNv Ãò¢Q(ãfF«‹=ÕRb®úY=Ýt5ðü†4 .ŒF·’Øú2øñ²¾Ûpzå TŸM9¸GÅ×8Íܽ}“¯»´hÊ:¡â¿ ±µ²jîÿeÿ}£ºx.$žýJ$ÒÐìnÞH<ß~Õ¦jЉ RULO¤1—‹€ *¾f¾ù‘Mê.Æ)¹Þ¾ø±ÈjTû³iºyT×i`5‡ˆmC³•±[ˆírb«Q¶xÂBóç4û~¼¾½S™SW«š¯ÜxœUÄÅÃk¯ª'2¼òÖlíœ}»ª¹œ—2úõ»¹ òLoïlïT懽€&Wè|>´ Ä ÀѸ ³ªmÉ• õ}Ù³½S™S×ïÔöàuÄI“MÌldÎû úwà-'[.pÔ¶JÙ‚¨Çû š-”Ý—vñƒcïo,~*Ýú¶ˆ­OïÔv‘yí±”KÓwà]D~÷Ì9¯ö(‡oYŸ ™Ïê¹mžÄ|vbÊõ;䦙úÚn$¦×`þçTnK&—ë(íemžÄú:ðNm·ÏóÞ<xQJt.;²)£ÛÀêä¹Z©Eœ¼e=‚f>«Ãòß+éžÄ—à4;UÑTû%ð>àq8•Ü„-‰}ž%ß³uÔO°_Ú8 ¼³µÕó3àGÄʉÅO78þô÷ç<àÀ[ÞO)û~ßìÔê,@[µißJL®¢›ÁtXû p*ð×ÌŸ¬[}÷Qʾ‡}\u?Îo›í½5Ïw*Þò^BÙ÷ûÔvÃÝ|ºËZ üׯ1]»?‘Óó¡Äþ¯û“𶏂¸£üŠ?øm }ëöN&¦6KOãçàï»BC}x;°u¡ã-+tœâæÓÀ;ÝZ¢êÏyÀ§üÿKˆbó÷%J îLpØeãoGäÝœÛ×^ǦBË7þ÷5D…Œë‰\Ô—ƒí¥Äuï[Äû´kc­þ½ÀqÔ®+‰çý·uˆ†ZAÜ¥>§À±®¡ú^þÖÌçw6×mlUóùj]_§¬^F¾z‘ºS5ótßþ§D MqàÕB0I$Ó¨ã4Ì‹=ÝC€céÏ4îJbWÞĔåd·á(é$b{hÖ²Bq4ÆW EÝib§™ïh9± uOà`b«Ì-Ç0IÌh¼ˆX,ù:¼Ëw+ÈÿP^Uãµ­Yˆ‹«´0]H ;&^»Ž~î߸‚ÜôÜE…úŸ$+,Þ8ˆÈFô "çúöûºøáÆþ¾Bì(hÛ%ä®y‰ ÁçÉmü"1p÷ÚD×HZîJÜÚöÄv½ÅÄ¡-Ù´s-1m|+‘½êz"ùÌ%À¯€ß·¸:ñvà5‰×F¿$K’Ô;[«Hw%°YñVæ3^IRŸ<‰Ñ hL·ŒïÝ•$©¯Î$—›yŸ.‚•$iœÝ‹\­å±ÊDèT³$©/žOî9í²ÂqH’4ïm\Eõ»Ý•ܾ $IÁSÉ=ÛýdÁJ’4îÎ!7ðÔE°’$³'“tÜE°’$³-€Ÿ“xŸÝA¼’$µw‘t¯¶ê ^I’ÆÖQÀrïñÄ+IÒØz"QÆ/3èÞìÔ~È’$Ÿ à%Àjrƒî$ðâÖ£–$i =˜(Û—p' °–¼$izð,`)°Í,ÿfxð7ÀQoÀ´Ç6r6’$õÜû¸ý€¸Šxöz)p1p-°†2ƒí }¬•3k˜·ë’¤¶ÚØšZôt1ðò†ŽÝ*«I’úîà)DA„±çÀ+Iê³5ÀÑÀ…]RН$©¯ÖÏÎì:’x%I}t+p$ðÅ®)ÍÅU’¤¾ùð4àü®i‚w¼’¤¾Ø¹ÐxµIDATœ@ì ž—ƒ.8ðJ’úálà@à…Ä*æyË©fIRWV§ ÞÁW’”ñâÎt_`?`æžE]G½?ø‘·ùæc쥉®$Í [K€í§´mD:É«€«‰}¹’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’$I’šñ¿öÒ¿Žgçü£IEND®B`‚vispy-0.14.1/doc/_static/vispy-teaser-short.svg0000644000175000017510000052114414507104216021011 0ustar nileshnilesh image/svg+xmlVisPy vispy-0.14.1/doc/_static/vispy-teaser-short.png0000644000175000017510000001634614507104216021001 0ustar nileshnilesh‰PNG  IHDRªo½Ý„EsBIT|dˆ pHYs  À½¥ÚtEXtSoftwarewww.inkscape.org›î<cIDATxœíy¸\U‘À/ï%!; „%, ¦ŠÁ Ä K ‚ˆŠꨃ¸+.¸! ‚¶"Ãâ #A1Adß‚€D$d__÷üQ¯'m§o÷­ºç.ý^ý¾¯>–¯ï9uîë>u–ZÀqœ<œ üX¬Vó€_‡æ§šã8ŽÓ×Ùx¨´«€ÍrÒÑqÇé£ì¼Lk#U•«óQÓqŠCGÞ 8mÉÎÀë•Ï”Y)è’;»ž»XÝâ3W'+Û ÜfЧ¯±°kŠíwËzþ}9ð`iŠý9Ž“€ÉÄßÔÊÞy(kàjôc{ èlÑî`¡íkƒ¬wóYlßË$²xY„ý8x#00å±:ŽÓ‚N`!úõ7òPVÉ dÕ¬Ûc´ýC»`A‘õ~ò0TQ²¸³G§±iÚqœh.Bÿ㛋¦:ŽÁ61£ímoú…\/§H†ª^¦á;-þåw¬XŽ£Æ"oEæxÃ3ó€b|®¿¡ml—ñY§ìüx8 ÿ{ªpCåX¹xÎðœÅdÅ`à(Ãs× «æV¼bhäÂ~ñY§Xl\ <ŠÜe91pCåX©`ÛUÙP½ jx.î{ø3ñ Z=žqŠÍÀíÀùø<Ü’ÞºýÜY¹ŒÆ#z`Ç]ënº Y¹.BÜN÷ˆebé+\ ü§ò™±À¾ÀÃáÕIŒÅˆ> <ó³ »€Söá^½“.àkÀàø®9’v6T]ÀžÈ¤·2Vÿ*šrÿðâð40§GüKµq’ž |îxŠg¨†Gž»Fùùó€ÙÄÿíÍAR*9½—€áÀ`}κ8 ٙྫྷJW’¯ëé}ÈYóIÀÖ)Ž»è|ýû{:M›3ý8ÊH©–Ó‰Oõ\íÄ£È^qä²ð¯ÄI›NàÍÀÈ}7ù‘šMXo„…åõÈøµïì y(Û„_£Ã] úÛ·§ÏF ®¯"«l'>ín¨*À‡‚¿•^@ÑR(õAvNSmòUÇÌkÀtd"šAï?&¼8@ùÌ·Ðßo¥ÅPä^r°ò¹?NØw`0 9öY„$¬uô|ù^ix x"ÆçºûíÈ÷e+Òq‚XŠ\ixwÙ Ù,"ÿMhY‚Lfƒ½­âqú÷òL.š6æôú¯G&+§8XvTÿeìk²8;ITu¼ !×urR`2AÌÂvtÔŽr/p*’¦§71ÛÑì~y(Û€Ðëþ‡\4u𑥡ª§?âŒó’Ïg݈ûº“#C€O Úy޼d1ðe丧·0 ý{ÐÓ¤Á0`zݧ堫Ӝ< U-3èR+W¦ —ƒQÀW£°¼ EQdräÙ®wqµ|ýøçå¢é¿ò>ôz¯ÆŠHQ È‚ü÷}jç†ÞvòRh ;¨WÈß0UV"»‹jPr;2 )y ûþy([ÃèuþŸ\4uZQ$C2÷ýÉ SUŽKQ7§‡Nà4¤VKÞ† ]äeàlÚ×½}:ú1;M…aÈîH«ó1y(ë´¤h† ä´ÄºH¿J{ÄV¬@ŽÿÞ«|n*ÙªÀÛ Ï%Mûàå3çÍêz<ðnä½€8$eðâ:ÉY Ðn;±YĬ|®YÈ=Z÷ÿû!‹X­á+Ç"¿Ó<Ø YèiíÎÂ.Å;°ñŽù$2¹w"çºóíA¦³qG98‰¯X‘ Í«‘UlÑyíqü÷~ƒžÿ ùâ®dè7ͺEÍ zYdpRí8´W[Qþ@â'-ïë=í]blï”à#‹Ï¿7Ñ«™|'iÇ“í½¶ã%HFèãØ´þÏzÇýÖ… Þ× $(ð2$u‹¶Íç·4h·H ^E?¶IëiqüHš. Šc¨ú!*Og§W‘ÄÎ{S‘ Õ~Ý*À‡#ÚÛÇØ^žê×6Ñ«™ŒOÒéÈn(ngK‘ ¶Ãi¾*½À8˜¢IIPE’×ðt±edÑò4Örú÷ÕȰ§ÅæØ\éCŒRNýÖ²+p·A4å`»„ã*²¡ÚÁ [8·I›÷Ú[O>q›C°(Ýoíp3âODc½÷o›ÿo=Ïäý£ %/"åIZ1©Esñÿ 7èt2úwõÙßiýæÒ¯dè;¤¡:Y4æýÛ¨—¹$OòZdC5È [)£ÅiÆ6?tdñ°äÓ¬±t6†xgÙ ‘AÇŠ.äÂ8ïLh¹Dñ@ªø{Œ¶ŸDVÇE£ùhß•6»•ßt •î©dè;”¡:YQçý›h$!&Ï"*Ð@U¥™¡‚mÑñ`ØaÅÂX¿©íh­BŽ-q&gÒ²Û|?dg2æŽ*K€7ÚO›¡WYÿÄvì*û}ÉÐwCu¶qg!Ë“•¥È†ª6‡³sZ´kuªØ=ØÈZ3[`½¶z6‡}A^F\/J0¡À? i¹%Á»™$¯!:Cù*Ä£¦HŒþ=Í'ýã¿ô é^2ôŸÔP GŽ¡óþDI'(¶¡Ú […Ö…­N_ 6²ÖXEzÙÝ¥|/ÍØqh©w<)#YAŠÂ7Ñ¿§ï¦¨ÏHµe­N!ïK†þ“ªÍ‘ÌÖïíräNï‹ÀIHxÄ~=rp²b>IÖ;OÙ~Hwé"*Ku ñŽœ­NYs¬¿¹oÆíà6ï ü…÷QM”µÊ*$þÀ’Æisà')è´ÃÅ` öbÓB™ÖçÚY1ý{šOz+½Ó ú˜Ýc#(tHb¨>j诂枉-(ws$^ðRZ;iVCŠl¨þÛ [™MãLauªøI‘5Çj cí²Oæ_ïCž"½;«± $J¦tÄi„¿|>-€^8x¸®¯<\PañäLrßÙŒ]Bý’A‡$†êfCs€­ôYÏDà|Ľ¶Ÿ§Iî’^KQ Õ`l»Ú'}Xœ*#•‰Óä6ƒ^÷Äiø]lܪ­>Mzƒ±þ£dðº€ú­Äz”ÜP·zú!»ÈjI2úÜbip>ú÷ô½ôØý=d7’’’R‡ É •ÖIi=é….e[±˜0“^n7Ò5ºRG ¹óªL;ý]ßó„;þ;SÙwøm ¾ë)t±ªÝ }% NÏ“¢ª1Øwjå0cß§Š™Æ¾šñ)ƒÏÓĹæòYm$ÌÄ_¶Ï@ßSéû— tmÄšgsO›ÙèßU¨ã¿Y†¾OÔw=%ƒ.VCeɆ°ØÉØ_Þäm¨†!¹ù^3èQ+³è`qªØ@ò‚·õXŽû¿X‡ L"ÌÄŸUeØQ„É^ÑWËJ[œ~ ß­Ñ{-'½£ä’R— vCÕ‰-ØòvÒw[Nƒ< Õ`ÄaéJÄ“1éüÐMòšÅ©¢Y9-¯7ô_ÆF5²½–QÚÉ*ð+H%Úݶ3™Ö'Ö¨½¸É香C*?Wô{,úÀï›Kæv§ÙÁkÓR†}¾—Þ¿°Hó»Èáˆ1Ü󹑈“ÊøžŽ#^書ü¸7a—£w :™0 Cï–{¸ºÂq,ÉW$µKV̤s—ôí€Åê„}Þnè3ÍØ’’AŸ$qTß6ôW•ç‰.…^D,;ª"É“„ÙÉ[*öÐ7HÀ¸¶ï¦¶!£Âµ„Š'z9P;qX¨8iQz#–˜© úÛ)Æ©a é\.çE’±ìü/âf|xuœ– ¤;ù•´póŽ@·Õˆ=ЗÄY\ßìyªPG_YNú¡â Öj§Ýø-ò¥Ôp,öï©åÙ{ÞÂ,௠Ûxü|’¨:äQ—#Ç«SŒæ¡¸ÜðÌûHîikqBú r¿Iž†jE vB{«4#T:Pco7V Ï|°=öËeËn,ÍHý<¨® åÁHrÕ¿#ÁúyÆæõþ ¼¸+p» O¨¼3ÉÚ-÷S¥„}¦Êa„9×½1#}û±1^Ù@vu`ŠÈôï쇆~,±[ HñVRêT!y=ªNl.ú­d=R“.«l+­h·;ª‡IîœÕ K¦ŠKôgqŸOÌßÜ$òñþÛ™0ìeˆçNÚ¼1¾Ïf k#:ﯼä@¢«EG‰Å€œ­ì£B:Épë)ô QŠ~ öœsqä1$)ršbZÑ.†ªñ°K{Þ‚>¦ëŸØv¿¥ì«‚¢Òð0àçHõØ,é$\ UÉb#¡ª¤ÎÈ@×z¶AâEŠrTcI-ó&ewúQ¦%ƒ^! ÈB%T6˜(yIôS 5´ƒ¡ºIm•–ÚzGúé@áš~ÊHÌUlvGòÁ}”lWÜOæÿ»”õN¸ÕèSÖµž“€…䟘¶KÖü‹íoþØonÂ1Å¥¤Ô«B8CrÔ¤P,²qLys@Ý[QdC5‹tâØÛ «%ÍÛ†~þdÐ;3ç[I¯M=I’6ÖË›SÔ3d–÷¬jDm‡xÓ” ãv’NÄÕ_óÞ^$þñŸ¥þÏW’+%ƒn! Hé‰ë zXå>Äa mŠf¨ G|{§9è܇NoKæöï+û¨§Z4 ™ØV#e3ÒvC=•p_Š9¤Sëikt…Sб–N¤ºo5ˤܟ•‹Ñ¿»¸1Q÷ÚŽUú:%ƒn¡ U•)Hå¬&ë<·¡ZÞ3Æ/"³<½ªkù ú±hŒHð‚²ý$ -úLMcO‡&i¬¡*ªr=a.G!± ¡ô 'шý‘܇Õþ¾›rI8ýûûQŒv·G_^ ËDÁ%¥nÒ3T %~¾AØJÛÍd5RŒ4+‹¡ZƒÜ«Å‘ùHšŸ»‘@èK{Æ2ÉS§MÕ•§Š?*Ú·ü–KɆ$|·¦Á2rf¹Kˆ†P;±†æ ³%ò… ©[ZEéÆ ÎµÉWAþ~ÍèžC÷þâÿ}XÙf…lw%ƒ~iª*£‘ßýJƒ~¹ ýñR+òΞ^d´NÝÄw¾ú²í ®j:t¾ù"‡ÎS÷iôƒl%·¬äž¤sáü†:5b(âÞY4ùKŠ»º«ÅâÎÚÊûO[7¬I”%¥~²1TU¶A¾SÚ;D‹ÜìèBá†*‹Sŧb´Ûä„Ô´û,ÑßiÐÉ$£u¨û í°•"h% ‘Õµ&FlK³&}UèÑŠÀGh<™\IqÎÆ[aùñ4 þ„~GpGà1µ¢¤Ô¯B¶†ªÊÄ G{¯•»w9Ú¿åÃ1ÚFØßG™0ÕœÝP5g0úÅx³ª»HÞGM{IªÇb ÍU–!+úñÆöݤív—ßÉàË4Ï&°8ÓØ~ØýûülD[ÚÍïÓP JJ+ÇPUéÞ‰-ûG”Ü@/7T­Ñ:U4»O²û½?…1mÂ!´NÁÒ83¼Ý1Ô.¤s7”·<†îž¬ ޼‘Ö»½eÈ{nw@÷Ng7hc¢² pJZjBÉ gÑ U-‡.ámÒVn¨Zc¹ŽÊ¨®=ö[F†¹ w$þÄò,²#ˆ›Ïéë1Ûm)?"Gäõɘm?A¸ŠœyS-7WÖ²im°Ï)ÛXÕ ,()õ¬PlCUåhô;ÚzI¾á†*Z§ŠFéË,Þ~W¤5 ("…¹4öÀ‡hîŽ:ñ4Éʤ-­T‡#à³Ñ妻ž|&Ù´Ø}n¾úÄ™3•Ï7­(š"¥˜úÕJ;*Ç*KÔª<’°7TñÐ:UÌkІåØO[m;§¢h_‡L*£qüÊž¤ç]”¥<¸K׳-r!}3¥¯is âTÑŽN­¸Ý»¨uSï>ÍÕ””ÇE)¦~µÒ.†ªÊÇÑ/<ªsCÿýº¡Š‡Å©¢Þ÷@{ì N‘ä6æ4ëB\ãÏC-?à ‡5±å_£}Ñf”¨]}kS¸¼Š¸úçA)†~õÒn† $tÅò=O’Û U|´;ßÏÔ=¯Mˆð…TG“.$‰mÒÀÝÀ/ÜÍ ÛÊKº‘û’O÷ŒaY€ö.$›ây²ºïO7  ²мÓ+³P¥šI;ªNlÙ]’”£qC­SÅjžÝ]ùl7𺴤a"áóãõe™C˜ø’vA»8©ßÍP>wD&£iL)B§fÒŽ† $Ë‹v¬IÊÚ»¡Ò¡qªXÆÆ´lÚêÙš· ÅàQ$Û’ÆŽUH}¤IÀ½9ë’%×*?rî=IñÌ?È èÐ$Ó¸–uÁµp¢¸\ñÙaÀ>=ÿ®­¨^R~~ÒH·SA’¢Ž~ŠÄ9ñ(¿B¶Ö_¦ïýh‹é¸ˆÄßi%_E8飉¬²<¸N×KŸ?ÙUiJ?-C ¸&"ͼp‹‘?™Ï5A»×£[|6$‹¦s©ˆgÖ-¸Áªç`2ro'[qoGsü7]v‰çsù¾ÂÀç‘ÒYÓ¼GùÌbÄ#ÓÉÍñß^èŽÙ!PÄ<Ø 9\OþÎ yI˜Ntj’¾Ìfèb<4îþd8Ž(Jè¿/VgŠízž_‹,K ·–ièÇySÂ>Ý™ÂF\§ŠåÀŸb~¶„¡Ï^ÐvUÖ±1åXÜPÙ±”ÿh&'f«~1„}Ùm5+à˜•¬AV ?DJÙû±^XÞH¸¿U‘‚¦K´—¡ÊBšU‘Õà†*ÚòQò*r›±äåÁjdg5³ç¿»þ€}±ÈŠlÒñ2y ñ`™‹dßxYY®O¡/G¸©oôºmyìTq¹9"wòçrôq‰¸™³ƒÑ.†ªž ˆ¡˜S÷ÿ#Ù—·F[#´["nÏý‘OG”‘ÝÙ:äèn ’bgr´ñ’fEª£qQA\ª£ÊÎÇ¥›0÷NxþŠÄZyöšbp ’Ÿ±ÕÝd+JÉUqœöa’CÜš¹ÖÍ)áG¤êh㸢ð£¿äü˜d׿¥¡T™)ÇÊ#$ÿâû±_±(#¥Í'#1”N±øYÂçK!”¨Ç •St’šµHˆÙÆ,nDŒC¸©®pò·qŠÇìiƺ«êòÿ¸¡rŠŽ¶ôG-· Œ}•À1H6þ €çsСŒÝ;IV$L§1Ö]ÕLä^ßqú$b;/Ÿš‡²-(‘ÝU=H©³‘Öƒ.qd-pp.ÄŸ~Gk¦Š÷¦¥PÛ' tÇL?$8},]•ByC/Ùþ žÝ€G]‚”æxxø RÀöÚ—!À‹ÀÅ3¯cøRÇqœÌé@ŠSVe`¾ê8)3 ýnê’<uÇqú&w£7TÚ:UŽã8Žcbú„à§­”{ý9Žã8UÎ@ï»PJAÇqÇÙ„è Ù®¶ÍCYÇq§ïq2ú»©é¹hê8ŽãôI,NÇ墩ã8ŽÓç8½‘Z ÈCYÇq§ï1 ½¡úb.š:Žã8}Ž©èÔ¤ÞŸã8Žã¤ÊØr>–rÐÕqÇécŒDr3jT7RÔÔqÇqRcKàÏè”ï¦ÇqœÔy;°›‘ZŽdIwÇqœXŒŒù¹À»ÙØ TU>P÷Øx=*Çqœöep0R l°¬GV!1N£]€A ûzØX°Çq§1ƒd;$Í‘ßøŒÆ´ ž=ÝqÇiÅGU.¸¡rÇqšñàWy+á8Žã´'iý•ð ã8Ž“€4 ÕE¸‘rÇq’†¡ZÜI9Žã8NbBªû€½2ã8ŽÓ« e¨žNÅúÇqœÀ$1TkßÇ]Y+®Á3S8Žã´/Û= À8`x CR,­V"yŸæ"é”Vd¯²žÿ0üJ>jaìÆIEND®B`‚vispy-0.14.1/doc/_static/vispy-logo-V.svg0000644000175000017510000000503514507104216017530 0ustar nileshnilesh image/svg+xml V vispy-0.14.1/doc/_static/vispy-logo-V.png0000644000175000017510000000435214507104216017516 0ustar nileshnilesh‰PNG  IHDRvyìñÕÄsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<gIDATxœíisTE†Ÿ$Ad³q‰®,*›T¡ˆ ê'Ëò›?Á_¦„Aq£Ô’Ü(ET°DÔ(d [®Ú›áÎÌ]ºû=3ä©ê* &Ì9}Òo÷=·û4Àw@b¨]&Ó^|¾ßÒí@7°1¨ËÅ™¬RQ€û€GÕFÔ±±P[‘Á j ð¢Ú€ ®Åôôò‘n_‡ò8o¢ï¯t;Ðý¿qƒœ.Ëb`¶ÚˆtÏ«¨c\¬59îÖ¨ÈÁàNµu ÀõÀ~ ü¬³%“v˜g׫ ¨ã°®`“Æ–†Xë´,¬ýòmÈú˧ÑOüõíNûf 0‚¾Òmi͸ôˆÝ üêÕõêXižnQ‘âÜ” Œ l‚½Õ±åÀZ³­ÃLV¡—“t; ôúñÛ;ÖžýW43¶8nÀÈt³˜^|}¿¤Û1Ü#â5ÒR 0нձ5É{+öd¸>°`/Ya1°Ölʳ`½¼ÔÚ`f§}ÓœDß/µö;4kÄ^Å–÷«ÕF¤X Ü®6"E?n CV`a\Ž›am~-« Àßèe¦Ö—ñ8Ÿ ïZû§h7ÐhÄ^¶”ó;ó¹j#€©¸Ô«pSç 4 lí‡,±Vm.h)aÒ0FÍû>0ìß–ÒX˜g-ØPãnZȤY`/[½›Sžµ4·7–Nƒ¸)³¯ _ ¤Û’²Žx`N›T­©z´ïâñVPJ¡¥Ýˆ']Í>Ð*°mÞÌ©Ž2°–æ×·© Ã5^C/;µv·s!6ÖÒˆ/ûpêVà¬gjM!‰++Øë» [œg•yØ‘ãs±PH¢¥ÕðfàR«å}|°”¬PÖÒüêõ¬Õm¸‘«–¡÷&ãnŸÎµ`n„¨ýNpO(“òwÄžvæülhºˆ{¬b-vÒˆ›q[^[R$“s³Ê±%ƒi¸CÉj9JpîÆlÞ ÈO‘|jÕÎPà@x‘{ø°ÀçCrqÏŒð=yØŠX¹(šT¿Ù严ߑ— •fà²?jYJˆóæiPè_º#BÆm§GÜJ½e¦€SüLhpŠ®eÞoZ‘ã)´8ÖP‘Àô€ÿ ÷y™ÀVzÁë™ó¬•Çœ`{Ñ*ئ[2"²ó­ä‡wàu¢ðúy'!Ü)¸­Ajÿàõþ5ä.\§ªN€Wøgåô.1T˜²›Ã†€Ý%Ö7!äØÊüº“’[“ªìú³²:îäÀJúø^Ü+4µ\%ø=%0߀? îUaéõC•{ØSáç}²Îãÿee5\iÃ~Õ Ø(ÇVdXZ•v6äøœ:+ˆ•4â% œÁý}G$¤ŠWUà~$¸ú•ðq¦“äØŠ ›èÓyèÃà#¾ì1àÇ`–_¼°}‡T=%0Ù´*øp _Ç-HÇDÜY–uøY€UÅK_ú láÁ¨2GZ˜_Gq®Lq½Œ}SÁþ#ìßUÁþ1øtûw;r5\ÏT4×¼Ô*ÉÅþÞ? ň=ƒ§™›gc³÷¸UÎT!MÝhªI¶ÀD{`®cîuZÈ[8,0„«{5ö«Fì)üܲΠ‚ ÂMðúJŒl'¢JŠîÀ­c'IbqWÁFr•rÄž>~h¤÷) -ÇRß”R îš—ãØ¨aè“aÜs³*7-±CÀgbB° aPAXèL9nës9¾¨=À+v5„hÃH¼X±–®yñÁfÄ2 6 %ÇäKeú°qÍKÕv˜ä¹oJaeľPá-¸‚`r¬:CÂÆWÃÌE/¥UÚÜÞiX±G‰t®%[q[~L`)°ÐÞrÜζç!ô’Z¦£ÚeNÞ±6bã®yi7¶ã‚kk…ö”´ñÕp —Ö"ín¯ô898ˆ>`y›É 0¥ÚKŽÛÉV9O¢‰yÚ0=Pt,?¢\«¦®:׫R í!qã«á,C?"›µK¸[¢Ç)…;]µmý®Œe)}!ëf˜ž*þ‰Ö/fü\IEND®B`‚vispy-0.14.1/doc/_static/style.css0000644000175000017510000000136314507104216016346 0ustar nileshnilesh/* Sticky footer styles -------------------------------------------------- */ /*html {*/ /* position: relative;*/ /* min-height: 100%;*/ /*}*/ /*body {*/ /* !* Margin bottom by footer height *!*/ /* margin-bottom: 220px;*/ /*}*/ .carousel-caption h1 { color: white; } /*.footer {*/ /* position: absolute;*/ /* bottom: 0;*/ /* padding-top: 60px;*/ /* margin-top: 80px;*/ /* width: 100%;*/ /* font-weight: 200;*/ /* !* Set the fixed height of the footer here *!*/ /* height: 190px;*/ /* border: 0;*/ /* border-top: 1px solid #e9e9e9;*/ /* background-color: #f9f9f9;*/ /* color: #fff;*/ /* background-color: #222222;*/ /*}*/ /*.footer a {*/ /* color: #fff;*/ /* font-weight: 700;*/ /*}*/ .jumbotron { background-color: #fff; } vispy-0.14.1/doc/_static/perspective-matrix.png0000644000175000017510000002117414507104216021037 0ustar nileshnilesh‰PNG  IHDRòR¯úVŠbKGDÿÿÿ ½§“ pHYsþþ“üÃtIMEÞ 6:XTB IDATxÚíy\”ÕþÇ?0€,‚( Š,¡ä‚Z*˜R¨hP ¦NIš .iÞŒò•/{ÝìJ™Þeób–PÝ.î傸µ°È–à¸!‚  Ì0C¬3s~øã©qQ˜ïûõšÌ™gžó=ŸçÌó}Îö=:Œ1‚ ‚ ´’~$Á£9uêrrr ‰°cÇ„ ‚ G®Mlß¾ÇÇåË—I ‚ ‚¹6!‹!—Ë1tèP :”!‚ È‘w–Ý»wãÚµkJi“&MÂ{ï½×-ùß¾}!!!044Ddd$üýýÁãñ¨ÖDñá‡B$)¥½þúëxýõ×I‚ÐFGþË/¿ÀÀÀ“&MâÒ¬­­»-ÿ#FÀÎÎÎÎÎXµj•Fj‹ÊÊJ477ÃÂÂ:::Z[!{[yHËÇÃÞÞÜû'NÀÞÞž9Ah«#€9sæô¨½rå Ö¬Y£‘ÚDGG#;;»wï|ñňˆˆè¶ *i©n”ÞÑÅ$ˆG £ÉËϼ½½!zÔ‘=ÉÉÉ8p FiÃÃ3Ï<ƒ˜˜Œ;P^^TTThÝ@o+i©V­Z…aÆ!00.,A´ƒ.IÐ>õõõhiiÑ8'<¿/,,Tš€gii‰êêjdddhÖ½­<¤%AäÈ{˜ÊÊJ\¿~¯¼òŠFÚ—›› 022âÒx<ŒŒŒ——§uz÷¶ò–At´ü¬ Äb1fΜ‰wß}W#m”H$àñxÐ××WJ766†X,Ö:Í{[yHK‚ È‘÷ fffسg¬­­aee¥‘6êééA.—£¥¥zzz\zCCƒÊ \èmå!- ‚è.zm×z`` tttžø5aÂX[[?Õ9~©sÂN븽T*åÒ ¤R)ÌÍ͵îzõ¶ò–A#Wƒ#gŒiÔKŽÜÕÕúúúH$\Z]]cxá…´îzõ¶ò–A#ï%Èår( µŸ×ÔÔ^^^ …\šP(ÄäÉ“agg§u:õ¶ò–A#ï%à·ß~ë’s‡……!** YYY(..FHH"##µV«ÞVÒ’ ˆî€Ât1£FBjj*ÌÌ̺äüMMMÈÌÌ„\.‡›› ´ºBö¶ò–O„!ˆGÓ«g­‹ÅbìØ±&LÀÝ»wÁæM›¸Ïc ‚½½=JJJ0wî\ <‘‘‘¸zõ*^zé%!%%¡¡¡H$‡D",X€áÇ£¾¾ÁÁÁ°··GEE\]]annŽ'N ¥¥‡Æ‚ 0xð`µ—‘ÏçÃÝݽ×\³ÞVÒ’ ˆ®¦×v­3Æàçç___,\¸ººº*cÕ7oÞDZZf̘ðù|deeaÍš5HJJÂüùóáçç‡êêj¤¤¤@ ÀÛÛ‹-‚¾¾>jkkÁÃ’%K0eʼýöÛÆŽ GGGÌŸ?k×®í'NA½¶E~ëÖ-deeaÊ”)€ôôtlܸQé 4...˜6mæÍ›[[[…BŒ=š[ªsûömÔÖÖ¢  %%%¨©©Á‚ `mm¼¼<ܼyžžž€¥K—rçOHHÀ¼yó¨–AÔ"\ÊÊÊ0uêTèèè ®®ééépqqÁ¥K—<ˆ~õÏþß|ó JKK!‘HÐÐЈǴiÓ8'Þ¯_?899ÁÞÞ¯¼ò ¬¬¬PXXˆû÷ïcĈÜöŒ1.]ºwwwddd ¦¦†jAA-òÎ2mÚ4ìß¿çÎCqq1ÆŽ‹¨¨(¼ñÆ™L===$&&¢¤¤Ë–-Cÿþý¹–´££#bccñ믿âèÑ£°µµÅ„ pøða˜˜˜ ¸¸sçÎÅ”)S0dÈ9rÆÆÆ\:Œ;¿þú+šššàêêJµ ‚P;½zÖ:c R©¦¦¦Ëåhnn†¡¡¡Ò1µµµ011®îƒÎ …B{{{ ¹¹Yiã‰Ö–¼J¨Ë¶Òchhhà‚x?~œi+Ú¨9iÙõ¬\¹’mÙ²….*¡1Èd2fllÌ ; '''888ÀÄÄ_|ñ÷åË—ãþýûHOO‡““ŒŒŒðå—_rŸÿüóÏ4h9òÇE"‘€Çã©Do366†X,ÖªŠEšSý%- B}dgg·ÙûÉÃòå˹]0ÿþ»ËÎÎÆóÏ?WWWlÚ´ ¯½ö÷ùôéÓ‘›› SSS¸¹¹aÓ¦Mðööæ>ÇôéÓ;´I£»Öß~ûíé.ÖÓÓƒ\.GKK‹RzCCƒÊÍQ“+–6¡mš“–݃¯¯ï#obÑÝ÷Û &¨¤ 33~~~JÎ](âÙgŸÅ¨Q£¸è¡o½õwÌ70{ölŒ=päȼùæ›Ü÷´Û‘GGG#;;»Ûó8p @*•ri …R©”ÛÚT*–6¡mš“–ÝCLL 袿½uëŒáààÀ¥¥§§£¢¢‚ka¡¦¦'N䎉D\¨ð¢¢"ˆÅb¸¹¹òòòÐØØˆqãÆi¯#ï)\]]¡¯¯‰DÂ¥ÕÕÕ1¦‘㋽Á‘k›æ¤%Aô^š››ÛL¯¨¨@uu5×zþ;cÆŒQÚÏC&“aëÖ­ ŰaÃMMM°²²â6éJHHÀÌ™3•œº­­-ïáĉx饗TB†3Æ”läycc#Äb±ÊK&“õ¹‹hjj ///…B.M(bòäɰ³³Ó([;ªX¤yß„´ìZêëëQPP€ëׯ«å|‘Qs`ŒaÇŽ‰Dí6šžþù6‡¨ðÉ'Ÿàã?Æ… °qãF¬Y³F)®‡³³3ÆŒƒ#GŽàôéÓ‰Dpvvæ>wqqÁ!Cpþüy:t‘‘‘mv«3ÆŠêêj.1ÆØéÓ§ÙÒ¥K¶zõjĶnÝÊüüü˜§§'ËÈÈè3ëÈcìÎ;ÌÇLJ]¾|™Ý½{—Í›7 …B[ÓxîÜ9æææÖ+Ögj‹æ¤eï\GÊyÜÅ‹™§§'svv~êú›7oÆ¡C‡0gμöÚkhllì3Of8vìêëëQXXˆƒ>rœ¢'HKKSšIš¤å“!•JQ[[«’ªôþÅ_Ä–-[Ô’§6Edlll„“““ÒÜ m¤½rܽ{W¯^Ui+ ¬X±×®]CJJ .\ØáùŒŒ`ccÓá1ÇWÙúZ$aÈ!¸ví ??¿v{ÑÌÌÌàææ†èèhååg‰‰‰xñÅѯŸòª4cccTUU¡¬¬ ŽŽŽ}æ‡Íçóáîî®qv) ¬Zµ ~ø!RRRzUèMMÕœ´ìýlÞ¼¹Í.ÌÔÔT|ðÁJézzzjÉ3)) ï¿ÿ>¼¼¼4^ŸôôtXZZª8 m£½rìÛ·+W®T9^"‘ ++ ÅÅŘ2e FŽÙ%véêêbáÂ…°¶¶FTTÊÊÊÒáwÞ~ûmøøø(OvKHH€‡‡‡ÊÁ¿üò FŽ©4è9º«bD_1©TŠ{÷î)=0‡……¡   ÃïÖ××£¼¼Œ±Çʳ§#2J¥RüùçŸí~öð8qBB^zé¥vÏWUUÕ¦r¹\eâeYYY‡zÕ××·kˆÅâ¿Æ‡3öʇ©S§¶Ùò={ö,ÌÍÍñùçŸwÙõ0`öîÝ‹ÜÜ\xyy!88ø‘Œ|>†††µÈ‹‹‹qçΕn…ØØX\¼xqqq´s’†ÐZ±ŠŠŠº´b„¶òí·ßb×®]Édؽ{7rrrðÍ7ßÀÚÚÐÕÕÅâÅ‹Áçó––lܸ•••ÈÏÏð`ÆðÅ‹QZZʵÖ7nÜÈ-€³gÏ¢¾¾---سgâããÁçói_OFd¼}û6‚ƒƒáææ†!C† ''‡ëqÈÏÏGXX¦OŸŽºº:$%%aÞ¼yHMMEdd$f̘͛7ÃßßÏ<ó 99À+¯¼‚‚‚\¾|;wî„ š››†’’x{{£¼¼ÆÆÆH$HIIQ qÚØØˆM›6aÔ¨Q0`ŠŠŠ —Ë9ýÓÓÓñßÿþ^^^¸ÿ>LLLðúë¯w*óçÏ#99¹Ír ÿþ*³Ã[±±±ydw¹:044l³1ÝS¦LùË‘'&&BWW)))HOOGSS$ tuuqëÖ-¥Êû02™ k×®íÔú«¯¾ŠE‹Ñæ)鮊EÚÈŠ+ ««‹¯¿þ>>>ðññA}}=jkk1~üxÀÊ•+1wî\ØÛÛ‚‚‚°lÙ2î ,ÇCqq±RHÍVJKKaii‰I“&vî܉ .À××÷‘ö=NDFuÞ_oܸùóçã·ß~ðaðk×.9r|ðA»áEÅb1þõ¯!88¡¡¡°²²âΗ––†uëÖ!%%&&&HJJÂ'Ÿ|‚¨¨(®õëëë‹cÇŽ! ©©©4h¤R)Þ}÷]DEEqΓ1@€ Àßß0kÖ,xzzrN|éÒ¥ÈÈÈÀ€ P(ðÉ'ŸÀÌ̬Syx{{cæÌ™m–ãÎ;>|¸VÖõ!C†üåÈàîî®2Ôúõë‡ÈÈHµ8~üx 2„îJÑGqvv†¥¥å}wþüùX·nîܹGGGÈårüøãصkx< çÄtª%ýwttt”{XYY¡¦¦¦ÓßïlDFuÝ_cð÷÷ÇÒ¥K¹uÍK—.ŲeË^ÔÁÁöööJÎO¡P`ݺuX¹r%LLL<˜0æîîÎiigggggddd €‹ž GGG¥ðÉ“'‘––†'Npiùùùغu+Àßßo¾ù&ÊËË‘––†ÄÄD,Y²<¯Óyüþûï*å€ÊÊÊvÇþñÙgŸõøoaË–- TI·°°Pvä‹/Ö¨±P(Ĉ#èn¦aœ:u 999‰Dرc‡ZÎÉëµC7"‘'Ož„T*ÅìÙ³1jÔ¨>½;Knnn‡c¥ajj >#FŒxê5õ¦¦¦*uöqândggs¡8»ƒÖ¢‡âÒZ’òóó‘™™‰Ã‡+ý&…B!^}õU$%%© »æçç#;;[ÉñÆÇÇ+8qvv†\.GRR‚‚‚¸ôÖ‰ÕçäÉ“ðññáÆ… QUU…I“&á?þ@NNF…ÊÊJ8;;ãå—_æôïlm•x0Ѭ½1ûÀÀÀ6¨&Ñøk|üqû滢ë‡Ð|¶oߎãÇãòåËj;ç¹sç`ffÖædm¦®®»ví§Ÿ~І†xxx`çÎJ›"ôÅëÝ],^¼ü1lmm1wî\( 8p®®®\÷mgillÄñãÇÕvÿÊÎÎÆöíÛ»íþš“““6W=*¼èÚµk±téRîáÊÉÉ 7nÜ€¹¹¹R—t\\V¬Xüü|888€ÇãáêÕ«Ð××ç&å2ÆpèÐ!C$¡¥¥–––ÈÏÏÇo¼¡ôPàîî===Ü»w|> .ls»³y$%%©”ƒÇãÁÒҲͥ‡Ú€X,~àÈãââ §§÷ÄỪkÐÌJ#—Ë1tèP¥ý®Ÿ–ææævÃ"j3yyy8wî¶mÛøùùáûï¿×GÞU×»»ðöö†¿¿?„B!–-[†yóæaÓ¦M3f ”Ž•ËåP(Ji666ÜìíÂÂBn^Š\.‡\.Wù~gyœˆŒêº¿Ž3:::hnn溾ÿüóO;v †ÍÌÌDDD ~ûí78;;ÃÖÖ\«øüùó(..ÆsÏ=‡~øk×®ð×j¨ÖãîÞ½‹’’xzzâ믿Æ;ï¼ÃÙ×1­¡¡\˾ÿþhjjBÿþý•l_¾|y§óh«0bĈG®NÐT Á«¯¯üöÛoÑÒÒÂ-”ïhb[wÑ£GãùçŸ'ªܾ}Û¶mCMM  ÆÏÅ ‹Åøì³ÏP]]³gÏ"99îîîH$ˆˆˆÀþýûQUU…ôôtÄÄÄ(u¿µ¶ ”Æ, ¶¶{÷îÅwß}‡~ýú!;;ÁÁÁprrâºE"vî܉¢¢"ÄÇÇcèС0`ÀSÛ¤¬­­ñÖ[oqN#** ŽŽŽ˜1cF›ÇkRyÛ»ÞÝ©kLL LMMŸx4‡»wïÂÓÓÏ>û,ø|>²²²0gÎ¥ÖçÅ‹ñå—_âÆ¨®®æÖu[ZZâÌ™3¨­­EJJ /^ŒäädìØ±·nÝBUU&NœˆmÛ¶áܹs(..†‰‰ ƌӡ]ÉÉÉÈÍÍÅš5kºí÷knnŽþýû#..úúúHJJBvv6üüü`ii }}}œ8q …_}õ·KW^^ôôô˜˜ˆ… ¢ÿþ2d®\¹™L¡P”••ÁÒÒ|>Ÿ{H ‡——7ÀÐÐ °±±Á Aƒ¸VôĉqôèQÔÕÕA(âøñã\o ŸÏǰaÃðÓO?ÁÐÐPÉv==½NçÑV9€K¿¾ûî;,]º”»§i ÁÁÁ…hÕDz2DkkØÚ¨¨(öÕW_±£GjdhÂî¶ñË/¿d¡¡¡Ji …‚yyy±K—.1Æ bÛ·ogŒ1Çêêê˜ +--e¥¥¥ìÌ™3L.—³’’îµÿ~vôèQîý½{÷˜B¡à¾offƲ²²cŒ}úé§ì»ï¾ãòž9s&KKKcMMMì?ÿù»zõêÙÔÕüþûïÌÇLJÕÕÕµ{Œ¦•÷áëÝݺª#D«T*Uú]Ô××?Ö÷ +((`j« õÙUÔ×׳¢¢"&—Ë+¼hEE…ÊýE¡P°ÒÒR®N+ VQQ¡tÌýû÷™L&SJknnfb±¸] XÿþýYss³RzCCC›¶?Nm•ƒ1Æ>ÿüs«6e2Û»w/ûôÓOÙ;ï¼Ã~øá‡65jkkÙìÙ³9òvøßÿþÇ6lØÀ½ßºu+Û³gFéÓ6úùù©Ä"¾yó&¿Ëmê,ÅÅŘ9s&Þ{ï=€Çã=2”§¦”·­ë­)ºj#ÝêS‰ŠŠÂâÅ‹qåÊLŸ>G푘î«W¯FEE…R¨Þ'¡±±ùùù¸rå —Öz¯.,,|j; <ˆ]»v=˜àG-rUbbbÖÔÔ¤”nddľÿþ{Ц»m¬¨¨`ééélýúõíö´>AËd2¥qÈÎŒ-vÔ"ohhPã{xŒ«¶¶VE‹§µ©§Ð„òvt½»S×ÞÔ"'´uÌ5jiiQzÊŒŒŒ:œð¤6öÓä§´¦¦&Ü¿¿Ûó•H$àñx*›ÇC,k„6Ýi£X,†@ ÀÌ™3ñî»ï¶yŒŽŽ÷Íãñ”–²t&bÖ´iÓTÊÒÊÃË„þ~îVÚzzZ›zŠž.wwê*‘H+ZA¨u§úû.¢þù'öìÙƒ   µ­ û»ݵÎçó¹p{݉žžär9ZZZ”ÒÚu6½ÙÆÐÐP$%%á³Ï>ÃèÑ£¡£££ö—¥¥%ÌÌ̺äÜ}©;zS```–çI_æææj½ÞO£«©©©ÊPAh6lÀûï¿ßnCè©HbUZoR©”û_¡P@*•ÂÜܼÏÙ¨ ! 5Ò ˆ¯¿þ“'OƪU«º,rämàêê }}}H$ÎIÖÕÕ1öÄÑïú¢ÝMOÆ' ‚hë·dbb…ͽ|ù2 ;Ñïq Yëm`jj ///…B.M(bòäÉO½ÑB_²±»Ù¾};–,Y‚iÓ¦Q%& ¢GIJJBbb"lll€3gÎ <<¼KBS‹¼ÂÂÂ[[[ 4!!!O^lì.´=&8éH½ëwäëë ±XŒ.ÝÆÆ¦KB “#o;v ™™™(,,ÄÁƒUf÷kkk…[·naÞ¼yJ¥¸pá6lØÀÅ”‰D‡D",X€áÇC,cÇŽ˜0aîÞ½ Æ6mÚ‰D‚ÈÈH\¿~S§NEKK ÊÊʸ½‡‡Û·o#$$†††ˆŒŒ„¿¿?x<^·äÝtl½1i’–¡É˜™™q›ítÔµÞ|>îîîðððÐ8'Þ6feeaÍš58v쬭­ñæ›obèСÈÌÌð`Ë@@ooo,Z´úúú¨­­c ~~~ðõõÅÂ… ¡««Ëí.uùòe¬Y³.\À«¯¾Š¹sç*í‹D"Ü»w¯Í×ÃK[÷–XµjtåIóî*´QÇV»4MK‚ ¨EN<3fÌÀ70yòdnw¡””øùùx½¬  %%%¨©©Á‚ `mmœœdeeaÊ”)ìo¼qãF¥sŽ7ŽÛ²õ/ð`|éÎ;mÚceeÅíÊÔÊ•+W”v’ºuëÖçM:*ïÈ¥‰ZAŽœxLâââ0{ölÀýû÷QUU…aÆ!-- „½½=·w0c ………(++ÃÔ©S¡££ƒºº:¤§§ÃÅÅ—.]ÂÔ©S6óóõõ},û„B!Æǽš¼IÇqJišª%A ®u¢Shr¼smŠ ®m:j²–A<@‡=j‡ÄÛÛ›¯#z–ÆÆF¥1ø††(… ”H$000PŠ,ǃT*…©©)är9š››¹žMMMOÒ³²²EEEˆŽŽFxx¸Òg]w_ѱ'µ\µj† Fu¢¨kèšï\“b‚÷f5UK‚ È‘ZŽ™™öìÙkkkXYY‘ ¤#A#'mcüøñ$éH}šìFAÔ"ïÄÆÆ¢²²ÍÍͰ°°€@ PË>µ}ÍFu’üÛ¶m£ Juƒ j‘·Ott4âãCH¾#ýIDATã±bÅ ¬]»¹¹¹ˆˆˆ {ˆ³gÏ"00_|ñ©‚RÝ âÿ¡ågmÀÃ3Ï<ƒ˜˜Œ;P^^TTTp¡+{m°±+ Å?þˆäädúõöºAËÏ‚ZäOÄíÛ·QXX¨´û“¥¥%ª««‘‘‘A6T ‚ G®ÉäææŒŒŒ¸4###äåå‘Õ_‚ È‘k2‰<O)²C,“Õ_‚ 4šµÞzzzËåhiižž—ÞÐРrs$ŸÌÌLܸq£Ãcttt0oÞ¼6£œ½·nAŽ\-´n!•J¹ÿ ¤R)ÌÍÍÉÆ§dÀ€°¶¶~¤#'§Ó÷êAäÈÕ‚««+ôõõ!‘H¸a]]cxá…ÈÆ§däÈ‘9r$U4ªA¨#oSSSxyyA(riB¡“'O†Ù؃Èd2Èd2ª¤T7‚ yÇ„……! ¶¶¶4hBBBI6öÉÉÉ8uêΟ?{÷îaýúõ?~jyEOñU+ ±ì(S(–å)AS3!-"/‚ ¨€0#÷á"03ÌúýáaÿçÂE.3ãçõ<óǬ½gïÏZŸYë³×Úk­71Æ@A„ÞaDE@Aú‰‰¾gàÛo¿Å¾}ûÔ;rä\]]ÉËA=ŒP(„©©)Ûý›ˆˆ$&&bíÚµxå•WºÝ¾íÛ·£ºº§OŸÇëÐïKJJ°wï^ðx<,X°ãÆë;P\\¬’>dÈ?~\ÿ{â"‘7oÞ„«««Ê§OŸ>Qªªª€k×®¡©©‰Z‚ tž-[¶ !!¡C¿Ù¸q#®^½ª5ð3ưtéRÔÔÔhùîîî8|ø0c°²²Rº+™™™Ø°aƒÚ2RgcwBA\O(**ÂÆqá >œ D‡ˆˆˆ€$ÀÚÚÞÞÞØ¿?:D…D¾1ø|>c¸té6mÚ¤öøƒpüøq¤¤¤ ¾¾VVV¸~ý:<==¹ žœœŒçŸ}ûöUJ÷ððà nܸ™L†ñãÇ£¡¡–––HJJ‚——ŒŒŒ —Ë!—ËannŽ/¾øBÉŽöLlKJJ¡C‡¸kK¥Räääàĉ3fŒÆß) deei Òšlì.è¸PXXˆ>ú111>|8ÐÜÜL£0ÆpáÂ899©8þû ááá¸qã†JOOO‡ŸŸ÷}Ô¨Q022ÂgŸ}†¥K—&Nœˆ;wîààÁƒ˜0alll:•[[[Œ?1111b—…°°0œ?_~ù%vïÞ‰'ªôòÇSSSµ×î*Û §ë0R©ÞÞÞ …8yò$—îééI…£#´ÎtV(Z‡pkjj0pà@*,òAàïï×^{Mãqwww?~|>Ÿ t666øúë¯<\böþûï+½¾g~ÔWK–,Ák¯½®w¼zõjÔÖÖÂÂÂBãCD{055Ejj*ª««aggÇ¥{{{ÃÓÓb±ýû÷W{ÌÌLÌž=[ãµ»ÊFê‰|>÷îÝãÖc¶~’““©pt„úúzP;2ÒúLÝZS‚|£ÏíRë{kMXZZr<''þþþܱ}ûöaÑ¢EJïÃ5=lÙÚÚªì¦fccÓfp0`&Mš¤õ§À¹ hd'''•{øøø >>¿ýö-Z¤õÚí±‘zâ¡´6fê6th "m5xù¦'(++éS§Ú½ùˆ££#ž¼§hd9rèÛ·/>øàƒnÍ«ƒƒ¦OŸÞ¥×œ9s&²³³±fÍ<óÌ3:ãW â°}ûv„„„PAtààà6·74Tlmm@íä•Ö]©ZÏ!È7½‰±±1ÌÌÌÚ=™ïñÕOÚ>¶§À»ï¾Kí#ñî âOk0":‡££#,,,P^^®r¬®®vvv4hù¦×±··ÇŠ+¨}40èø  UÔkÚ"""sæÌÁÏ?ÿÜ#ö­X±þþþZJSRR‚Í›7cË–-øý÷ßÉáêž‚MLàëë«vkŲ²2øùùu‹A¾¡ö‘ÚG âOHoJíµ}‘ÚÓw‚‚‚˜˜È½g¾sMNNƺu먢o¨}¤ö±û ½Ó{–¿þú‹õë×µ´´h<';;›ÙÚÚ2¹\þÄ÷{뭷ضmÛ:ü;¹\ÎúöíËD"Q·ÛhìÝ»—Í›7 …B& ™¿¿?‹ˆˆ ‚!ßÔ>vëÞéÔWIíé–Ôž>ˆƒ"55ñññÇÆ©2‘o¨}¤ö±[ýa¼]Ïg*¤¤¤àÊ•+ܦù]App0RRRpúôidffÂÔÔþþþøûßÿKKKDDD@$aÍš5X±b7#‹±jÕ*ãæÍ›øôÓOQ]]^x»vXXø|>*++add„åË—cêÔ©ÈËËÃñãÇqèÐ! <"‘ƒ RYOÙ‘w6!!!8pà€ÒîB'Nœ@DD,,,põêUdffbüøñÜñèèhDFFB.—C&“ÁÔÔ”›ÔÕ6VVV=z4ÜÝÝiF:ùÆ`¡ö±÷ÛÇŸ~ú X»v- §ë«Ô^{Ðg©=‚ HŠ”ÚG’"í0º(µRû¨ëÌV5:DXXvíÚ…ŠŠŠ.»¦T*…\.×*ŽÐÐР$µ÷(åååprrByy¹ÒpQë°Í£H$%©=]&cÇS‘ÚkÒ&µ·mÛ6XXXàý÷ß×xíž”Ú#Bw ö±÷ÛÇM›6!!!ÙÙÙÔW‡>Híµë Í@¤ö‚ ö‘ÚGõP+Üô†Ô^w «R{APûHí£†‡N'‚ ô‹/"** #FŒxªÕh8 ºªª*$$$àÚµk*ÛP„¾Ó¢b±qqqO›6 fff,Ñ–}t¢ N]@ll,¾øâ ˜™™!==nnn¸ví a0üùçŸ8w­»5ˆ§§§k='%%¥ÃkÂÛÊCOä‚8Aè(---سg–/_lذ‹/ÆâÅ‹©p½§½¢b±eeeJir¹œK€ââb%YØŽR\\Œ{÷îa„ (((кûQ{4å¡#‚%篥¥¥¥¥Ü÷ÆÆF”””(}ÏÏÏïöQ9šØFOˆL&Cii)nݺ@;v, Õ.›!}"??qqq8|ø0ÜÜ܉×_ƒ\¾|_~ù%fÏž¢¢"äää ** FFF†T*EUU\\\ð /àÈ‘# ”)S:lKrr2lmm±ÿ~Œ5 o½õ¾ùæ%ýquö¼ûî»8þ¼JµæM[þ‚ƒƒÑÜÜ SSS8;;ÃÙÙwîÜAjj*–.]Š’’¸¸¸`Á‚HHH€½½}÷8ˆPtŸššËÎ;ÇjjjH…Aغu+sss£‚ :dhõ¸té;v,«««ãÒ¼½½Ù?þÈ233Y||<‹ŒŒd“'OfÍÍÍŒ1ÆŽ;Æ^}õUµ÷¹yó¦’Êã¬ZµŠ½õÖ[Ü÷ÀÀ@¶cÇŽvÙ£)ÚK4]o×®]œL¿~ýXqq1cŒ±üü|ÆãñØ©S§¸óGŽÉIåi%&&111رccxóÍ7ñÆop;!ºGNNNž<‰³gÏRaP24‰z¬Y³Ü–ªÀÃNÄb1|}}1vìXüãÿÀüùóÁçóeeehhhc +W®Dee%÷[‰D‘H„ŒŒ .Çã!44Æ Crr2"""¸ceeeèÓ§O»ìÑ”m‚%š®gkk ___=zo¿ý6ÈÊÊÂèÑ£¹uñµµµÈÏÏÇØ±c»Ï9úþ„j°=ñììlÖ¯_?V[[Ë¥ÕÖÖ2@À²²²¨{ ƒ²—^z‰åååQaP2öïßÏæÎËcL&“±ÆÆFVUUŰ۷osçݽ{—³üü|®—ëääÄÒÓÓ¹s^~ùe¶mÛ¶÷Ävÿþ}ÎŽ¿ýíoœœi[ö¨Ëƒ¦¼µçz …‚ 8¥¦¦* lÞ¼YIîôÅ_dŒ1¥Þü“ÈFÍcLï'¶UVV¬GDD|||”fLZ[[ÃÛÛû÷ï§îŽQXXˆ>ú111>|8ÐÜÜLCuHïQ'êamm [[[nÎc »víBPP† ¸uëJKK1räHn”*??AAA¶ÁÌÌ vvvÜNo111xî¹ç0sæLίÚìÑ$L¢)½­ë ¶¶VI&))IiOøÓ§Oãõ×_GVVΟ?ߥqïÁƒ `b›@ à†i Æ.\¸€yóæ©Ís\\c^/It-xï½÷ðÎ;ï@("//‡‚¯¯/Õ!½gâĉÈÊÊRõ8zô(¶lÙ‚Y³f!55£Fºu딂ÚóÏ?½{÷bÀ€HNNFRRlmm;lƒ±±1>ùä|øá‡8p rssñý÷ßsþ311Ñj¦ýôS&—ËYuuu›÷hkb[« k³GS4¥k»^ss3kjjRJS7dÞ)ØFëÄu‰D@½(@ëР>î.dˆðù|Ü»wŒ1¥Orr2Õ!ƒA“¨‡‰‰ UF4 áååcccÁu 0“&MjÓm,šìÑ–m‚%š®Ççóaff¦”öè¸V:3êÐ(ˆë(­"<þ'sss@]]APÒÉW;vìÀ¤I“pîÜ9îým[888`úôéT€€–˜é(­OšêvjmœÚ’4$¶oߎúctÁÁÁ/ Auèá¬S§NiÜ…ìqÐåõn÷îÝT麩ÞQ×QZ‡`Ôͼ¯­­U:çi âO³jAu¨3ÃÌÌŒÛö´-ZG(¨ÞQOœxBaaaòòr•cuuu°³³Ã Aƒ¨ ‚êFìíí±bÅ ú30Z߉·nðn¨ë°uúéÊľ¾¾ …*ÇÊÊÊàççccc*¨^äâÅ‹X¸p!õT¨Tït'ˆ3ÆðÃ?`Ù²eøàƒpîÜ9ìܹÓ§OG||<•X„ÄÄD%ÕŸúúz$''+­Å|Ú`:¢ýKÚÆT‡¨ÞQ½ÓÇpˆD"öòË/³5kÖ°(­K‹ÅlðàÁ,44”Ö‰÷ {÷îeóæÍcB¡ …BæïïÏ"""žú­4mmm™\.ïu[† ÖiqMùÐ¥üQ"¨Þéj׉߾}“'OÆ”)SðŸÿüGiSyàáÔÿÝ»wcÛ¶mÈËËÓ©§CC&00Djj*âããŽ7>•=]Ò5n½F{´·çIµÕåOW´©Q½£z×ýõ®u/ 0Þ¾}ûv‰DŒ5 ‘‘‘‡)ìíí sssøùùéÄìòå˸zõ*¶lÙb°•ÈÊÊ £G†»»ûS5#ýqòòòpüøq:tƒ†H$ Aƒ¸M$._¾Œí۷×.]‘#G0{ölðx<|øá‡HIIÁ‰'‘‘ccc|øá‡pvv†‹‹K§ì‰Err2LLLÀC`` üüü¸ 4Ù“ŸŸ¯6÷ïßïtþ>úè#\¹rIIIÈÏÏGee%®^½ŠÏ?ÿ–––HII¼ñÆxýõןªå‰T‡¨ÞZ½ûñÇQXXøð•cŒ-_¾œñx<–““£µ ¯P(˜™™›1c §½BOé·mÚÆÚìÑ–Îä/++«Wµ ªwTïz¶Þ)é‰çää૯¾Âœ9s0zôh­Ñ¿©© ÍÍ͉DôxJô Ý¡k ÉÉÉ×zo@€¨¨(n¤J›¶±6{´å£³ùsuuÕ mc‚êÕ»­w&_}õcÜ µñÇ@»ÖV^¼xQé]A{ðöö¦µÏ„V’’’àåå###ÈårÈår466"''/½ôwÞ½{÷™™ ðù|0Æ””„wÞy‡;ççŸæ‡¼¼¼àååÕn;9sæ ªª þùg‡8A„®‹/¾øfffHOO‡››®]»FÓIZZZ””„qãÆá§Ÿ~ê¶þh'þ§'îää„ï¿ÿ·nÝBVVrssñì³ÏâÔ©S033Óé úÞéAtOÀÙ³gN:@ÔÔÔ`ñâÅ((( ê MMMÈÈȟχ••d2™ÊëT…B¡P•ùW2™ b±˜Û'äîÝ»pttìôÈHKK î߿ϽþmllDuuµÆ×ÁŒ1£oß¾°¶¶î5»;÷ZcŸIk"ÇÃðáÃ1|øp½«ŒÈ ‚è2™ ¥¥¥¸uë`ìØ±(,,DCC·6˜hß}÷Nž< DFFbêÔ©˜4iw<&&/^„¿¿?¢¢¢ðÓO?áúõë02z¨Á‚ææfÔÖÖÂÙÙãÇÇŒ3œœŒþýûwØžàà`477ÃÔÔÎÎÎpvvÆ;wpýúu|ûí·Jçž8q¿üò f̘±X >ŸU«VõŠÝ‰{&z?”`bÒi]Y}A"‘àòåË`ŒÁÓÓ“&’Ÿˆ'ÄÜÜùùùJi©©©pss£Þ –,Y‚””¸ººªlä‡ððp\¾||>555øá‡¸@øÇÀËË yyy8pànܸ¹\ŽiÓ¦¡_¿~¶åÑë}ôÑGøã?0pà@äåå!$$Déܘ˜ìÞ½ׯ_‡©©)ÒÓÓß+vw4îµÚaB?Ý&&&111رccxóÍ7ñÆo´kÂüD´œœœùîîî8|ø0c°²²ÂÖ­[»ÄîCß7 0äÍ^²³³Y¿~ýXmm-—V[[ËËÊÊ¢Ý'ÈOÄ#$%%±9sæhý¬X±BeÓŽÂÂBöÒK/±¼¼<*ÄNÒÒÒÂ,--Yvv¶RzEEÀòóó¹´‰'²3gÎ0‰DÂùB¡P°²ÔÔÔvÝïæÍ›,88X«êÚã× `¡¡¡L*•²¦¦&ÖÜŲ̈ØÜvwÇf/Äu˜•+W2•ô€€%)>‚üDtŽ‚‚¶dÉV^^Îcì—_~QÙ‹h›üü|Æçó™T*UJohh`&&&¬²²’;ÏØØ˜•””°={öpÁðöíÛÌÊÊJíŽi â·oßfÖÖÖÜõ™¹¹9+((`{öìáì:t¨’·L&cÇï6»»eÇ6ÒMc¸páæÍ›§rL  ..Në.{ù‰ÐNAAÞ{ï=¼óÎ; …ÈËËáC‡àëëK…ÓA2220nÜ8•é ÅÎ;áææ†`üøñ8þ<œœœ¸z‘––nò'åñ=Íù|>ÜÝÝqñâEØÙÙ¢¢¢†€€ˆÅbˆD",\¸°×ìîÔûqúûé&"‘%%%j'ÙØØØ ¸¸¥¥¥Üû‚üD´©T ooo…Bœ}°jÕ*ÔÔÔpx(˜õÚk¯u™=þþþJÖFFF¸rå êëë•¶Wõöö†§§'Äb1ú÷﯌{ÃîÎ`D?ÝD"‘€ÚàÐºŽ±¦¦† ŠüDt>Ÿ{÷îqëm[?ÉÉÉT8ÀÇÇñññøí·ß°hÑ"çÙØØ OŸ>\@}4¶ú£#Û²0@i ›:ÿ>¾Ç‰‘‘‘ÚýÑŒŒàä䤶7ÝÕvSЍ¯¯µ›í´n$PWWGE~"ˆ^cæÌ™ÈÎÎÆš5kðÌ3ÏôØ}0}útrh8]gi}ºS( Ÿ¢£”••áÔ©Sjÿ³êpttD@@€Úc¯ '¨'NüÖB¤R©Ê±ÚÚZ¥sò“.²}ûvðx<úhùô†8±±1ÌÌÌ`nnÞîùVw}K=qÅÑÑ(//W9VWW;;;n¿^‚ü¤«Aœät{{{¬X±‚|k è}·³³Ã˜1c Ï1&&ðõõ…P(T;æççcccú“ŸôŽG5§,X@ ‰äW¢‡V½N¯ªªBNNŽA:*((‰‰‰Ü»Uàá{Öääd¬[·ŽþÉä'½dýúõðööFff&Äb1ù•è b±˜ÛûŸÞ‰ë0øøã±téR£¸¸Ë–-CHH¦NJD~Ò;zRsš ¿> P×qqðàA¤¦¦">>áááØ¸q# ùIïhjjBZZš’æôã( ܽ{W­¼°L&ƒP(äÖt¡©©é‰Oii)÷½±±Qéûã0Æe!œÙðIDAT  5.Ôd{WÛ­~%ßv4±MpppÀüùó© ÈOz¡jN·e{oiNëŠ_É·Ý  ÑS¬^½šmݺU%=66–M˜05773Æ‹ŽŽf®®®Üñ¬¬,ÏöíÛÇFŽÉ˜D"ak×®UÝh^¯_¿~¬¸¸˜1ÆXnn.ëׯŸÒ¹ÑÑÑÌÍÍ»OZZÛ¹sg»lïj»õͯä[@!€è.ÍiHNNFxx¸Öû DEEu™æt[¶ëŒæt/ùµ+|Û¿òx¼§Ï·Ô'¢'èiÍé¶xRÍéöÚÞ[šÓ½íWòmÏôÄibA=Â;w “ɸõ­­ôéÓ&&&°··ܺu ééé˜8q"Ž9ÂWPP‰DÒeë P[[Ë]¯©© qqqÀ¾}ûÐÐÐ>Ÿ¡C‡*IÉÊårDGG·Ûö®¶[_üJ¾íh8 ˆÁÐ4§Ûk».hN÷†_É·=©›ï¯G„……a×®]¨¨¨ V’ t˜mÛ¶ÁÂÂï¿ÿ¾Úãj7+ íf©T ¹\Þe‚2R©Œ1%:…B¡¢9Ýš®Nsº=¶wµÝúæWòm׳iÓ&$$$ ;;›Ö‰ѽô–æt[t•æt[¶ë‚ætoú•|Û½P'¢[é-Íi‚üú4@ïÄ ‚èVHsšüJPOœ ‚  âAaÐpº ‘Hpùòe0Æàéé [[[*òA¾yj`Œ!55UeOv‚zâ:OLL –-[¸¸¸àÍ7ßĉ'¨`ÈOùÆ ‘ÉdÈÏÏGll,fÍš…€€*ê‰ë999X»v-îÞ½ kkkÀ±cÇ0dÈŒ=Ze`‚üDo …ÒÒR|ûí·˜0aõÄõˆˆøøøpX[[ÃÛÛû÷ï§"?äƒeðàÁ ÁÌ™3¹uÚq½1† .ÀÉÉIå˜@ Àùóç¡ç›í‘Ÿò AP7LD"JJJ`ii©rÌÆÆÅÅÅ(--¥‚"?ä‚‚8¡kH$PÛµ ÖÔÔPA‘Ÿò AAœÐ5êëë@eÿ_077ÔÕÕQA‘Ÿò AAœÐ5Z7ÕW('CUE"?ä‚  ®×´nF!•JUŽÕÖÖ*CŸò AAœÐ!aaaòòr•cuuu°³³Ã Aƒ¨ ÈOù†  Nè&&&ðõõ…P(T9VVV???SA‘Ÿò ñ4ÿÏ©t—   Ìš5 õõõ°²²ðð]^rr2ââ⨀ÈOùæ©@&“A&“1GB=qýÀÃÃü1–.]ŠââbcÙ²e ÁÔ©S©€ÈOùÆ`‘H$X¶lfÌ˜ØØX image/svg+xml 1 2 3 Vertices VertexShader PrimitivesGeneration Rasterization FragmentShader TestingBlending Framebuffer vispy-0.14.1/doc/_static/gl-pipeline.png0000644000175000017510000005372514507104216017420 0ustar nileshnilesh‰PNG  IHDRèÂÚ|è)sBIT|dˆ pHYsèèÜt øtEXtSoftwarewww.inkscape.org›î< IDATxœìÝy˜ÕÕÀáß™a•MÙ\ÆpW5F@\PPcb¿$FM4‰F³jâ—Ä]ãQ6£Á!ˆ€‚ (;ˆ ë,ÌÌùþ¸UP4=T3]5Óç}ž~œ®º]}gh»êÔ=÷\QUŒ1ÆcŒ1Ư‚¸;`Œ1ÆcŒ1Æ Ð1ÆcŒ1ƘD°ÝcŒ1ÆcŒI Ð1ÆcŒ1Ƙ°ÝcŒ1ÆcŒI Ð1ÆcŒ1Ƙ°ÝcŒ1ÆcŒI Ð1ÆcŒ1Ƙ°ÝcŒ1ÆcŒI Ð1ÆcŒ1Ƙ°ÝcŒ1ƘLDšÅÝcL4âî€1õ‰ˆÜ<£ªï¶ tVÕ¿oÇqZªêÿê ›¦–‰È%@+  ø˜¤ª«khÛxøŽª®ÝŽ÷ظªªU"Ò¨RÕjoÿÕÀ|U}~ç~cÌŽ‘À囟VÕ§âèÏö‘ýµªº8-‰È‰ÀÕYvÍWÕ‹·óXû…ª:×{~p ð“î¨1¦ÎÙº1Ûgp~ƶÛ{±sð×Zé‘É…ßÍ€`0𹈠ÍÖPU×ÞžàÜ{ÝBàFU­ò6=œh2xm{;nŒ©U{Mkúôÿå/Ùò{Å$Çt6¦RÀ‹ÞÏÛc}8Ï¢ª/°õ%cLBÙº1Ûgðp)€ˆ´ŽÎôžï \‚ äóGYEäw¸QÕ_oゼn"r 0ZUß‘ÀÝ€Uu©ˆ|ØÕ5‘ËWT5«_Øl2ZU§ˆÈ\ÜHÄÙX ´záéÝDDTUEä8à=àx -0¨йªú±w̸QzD¤îípAU_š*"À¾ª:É,RÕeÞ󀕪úQ MÜÊLUý´NþBÆä‡Õª:=¸ADúÆÀÜ÷C'àl`Oà!UýÜk[ˆ; Vᾦà¾.^ÅcÖ·=€ ×Q¬š¶ÀOjàaU]åmÿð0pи_U¿‘3€ã€=D¤p­ª–×ú_ÇìïßÏÿ7,ÒÁÏ™ˆt~|ŒPÕÞùà\àDà à%à(` °QDZw]nÀ³"²7И»æøw½²Þ{Ÿý€ï»ânJüAU+ëð×7ÆØº1Ûg2ÐHDzzÏOÞRÕu"Ò×Û?X LòNžî¦ŸËp_Ó"Ò —:]| ¼)"»3;D¤«—þö`^Ýÿš&DPèýü#à.àAÜ…xSÜ…¶ôqÜ÷OqŸƒgÿgãDäû^»½p£æà.¶öŽNó¶ýw±¯ÀxÙÀûŒ½ ‰È^"ò.Ëã·"òœ×fWÜg©;p½ˆ|¯ÖþÆpAôm¸ïèqßánÞ}LöÿŸ.Àe^Ý ì´Çš7Oáöþ¸Ñùëq7voÇ}7 "âµåÀRà}oZ ¸ŒŸñÞ¾VÀ“Þö¥¸ï®¯pç?[Ç$œˆ\ˆ;wühòvŽ;=‚û~?X‰»¾X‚ûw.úàÎ9{à‚öûp7‡á®‹H`îó6ø0wÈ“#6‚nÌvðFD_áéÁl>Q^ ÜãÏE‘“€ÃqA{à&U}ÛÛ×8TUýêz`ŒªÞí=ï «ê¿D丷NÀÏüÑ“s‡z#V‡á²$~Ø·?p˜7¢Ñ<ËkŸRÕ§½›8Ó€žª:KD~Œéøg°±ªÞ-"ßîòFσûVŠÈDÜgï 0GUˆÈ£ÀU½@D&ŠÈ¸àaªÞ´³c §ŠÈRïçªÚËû¹1p¡ªVxÏÀ¦ çB\jùK¸h÷ªê‡"r/n${–÷ýÒ è¯ªå"R…¹<Ò;N\&ÎëÀ•À#ªú˜·ïXÜèø€àjY¤½›s‹D¤‰ªN‘EÀûþ¹ÇÔÂ}¶Þ^‘™Þ¹¦•·ÿ3Uâ7‘/pYUþ5FæñÖýTµZDäß„ûŒ~¢ªã¼×ý7’oׯäèÆl¿QÀ_Dä6\Êð/½í=€NÞH7¸ô°n¸\@_“À^"ò’÷|ÀO=ü'p ðŽªN«_Áì€Óp™Sãýâ;ž1ªºq¯õSÊ¿Èxþ);V´ç_¸‹ügp£þÈ{?àcyÐ{ÞwAÿ,ð"ònää)KW4f‡WÕ3²lÿ"œ#"—'¸ïô½½]ÏCEäm`(.ãf“@Úù2ïµ¾%À¾ÞÏ=ýDäïy[ K m™w¬•"ÒhÉæsŠ©GD¤)îZâ7@»7 êiÜ âODäÜÍžõ[º—á¦Ö-ÿ»ˆì‹»YÔ˜S¿‡1&: ÐÙ~oá.’âî4ím_ˆ»p‹RÍ]q£¾…ÀU½4KÛŸã€þ"²¯ª~‘¥©{öç ×"ÝÁ×îñæ®õ¶¯Æx_‰ª*0LDÆèÝßïà{cBˆÈ0Ü<òCTµLD‚Y2Óp7æÅ¥°ïHøÀ‡ªzãv¾.óÜcÎûü¬n® ð{¹x—ª~;øï¬ªDdn:Ö àïüaŒÉ!›ƒnÌvòªl¿Š t^ ìz¸LDN‘–"2X²ä•yfŠHomÒÇ€sEä i&"§zÇè üø5pðÈ6Ži–…¸yè[QÕR\þ`†ª~ãí+´VU—eÞÅ]7鮪3›q£êƘºSˆ›ó]("ßÁ›;îù!. æû¸â\¥;pü‡ŸŠÈÉ"Ò\DNóŠÏ…™+"-5RLò=ŒËÜë!"í½²ˆÈDd°Wë ¥×~p”wy0NDR¸lŒ+ß©ê'µú[c"i°_Î^4 c[+ùõ«(ãùãÞH”É_Oâ–Vó矣ª£pU~‚+®ÒWyàMÜ Ôo;W è à^¥ÖñŠÎኵÄ-“r­ª~­ªÿÄ€-¸Êÿ®‘ÉâÖVÏô/àçlNoWñù}`ª—>û¤—™‘÷qúõuÛõ†AD®‘kDä*/j±Ç*ñÚÞמ+"¿ÙÁ×JðæÝüwæyÍD¶˜ìÓ•–á }úFi\AÈ]pÿo.ôö®Â+*"ˆÈ‘¸sÄ›c¬Â‚ó-À›&£ªãqÓ[¾‹›“Þð?Ÿo³e:{ðüónÞò$\±0“Lÿëèîù#îšáo¸sCWoûÀ±À»¸wzÛGâ>““Þ¸Ïíloß\B_ ·BLsÜ5Ì I‹È͵ù‹cÂICÍ\‘Ó€[Tµg`Û¹ÀUõÛÛy¬ù@ßÀ&½Y6‡Óc.)ÁÕX‰ €ÎÎPÕwwàXgg©ê÷CoýÚ]]Tuþ¼¶'nÙÆcÛ«ƒé&"™€»1»Ø{þ[ ³ªþ"Þž™|ç]'ﯪñž·¾ÚÛ’|ÆäNƒAÇÍëÚ[Dº¶ &’,nI¢ÁÁ´0é#"­Eäh("ýpÅ3N ci»#Nàuß‘Ã2¶u‘3ƒ£&Þ(Ê1ÞûîÐhŠ1ƘœyQUöêC<…Ë’ÀK=[Dº_ "m½Qïý½çíqKæí."Å"Ò!Ðö˜,çŽãE¤HD†ˆÈA@k¼l/ŵ8ð8$ðº¼þyÏwÁUenëµíâeT´Éx¿V"rޏšÁí'x#ðýEd¥D×Á‰;@DãUu¹OÆ€+.Øß»>7ê ÎMm‘ÝEä‰|m¹RDž‘ý¼i=¯‰È_j¹›‰Ð`O¶Þ—ÉÜrXxAx1n‰Ä-mòO\ÕãÉ"ÒÝ{épëU_ t†àòA¸4"¼ý{Ç9JDfã y]#"WzÛo†GàÖÃöSâÁŽ9xÞ.xŒ1¦ÞØX "?Å¥‰[—þ2o{{`®êòâæ§pçš½3qún"ò&nJÌ¥",ì÷o\±Óq7ƒÏ.óöõõŽq&®(ÔPiäMi¸W¼2-"ípç°SqÕüÏÄ·lO`jޏi_â–èš–‘J?W×à—¸eGajÃ…¸êì—ãþ=ŠUõóx»d ¨ê¿»q×¾gàªÄ_k§ÌNóÎ7e"²4ðøo ]i;GíˆKpçÑ;p+ZÜ»t{ít-YlŠ;lª¢úU("ýÛTõHqË`=„KãQqk·UÕ¿ŠÈ @U8N5°k Å}:p¹ª¾)"Ó€©êö‡/]¼u‘‡«êÄUÇ<Ç›slŒ1&¡¼÷GÕÀѸ {ªÎ‘"I-qkPߦªG‹«}ò[UíŸq¬_‡ú)î"r;ÐXU/÷ž¿üÞ[»ø¥ª>èíû-°—ª^8ÞI¸ éC½B€ÁþŒFªês"2Èëϱ޾½pS´ZyfŸãÎU‹E¤nžsUý\D–?QÕ—D¤#nNuÛ%1Æ$ˆˆì†«ÊßÈ+tW?öÅ-Üq^;—Í1ÂËB[ ªMj½“ ÑÐGoÇG‹+ìs›ïþ÷Ã¥ñ< n½àbà„Àë^"iŽ+¾1"c×1ÀZÜ2Hg±¹‚ëo€‹È="²7Æc’l÷ßc€Ã½"rˆ<Œ+àägI–‹ÈLù¾Ô\A¹ÐMDôÎ{ñ<$"­ßWÕ2osW¹ZDFâÎKQ ™|äÏ…ö*ÿO ´™åí[†[º©S„ãcŒI8i*"#ÅMÇ-"¼)M÷‹È,¹YDÚxm‘ëÅNýLDF{#ów‹È'"ò‡ÀqyçÇwDä éxÛj&"ïŠ+y¼÷šV"ònà{xA9^f×QÀ âŠÝŽ‹Èû^›ˆÈÅÕ}À»€ˆìý¿WðpŸ:ÿ£Ö’½ºª®‘·€oãR*†x»Ö㪩^h¾#ók6Õ¸%)¶¯ÇÝ© ¿ÂëÓ(y —?IDR ,cŒ1Éòœª~æeF]Š«‚®ŠþjÜzò‚]FU«sD¤./ü)Ëq×ÿeË*ük²´Ëæ.\æÖTÙx¸WÅùz¢­¼wþ j l¨¡}ÃM¹3Ƙ†ïH/+ÜjÕ¸)¼•À)Àr\MŒpç¶'pç•ûpµP~ ü7è8·À¸sÒû"ò´ªÎ®ÆMñ= Ø·òQ_ï}wÃÝD>øn Áýp1éQ¾6üú,â¦\< ŒÃM únêÖ*Ü*m½ßáÜtâãq«!]ŒË„;Æûýê…†>‚nÔü2\:¿?Çk,n«6ÞˆÁ*܈zM’e9/Íïuàbq}šywmþƒû îî%P.ΉÀU½7ʾ_­ü–&6"rˆxk’îäq¾ë¥Ÿc’ç*à*ñχá.Z–ãæynaïÆëtÜEƒŸêžy Åü5ëk Œ7‘3pK8—Ê;˜­ªãpóÍ ì[ìYÃá&­DäïØßÆÍ³ßî*õfÛDä§^æÃΣ«ˆœW[}2ÉæjÖê5¢ˆ\*^1HiV›Ç6õÂÕÀ5ÞÃ?/4ÁMÛ]¨ªªúOU}èŒl<9ðúÙªz—u5xCUõ–ç{\„+|Yˆ;­‘”·o•ªþ^U—àn(· ìËJU×à:K¼óåroûRoû€{€fÀ'À®âV w#úU]¦õh^w>è£qiƒ›ª·«ê§¸;BÿðR%ÞÀÝé©ÉýÀ+^ê`¦ÿÃ}?Æ`œªª_g·‰ÈܺÖÇáîî Å­-9 ˜¨ª3wî×3¹""'‰ÈS"2MDîW ÜÄÚ¸hºØ¿Žcj‰—¾õ oJÊÏÅ-w;iø¹ÀKQ³›;uÈ»Áû$.è·ñ ¸©T»²yéîÀÃÞ¹åÜèÀD µˆ|èY÷áj•LWàí9?•0Ä=¸5çˆÈ|Qhî½ç]l™Ñ5øÜKWܢؓª–àF%~-"³p7!«êבþ(f ^ŠæÕ"òžˆŒ‘ó»oÄ팸,Óˆ«â?Î{ ‘_gä?eË›mµá6Ü÷À3~z±ÉCTõLïñi`ûJÿ¹ÊË>þ>.®éÆâºŒçkf""¸›Ñâ2‰¯ÃÅH[­\åÌKp7™wT+¯? ¼ß;¸‚tkêã²Ø :Å@U¿!û‡â߸¹àmýâoÞös²´½EDîÇ»SÕ>} ŸŠHKÜHHµ·}<®²ïÇ.·Ì¨jiíü–¦®yÙ¿pw龯¥Ìœ‰K·1 ¸•Ưâî ˆ ÐÅÙ/Ϧ“¦ªV‹ÈMªº ä5f;©j›ŒçW~¾KDÖ{ßûwxÛ³~÷{ç¢ÃE¤£7Ÿàf¹héøm·ÙRÕ›?×T»äPi­ª«3^«À)Þ Å•^_[ö¿ œ*"­‚}ðöuÈx¾Å2lf+¿ÆÝ ùn$é{"òjæßÕOÜM—ãpAó¸4á½Tu}Þÿ;vj‚Ä ½H©ê29W 52¯øölÜèúãÇß7ãùî¸ÿ>Åüå"ÒAU—“±œõ6Þoµˆ,þ­ª¯d¿ÞÖújðz˜Œày[í¶y‚UÕÌ;I5ßæœ×K's½‹o€g쯷ží`\‘¨ý›5"âϵ) ŽN‰«þ|nY¥Šà½/²‹©À ÞŠ{áæÕÌÃ]^ëÝ$2µïà~U½Éß Ë"z7nŽÆøÿ_‹[ûz5Ðw‘>^UWf¼î$`‰ªÎöž7Â-×5WÐrŠªÎóN^ÇyÇ(ñÞÿ ©ˆ‹½,œÖ"ÒÜO‘‘Ƹ SƒŸqëm‚›}0Ú.Ðvœª®Ýƾ¬ç–@pî?¯&úÜó°þ¬Þƾ!¯µ rç†ûNþwÁùXÆþ6"r-n^ä}ª:@6²=—í7ÁOÅ·–ýOp©›_æ}o òþ߯'"ßÃÝT<¨ð¦Ô™dªô>óÅ­tnyÄy™ ½óÍ%¸@æ1ÿÿwùn®ð%¸Ú÷ù…½Ñ̸i8O³åtÎ EäMUýTDNæâ>KÇ#TõµÀ1ŠquœÖà«>RÕ§kñï`’áÜç«Øû¼ý€Àèúv¸¸Ó›Ö3WÔ/"×JDžÅe~]<ª^ñU/»øf™Èöe¦þ¸]Döôú»¿ªÞ°ýNŒ|Hq7¦6ŒW­r«z¸) ?Â}úÞ¼S9W êCÜEÙëþ ¼ µ{qS#ÎÂÝEô÷ýw2}W§`¸·kwàϸ5‰'àæ™ZæÝ9ØbZ‹ÓÅks ð î‚æmïf ¸i/ÿ¼Ÿ„[“º“÷šnÞ èlàZù§÷š–ÀsÀ³¸¢–MDäQà)ÜÝëY"rn:Íw¼ÿžÉæ*Ïã>xZŸàù±"ò˜w.Ív8®àÊPà“À>cÌÎy¸KDΑÂ,ûŸÆÕ¼)Âý¿î»8Wç6\ÐíîŸãæŠ.À­G·¯˜†»Ñö:ð/ï;Ü÷Ë\á§8Ö:6Ñ5‘."r$îs0Ö+²µ9˜Œ vÖï¾»¯Ã‹¾ÂÍfö=[Çüàç¸Q{߀?¢y îœu<îæÎ ²¹âõi¸)xàÎUÝðVw0õÆܵcuÆöÞö*uKuÆ­ò)îsñ„×v.nõß{¸iZ¾Q¸ï$?KùHÜw]/\±UÅÏ?ÆMÿ:·œhpÀëÜȽ¼Ÿo ì{Â븛›po¤þ[¸âqÝØ|ͼ7¬þQU{ØÃ¸95Ï¥¸/©VÞö_ã**ûíî®Ëòú¶¸ëîÞóy@¯Àþ€o{?ÏŽ ìû7"{$Pì÷ߣ!?p_ôóÏOÂe3\ 4Ç@ƒKMö÷?àý|.`.òžÎ÷~~W¬Ä?î{¸y©mp²Ïì+ üü[àVïçθé4Áþ®À¥ÖKÞöBÜÉn˜÷|<ðçÀë=ãþ{Ûà å»6ÇûØþ5n}ypS ʯÜ(ÓÞóóWûOþØ÷z`ß¹ÀÞÏ/·Åý·°Gègå(Üj/አ…Qlêí œçýü<ð‹ÀkŸŽð~V “÷óÞÞuF.˜^ ´¼nÐÕûù-\Í pƒ×ÚÆ-ã蟷¾ëý| .Ã+ö¿Ÿ=ìÑyŸânLTªºøŽˆtÅ|w1[¦-ÃQ~•T䕸‹²½¼Ô¡ÖªúQæûxõ ö~Ü,ÃÝXªVÄ©®•D¤©ºÔõ6¸õŸ/Ç]L‰»ð¹Ýû7j–˃|¡îN4¸‹u¿Bi?܈ɃÞóÖ¸l‹7`Ó]g_;‚K7Ü—hif]qÿþñªÄ­#z›ï(G>æà>WG8¶1&„ª>#"ÏáÒ3‡‹ÈQïù2¯ÍÙˆû^Y/"ýq7ÿZâ‚t¿¸Ñ`àÍÞª'°‡ˆ¼ä=o‚«–ì³ÿ§ë‡åªz&lJ%ãm¿7£] £ˆ|Ë{¾+î¼2Å{î_ƒ,ÃÍÝ-ÂÝ8þB·1õ%CæuŒ_Ôp8ðq)‡áncêèÆD "º¹à<ù#[¦öÔä܈ùiÞqüôœ4n±_ cU]'"«[TõýŒ~¹³¿‹‰äsÜÄ À8U Œ‘ï{û×{믩©JhpYõ¸ÏÍëm%¸ìˆM¼B’Spëþ7‡ð¢ý^»PoÁæ‹­¶Àâ¯5Æìÿ<á+ž‘³qß![݈ ¼¦.µøUý@DÎÂ¥"ƒ ²kZÙc®ŽÅ™µö ˜X©ªŠ[ùç(¶ÐâÒßïÚŽCκ¯_vÐtÜ4‹Gp7Œþ¹íæÆ˜esЉæ~y@Dz‹HwÜÔS^W,·üÎU¸y5¨+æ5¸Þ›ö\вïaoßA"ÒÁ+*frD]ÑÇ¿á²v‘l¤ßÁMy8HÝ:œ+Ørô¡&£q#k«uóÚ×Ù^×7 ò.À.…³Pɲä›wÌiÀÿ‰“¢؈‡1uÈ+Nô©ˆœ'"Å­-ß7|[ qó@WzE/ ì{8MD‰ÈÀoûF‹Èÿ‰H 9Fl©Åú¨PD:‰È¾Þ àKpË7‚›Nç/qõ0ðs9ADZŠÈà°ú!ê o-þ("]EäN2nGô}Üüâà ÝÖXÓS;,@7&š_ã£q…*>`óúÆ €Ïm¿¾ð~þ;.…øܨìƒl®Ø|‘wÌgqóÑïÀø×önàq ‹·} ›SÚLÝúnŽøg^æÃÜ<À•ꪲŸ\)"S½v"óz`0MÜÚ׉«¸¾Uý —êø!îó±:°¯WaŠˆ<”å=ÎÇ¥¿~ì½öUíW6Æìu«%üW í \ÆaÞM3p…·‚+&¼ ”««öÿ;\ ?¸7b‰ªÎðŽy9.“æ¸sêªò÷ÅMkywãÏOÿ˜™d[ƒ+¼õî\¿pªªúÓžÆËœò²¸~ˆûŒÇ}Ç7÷ÚMdsW5n”Û1Œ»áû8nžû£l¾™<WÜ5Kp•€Ïp×6ànðþ wíñ‰ˆL·Acê–¨jx+cŒÉSÞ(Åî¸tÒ­Ò%c½ëˆÇlŒ+·Íµn%˺ÖÁ÷Å­Á]QÃþ­Öµ6Æc¶‡Wçà7ºy¿‹~ªú½x{fLÃesÐ1fÔÝÅ\´ýÛœ{¯ÙˆKk k·­u­·ù¾œcŒ©À0 ìƒ[Jôâx»dLÃf#èÆcŒ1Ƙ­xuXÎÁ¥ÅSÕ/·ý*cÌΰÝcŒ1ÆcŒI+gŒ1ÆcŒ1Æ$€èÆcŒ1ÆcLX€nŒ1ÆcŒ1Æ$€èÆcŒ1ÆcLX€nŒ1ÆcŒ1Æ$€èÆcŒ1ÆcLX€nŒ1ÆcŒ1Æ$€èÆcŒ1ÆcLX€nŒ1ÆcŒ1Æ$€èÆcŒ1ÆcL4Š»¦~‘Î@Ǹûa¡ZUgÆÝ cLý$"ûmãî‡I„rUý$îN³=DD€@?`˜»câWL^VÕ¥ÛóBQÕºé’É "2 x>î~˜Ä衪³ý'étº9Ð*Æþ˜ømL¥R+37¦Óé]Æ1ôÇ$ÇšT*µÁ""¿n±?&9*öªZâoH§Ó»-âë’I€²T*UÞ,÷D¤ðpbÜ}1‰SüDUGD}èf§ˆH `Ь°°Â¸»dbPUUEUUÀŸUõ:{:>x+®~™D(:¤R©uÁétúQàGñtÉ$ÄS©TêBÿ‰ˆtÒ5¢ Àfáå£ÊÊJª««~¨ªOøÛÓéô÷€'ãê—I„¯€.©T*qÁ‹ˆ¼ Ž»&±ÊC‚ƒXÛbºÙi"22tèPn¾ù渻cbðôÓOsíµ×ÌVÕþöt:],:ÇÕ7“ç¥R©-2mÒét106¦þ˜dX tL¥Råþù8äŠ+®à²Ë.‹¯g&6·Þz+?ü0ÀUäoO§Ó­e@“¸úfá¨T*59îN‰HOࣸûaïqU40a·§MmðÚk¯QYYw_L N9åIÐ IDAT´ë 9ÈßžJ¥ªbë˜IŠaY¶½|“뎘Di œ’±m8Àرvï&_ 0Àÿñdiã?I¥R« ±tÊ$I¶óIÜ»¦^82jC ÐMmx(-))aÒ¤Iq÷ÅÄ }ûövØaþÓÌ“gä97¦ÁN§·˜;šJ¥6/ÅÔ“C3žøì³Ï˜;wn Ý1q;øàƒÙsÏ=ÁÕ¨83c·OÌ9étZâîD+ni¢Ø5jC«ânvšª®‘±À1cÆpÜqÇÅÝ%ƒ2eÊpÜ×v½ ,ÁÒÜóY3à4¶.(9›‡žïNO§ÓE©Tª@UçŠÈ4à1cÆXš{ž*..æ‘Gw>y"°ëe (Š¡[&öŽ•æ¾ ×p5›LÃÔüZ·9è¦VˆÈyÀ³mÚ´aòäÉV,.­X±‚~ýúùÅ}zªê,_:¾øyl3I0*•JÜN§KvñtÉ$Äé©Tj´ÿDD®nÙÿýyõÕWcì–‰ËÌ™32d¸à¦CF5w+ÆeîL¥R¿Ž»>¹ø[–]•@UµôÒ î|òx`—Ÿæ^”íu&/ìN§L¥R“ãîHÂí}Ä·ûî‡iXÃ>z¡JDîUÕò\tÌlÍFÐM]±4÷<—‘æÞÓßniîÆciî&›ÓÜmzþ²4wÂFÑÃÉ ,ê×ïâfa.]ޏ;œÏ,@!"‰H—¸ûQYš{žËHsÏÉÒÜ¥¹›šXš»Ù‚ˆÓÜ3Ï'–æn,ÍÝ4 ›º6,Í=Ÿ#"Xš»ÉÂÒÜM6–æn¶¨kr’¥¹›,,ÍÝ4 ›ºö –æž×:tèÀa‡æ?Íõ°´Dciî¦&–æn¶QÍý¬ŒÝv>1–æn ÐMRÕµXš{Þ ¤¹[5w“©0(Ëv»Ø6–æn¶‘ænÕÜM¦}Òéôqw˜%ªwID~\ ´(ÎRÕcíX=$"ßžnÓ¦ “'O¦°°0ç}X¸p!o¾ù&Ó§O§ººš¾}ûÒ¿öÞ{ïœ÷%-_¾œ~ýúá}ßôRÕý}étúಸú6iÒ$¦M›ÆÌ™3éÒ¥ ½zõâä“O¦I“&qu)L¥R[Œ|¤ÓéÆÀ2Üw°É_ƒS©Ô+þù pëÀ+¯¼²—™†júôéœsÎ9Nªú¿/N&{] “?îH¥RWæò Eä àoYv¨ªsÙ—šˆÈ¾©ÔiW´mÛ5´í‚ïVT¬xº  0ô†WEEEÉš5‹>«•N6"rðl–]ËTµS”c4ªÝ.5ªú8ðxÜýh üjîM'MšÄ±Ç›³7VUžxâ n¿ýv (--`„ ÜtÓMüò—¿ä¢‹.ò—3uįæ>uêTpiîv †}åÊ•\uÕUL™2…ªª*6nÜHaa!EEEÜvÛmÜ}÷ÝôîÝ;×ÝÊWÓét‹T*µÞßJ¥6¦Óé—€ÆØ/¿¡¸©R¾€[?ýôSæÍ›G׮ᛦaéÝ»7;wfÉ’%~5÷Ç»G`z¾;Èi€^OT¦ÓÇ®ãC/x[´ø¼ó…ÞsJ‹í*ÃÚŽýëe€èµÌ¢Sç‚iújNßûöÛoçÎ;祿¼|SpPZZJEEwß}77ÞxcNû”¯’”æ^ZZÊgœÁ¤I“(++cãÆTUUQZZÊâÅ‹9ÿüó™5kV.»•Ï,ÍÝÔ¤Æ4w›6•Ÿ,ÍÝ„°4÷ìöÝG…>DZ•ï¶[ŠŽ}5³ ÐuÀt“+# ·ÕÜgΜÉc=¶E`ž©´´”gžy†iÓ¦å¤OùlÀ€Ájî½üíqTs¿å–[X½zõ¦À<“ªRUUÅ/~ñ *+Co ›Ú‘Y@à?X5÷|—­šûp°yèùlРM÷óN‘vþ«æn<ÙÎ'ÆÔ ›\ñÓÜ™4iRNÞpäÈ‘‘nTVVòÜsÏå GùÍOs÷d­Îœ ªÊÈ‘#·yãÜhú²e˘9sfŽz–÷¬š»©IÕÜçÍ›CwLÜü4w\5÷33v[æ±jî¦^³ÝäDiîS¦L¡ºº:´ªús£MKBšûܹs#Š‹3f̨ãObÓÜ7lØÀš5k"=¬ðjÈLsŸGÒÜ«««#.Ânší“ô4÷ªª*ÊËË#=¢\§˜ífiî¦^³"q;@DÚ}}€ÙÀLU-‹·WõÂàÌ×^{n¸¡Î«¹¯^½:rÛ5kÖÔaOŒ¯¸¸˜n¸U=@Dz©êGàÒÜÓéô(àÒºîCII 7ޤWTTPRRR×]2› ÅK_ðÓÜc«æ~Ùe—qÜqÇ…¶[´hýû÷§ÿþ9èU^i œ ïî;v,—^Zç_Y½üòX^}õZ¶Ü5´í’%Sxî¹rЫü1pà@üqðÒÜýjî©Tju:~ì7ürbüø·(,Œö•Õ¬YýûUÇ=ÊKC)qw˜aúv‘æÀÀOÿvxP "W÷ª ŸlKN«¹÷êÕ‹×_=RÛ=zÔi_ŒÓ±cG9ä>øàp'Ï»‡“ƒý ƒ¢¼¼}úl«šûpb Ð‹Šš³Ç'Fj»l™ÕR¨#C«âî„1;ÂRÜ#òFÍgßÇýÝZxf@à`¬xU°ÌÖ¼4÷ñ›´ÄþýûÓ¬Y³ÐvÍš5ã„N¨óþgàÀþ™E\Þ–Öõû7kÖŒîÝ»GZZ¯¢¢‚¾}ûÖu—ÌfÍIhš»‰]¶4÷©`ÅâòUFš{æù$ö4w;Ks7õ–èÑ=tÄäÙ4úç¬GõÓppë×u5÷óÎ;½öÚk›©ôtêÔ‰ï~÷»uÚ³Yqq±_Í}ÿ¸ª¹ÿõ¯ bQXXÈ/~ñ vÛm·\tÉl–9Ÿ¬š»Ùœæ4l¹µ|¸á›­šûk±tÊ$I¶ó‰1‰gz"’ÂU„lÒ´psÝ÷¨^ËY5÷ÂÂBxà:wîLÓ¦[ÿÓ5iÒ„:ðàƒÒ¸qã:í‹ÙÌOs÷dŽzdÎ?®={öäºë®£¨¨(ëHzQQ­[·fÖ¬Y9[ÐlbÕÜMM2¿/¬š{žëÓ§:u7eó¬ŒÝ99Ÿ˜D³ÝÔK6=š#€2 e„¶-E¤«—~g2¨êZœ1f̘:Ÿ‡¾Ï>û0aÂî¼óN^~ùeV¬X@ûöí8p W^yeÖàÝÔ­ç¡ÿ1°ËOsïT×}8÷Üs9üðùîºë˜>}:6l Q£FtïÞ?ýéO{챜uÖY\qÅüýï¯ó¢†f?Í=óâz1ÎC7‰pF:.J¥RàÒÜEd*pØØ±c¹ä’KbîžÉ5aÀ€~±¸¡À£Ý~š{Q¶×š¼°O:>"•JY±8(*(¸£;<:êP^¾ ÛC}¯iAA£am+*–еk¿µQ:P^^:gñâi¶lR G³nžye^{ Ðk68c„ \ýõuøqÍ5×pÍ5×°~ýzT•–-£Ük1ueÀ€Üxã¨êþYª¹$ÅâºuëÆ“O> À‘GɨQ£èÝ»÷¦ý/¾ø¢éñHd5w»lÕÜG‡3Æô<ÐWÍÝ$‚Usw*š7ÿõœ¦M‡„fO¯Y3¬ª²òž¥C—»iÒdð>—^:zÿ(¸ë®# ñj‡˜m³÷h>¢•}viðŸÖa_‚—ò\¤¹gjÑ¢…ç „4÷L[¥»·jÕŠ_|‘åË—sÅWXº{îX𻩉¥¹›-ôíÛ×ÒÜͶXš»©w,@f áóÏ}kUõ˺ìL}çUsVÜ'Ÿ 0Àÿ1ó䙓jîQY «ænjrºUs7A~š»'ó|ò2VÍ=ßY5wSïX€w0—¾¾-¥À5uߣa䦚»I¦«¹ìoÏe5÷¨,H…Us7Ù´¡†jî ç¯@€žYͽ«ænlÝÔ36=ºŸýpK­eM/þ«ªå´Wõ—ŸæÞdÒ¤Iu^,Î$OÇŽéÛ·/Ó¦MwòœØ=‚ÍCÊÒmNzÎ L§Ó-R©ÔzC*•Ú˜N§ÿ ü —ùàƒ¸á†BÛ}õÕW4jÔˆ¢¢ðšT7æè£®î壡l=ýÖO>ù„yóæÑµkלt¢¬¬”÷ß-—büòËɼõV¯Ðv{챩Tjg»—Wü4÷¥K—úiîÁbq#Èñ<ôU«¾fõꉑږ—/àË/;Gj»ûî»Ûª3;f(pUÜ0&* Ð#RÕ5"rpðcÜhº…¸L„«{ãëaýâUsGŽª¹›d0`@0@Vs›UsߤçTMÕ܇“ã½]»v‘¾£^}õUvÙe—HâSO=E=hÕªUmt1ß$¢š{yyï¼s°whÛ=÷|É“£Ý8Xºôî¸ãêì]~Š‹‹yâ‰' ÕÜ7lhÄ‚íÂÕÕ«ØgŸ6¡íÖ¯_ÁÆ èÞ½ÛÎv/Y5wS¯X€¾Tuð3¹8è†õûHUC—"0[Éi5w“< ছnò«¹¬ª3!÷ÕÜ·‡é9•ˆjî]»vå„Nm7gÎÚµk©í»ï¾»óË_~šûVÕÜs 7iÒ· ë¡m7¾ƒTê„HÇ]µêÅêW¾8p  Ÿ$"»ªêJpiî¹®æÞ²eZµ:8¼!PY9™víº„¶+,l,Û¹Žå7«ænê ›ƒ¾Tu½ª¾¥ªO¨ê4 ÎwØhbªæn’¡S§NôíÛך9G,±ÁlNzÎÔTÍýß1õÇ$GÖï ?ÍÝ䟾}ûÒ±cGpƒOgfìNìùÄäŒÍC7õ†è&6ªºVÍ=ŸŠûd.Ÿä§¹'’é9QS5w[:ÉœaÕÜMPF5÷Ìó‰Ÿænò—Us7õ†è&nÃÁª¹ç³@5÷ý’^Í=“é9aÕÜM6VÍÝl% KDvõŸX5wã±QtS/Øt7?Íݪ¹ç)?ÍÝ+7Œ„WsÏdsÒëÜÀt:Ý<•Jmð7ÄUÍÝ$N"ª¹›ä8äCèØ±#Ë–-ó«¹?Øójî&qÎ!«¹Áß÷­¬|²2¬aAÁÜTuõŠª«‡Ž:TT,Ùÿ®»2g”d·aCÙA|Nóðc–®Xñù[+W~1;Ò ÐM¬¼êøãÓ­š{þʨæþ‡À®DVsÏdAzòÓÜ3çŽÀô|—ˆjî&9ü4÷@5÷`€žójî&qº¤ÓéÃS©ÔÿâîH *R©+¾èØqHhöôŒê<ðž¥EECƒù?ÌA½ôe”Ìžýí®^øÂîaí–-û”'Ÿ¾´Af)î& F€¥¹ç³úœæî³t÷:•9Ÿ\ºª¥¹ç·liîÃÁÒÜ󙥹›ÙÎ'Æ$Šèõœˆœ-"÷ˆÈwE¤YÜýÙA/cÕÜóZ§NèÓ§ÿ4óäYoªïZ^g¦Óé-Ò⬚»ñdÎ)}\5÷ùóçç¾7&v~š;.Kô¬ŒÝõæ|bêÌ9qwÀ˜0 ×c"òÜÍ༠“úƪ¹pkØz2/¸]Í=“éu¢¦jîv±mj¬ænç“ü$"ûO3Ï'VÍÝtI§Ó‡ÇÝ c¶Åæ ×oWÕuÞ<î%"²›ª~s¿vÄàô &pýõ×ÛüÝ}úðᇂõ˜Øøjî™,H¯u5Us‰:.ׯ_?n¹å–ÐvO<ñmÛ¶ÌŸþùüþ÷¿§uëÖ¡m¯¾úêHýÌc™ÕÜ_þê§¹wéÒ¥NÞ´U«](*ºÂÂýBÛVVžÃºuÏG:nÛ¶?$•º1´]YÙjÞ|óï gw5÷½÷îB³f'Elý(­[_¡Ý4š6ÝH»vG…¶\ºt2ååå4oZ0;_ås5wSXŠ{Ãq%ð¨ªÖË|ZKs7ÐpÒÜ}–î^«bKs/,,¤iÓ¦¡FѸqãHm hÒ¤Iä¶f›bKs)B¤i„‡DlçÚ6jÔ4ôQXؤN·ú, iî#> #¶k„HBvyÊÒÜM¢ÙÿÁ €ˆ Žî»/;ɪ¹ç¹âââ`5÷Þþv¯šû¨Ø:¶,H¯U™Ú°9ÍÝä/«æn¶bÕÜM«ænËôzNDŽîNWÕúšÞî³jîy®sçÎôî½).Ï Æ†ç¸;µÆ‚ôZ3¨†jî/ÅÔ“™ß#Àª¹ç³C=”:€Us7ÙY5w“X ×c"Òxø ÐXDö‘Ž1wk‡yiîÀF=òÙ A›²˜3ïn×Ë4wŸéµÂª¹›šd¦¹ÏÇ+eç“ü$"ÁQôÌó‰Us7y—æ^Y¹¢QYÙ—¡êêò‚ÒÒE‘ÚªVFiWVöe£ªª¾ùæËÐÇêÕKUWW‰HÓ9×ÇŠÄÕoÀ`™Ú—;ãéN­ ž0aùË_¬¨V Tsß7K5÷Qlùy¯W¬p\­JLÕÜM¢µ¾ '3f ?ûÙÏâ镉Հüjî'f©æþ`à6`º¡äO5÷Æ«WÜCõ kXU5oŸo¾ù»4nÜ4t$Auù>_}c¤›]åå+÷~å•KÃÚ­_¿¤ióæ»üñCú†µ]´h²ˆÈã ‹x  ×cªúÛ¸ûP^ÊW­ZeÕÜó”Ÿæ>}útp7¡‚Õ܇St° ½ Š«š»I¼al ×y5w“\~šûòåËC€‡»‡cz¾ŠËBÍ{õºøË}÷š==vì0ú÷¿mi‹+ÃÚ¾üòàÂoû £t`Ô¨o7>ñÄð¶_~9¾ùºu“[ÿ©UXÛwß}°tΜÿ6¸‹(Kq7‰biî^5÷L–î¾S,ÍÝÔÄÒÜÍ’PÍÝ$ZÞ¥¹›úÁt“DVÍ=Ϫ¹ïÛPª¹g² }§X5w“Ÿæ4lùÎ|˜‡~¢ˆìæ?ñª¹ÿ'–N™$Év>1&V 'œˆœ,"7‹ÈDyVD.¯Ï…à"ú7.Íݪ¹ç©Ýwß=XÍ=³¸O½­æžÉ‚ôfÕÜMM2¿/¬š{ž ©æÞ`Î'f‡Y€nÇæ '”w—÷QÜÚ®ÙüoupƒˆüXUŸ‹«uIU׈ÈÀ16=}ôÑþ<ô3v½‹KK,ÚêEõÍIß!Í£€72¶¿AÌCï½÷:4ünéÒ¥ðì³Ï†¶7o?üáiÔ(ü4¼xñbî¾ûîH}íСçw^¤¶ Ô·‚OTu¾ˆÌº½÷Þ{µ:½´t=Íš]EAAÓжëÖ-¥iÓhqÀW_}É?þÞ¶ºº’FV²dɆжë×—ð«_}®]»FêCCRPPÀQGÅË/¿ î|œ‡žùR+-šÏÊ•Dj»`ÁG…·]½z –3gΌж+V, ¬ìpš4iÚ¶°°šO<:R_¨.étº[*•šwGŒñY€ž@"R¼ìd.àÛ>&"•ªúBN;—"Òè pøá65(_M:ÕÿñŒ]GÐ@‚sŸéÛ­Œì•w«‹7;æ˜c¸ãŽ;BÛ=òÈ#´k׎!C†„¶=ûì³yôÑGiÓ&¼ðü\À]wÝ©¯?ùÉO"µkÀÞ>‘=®GqD­¾QË–-èÚõ6Z´8 ´íìÙƒ9è he¾øâºu{:´]ee K—^ÄG„6fÏv™:ù «ê¶Î'uòѵkºv-k@£FÓ§ÏCÛ-_>ŠŠ2öÜó˜Ð¶sæŒd·ÝŽ¡U«ð„Ë ÆGêg¶˜w'Œ ²÷dºèÎÖÁyP3\Þ>7]ʩӀf­Zµ¢_¿~q÷ÅÄ`ùòå|ðÁþÓ̫ڙŽféîÛe|*•ZÜN§³¥¯šü“ù}q Ý»w§{÷îqôÇÄlæÌ™,^¼ ™±»AžOÌv™J¥B—3&—,@O¦ãR8Ãpj÷%ÃN>ùd7nw_L ÆGuu5À|UÝ4RšN§wÁÝ YY¶y£'»eÙnòÇZ`\ƶ¡°ÅÊ&ϼúê«þo©ê2ÿI:Þˆ6Ìm2«C`Çô„‘–À^›7ÔÄ!/½}lQyÕä™À’H™£aG{æ¶7¹eAz¨2`t–í6fF§R©2ÿ‰—Þ~4X€žÏÆÛtÏ&ó|2/ `²…€U#6‰csГg—íh[ìZW‰‰¥·ç¹ŒôöÌ;Ûy„Ùœôm²ôvSKo7[˜1c†¥·›mɧôöÂyó^j·n]:tpvÍš¹»ÎšuyãÆ-CG6lX´Û‡Þ¶6¬@ié²ö~xÛê°v«VÍiVQ1¿Õĉ·U†µ7ïMmÒd—}[´hUWW•––~óU”¾ÆÍô„QÕ%"²·žk˜ À{uÜ¥\ –ÞžÏÆï§·ÏSÕM•}zz{& Òkdéí&Ko7[3fŒÿ£¥·›lò)½]÷ܳCõ>ût m¸ti‹ªTj/mÚ´uuXÛÅ‹[Tï·_—Ðv_}Õ´*JÛÅ‹Wë×_¯­îÔ)¼í‚ïî6`À-guìxÐú°¶'ÞZD«¸3 Гin9µm‰÷ï÷ߺïNnxéíÁÒÛóYà‚*s…‚ŸÞžÉ‚ô­Xz»©‰¥·›­lcº”¥·›EäWz{u·nÇ”ôé3$t}úôZ|ðà’]vé:‚=eÊ“ßzèÐ’(x÷݇"µmÖ¬UEAAQe”¶éô;E{îyˆî½÷Q¡ïÿ¿ÿ=úû$…ÍAO¦Ë!m6©êG9èO®Xz{ž³ôö­Ùœô-Xz»©‰¥·›-Xz» ñB¥·›zÆôRÕ¥¸ôU@y–&¥ÀkÀÕ¹ìWXz{ž³ôöì,Hß$ÛBÒ'`éíùÎÒÛÍV,½Ý„Èv>1&,@O(U}·ú¿€€âFͧªê™ªZck•¥·Øâ‚*覆‡± ràå,Û‡åº#&q,½Ýl%P½=3ËÒÛÍ"^ 'Ó€ØôSÕo€ÿ‘¦ªZò’úÌÒÛó\Fz{Öë,¤:IDATf€néˆäýœôqq¥·Oœ8‘‹.º(´]:¦   ¸îræÌ™Ãe—]F“&a¥Fà“O>‰ôþ|ð÷ßh»’’;ì0N9å”HÇM¸XÒÛ7lXÇÊ•dݺV¡m7n\ȲeÑþ 7lø$RÛÊÊr¾þzÏ>Þvùòøè£=èÜùƒÐ¶ Nã…‰Ôפš1c‹-—Þ>*cwŸO.œÇÚµÿˆÔvþüi4ižiýÍ7K©¨(eíÚCÛÎû1¥¥ hÒ¤yhÛE‹>§ºZ"õµY³*Ž?>|®o=`éí&Ñ,@¯'xp–Þž÷,½=š<ÒcKo?ᄸãŽ;BÛ=òÈ#´k׎!C†„¶=ûì³¹÷Þ{iÓ&|ÁŽ .¸€G}4R_Ï?ÿüH}ýôÓO#ÝH¨bKoße—–~øõ´m{@hÛñãsüñÑþ ÿûß "µ-//á­·.â¸ãÂÛNžü;úô9…Tê„ж'^¥›‰wz{—.]iÓ&ÚÛ³ôö“GAz¶tıêíùΪ·›­²±Þ´êí& Ko7‰gsÐM¬,½Ý€¥·ïŒ<˜“néí¦&–Þn¶HoÏ<ŸXz»Éçôö'ÞW:{öøÐ;ùK–ÌÚ8jÔ•«7nÚö›oè3ÏütM”””,ŽÔ¶¤dAÕÚµ+ŠV¯^ÚöË/§¶Y¾|þš]vé´!¬íâÅÓ£UCL ÐMÜ,½=Ï­X±"˜Þž9_ÐÒ#hàAº¥·›šXz»ÙB ½½ ™±ÛÎ'&oÓÛUup{Üý0ÑXŠ»‰›UoÏséí›"uKoß> 8ÝÝÒÛM6ÙÒÛ‡žç³±cÇú?¾¥ªËý'–Þn<–Þnê ÐMl,½ÝÁ¥ž2OœGaéíÛ¥é5¥·ÛH˜y%KzûQ`z> è™ç“Ó°ôö|—Ïéí¦ž±ÝÄiÐÜÒÛóWHz»Í1Þ ,H¯)½=|¡qÓÐe~_Xz{ž Io·ó‰ÉÛôvSÿØôD¤ø>ÐxxYU×ÅÛ«ÁÒÛó\ ½}®¥·×ž4'=éíétšçŸ>´Ý”)ShÑ¢7n m»xñbFE‹-BÛ~õÕW‘Þ{Ú.^¼˜ ðÉ'Ÿ„¶8à€HïŸ#‰Ho߸q#óçeÅŠ¡mׯ_ÆçŸGû7\³æ«Hm++׳fÍâHmW®ü„9s Y·nYhÛÅ‹?‰ô¹hÓ¦ ;wŽÔ¶®%)½}Æu”–ΊÔvÕªe,YÞvåʹlÜXN³f­CÛ~óÍ"–-ûœµk¿m»zõJ,ˆÖ×5kV²bÅŠHmÛ¶mK£F‰ 3âHoïLŒá}M¼öÙÙ$êÿœû Ð x8è\kê9Ko7°Íêí–Þ¾“@^ŒÎ²=çéí6lˆt«*UUU‘ÚRYY©mAAA¤v~¢´]²d %%%¬]»6´íĉ)**¢[·n‘ú‰Ho¯ªÚHûöhÝ:üïýÅéÜ9Ú¿á¼y‘ÚnÜXÉüù…‘Ú.Y¢4n\E“&ámKJV0bDøç`éÒç¸ÿþ?Gj[×zfvEÎÓÛËË7дi´@¶²r-Þ¶ªj5••e‘ÚVT¬Fõ\2Á¶•—o`Æh}={íÛ÷ŽðþëéÐa9½{÷ˆt܈+½}ðX ïkb""ÔB[ Уù­ª*€ˆÌÞÆôeéíynÅŠLºiÉóÌÝÒkA=ÒÇ¥R©-–X‰+½½W¯^|÷»ß mWVVF»ví2$¼‹/¾ø"Æ £M›6¡mÇŒéýÁÕtˆÒv̘1|ùå—œþù¡m7lØ€w LŠD¤·7oÞœž=O£S§ðì‚éÓŸçðãý~öÙ˜HmKKKøì³ñ‘Ú.Y2›8…ýö;!´íäÉOѳgøç`ýúÉ‘ÚÕµŒôöQ»s~>iß¾]ºœ©íâÅ_ЫWxÛæÍw¡¢¢œ<&´íÚµ+9ðÀ£hݺchÛ fEz×öS:tèÚ®´´Õ"3GâJo?UD~ܬ û5µODŽÜÙcY€A 8ßø ðïx{Ô Xz{ž³ôöܨÇAz"ÒÛMâ¬#éí&Y¶‘ÞÞ«Þnâ­Þ~#p®ˆ¼¬Œ±¦nu¾ìW³="9x˜ swê5iŽ¥·ç=KoÏz¤'&½Ý$ÎèŒôö½°êíyoܸM÷l2³+cÕÛó]ª·ì=Œ‰Äª¸G¤ªÏ»·ÓE$ÑW· [z{UU•••9}Ϥ)++ oTÇBÒÛc ÂzöY”êîååå1ô,«ñIIo7‰“ù}aÕÛóÜÌ™3Y¸p!dOo·›zf¤Uo7õèÛAUW©ê¸“@x• S“a»ôöªª*|ðAN=õTzôèA=8å”S¸çž{ò&X_²d ?ÿùÏ9úè£éÕ«½{÷æ¼óÎcòäxæ&%½=øwùúë¯9õÔS)..æwÞÉUr*[þŸÿü‡³Ï>›^½zѳgOŽ=öX®¾újV­ZgW3GÁÀÒÛKo›±m(Øèy> dc½™%½ÝÒôL¶ó‰1‰fz)‘—Eäi!"çéq÷­>Êuzû¢E‹8í´Ó¸ï¾ûH§ÓTVVRUUÅܹsùÇ?þÁÀùꫯê¼q=z4§œr &L`ÅŠ¨*ëׯgêÔ©üèG?âÚk¯ÍyŸBÒÛ÷ÊE2ÿ.àŠa½ÿþûœsÎ9üêW¿j£ê~¾lÙ2Ž?þx.¿ürf̘Aii)ªÊÒ¥K=z4'žxb\7*,½ÝÔÄÒÛÍVéí™çKo7IHo7f»Y€BU«àÿÛ»ÿتë{ã¯O{ú‹UÀ{çUp®#ãNQŒM;ð" Îá†elänƲø{7ÞÜÜ+×Ü $›3&jn4&38M4&ZÍÌ€°±D™- eÄUf›nmïtÒžžvô³?ÚcN¿çÇ÷[ùžïç{ú}>þ²§9oo?ßïó|–ôo’:$Ý(é[ÖÚq§ƒU®ÈòöññqÝ}÷Ý:uê”ÒétÞ÷Óé´Þ{ï=ÝqÇsß™àäÉ“º÷Þ{•N§‹&Í/¿ü²vîÜÙLž¼Ý{f;’M˜ßë222¢;wê©§žŠbœÈÍ;Wmmmêïï/˜µŽŽêìÙ³ºë®»æmˆÈÛQ y;¦ o‡òvT$6èXkXk7Xk?o­ýžµ¶³ØZcLÃäÕöÿ0Æl5ÆÜhŒ™å¼1ÙÝÛÛÛÛ?¾j^̹sçÔÛÛ«^x¡¬³¸²uëÖlJ^T:Öƒ>YÒ¼wïÞܼýpöñ(óö ¯Ë< (FŠÔ;ï¼£gžyÆ÷5sQX·£òväɹ{;y; !oGEbƒ"cÌjI§$í’ôCIÛ%½"©ÇsÞZ_é&óö¯JÑäíû÷ï/xåÜ+Nkß¾}eŸ'jétZo¿ýv : ªªJ‡ö]†W_}5ûŸNòöé¼.©TJå)rP*åÿ!ccczíµ×"˜ècäí(†¼yr6èäíð"oGÅâcÖB2ùÞôgUü€ðSc̬µE8VÜDz÷ö7ß ~›€®®®2NâÆ‰'TWWè$Åè診ººtà 7”u¦çyû‰'T[[ènöÃÃÃÚ¶m›ž{î¹&‹Î±cÇ444híèè¨z{{µpa$·(–·;=ÁyôèQ=ú裾ë:;;U]]­S§Nù®íëëÓO<¡Y³fù®íéé ôü’ÔÝÝhíÉ“'õþûïkxxØwmGG‡n»í¶@Ï_F±ËÛS©j½ñƳš;÷|× êõ׃ýöZ›É ëƒú­íë;¤‘‘Aõ÷ò];44¨ŽŽ`³þõ¯g­+‡8çí££Ãêéy#ÐÚ>ú¿@kNittT==Æwí_þÒ£Ó§êÃçø®M§?<ëÙ³ƒêëó_›É¤ué¥u~Í2"oGÅbƒcÌÅ’v¨ôÙÚIÛ1»¬µ3o7Ldy»$Í™3Gýýýþ '×Î4MMMïRŸJ¥ÔÔÔT扦ܽ½ÛUÞÞÔÔøž©TJK–,ÑÊ•+Ë>®––­^½ZK–,‰t.¹ä½øâ‹Ú»w¯:;;uìØ1]vÙeZ¶l™Ö¯_¯ÆÆÆ²Ï—¼=W^×nºé&µ¶¶j×®]:räˆÎœ9£k®¹FmmmZµjU”£·£òvLÑÕÕEÞŽR^"oG¥cƒŽ’þ]Òg$•º½å¤E2Q¼l”¢ÍÛ½/^¬Å‹;yî80ÆhÍš5Z³f“çSÞžËõëóæÍÓæÍ›µyóf—c·£òväÉ©±~CÞŽÈÛQñHÜC`­ÍHÚ é#M¤š^“”–´ÑZì3Yf—y;â#Ny;b‡¼Å·#ONÞî=ž·ÃUÞîÿ‘(ÀÄ…Ú@Ø ‡ÄZ{TÒg%íÑDv•ÑĦ|\Òo%-±Öîs7¡3Îòvă'o÷¾_äí(†¼S·Ã‡«¼}:÷¢Br ºÄ=D“©ÕzcL­¤+%ÕH:f­M»Ì©ÈïÞŽxñäíG²»ÎÛäí(¤PÞ¾Qâêy’å\='oG!®òöÃ’ŽIšyŸËŠ0ý$èB® —µvÔZ{ØZûû$oÎÉÛ!•ÌÛÛDÞžtÅòv®„aW¼½Mbƒžd%Ž'ëDÞžtÎîÞn­“´YÒÏØ³’²Öî ú?på´VR#y{rùäí¼ÇÅòö¯;šñáý÷‚¼=á|òvŽ'pz÷vkíQcÌ•’þK'/v5Ë 3â¿$¶F$uIú™µvÿtþG6è('òö„#o‡òvBÞŽ<äíðáüîíÖÚAIÿízT>w”y;¤)?Py¯†‘·ƒ¼Å·#ONÞî=ž·ÃYÞ”t” y{ èàÁƒÙ/½g¶ÉAÞŽbÈÛ1…'oÅómŽ'pš·acƒŽr!oO¸œ¼ýäí(€¼…œ•´Ûóy{Âùäí79 qâòvHS~ âîíð"oG1äíÈCÞŽ¸{ûy2Æ,´[R¥^Q»JÒ/1‹¬µv=L9paøŠÈÛ¼>ÈÛQ y;¦xë­·ÈÛQJ{ssó¸ë!*ܧT¹›ó¬*UΕÿiã :°Q"oO2òvø oG!äíÈ“sõœ»·£òöód­=nŒ¹]ÒrI•ø~n+éwÖÚC®)c-÷XÀ'7™·÷Kš]WW§ÚÚZ×#Á‘‘IÒ¬µÿ“}¼»»û ’Þp6â #é›››?Ì}°»»ûÇ’nw3bâ…æææoe¿˜ÌÛÿ$É444(•âB gOøÞf­Ý‘}¼»»û_%ít6â OÒB® c¦ãè‡óõI³%)“É(“É8Žyß/øIÿìbÄFÆ»9Ÿô¤ÿzÄJ¿çëohòjN:Î_$9§ü¼ýWâx’tgÙœ# ¸‚ŽóbŒY(é×s Æ­µý—@>cÌg%-p=b!c­=âzp :1À]܈6èÄtb€ :1À€`ƒ@ °A Ø lЈ¿ FGÁÀ¥±XIEND®B`‚vispy-0.14.1/doc/_static/gl-history.png0000644000175000017510000012127414507104216017307 0ustar nileshnilesh‰PNG  IHDRäŒSehbKGDÿÿÿ ½§“ pHYs%†%†ìVLœtIMEÞ1ÈLP IDATxÚìÝy\T×7ð߬ û¾#ˆ ¢&â‚➘•ÛlFÓj6cÔĤj«‰1­¶©ékSc¢T³4Zc5µZkVƒ;ˆJ@QeØ·``öûþ‘ÞR@g»ðû~>þ‘¹sãã3çœ{Ÿ{î=W"‚""¢>lÓ¦M8þ|§ÛÃÃñråÊ6Ÿ566bãÆ8rä®^½Š°°0Ü}÷Ýxî¹çàããÓå¿»ºº þþ*ÈdÒ.ïSW§ÇÙ³e0›­¸õÖP„‡{õºü:3Ç=ùMÈuúU}½ÙÙhj2aÀ à©TÒíøÅÖψýËý«¹ÙŒ¬¬rh4zDFz#>>J¥Œýë&ÈÙœ‰ˆ¨¯û÷¿ÿˆˆL:µÃí¾¾¾mþ»¤¤“&MÂÝwß—^z ‘‘‘(--Å—_~‰Ñ£Gcûöí?~|§_Q‘Çã›o®àܹJ80!!ž]ŠuçÎsøÛßNcäÈ0¨TrüéOÇ’2K–ŒïQÑáŠùuFŽoæ7!×èW‚|òIvíÊERR$”J>ýô'ÈåRüîwÓíÛåØÅØÏˆýËÞýkÏž\lÝz£F…#(ÈäáêU-^}*Æ‹dÿbANDDÔs'NÄœ9sºôÝùóçcþüùX±bEëgñññ¸ãŽ;ðý÷ßãêÕ«×=±ÉÍ­Be¥#G†áÒ¥ê.ǘ–V„­[Ïà“Ol-.êêôx晽 òÀO$ôŠü:#Ç=ýMÈ5ú•Õ*`ÕªChj2a×®™pw—·Û¶ý„å˿ömué®1÷3bÿ²×˜ºgO.*ÂŽ3àï¯jýüÄ 5–/ÿ;wÎèÒELö¯¯võm)))ÂÇÜ¥ïFÁÝÝ](++³Éß}Ûm ]úîܹ{…ï¿/h÷ùÅ‹UÂ=÷lÌf‹èóëì÷äûäý*#£¤Ã>`±X…3>ÒÓÕ½ºŸû—=û—Éd,k‡Û/þðÕWØ¿zˆGuƒDòóítÍÍÍý{¯]Ó¢  “'G·Û%NžT3ÇÔgó±c#:œ—J%:4……uìgÄþÕCr¹´ÓÛÉCC½PY©cÿê!äDDDÝ:)‘cÚ´iøÃþG®‹š™YŠqã¢:\:‹?üáfÎü FlÜx—VGgÿêWY'""0kÖ,üâ¿èà¡ãÛðüýýñî»ïâ7ÞÀ¦M›ðÔSO!""ùË_pÛm·Ù<¾ÆF#bcý:Ýîá¡@Cƒ±×ä×9¦Þׯjk›±ys&6mJDÒûû±Ù»éõf¨Õõ¨¬ÔÁb±Âd²@«5ÀÃCÁþÅ‚œˆˆ¨çbcc‘˜˜Øíýñúë¯ãå—_Æ|€””lÞ¼¹[¯øê ??êë n¯¯7´yMoɯ#sL½«_Y­Ö¬9ŒéÓãºô|koègÄþeïþí‹U«~.Þ 3öïÏÓO~…Í›§cÀö¯à-ëDDD6àî_~;wîÄ /¼€úúz›þÿÝQW×ù ¹µµÍ ò`މ¿ùÿ³qcôz3^|qû‘ÆT779ydžzj$Ö­;ÆþÅ‚œˆˆÈù¦OŸ///üôÓO6ýÿ{¢¤¤¡Óí%%õ×],‡9¦¾ô›ïÝ{‡âí·ï‚\.e?#²ã˜zç±ÈÊ*‡ÉdeÿbANDDä|^^^0™L6ýN˜…3gÊÐØØþù:«U@ZÚUL™ÃSŸÿÍÓÒŠ°ys&þú×ûàããÆ~Fdç1U.—viö/äDDD7íÏþ3222:Ýž——‡¢¢¢?/Ý™  $&FààÁüvÛÒÓK RÉ»ôZ'æ˜zóo~ò¤kÖÆ»ï&#:Ú—ýŒÈýËd²â‡ ;ÝÿرbÄÇA¡²± '""²¯˜˜Ü{ï½X»v-jkkÿç„"<ðÖ­[Ÿÿju=æÎÝ‹ÌÌÒ6Ÿ?ýôHlØŽŸ~*oýìÚ5-Þxã<ǫ́.ÍP0Ç×Ï1‰÷7?{¶+Wþ€uëîÁ!AìgD6ê_:Û·gcíÚÃíVAÏÌ,ņ éX²d<ûWq•u""" ,À /¼p“w5üüüðè£bĈX¹r%ú÷ïÁƒÃ××EEE0›ÍX»víM¯þ]T¤AvvΫDbbDëç aX½úv,[ö-‚ƒ=¡RÉqåJ-,HDJJ\¯È/§æ˜ÄÛ¯/þàÍ7A§3Áh4·ûÎæÍÓ1|xH¯ígÄþeþåç§Bjê/šš‰GýÑѾðõuƒZ]£ÑŠ5kî@BBX¯?ŽÙ‹DèîÛà‰ˆˆÐÐЀüü|Ô××cÀ€ˆŠŠ‚Ä—÷ÈϯEl¬_‡ RäåÕÀl¶bðàxz*™cç˜zßoÎ~FtóýËh´àÚ5-´Z="#}êÕá¬6û r"""""""—ÆKÂDDDDDDD,ȉˆˆz«ÕŠúúzX­V&ÃÆjjjpøðaæ–ˆÈNŠ‹‹qêÔ)&ÂŽxË:‰– vy¾˜ˆˆ8β '""""""ê¥xË:‘Y,ÔÖÖÂb±0DDDÄ‚œˆˆÈQôz=rrr ×ë™ «®®FZZŸ!'"²“k×®!==‰°#9S@DDDDD®¨¹ÙŒ¬¬rh4zDFz#>>J¥Ìaû± '""1™L†ÀÀ@Èd<$"ê*AöìÉÅÖ­g0jT8‚‚Él¶B*•@*mÿº­%KâöÛûãÁãí¶?‘£pQ7""";²X,¨®®æ*ëvÂy¢ÞI.—vXL@h¨*+uvÝŸˆ9Q/ ×ëqþüy®²nÕÕÕ8|ø0WYg;@EEÑGX­Ο¯D\\ Söïk¸ÊºýqQ7""""uAn4ÊdôrF£ûÛi(•2Lãðý‰X‰í@+—#((r9¹¶&‘H ‘H˜¢^¬²R‡ýûóP^ÞˆŒŒ$%EaãÆû;½ÝÖû8β '""/777 >œ‰°ƒÀÀ@L:•‰èゃƒùØB/¦×›¡V×£²R‹Å “É­Ö…Cöï뢣£ÍDØWY'"""""—g0˜±RSOcóæé0Àß¡û± '""‹Å‚ÚÚZ@&“1!6fµZ!•rZ¢¾dÇŽ>|›7OwÊþ}‰ ã¬1³DDDv¤×ë‘››ËUÖí ººGŽáíÊ}\UUÊÊʘˆ>äÎ;c‘•U“Éê”ýû’ââbœ:uЉ`ANDDDDÔ^MM ªªª˜ˆ>D.—âfÖ»Ùý‰X‰æÄQŽàà`®²n‰„õB&“?üPØéöcÇŠ…Bj—ý‰ã, r""¢^ÂÍÍ Ã† ƒ››“ac˜¬²²‹…·­ÙÁµk×PVV†¤¤$&ÃNxô"""""Ѫ­­å*ëDÄ‚œˆˆˆÚ“Ëå åmÕö8‰‘J™W""޳¢Æ[Ö‰ˆˆˆH´êêê`±XÄd r"""""""º1Þ²NDDdGf³ååå0›ÍL† ‚“ÉÄDÙ‰Õjåñ‹9‘x \ºt ƒÉ°±šš?~V«•ÉèÃ***PRRÂDÙZ­ÆéÓ§™äDDDDDíÕÕÕ¡¦¦†‰ "äDDDÔ–\.GXXW©µÇIŒT …BÁDÙ‰L&ã8kg\ÔˆˆˆˆDK£ÑÀb± 00É "äDDDDDDDtc¼eˆˆÈŽÌf3ÊÊʸJ­X­VF&‚ˆÈN, ßfÁ‚œˆˆH¼ òòò¸ÊºÔÖÖâĉ\e½+//‡Z­f"ˆì ¤¤gΜa"Xµ§ÑhP[[ËD r"""jK¡P ""‚«ÔÚL&ƒ››ADdÇqV©T2vÄE݈ˆˆˆH´´Z-, ˜ "bANDDDDDDD7&g ˆˆˆìÇl6£¢¢¡¡¡Ëyص¥–UÖU*“AÔMÍÍfde•C£Ñ#2ÒññAP*eÝúTW7Áß_™Ì1OÁèt&øûw¯Ï×ÕéqölÌf+n½5áá^lÝ8†Y,>dG|†œˆˆÈŽ òóó¹ÊºÔÖÖ"==«¬÷qeee(..f"ºH€Ý»sñðÃ;±oß%\¸P…-[NcÆŒ/‘QrÃý‹Š4ر#O=õO$'oGMM³Ãb_³æ0V­ú¡[ûìÜy<ò9öïÏÃ?aΜ=X¿þ¬VÞ$Ü¥¥¥ÈÊÊb"숗ꉈˆˆH´´Z-ŒF#úõëÇdtÁž=¹8t¨;vÌh3Ó|â„Ë—‡;g $ijÓýss«PY©ÃÈ‘a¸t©Úaqûm²³+ЯŸO—÷IK+ÂÖ­gðÉ'":ÚÀϳåÏ<³AAxâ‰6r:ÎÙ‘B¡@dd$WY·™LÆÛÕ‰ºéâ±aÃ}ínûž0! #F„âøñëßmpÿýƒ±dÉx,Y2nnŽ™Û«¬ÔaãÆ <ÿüØní÷é§ÙX±bJk1þþ*üñwâÿÈÅ»knD.—sœµwŽ™"""ûQ*•4haþþþHJJb"ú¸ˆˆX,&¢ËVçóq¡¡^¨¬Ô¹T¼V«€7ß<„gŸ /¯®¿~ëÚ5- j1yrt»mqqAðñQâäI5&MŠf£¸AÿŠˆˆ`"ìˆ3äDDDD$Z>>>ð÷÷g"lPøž?_‰¸¸@—Šë³Ïràã£BJJ,–®?÷™YŠqã¢:\¨N"¦L‰Á©S¥üá‰9Qof2™ V«a2™˜ ³X,hnnf"ˆn’ÑhÁœ‚R)ÃÔ©1.W~~-¾ü2+WNDÒ½}««›ìÑéöà`OTW7ñÇ¿³Ù ½^ÏD° '""뉮W®\Ñhd2l¬®®\e½+++õkטˆnª¬Ôá£Îâ8‚™3¿DCƒ7Þ©Tâñ¬Zu+WN…O÷_¹USÓŒ€÷N·º³ ï‚ÒÒRüôÓOL r""""¢ö´Z-4 ÑMz½ju=JK`±Xa2Y ÕºÎëßÿ’’"1vlÏž_nl4ÂãóÅ4=<hhà…Rr>.êFDDdG …ýúõã*ëv “ÉàááÁDõ@t´/V­º `0˜±ž|ò+lÞ<8÷™üŒŒdf–âãèñÿÃÏO…úúÎ/0Ô×Ú­4OÃÜÝÝ™äDDDâ¤T*1`À&Âüýý1vìX&¢‹ŒŒä*ë7ÉÍMŽGƒÁ‚uëŽaóæéN‹¥¡ÁˆÕ«Ä´i±Ø·ïR›m—/×¢¼¼»wçB*•⡇â;ýÿº£ªªóãkk›Ä z7Žððp&‚9Q{ÞÞÞL‚Üyg,6lH‡Éd…Báœ'[››Mxä‘¡¦íbb:f³Réõã öDVVy§ÛKJ꯻è r""¢^Àd2¡¼¼aaa¼mÝÆ, ôz=<==™ "[ri·W3·µO<óÌè·9r ¾Óíÿm„(üéOGÑØhl÷þr«U@ZÚU¼ýö]üÑ»p 3›Í¼mÝŽ¸¨‘Fp•u;¨««Cff&WYïãJKKqõêU&¢KÅ•?üPØéöcÇŠä´Ùq[ ò@bbÌo·-=½*•·ÞÊFqeeeÈÎÎf"XµW__­VËDtNgÄöíÙX»öp»Æ33K±aC:–,ßæsµºsçîEff©Ëþ»:‹ñé§GbÆtüôÓÿ¿uýÚ5-Þxã<óÌ(§ß @ð–u"""»R(ˆŽŽæíêö8‰‘Ëy»:Q7øù©šú ¤¦fâÑG¿@t´/|}Ý V×Ãh´bÍš;ÖfŸ¢" ²³+pî\%#\òßÕYŒ aX½úv,[ö-‚ƒ=¡RÉqåJ-,HDJJDa|›…}IA˜""""£ÆÆFX­Vøøø0Ý`4ZpíšZ­‘‘> õêpÆX€üüZÄÆúA.wÍ›ko£ÑhA^^ Ìf+€§§’ €Xõe|†œˆˆÈŽL&®^½ “ÉÄdؘÙlFcc#ADd'F£MMML r"""ñžÌq•u;Ðh48}ú4WYïãJJJPTTÄDÙAyy9rrr˜;â¢nDDDD$Z ¼àED¢År""";R*•ˆ‰‰RÉE„lM.—ÃÛÛ›‰ "²ã1Œo³°/.êFDDDD¢ÅUÖ‰ˆ9u oY'""""""bANDDDDÔujµ………L‰WY'""""Ñjllä*ëD$Zœ!'""""""r.êFDDDD¢¥Óé ¼¼¼˜ "bANDDDDDDD7Æ[Ö‰ˆˆˆˆˆˆXu]qq1 ˜"%®²NDDDD¢¥Óé¸Ê:± ·…êê&øû« “u}â¾®N³gË`6[që­¡ç‚D¶îg=ÙÇÑqÖ×]¦& ðÇ€þJ%üÁûX[mn6#+«‘‘Þˆ‚R)sÉXÿ—ÑhNg‚¿¿Ê¥â­¯7@¯7w¸ÍÛÛ îî¶?•°Uæ9‚}sìŒcƒ«²ÅØã¨öj‹X]á˜Û“1ÓÑm¶'1:+·=‰ÕYÇÜÞÆé‹ºipüx1¾ùæ ΫijâÙ¥}wî<‡¿ýí4FŽ ƒJ%Gzz RRcÉ’ñ<'ºÉ~v3}Ó‘q ðÉ'Yص+II‘P*e8w®r¹¿ûÝ4DGû²ôò¶ÚÒöìÉÅÖ­g0jT8‚‚Cž›[…ÊJFŽ Ã¥KÕ]Þ/-­[·žÁ'Ÿ<ØÚ@ëêôx晽 òÀO$ð×%º‰~ÖÓ¾éÈ8­V«VBS“ »vÍl©`Û¶Ÿ°|ùwضí!ÎÞôò¶ ü|bpèPvì˜Ñæêþ‰j,_þvîœaó0[ö‘o¿-@vvúõóq¹Üjµz¼öÚdL™m×ßÞ–ý™çö˱3Ž 7âááá´¿Ûc£ÚëÍÆêJÇÜÎj³Ý‰ÑÙ¹íî1ÈÇÜ^Mp!·Ýö±PQÑØ¥ïλWøþû‚vŸ_¼X%ÜsÏ6Ál¶DtsýìföqTœ%öw‹Å*̘ñ¹ž®æÞÚªÉd,k‡Û/þðÕW\®_µ¨¨h~ùËÏ„¯¿Î^xáß.—ÛÇß#ääT8$.[õgž#8fÌtƱÁÕØbìqT{µE¬®p̽™1ÓQm¶'1:+·=‰ÕÙÇÜÞF”ÓF×®iQPP‹É“Û_­‹ ‚'Oªyµ…¨;6¢Ã+ÆR©C‡£°°ŽIêäri§·u††z¡²Rç²3–o¾yÏ>;^^J—ŒQ£Ñ# À]4ý™ç}o̼ví®\¹"ʱǑíÕ㤳ÛÆÌžÆèŒÜö4V±s]•( òÌÌRŒÕᢠ0eJ N*å¯KÔÇý¼x‹;чY­Ο¯D\\ KÆ÷Ùg9ðñQ!%%‹à’1jµ—èG]íÏnÏž‹2$aa|ÍQ_RY©Ãþýy(/oDFF ’’¢°qãý.·ª¶ÑhÁªU‡°råÔW/wõõz˜ÍVÌŸ¿cÇF <ܹ¹Uøë_Óqß}ƒðÒK’Ûîôgž#ô½13&&N~iPÇg´W{Œ“ön?b3í£=rk«XÅrÌeAnFÄÆúuºÝÃC†#]¢>ª¶¶›7gbÓ¦—½ŠNö¡×›¡V×£²R×:³«Õàá¡p©8ßÿ’’"1vl„Kç3 À7Þ#BÛÜRûÌ3£±pá~ìÚ•‹™3‡»Tæ9Bß3ÝÝÝE;ö8£½ÚzœtDûØií•[[Å*–c. r;ðóS¡¾ÞÐéöúzC·^jOD½‡Õ*`͚Ø>=ŽÏ0õAÑѾXµê6€Á`Æþýyxòɯ°yót àï1fd” 3³ü€ËçS"ÛŸ°{àå—'`ÍšÃv-È{ÒŸyŽÀ1SLc3Ú«-ÇIG´1Œ™öˆÑ^¹µe¬b8æ² ·“À@wTUu¾xGmm3‚‚<øëõA7f@¯7ãÅÇ1}œ››<2 ƒëÖÃæÍÓSCƒ«WÿˆiÓb±oߥ6Û._®Eyy#vïÎ…T*ÅCÅ»t~ÇŒ GuuŒðöVºLæ9Bß3¯^½ “É„Aƒ‰nìqv{½ÙqÒÞíG c¦½b´Gní™OW<æ² ·£à`Ode•wº½¤¤þº dQï´wïE:Tˆ¿ÿý!ÈåR&„wÞ‹ Òa2Y¡P8·]47›ðÈ#Cüü:±ÿ¦Óa6[¡Ñè!•º~ûU*åP©ähh0Ø¥ ïiæ9Bß3›››a4E9ö¸J{íÉ8éˆö#†1Ó1Ú+·ŽÈ§+sYÛÑ„ QøÓŸŽ¢±ÑØîyV«€´´«xûí»øëõ!iiEؼ9©©¿péE²È :¹Ôež‹ ñÄ3ÏŒîpÛ‘#× Ñè;Ýîj**!‘H첈ÓÍôgž#pÌÓØã*íµ»ã¤£ÚÆL[ÇhÏÜ:"Ÿ®tÌ Q^¶ ò@bbÌo·-=½*•œ¯í êCNžTcÍšÃx÷ÝdDGû2!}ŒÉdÅ?vºýرbÄÇñJ}òš]Ñéö;Ïa„(›¯¦{³ý™ç}oÌìß¿?,ʱÇQíÕ–ã$¹}»oò˜Û rµºsçîEffi›ÏŸ~z$6lHÇO?ýÿÛ|®]Óâ7~À3ÏŒâ•"õ5WñìÙr¬\ùÖ­»C†ñ‡ìƒíT§3bûöl¬]{¸ÝjÄ™™¥Ø°!K–Œg¿êf¬••X½úG|øá–ÖïY­vî<‡.cÙ²‰6¡;ýùz¹å9‚ýsìJT*•SVZïîØãÌsÚžŒ“b<æŠõ\Æ•sû¿ñºò1W¬\þ–õ¢" ²³+pî\e›•^°zõíX¶ì[{B¥’ãÊ•Z,Xˆ””8þ²D6êk®ãâÅÿ¼ùæ!èt&ævûmÞ<LJðGî¥íÔÏO…ÔÔ_ 55>ú¢£}áë뵺F£kÖÜ„„0ö«nÆéƒO?}ëןÀ/ù ð‡J%G^^ bcýðñÇ 0ж϶v§?_/· õrêl¨˜Úìõbmj2A­®‡Ng€~ðõU‰b<à9Bßh¿EEE0™LN¹m½;c«œÓvuœã1—ç2Ž×ÕŽ¹,ȉˆˆˆˆìâÅ‹01b“AD¢Ã)†ÄQ IDAT&"""""""'à 9‰–Á`€ P©TL± '""""""¢ã-ëDDDDDDD,ȉˆˆˆˆˆˆX r"""""""äDDDDDDDÄ‚œˆˆˆˆˆˆˆ9± '""""""bANDDDDDDD,ȉˆˆˆˆˆˆX± '"""""""äDDDDDDD,ȉˆˆˆˆˆˆˆ9 r"""""""bANDDDDDDÄ‚üfTVVbõêÕ¨¬¬ä¯FÔ‡ûÇ[;ÈÍÍÅí·ßŽÜÜ\æÖÆòòò0wî\”——³SôáqöòåË8þÏ»Xxë­·PQQÁ_¨÷/Ž$¶vpñâE¤¥¥áâŋ̭ýç?ÿÁÇŒââbvŠ><ή]»K—.enûðØUQQ}ûöaݺul6öÑGaË–-<ïbANDDDDDDÄ‚œˆˆˆˆˆˆˆ:±gÏüóŸÿ¼á÷äL‘m˜L&Ì™3&“ EEEˆŒŒìô»œ!'""""""²…B!C†Àl6ãÿüŸÿsÝï² '""""""²¡W_}°aÃìÝ»—¹£­^½šñ2Vѵb;`›%bÿ£+W®0 DäRãìc=†‡~‹¿þõ¯‘‘‘Á‚Ü‘Þzë-ÆËXE׈í€m–ˆýKŒ ˜"r©qV"‘`ûöí˜8q"šššpÿý÷#--9‘½¹»»ã_ÿún¹åÔÔÔàž{îÁ–-[Ú|GþÇ?þñº÷´»Š¦¦&ÀìÙ³ááá!Š`üøñ¢j0bŠ—±öÝþ%Ʊ€m¶o·ƒÚÚZÀòå˱nÝ:æÖ†ÊËËsçÎ…§§'ûWg[nWgnûîØÕ’×ãÇ»|;[ÿ***b f*• ¾¾¾Ðjµ˜?>öíÛ‡ÔÔT„‡‡C2oÞù${ì±N¿Ç‚œˆˆˆˆˆˆÈFL&N:‰D‚åË—_÷»|†œˆˆˆˆˆˆÈF þô§?A*•"..Ž9‘£,Y²¤Kßã-ëDDDDDDDNÀ‚œˆˆˆˆˆˆˆ9 r"""""""bANDDDDDDÄ‚œˆˆˆˆˆˆˆX± '"""""""äDDDDDDD,ȉˆˆˆˆˆˆˆ9 r"""""""äDDDDDDDÄ‚œˆˆˆˆˆˆˆ9± '""""""bANDDDDDDD,ȉˆˆˆˆˆˆX r"""""""äDDDDDDD}‰œ) ""[µµµÐh4000›ÍP(P*•pssC`` |||˜,;ÊÈÈÀ¸qã\26£ÑP*•.ŸÇ-[¶ ºº¿ýío]>V³Ù ³Ù •JåÔ8¬V+šššàååÕiÛ<}ú4Š‹‹‹I“&aذaì´ÝÐÐЀüü|ôïßþþþm¶?~Û¶mCee%ÆŒƒçŸ~~~NY¯×C­VC­VC&“ÁÇDZ±±<ý?œ!'"¢›V\\Œ“'O¢  ‰>>>Gÿþý ///˜Ífäææ"##LZ ­ï¿ÿï½÷>ÿüs\½z‚ \wŸ;î¸Ãim`ëÖ­xë­·°wï^X,–ÖmçÎôiÓàååoooL˜0ÇwéÜ[,–6ÿgÅP]]Ýéö’’œ{êÓ8CÞ Wžah9“H$.ò²2˜L&DGG‹æÄ×fZNÂd2Y‡ÛÐÐЃÁ•J___xxx°Óv‘g€Ÿgž  $ d2ÜÝÝ;m'Ž”ŸŸÚÚZÜzë­ÎŒµýôS( 8p<ðvìØ»ï¾Û¡±VUUáñÇ¿á÷®]»“É„£G¶ùüàÁƒ=.]½zAAAö»ºº:ÄÅÅÁÏÏR©ƒjµ§OŸÆ¨Q£z<«©©Á‡~ˆsçεÛöÛßþr¹EEEðôôl3?÷ÜsxõÕWñ׿þÕ¡í@&“¡¼¼Á˜1c —Ë‘ššŠK—.aýúõÐét¸páRSS1vìX¬X±Ë–-sê9à×_C‡¡¬¬ eeeÐh4Fxx8"""ð‹_üãÇwùóoÖ_,È]¶ÐJKKCnn.BBB0~üxDGG_·CÝqÇhlltx¬ƒµµµ0ðôôD```kœ:ùùùÐjµooo 8Ðeoùá†39Ž*nëêê:<¡~ža(..†Á`h-n0hÐ §ÜVYTT‹Å‚¶ûw\¸põõõ€R©„F£Aaa!‚‚‚0hÐ H¥Ž»éE­V#!!ëׯÇO<áò¨šš¼øâ‹8pà „ÒÒRL›6 ï¿ÿ>üüüðî»ïâí·ßÆœ9sœœŒ´´4Œ5 ßÿ= à´¶[QQ’’477C¥R¡¹¹žžž0 ­‚‚‚á”J½^ÊÊJŒ; …¢Kû`äÈ‘8{ö,ÂÂÂvQÁl6£¼¼¼ÇûGEE9ô·üñÇ1{öl¼üòË­}Ûl6#55wÞy'ž|òI¼ýöÛ]λ=[óçÏÇgŸ}†{î¹§õÒŠ+ðꫯ¢¹¹¿ûÝï0þüÖ}æÎ‹þýûcÑ¢EÈÍÍuèøáããƒ?~k×®…¯¯/Ìfs»ï8põõõxì±Ç\núC† Áïÿ{‡žÏ( $$$ ¢¢ÙÙÙ@DDD»]ÁÅ‹Q\\ŒþóŸ­cL&Ãܹsƒ»ï¾Û¶mk-Æ[,X°ùË_ ÕjÛý.öbµZ1sæL,_¾Ï=÷\»x/^Œüã˜={6‘˜˜ˆ5kÖ`úôéðññÁ‚ š[«ÕŠwÞyï¾û.’’‚qãÆ!44ÞÞÞ¨­­Eee% ðè£ÂÃÃo¼ñF—.êõubª¿XÛ™˜fêêê›› ___¸¹¹¡ººÅÅÅ1bL&²³³Ñ¯_?ÄÇÇC"‘ ¶¶çÎÃСCÛÍôÙ“ÉdÂÅ‹»t¢.êëëÛ|îÈ„–A,3 &“ åååHLLl·­°°‰IIIm ‹Å‚¼¼<`РAÍ­‡‡>þøclÛ¶ ©©©í."¸ÒÅ!1Í.´œpgggÃ××C† ··7$ ÊÊÊÐÔÔ„Âb± ©© eee8sæ ¢££Ñ¯_?‡ÆY__ÿn…ððð€N§sØIxUUúõë¥RÙ£‹-·†:BNN¬V+^yå•6'/r¹‹-ÂŒ3ðì³Ïâ¾ûîÃ_|€€§õ¯ÌÌLÄÇÇ·ã •JñÖ[oaذa°Z­Ø´iS‡'^ …jµÚ¡íÖÍÍ Ÿ~ú)¾øâ üö·¿ÅªU«:¼¨¨V«Q]]éÓ§»Ü˜¦ÕjÜéEãÈÈHÃl6C.wÌ©^cc#L&S‡ý.$$¤Ó~®T*1zôhdggcòäÉÏehh(QRR‚œœ¨T*„††ÂÇÇ.q±ùüùó¸ýöÛ;¼è®T*1xð`üêW¿j·M*•bÔ¨Q¸páÆïXOœ877·vÅx‹•+W"%%³gÏnóìÚµ ãÆÃܹsz‘qéÒ¥8xð 8€Q£F]÷»ï½÷¾ùæÌ™3/¾ø"«î^P± ·31Í0X­VäååaذamŠë‚‚Àjµ¢ÿþoÝÖz|ìØ±‹U&“A¡P ¾¾±±±Éd΄×ÖÖÂl6#$$Ä%Û‡+Î0Ô××ÃÏϯèÊÊÊvÅxËŒÖßÃQqèÐ!lÛ¶ wÞy'RRR°páB‡_t¹1Í.´ÈÍÍEtt4"""Ú|Žüü|TVV"$$ÞÞÞðööFÿþýqîÜ9Èåò6ㄽI$’?gkµZz²ޤ¤$Lž<þóŸ]úøuñâEŒ7®Óü„††â«¯¾Âk¯½†¤¤$üûßÿF\\œSb-,,D|||‡…ïwÞ‰âââN/l4¿3gÎÄĉñä“Oâßÿþ76oÞìÔ Ýa4oxQÉËË :ÎacWbb"^yå•vüüüÐÜÜ A:mÏçÏŸwjîår9bbbššTVV¢¨¨f³ÞÞÞpww‡B¡hýæÐøZ~ËŽüñÄ‚ :½C®±±Ñ¡³þEEE×½å8$$¤Ãg÷cbbŽììlŒ3Æac׎;››‹ààà~_*•"99ÇŽCRRžzê)x{{;$VÞáÕ»õêEÝþ{†á¿ª–†óçÏãòå˸ï¾ûP[[ëÔXàááÑn¦»ÿþ­«‡††¶ÛÏÏωġ ¢H¥RÄÇÇ#660™L l÷ÇÓÓí>w]™aÐétÞÊhÏ 3]Ü0™LP*•Û‰^^^N¹ÍG"‘à‰'žÀÙ³gŽ””Lž<©©©ÈÉÉqhþ:c‹ÙG·M©TÚ®ÿïµZÝîß1lØ0:ôQ___hµZ477wûßh0~{è¬Y³Dqüòôô„^¯¿îwd2þüç?ãµ×^ôiÓpöìÙÖBØ‘;=QŒŽŽ¾î:ÕÕÕ]:¶—¨¨(|ûí·?~<Ƈ~øAíC¥RÝ𸽵HìaàÀ7n–/_Þf R*•˜:u*öíÛ×á~»ví‚B¡ÀСCžW‰D‚   6 &LÀرc•J‹Å‚†††›*ŠnæbÇ7ß|ƒªªª6Ÿoß¾yyy8}út§ç–YYY½X7dÈvº½ªªªÓq?<<¼Ã»,ì%==wÝuW·Ç !C†`øðáÈÉÉqX¬-wx 8·ÜrK·ÿ°þbAî4]a9r$’’’——ç´Xõz}‡ sI¥RøûûÃÃãӫŸîîîÝ>¶…àà`Œ9åååÈÍÍu‰Â«;º3Ãà(^^^Ðjµí *¹\~ÃYȦ¦&§^iô÷÷Çë¯¿Ž‚‚,[¶ ßÿ=î½÷^øúúbêÔ©xúé§ñꫯbݺuøè£›˜f€ŸŸÉ½ÞUw…BÑá*Ê*• nnn½0£T*ƒ³gÏ¢ªª V«õºß7›Í(--ENNâââºî$''cäÈ‘.?>Mœ8ß}÷]—ÆŸyóæáý÷ßǽ÷Þ‹#GŽ8lƦŔ)S––Ö®X€§žz ¯¾új‡ûi4\¹rÅéºH¥R¼ôÒKسg–.]ŠeË–9|Õïë1™L¨©©ACCôz=, QWW×ikyý˜£×•غu+Nœ8‰'âóÏ?o-®6lØ€7¶9Ϫ®®Æš5kðüóÏã£>rÉuHT*BBBƒÁƒcذaN?‚‚‚ðꫯb„ زe þõ¯añâÅøýï£G",, +V¬hÓ, ^~ùe<öØc½H—€òòrìÚµ«Ý6A°bÅ $''·ÛÖÜÜŒ¬¬,‡ÞʬP(ÐÔÔÔ£}u:CϹZîðzñÅ¡Ñhºý‡õ—këÕ·¬wg†!>>Ó¦MÃþýû1jÔ(‡Ï0tv‚ ü<Ûq½¯–ÙSgpssCBBÔj5Μ9Óú,¶¨Tª.Žžapww‡··7 Úœ¤J$øùù¡¦¦¦Ã» ªªª ‘H\bµu¹\Žx<ðAÀÕ«Wqüøq\¹r8uê*++1wî\‡Å”˜˜ˆ… ¢ªªªÍ•ð–Ù…ÎÖ`pÆìBK;¸ÞëxL&S§íR©T:|1Ũ¨(¨T*·æS©T¶ÞÕa2™`2™ ×ë¡Ñhàïï#F8eAÊ¡C‡ºÄLÜbáÂ…øõ¯Ý»wßð9à|îîîx衇^Lzyyáõ×_Çã?Ž´¹ÈÒÙm’‚ àÕW_ÅìÙ³~¼í̈#žžŽ+V`„ ˜Ãàëë‹K—.Ád2µ»âÖéUp³Ùܺú²3EEEÁßß.\@@@ú÷ïïRm¡e†A©T¶>ˆââbX­Ögéœ5Ã0dÈäääàìÙ³ˆŠŠBPP$  „óçÏÃÃã5&“É„ÒÒR”””¸ÜsÛ-ú÷ïïôöðß³ ¯½öBCCñý÷ßãàÁƒ8zô(Þyç¬X±kÖ¬im Κ]h)pŒFc» - ;|ÞÒjµ¢±±ñ†¯³WŽƒ‚‚ÐÜÜ F£Ñغ¼B¡€››¼½½1dȧ]@›åË—cΜ98|øp—Þ-~ï½÷bÏž=NY„láÂ…­ë t宇ݻwãèÑ£ÈÈÈp©œ«T*¬_¿ß}÷žzê),\¸Ð¹'iry‡Aèôb|}}=***0zôh§Å¼hÑ"<ÿüó(**BVVŠ‹‹Q__I“&!** Ï>û,œúܸ£ïα…{î¹§Íâ‰ÿ]Ülݺ?þø#8€ææf,Y²3gÎtÊ«1ãââ““ƒ?ü°µxíß¿?.\ˆÙ³gwÓ¢E‹0lØ0‡ÆŠ×_“&M¦M›0}úôëï5 vìØ×^{ Û¶msø¹Arrr§'°þbAî²Ä4à “Éƒ‹/¶+¬®×á êOOOŒ=………8{ö,|}}]b±±Í0ÈårŒ9eee(**Â¥K—àîî777H$œ={þþþhjj‚^¯Gpp0FåÒïPub™]h¹1lØ0äää ªªªuÆÙ`0 ¢¢V«µÃÛ&óóóìÔñÀÝÝ]mQ H$ضm[·~Ï©S§¶{³…£bíNñúË_þS§NuÊÅ£®¸ë®»pîÜ9—}ýŽD"éô–··7FŒá°ÕÕ¯cll,bcc].nnn7\Q[Œn¿ývÜ~ûí.sqkÑ¢EX´hQ—¾ï¬G‰–.]ŠØØX¼óÎ;˜7oîºë.DDD´.šZ[[‹ªª*àðáØ6m¾þúkLœ8Ñá±ò/ä¢%¦†–œ®·é«ªª‚V«uÚUðŽH¥R 8uuu¸té’CW{¾^+¶‰D‚ˆˆDDD@¯×£±±±u¶±åµxáááðòòrÚI—D"ÝE±Ì.´¶‰‰‰(//‡V«…Ñh„J¥j=QèhŒpÕw躱½sVŒ³x]¡T*]ö -üüüDóÖÿŽÍξkŽHL|ðA<øàƒÈËËáC‡P^^µZ ­V‹   DFFbìØ±øøã¾Â>ë/ä.E&“aôèÑÝ>8:k†aøðá×]YöFEyWÁÏÏï¦ †ÐÐP¼ùæ›®âÞSþþþHLLìñ뫽gl«J¥rÈÉUBBB·bŒŒÄ‰'œ2 8Ð."ì1»p3í e¥õ®Ž 7;Ûx³mV„Ö·BFÆÖ[Ö•J%ÜÜÜh³gÇÇŒÓí778ë³ñññˆŽŽîÑ¾ŽžÍŠŠêñ­£ŽŽÕÏÏ£Gîñ… GÆ+¶þuË-·tëœÄ™íàfrëè¼¶ÜÕÖSŽÌ­»»;FÝãõiÝFuSomˆ‹‹sØš1ƒvÊ:*=ÞíÇ"Åt‡—+Ž^ýÇÉôz=Ôj5Ôj5d2|||ë’ÂjµÂ`0À`0@"‘@&“ÁÝÝÝi3vD½©‘mC­VC.—#00nnnP(Éd0›Í0™LhnnFMM ¤R)bbblz°+ 1nܸ.¿s¶Å¥K—””„ââb‡>ßÖÑl~ddd‡³ù{÷îuêl¾˜bc¼bè_Ì+s+Öv@ätBÐØØ(¼ÿþûB||¼ T*…Áƒ „áÇ þþþaÀ€ÂË/¿,\¾|Ù©±šÍf¡¤¤DÈÈÈÒÒÒ„ôôtáÇN:%=zTøñÇ…“'O ùùùBSS“ËäØb±MMMB]] Ñh„††Ál6»l›S¼®«˜ú×knn._¾,:tH8|ø°••%hµZ¶Ù¸|ù²žž.444téû555±cǵZíÐ8?ûì3á±ÇëѾ'NŽ;æÐxó›ßqqq™3gºÔ>þóŸÿ† ÞÄ«ØâKÿb^™[±Žb`2™„âââÿ!×ÖëgÈ/\¸€äädLš4 ‹/Ƙ1c —Ë‘ššŠK—.aýúõÐét¸páRSS±k×.¬X±Ë–-sx¬MMMÈÎΆ¯¯/"##áíí ‰D‚²²2455aàÀ°X,hjjBYYªªª~ýú9%·‹())i]é½¹¹žžž­Ï;«T*!""ÂéÏ‹)^±Ä*¦þüüÞпÿýïxï½÷PPP€˜˜\¾|ÇGii)êêê0`À<ôÐCX¸p! Ä6Û…»"Μ9ƒ±cÇvkǦ¦&œ={ãÇwØ]?»wïÆ§Ÿ~н{÷v{ß‘#GbË–-;v,góE~ç˜âSÿb^™[±Åj6›Q^^Þãý;{­£=”••!""J¥²GÇzG¾‹\Lyå ¹ƒf”n¹åáƒ>èô ÞöíÛÛ|V^^.$&& ›7ovx¼§NJJJ:½’[QQÑæ3ƒÁ œ>}Z(--ux¬:N8qâ„››+hµZÁjµ ‚ ¥¥¥B~~~ël}}½péÒ%áèѣµkלÖįXb[ÿÊÍÍ¢££…_ýêWÂÉ“'“É$‚ lÞ¼YXºtiëlÿ©S§„yóæ ~~~ºuëØfo ¢¢BÈÍÍíѾgΜqè] åååB@@€——×­ý>,„„„W­û IDATz½ž³ù½àÎ1Å+¦þż2·b‹µ´´T (•JÁ××·Û-))Ixùå—]¾þ[^]´7_l8qâÜÜÜðÜsÏu¸}åÊ•x÷ÝwÛ|Š]»vaÕªU0™L‹U«Õ¶.àÔ‘˜˜¨Õê6Ÿ)•J 6 ………pô¹¹¹ˆŽŽÆÐ¡CáããÓºGxx8A@ee%d2¼½½‡±cÇ¢ªª eeeNi bŠW,±Š©Y­VÌœ9Ë—/ÇŽ;””Ôº(Ü‚ `±XðüžžžHLLÄ–-[pñâE|ñÅHMMe›½Ž–wO÷ôwqä+Åþû³»wï¾áëU4 >øàÜÿýHMMuè;g šššz|'ˆ#_9)¦Xů˜úóÊÜŠ-Öððp$%%áÅ_„F£éöG›5k–(ê/±åÕôꂼ¨¨ãÆët{HH***:,~ÃÃÑí°X ÃuoÑQ(0í>W©Tpsssè»RÅvñ@LñŠ)V1õ/1]<[;ðõõ…V«Esss·ÿƒÁá¯i[ºt)þö·¿aýúõ ã>ŠÅ‹cíڵذaV¯^E‹á¾ûîCdd$8€¯¿þ>ø C㜯¤T*z.¦‹b‹WL±Š©‰éâØÚR©DLL Ξ=‹ªª*X­Öë~ßl6£´´999ˆ‹‹sÊ{¶|ðA;v ééé¸ë®»µZ“'O¢¶¶‘‘‘˜5k®\¹‚ýû÷câĉQL³ùbŠUlñŠ©1¯Ì­Ç1¹C‡Åœ9sxñ ’÷æ\BBÊË˱k×.̘1£Í6A°bÅ $''·Û¯¹¹YYYHHHpX¬^^^0¨ªªêpŒÂÂB´ûÜjµ¢±±ñ¦ß?ÜÝ‹/®xñ@lñŠ)V1õ¯!C†`ûöí¢¸x Æ>•J…ââbäååÁßßJ¥J¥2™ &“ &“ z½þþþ1b„Ó_‡çÈwÎöÄÒ¥K‹wÞyóæÍÃ]wÝ…ˆˆ„„„´¾:¨ªª 8|ø0¦M›†¯¿þÚ)Ä«ØâSÿb^™[±Å:tèP :TTõ£ßïÞWòêL½~•õ¼¼<¤¤¤ !!ÉÉÉ Z­ÆöíÛa0––Ö®˜}öÙgááá¿þõ¯µ¹¹999ðòòj=0 TTTÀjµbäÈ‘íNÂóòò •Jº´ 8sæ ¢££;¼x——™L†¶»xžžŽ¤¤$‡^]S¼bË­Xú—Éd¸qã°råÊ/ÌŸ?^^^øË_þÒ®O0]Á\líàs¦Ñh`4a4a6›¡P(ZOt T*yôíA_;tèÊËËQ^^­V‹   „……!""ÉÉÉ c¬½<^1õ/敹ãxàÊø~w䢦×ëñá‡"##eeeèß¿?&OžŒÙ³gw8Ë”••…aÆ9eðµZ­(//G}}=ŒF#T*|}}ÒáU®ÆÆFxzz:ü ˜X.ˆ1^±åV,ýKLçÄØÄF 3 DDÄcB‹Å‹ãàÁƒØ¹s'FuÃzâ›o¾Áœ9sðæ›oâÅ_d^YSo$–‹bŒWl¹ 1]œc;°_N9Ã@DDb:&ˆéýî<Ö² '""'µµµÞúéææ†ÀÀ@§>;ÎÆ*ÖxÅп˜WæVl±Šå˜°sçNìÝ»Ÿ}öY·÷4iÞyç‡>£/Æc- rÒëõP«ÕP«ÕÉdðññAll¬Ë ´ÿÛ@  $ d2ÜÝÝ;]̉˜[ö/r–ââb¨ÕjÈårÂÍÍ …2™ f³&“ ÍÍͨ©©T*ELL BCC#g«XãCÿb^™[±Å*¦cÂîÝ»ñé§ŸbïÞ½ÝÞwäȑزe ÆŽ˼º*¡hllÞÿ}!þÿ²wßQQ\ÿÿøŸAˆ€(ØÅQcð[4ˆ{؉шïXbÔ„DÍÛ$j4Æ$Æ|ì15ö·+TDQDlˆHée÷þþð0?Wva–……çãÏÁi{ïkfッ™Ù™æÍ…‰‰‰pssD«V­DݺuѸqc1gÎqïÞ½ -kAAxúô© gΜ—/_§OŸW®\çÏŸ§OŸ—.]÷ïßYYY•&Æ2™Ldee‰/^ˆÔÔT‘žž. *ÕqÀØòûõºììlqïÞ=qêÔ)qöìY&ÒÒÒ*m;V™ƒ{÷î‰Ë—/‹ôôtµ–OIIÁÁÁ"66V§åܱc‡>|x©ÖõòòÁÁÁ:-ïÌ™3EÓ¦MEhh¨ZÇÇ‘#GD½zõÄš5kt~ èSYõ­¼úòýb\[}+«>õ ÂÚÚZDEEi´ÞÙ³g…­­­ÈÉÉa\+±*ŸGDD'''1bÄqéÒ%‘ŸŸ/„â石gÏ–Š+W®aee%¾ýöÛ )kff¦¸xñ¢ˆˆˆiiiB.— !„ˆ‹‹÷ïß—’Ê—/_Š»wïŠóçÏ‹˜˜&¸Œ-¿_Uôä>ÙÙÙ"88Xäååiü<þ¼NO*ìÞ½[øøø”j]¢³²>|øPØØØˆ¤¤$Ö‹ŒŒuêÔ/_¾dY«@yõéûŸ2¶úVV}ê„âûï¿õë×»wï.1Á~ñâ…X·n°´´{÷îÕi9õ-®LÈË™L&­[·?ýô“Òù3gÎ[·n-rªC‡âçŸÖyy¯\¹"ž>}ªtÞ½{÷Dbb¢Â´ÜÜ\qíÚ5Ç—±å÷«Š<Зã 11QDDD”jÝÐÐPÞ•À+ ,«¾•WŸ¾_Œ+c«oeÕ§>¡ÐÞ½{…———°²²~~~bæÌ™"((H¬^½Z,^¼XLŸ>]ôíÛW˜››‹þýûWÈÕf}ŒkE3¬Ê·ã_¼x¦¦¦˜6mšÒù .ĪU«¦ÙÙÙa÷îÝøâ‹/ŸŸ¯³²¦¥¥ÁÐÐJç;;;#66Vaš‰‰ Z¶l‰GAèøQprrB‹-P»vméaöööB )) FFF¨U«š6mŠŽ;"99ñññ:?[~¿är9†Šyóæaûöíèܹ3ŒS¦LL&ömÛ`aa:à×_Edd$víÚ… 6THû¥/Çd2Y©÷‹.äcgg‡Ï?ÿ]»vÅž={››[ìò©©©øé§ŸðþûïcÆ 055ÕYYkÔ¨¬¬¬R­›™™‰5j°¬U ¼úôýb\[}+«>õ …|}}ŒË—/ã½÷Þƒµµ5bccqéÒ%<þo½õ† †ààÁƒ:}›>ǵ¢WåÊEGG£S§N*çÛÚÚ"11Qi‚foo›7o¢}ûö:)knnn±0¨Q£òòòŠL733ƒ©©)222töuÜððpØÚÚIpCCCÑ AvfŒ-¿_êœ<èß¿?FUääA§NðÑGét OÇA:u…ììlÔ¬YS£:æææÂÂÂB§ýÂìÙ³áââ‚ÿþ÷¿ð÷÷Ç{ï½ØÚÚJJNNÆÃ‡qöìYx{{ãØ±c:ÔtëÖ “'Oƽ{÷àææ¦özçÎC||<ÜÝÝYÖ*P^}ú~1®Œ­>¶úÒ'¼©iÓ¦hÚ´i¥ÍÁô5®¥J_!oÖ¬=z¤r~rr²ÊFÕÞÞ^§WðjÖ¬‰œœ•óóóóU>ÜÄÄD§Wqµ‘àêcËï—6Nè’>&&&pvvÆõëבœœ ¹\^ìòˆ‹‹Cxx8š6m CCÝwC¼ÂP}˪oåÕ§ïãÊØêc{ /}‚>b\ÕW¥¯{xx !!»wŸŸÂòóó‘““ƒÔÔTÔ­[îîîþ:<^a¨žeÕ·òêÓ÷‹qelõ±=З>¡´BBBн(Á¸V¼*ÿò¨¨(ôïßèÛ·/lmm‹­[·"77gΜ)’pMš4 æææX½zµNËšððpXZZJCnn.!—ËѶmÛ"ƒð¨¨(ÂÕÕUgåB 44NNNJܨ¨(¡I“&EÜË—/£sçÎ:?»ÌØVïïW~~>:uê„… *=y0yòdXZZâ‡~(rÜ4nÜ>Ôè¶Æêø{=f©©©ÈËËC^^ P£F i kmm ö¾¥ø®:u HHH@ZZlllРA888 oß¾hРËZÅË«Oß/Æ•±ÕÇö ²+((À™3g[[[¼ýöÛprr*ögj–––:¿;U_O0!/G999ظq#BBBF¡[·n5j”Ò«LaaahÙ²e…4¾r¹ xùò%òòò`ff†:uêÀÖÖVé—-##:Àˆ¾$¸Œ-¿_úxrN_¿cUYu$Qå袢¢0tèPäåå¡}ûöHHHÀ¥K—àéé‰5kÖ¨¼ó°¢òª~ò€ 9U ú’à2¶¤o'xpP]NðDGùIOO×ÙIWbŸP¾d2¼¼¼àçç‡9sæHwÁ`Æ X¼x1ƇåË—ymEô_úvò€ 9q`ËA‚ÞŸ<¨WC*ê‚©©©ÈÊÊ‚‰‰ jÕª33³b×9þ<ºuëÆ¸Vã¸Vdlõ¥¬úÒ'„……aÒ¤I QÚ¦&&&bÒ¤IÈÊÊ®]»žÄ“Lȉˆ¨Ї-¯0°¬úx!;;Ëå¨U«òòòðòåKXZZÂÕÕUåƒG+*qd\[})«>õ ;wîĹsç°nݺbëóÙgŸaÿþý8tèô 5ž<¨üŒA•f0«ì5Fꪈ×DPõVPP€„„„R¯ß°aC±ŠP6°½{÷n‰[] ‡\.G`` Â ÁØØ3fÌ€ŸŸ&Mš„~ýú$èšL&Ø1c0jÔ(¥Åÿ÷ÿþŸÊ"ËZºþþþ%ž@¨ˆ±Á;w`kk GGG…éñññ¸yó&ìììиqãJñSÆ•±Õ§²êSŸ`aaQì+|ÀÈÈ+W®DóæÍáí탢]»v:Ï"##Ñ©S'•ß;;;ìÝ»Ÿ}ö:wî¬pò ºªÒ ¹>% ùùù¸té T¾Î¨8]»våɃ*P^}*krr2abbRª'§¦¦²-¨"Ǭ¾ lõi OE}*«¾@ÈÌÌ…ïÀÁÁ666ˆŠŠBxx8Z¶l cccƵŠÅUßb«OeÕ§>ÁËË ÈÌÌTùšÖBþþþ°±±AŸ>}°gÏÿ4LŸN0!g ÀÄĵk×FíÚµ‹¼Ê¨²Ñ§“úV^}*«½½=:wîŒnݺaåÊ•l ªéq O[^a`YõíBVVV±ƒi´jÕ >Dhh(Ú´i£ÓW42®Œ­>—UŸú„zõêaêÔ©9r$öìÙSb_êë닚5kbРAÈÍÍåÉ&äGŸ¨_¿¾Î¿4Uý侕Wßb;lØ0<}ú”mA5>ôi`Ë+ ,«¾@022‚\./v4iÒæææ C›6m`ii©ó;RWÆVßʪo‰ã¼yó0zôhœ={={ö,qù>}úàï¿ÿÆ€xò ’3¬ê6l˜Þ”ÕÚÚºÒüÖR“úDŸÊ«OeíÛ·/Ú¶m˶ š l&=°E×Û× %.ïëë‹-[¶`РAHIIÑù@ñĉÒ% ×­[‡>}úàܹs:(êSY5=ðÙgŸÁÛÛׯ_ ûŸ\Õ®]/^¼€L&+qY{{{¸¹¹áæÍ›HKKÓùÝ(Œ+c«oeÕ§>¡°Ïܲe‹ZÉx¡wÞy/_¾¬“–––8{ö¬ZËž<¨¶¯mU\DD„زe‹ íÊÌÌ ,/c˶ šyyyââÅ‹¢  @­å“““Epp°HMM—.]Òyyår¹9r¤8yò¤Úëœ9sFÔªUKçeýæ›o„ÈÏÏWkù£GŠzõê KKK–µÏž=NNN"##C­å÷îÝ+êׯ/Ξ=+œu^ÞÇ‹ððp!—ËÕZ>%%E‹sçÎ1®U ®ú[};ô©OÐ72™ŒAP_{FDDeƒ—/_¢U«VjÝ~þü9"##!—Ë+äõAr¹\zÐP%?aŽÑ£Gcâĉj_9{ö,  ó+"úTVX¶l.]º¤Öí”pìØ1Œ5 ¹¹¹HOO×yyïܹ{{{XYY©µ|ZZÂÃÃuþýb\[}<ô¥O ª‹ 9U«­>ѧ¢>•UßN è Æ•±åq@Ä„œˆˆˆ¨J@`\IßbË〈 9Q¥ÆSWDDDDDDDLȉˆˆˆˆˆˆ˜r"""""""&äDDDDDDDÄ„œˆˆˆˆˆˆˆ 91!'""""""bBNDDDDDDDLȉˆˆˆˆˆˆ˜1!'"""""""&äDDDDDDDLȉˆˆˆˆˆˆˆ 9r"""""""bBNDDDDDDÄ„œˆˆˆˆˆˆˆô,!—Ë刈ˆ@FF÷!==B0DDÄ„¼úè# 0€A$"¢JáÁƒX¶lúõë‡:¨·¬{{{#,, ~ø!Ö®]‹cÇŽ¡fÍšÜÛDDDz.33ï½÷j×®#GŽ N:j­7cÆ tïÞ @½zõø°W""ªp§OŸÆW_}…+V`êÔ©Òô*ñ”õºuëâèÑ£055Ÿq㠗˹ljˆˆô˜\.ǨQ£`mmƒªŒrwwÇùóç±}ûv¬[·Ž%"¢  ???lذA!¯2 9˜˜˜`Ïž=ˆŒŒÄÚµk¹×‰ˆˆôØwß}‡˜˜ìÚµ«Ô?GsrrÂáDZpáB„……1¨DD¤sùùùðóóÃgŸ}†áÇ™_¥ÞC^»vm¬[·Ë–-ƒL&ãÞ'""ÒCX±bÖ¯_ ‹2mËÝݳgÏÆòåËX""Ò¹}ûöÁÈÈsæÌQ:߸ªU¸{÷îxøð!ŒŒŒ¸÷‰ˆˆô±±1žiÒ$ÈårÄÇÇã“O>AÛ¶máé鉀€¤¦¦–[¹ˆˆ*‚‘‘¦NŠ:.^¼(µ]¯·‹B«*‡ÿþû/ѧO4kÖ ýû÷ÇÚµk!—Ë•ö…íýÇ1aÂ4oÞíÛ·GPPòóó÷îÝÃøñãѼysx{{cíÚµEÚvmoOÓú¼ÎÌÌ ×®]ƒ¿¿?Ú´iƒÞ½{ã›o¾)v_¨R–¾­4}}yŒ´ÑG—Ǹc= ˆ¨RËÎÎÑ¿ÿ—?r䈴ü?ü „B.—‹=zÂÈÈHܺuKZ~Ê”)€066wîÜ‘¦‡‡‡‹fÍšIÛzýßèÑ£…L&SøÜóçÏ [[Û"Ë:88HOœ8Qí²;VÙž———HMM-±ÎeÙž&uWõÙê–këÖ­ÂÎήÈçxxx‰±¦û„ˆ¨2öY>>>Ò2¹¹¹EÚÅË—/ …ö[!nܸ¡² lÙ²¥BßVhþüùJ—ý_×®]•¶ÏÊÊ'¼¼¼TnkàÀB.—+ÝÞ–-[Dݺu‹¬3iÒ$ñï¿ÿ ++«"ó>ÿüs•ýŠ6¶W–útìØQ)§ºý´6ú¶ÒôõÚ7hkì íqÇúWȉª¬_¿5jÔ€L&ÃÌ™3!„@LL ~ÿýwÀ'Ÿ|‚æÍ›òòò0tèPܽ{ÖÖÖXµjŽ;†¹sçÂÀÀ[·nÅž={®XŒ9III ½âì“O>)öŒmq6oÞŒ¦M›âË/¿Ä7ß|À… °fÍšrÛž¦u/«Ñ£G#//&LÀgŸ}+++À7pôèÑ +QyÈËËCXX AƒJß!?dÈ<{ö D@@ „œœ© €>ú¿üò ÆŽ ˆˆˆÀðáÑ——'mçìÙ³X¶l`„ HMMELL ºví hذ!Ö¯_ÀÀ@¥eUV[[[dddÀÜÜ&LÀúõë±víZ©OÙ¿?Nœ8¡t{cÆŒ‘‘0iÒ$¾†ÿúë¯èÝ»7ÌÌÌ0sæL©N°jÕ*¤§§—ÛöÊRŸ+W®ÀÍÍ­H¿¬v?­í¾M›cM¶¥Ë>ZÝqǼBNDåpµÁÊÊJtíڵȿÿýW­³ÐŸþ¹4ïСCbæÌ™€°µµU8Û»|ùr@ŠK—.)lÃßß_:u’¦­X±BÚî”)SΦ_»v­TWÈ}||Dff¦4/66V˜šš ÂÚÚZäççktFYÝíiZ÷²^!÷ðð111Ò¼íÛ·Kó–.]Zê}BDTÙ®?yòDŒ1Bš?}út¥í"±k×.…uƒ‚‚”¶o^ÿöÛo¥é .”î{½ý––ŒŒTÙn++‡BܺuK$%%)L “Ö Pº"ÈZ IDAT½¶mÛŠØØXiÞÔ©S¥yo¿ý¶HLL”æ 6LšRnÛ+K}´ÑOk£o+M™´9nÐæØA›ãŽx…œˆÊAjj*‚ƒƒ‹üKIIQký H¯ÔøôÓOñÛo¿–/_Ž:uêHËmÛ¶ À«òtîÜYa½{÷ܾ}[úíÑ©S§¤ù_|ñ…ƒÜlmmKUWoooéÉñðÖ[oI¯Èyþü9ž>}Z.ÛÓ´îe5~üx8::Jÿ/üm%…:êº\DDeuòäI4jÔÎÎΰ²²‚££#vìØhܸ1–,Y¢t½¡C‡bÈ! ÓvîÜ 055Å'Ÿ|¢0/00FFF Ë@LL  N: í¿½½½ôwll¬Êò++´jÕ õë×ðêAu÷îÝÃýû÷¥+ÔÑÑÑJ·7nÜ8¼õÖ[Òÿ;vì(ý=qâD…þòõ'l«*£¶¶WÚúh£ŸÖvߦͱƒ&ÛÒe­î¸cýÁ§¬é <¸Èô¦M›ªµ~Íš5±~ýzôîÝ[ºí¯cÇŽ7nœ´Œ‘‘‘R#ݺuk…mdddxõ€””ØØØàÎ;€úõë«|-›¶ê_(::ÎÎÎZÝž“““Æu×6KKKéïÂÇ”fŸU´œœ<~üXaš……¨²­êÒ¥‹Âÿ…ˆŠŠ8;;+œ@kkk8::"::ZêÛ ]»vزe RRR†¶mÛ€Â-Øîîî*Ëÿf9 Éårœ$­´ÛÓV}ÞìW=zTl?­«¾M›ceÛªè>ZÙ¸c&äDTNZ¶l‰•+W–ioþ­k×®ÒYðÂŽ¹ð ­yyyJ^xFÞÂÂðâÅ‹"Byxý îëeÖÖöJSwmSöš¸ÊP.""MyzzbþüùRÛÔ¨Q#¸¸¸ÀÌÌL£íÈåré·áªÚ¸Âþ'''BÀßß¿ÿþ;nݺ…~ýúáã?FZZšôûßQ£FIW†Õ%“É0~üxlݺààà€qãÆÁÓÓ³fÍBVVV™Ú{uæis{ڬϛýª±±q‰ûU}›6ÇʶUÑ}´ª}˱r"ª„222¤[ý4h€„„¬]»ãLJ‡‡€W¯¥±µµERRÚ¶m‹ .”¸ÝFáæÍ›ˆ‡\.×J²¬Ì£G¤¿[´h¡õ핦îºPYËEDT{{{øùùi¥ ´³³Cbb"bbb¤„ûõÄ£ðöt{{{i^­ZµðŸÿücÇŽEBB.\(moÊ”)¥:ÁýóÏ?KÉëÒ¥K±`Á)ñœ;w®Þí#m×çõ~µðA±Ý·isì l[;PYñ7äDÕÈ—_~‰ØØXXYYáÂ… °²²BAA&Mš™L&-×£G@HHnß¾]âv]]]¼º2ñú{3W·BiCrr2Ž;&L(ë­Uª¶§iÝu¥²–‹ˆH¼½½)))ø÷ßæ9r/_¾ôìÙSš‡©S§ÂÆÆOž<Á¹s猤¤$üüóÏ¥º««ð³k×®Ï?ÿ\J^…ž³»²Òf}^ïWíííÕê§Ë»oÓæØ¡¸mqì@Lȉ¨D·nÝÂ?ü˜={6\\\°téR¯^]òú+< ¯¢Ëd2 <!!!Ò¼§OŸâ›o¾QHà'Mš$ý=uêT\ºt ÙÙÙ8}ú4ÞÿýR•wõêÕØ·o²²²pïÞ=|øá‡ÈÉÉÌ›7¯Ü¶§iÝ_Ð=yò¤ÜöŸ¦å""ªJ/^,½"mâĉ AAA‚ƒƒ1eÊ€™™-Z$­sòäIdee¡fÍš°°°@·nÝàååkkëR—£F^ÝqvõêU© öõõUùz²Ê¬,õY³f öï߯V?­ª¯Ôvߦͱƒ&ÛâØÊ‚·¬UBLŸ>°±±ÁìÙ³Ó§OǦM›†Ï?ÿ¾¾¾pqqA×®]±téR,^¼QQQèܹ3êׯcccÄÇÇxõþÖÂw›öéÓ={öÄ¿ÿþ‹èèh…áÔ¨Qæææÿ-::ƒ *2½]»v˜6mšÆ1Pw{šÖ½iÓ¦066FAA¾ÿþ{Ô¨QË—/×ú>Ô´\DDUIóæÍñÍ7ß 00?FçÎadd¤L¬X±Bºc xõôð„ÇÚÚ5‚…… `mm '''Œ1ýúõSû7ÛÄŸþ ¹\.µÃ…=³¶¶ÆóçÏõ*®e©OBB|}}•ö«S§NU˜¦ª¯Ôvߦͱƒ&ÛâØÊ‚Wȉª-[¶àܹs€… JO[566Ɔ ```€¬¬,L:Uz`É_|ãÇ£Y³f^ݪ333Lž<Ý»w—¶o``€ƒbâĉ qqwwÇÅ‹–Uׂ ЫW/éÿFFF;v,Î;']%)¯íiRw[[[éw‰VnûQ“rU5sæÌÁ‘#Gиqc’q777œ8q3gÎTXÞÓÓãÇWH°nß¾[·náìٳغu+ú÷ï/Ý=¦ŽáÇcþüùÒkÖ’““ѦMœ9sFºR¯OJ[Ÿš5kâÎ;j÷«Åõ•ÚìÛ´9vÐt[;Pi¾xŽˆJðüùsܹsõë×GãÆ‹}rjvv6ÂÃÃáàà€† jô9G•Þñ¹fÍ|üñÇHHHÀ£Gàîî®ñS@µ±=uëþøñcÄÄÄÀÍÍ 4¨Tû„ˆ¨ªIHHÀýû÷áææ;;;¥ËìØ±cÆŒA§Nðý÷ß#11ùùùÈÌÌÄíÛ·±fÍäææÂÍÍMz­šºRRR‰&Mšè¤Í/oe©Obb">|¨V¿ZR_Yš¾M›cmm‹cÒ÷•ÈÚÚ]»vUkÙš5k¢S§NeþÌÂ+ 4ÐJ'UÚí©[wggç2¿½¼ö QUSR[.„À¢E‹ “ɰ`Á¼ýöÛE–¹~ý:Nœ8Qª7ƒÔ«W¯JµÁe©Ê“"šö•eíÛ´9v(˶8v Mð–u""""ªR„HII|Xú)—²ß 1!'""""*Í×Ð}ô`Æ pttÄ|€Aƒ¡eË–èß¿?rssáããƒ/¿ü’#¢ c´dÉ’% U¹¹¹ÉdèС¼½½¥÷T–í‘þèÝ»7lll‡ ** ‘‘‘€nݺaõêÕ ïÞ&Ž8n ŠÀ‡ºQ•VPP€äädÔ©Sæææ 1!'""""""ªÎør"""""""&äDDDDDDDLȉ¨ HLLdô€III Ûkzƒ\.Ç;wpíÚ5äçç3 DT¥ÆOLȉª° .`þüùzSÞÖ­[ãÒ¥KòÙ[·n…¯¯o…Õ=%%~~~Ëåûöí9r¤ôÿ””Ô­[—‰Žöç›ñ§ŠÿNPÕ?1!'ª‚ÒÓÓqåÊxyy1zfäȑشiAÄöºBdffVØg'$$ 99mÚ´Q˜Î«µºÙŸªâÏf2?•+cî>¢ªçÒ¥Kðòò‚¡aÑsnGŽ··7LMMqîÜ9:tyyyèØ±#†®tÂmž;w±±±hÛ¶-úôéµÊ“››‹;wâÆ033Cûöíáããƒ5j(]^¡vÙ²²²pôèQ„„„ 33íÚµÃ!CP«V-•å Á™3gðôéS´k× €±qñÍáÅ‹qúôi$''£mÛ¶³fÍB­ZµÐ¹sg\¼x:uÂÝ»w•Æ7>>¾¾¾¸yó&Z¶l µã`nn¾¨š¶×7oÞ,vP¹sçÎ"/Ò¤RÕFÅÅÅáÔ©Sˆ‰‰A\\®^½Š«W¯âÎ;}޵µ5‚‚‚ð×_)ûÌ™3ñøñc•uÛ±c‡”p§¦¦âÔ©SHIIÁ£G¤ò”TÒô QQQèСîÝ»‡÷ß 4À’%K°|ùr„……!,, 2™Lá³=z„‘#GbãÆhÓ¦ LLLàíí={ö ''£FÂþýûÑ¡CÈd2¼ûî»Ø»wo™úMúouögqñ/Ml4©KiÆºŽ»º1Tu,FFFâã?VJÆ«Óø©¬c'½? "ªr  .^¼¨t^«V­DóæÍŶmÛ¦§§§‹zõê‰Ë—/+LŸ>}º8p ÈÌÌT˜*ìììÄùóç‹-ËçŸ.&NœXdz\\œÒ²5nÜXí²íÙ³G<~üXaš\.~~~⫯¾*²ý™3gŠwß}W¼xñBazRR’xÿý÷ÅÀ‹¬3kÖ,1vìX‘——§0ý?þÞÞÞB.—©CÿþýÅþýûK‡1cƈC‡ñ@&ª†íõóçÏ…¹¹¹ˆWº|÷îÝÅáÇKÝN©j£ }òÉ'bñâÅJç©û97nÜ 6)))Ò2ÇŽ^^^B&“)Ýöýû÷…——W‘éÆ ›6mRÚ_¨ª‡&}ƒ\.:u[¶lQ˜žšš*\]]Å;wTö£{÷îU˜¾k×.áââ"z÷î-þùç…y›7oÍ›7/Òghºï4é¿KÚŸ%Å_ÓØhR—ÒŒ-**î%ÅPÕ±˜‘‘!¬¬¬Äƒ”®7xð`ñûï¿sü¤¥±“>Ÿ˜UA:u=RÙqìÚµKé¼ &ˆÕ«WKÿ¿qㆨS§ŽHJJRºü† „‡‡‡Ê–BŒ5J¬ZµJ­rkR¶â>|¸ÈÀâöíÛÂÜÜ\DGG+]gË–-E:”ˆˆáìì,ÒÒÒŠ,/“ÉDóæÍÅÕ«W‹Ôa„ eŠÃܹs•@‰¨z´×Æ ?üðC‘e£££…ƒƒƒÈÏÏ/u;¥ª*)ùÐôs–,Y"Æ/„";;[´lÙRܾ}[å牵k×j”Wuû†Ç ›" ›B|ñÅ"((Hégÿõ×_E¦ççç 333¥órrr„¡¡¡xþüy™ö¦}¤º ¹²økMêRÚ±EEÅ]„\Õ±8cÆ ±hÑ¢"Ó_¾|)¬¬¬Š$¸Õuü¤±“>ŸxË:QTÒSi•Noذ!ž>}*ýÿСC:t(êׯ¯tù &àÁƒˆUùY½zõÂúõ닽M±4e+ŽM‘eÿùçøúúÂÙÙYí88p>>>¨]»vÑßû¢K—.ˆˆˆP—²Ä¡nݺJo#¢êÑ^?[·n-²lá°_ÿÍfiÚ)em”¶ÛÃùóç#44'NœÀ²eË0tèP´lÙRÕݚصk† ¢Q™4­‡²¾áéÓ§pppPzÛpÆ qÿþ}•}Ò›Œaoo¯tž©©)ìììúËÒì;mô‘êÆ_“ØhR—²Œ-**î¥=§L™‚?þøCá'…c’ž={ÂÊÊŠã'-ôyüćºUA077×x=sss¼|ùRúÿ;wо}{•ËרQM›6EDDœœœ”.3vìXÄÇÇ£cÇŽèׯСCŸšûfÙ^w÷î]lß¾W¯^E||<ŒŒŒ““£°LDD„ÆO޽}û6,--±{÷n¥óÓÒÒ­4.e‰ƒ¥¥%r¢jÜ^÷êÕ 'NÄ;wТE )iÚºuk‘ß—¦Rõ@(m¶‡&&&ظq#† ‚ºuëââÅ‹*·}óæMØÛÛÃÖÖV£2•Tuú†¶mÛâñãÇÈÊÊ*²ÂÂÂTžD(®¯*n^nnn™ûMûÈ’¨Š¿&±Ñ¤.Ú[T–¸—t,¶iÓ 6ÄÉ“'Ñ»woiú®]»J|Õau?•uì¤Ïã'&äDUP:užžkkë2m'11QåìB¶¶¶ˆW9ßÀÀóæÍÃôéÓñ믿Â×× 6Äwß}‡nݺ•©|qqq5j 1~üxLž<xðàA‘mÇÇÇÃÛÛ[£í'$$ÀÚÚZå’Ž;âwÞQk[šÄáåË—Åž5'¢ªÝ^a̘1ضm‚‚‚¤ÈÔÔ´ÈÀX›í”¶ÛC+++¤¥¥¡]»v055U¹í;vhõÝךô 5kÖÄ!C0oÞ<¬^½Z臅…aïÞ½¸víZ¹í{]í»’¨Š¿&±Ñ¤.Ú[èSܧL™‚ßÿ]JÈÓÒÒpñâÅŒËñ“æ1Ð×ñr¢*ÈÚÚ©©©eNÈ]]]ñèÑ£b—yøð!š6mZâ¶j×®9sæ 7nÄûï¿£G–úݻϟ?G÷îÝ1þ|øûû—¸|Æ 5îÜ]]]áää„Ï>ûLkûF8¼xñBí«DT5ÛëqãÆ¡ÿþøê«¯```€mÛ¶aܸq:i§´ÑÊårLž<;vìÀâÅ‹±gÏ|øá‡J—Û·o,X •rjÚ7À²eËШQ#„††¢W¯^xúô)NŸ>mÛ¶ÁÞÞ¾ÒÄ´<”uc£I]´9¶Ð‡¸:óæÍCJJ êÕ«‡}ûöaÀ€Ò«ü8~Ò^ ôuüÄßUAöööxòäI™·Ó²eËbo3LLLÄ£G¤[*Õajj*½cÓ¦M¥.Û¾}ûP¿~}µ\7Æ74úŒV­ZáÂ… 岊‹Clll¹‰¨ò·×-Z´€­­-.]º™L†Ý»w+½ŠYžíTY>ç·ß~CÆ ѧOlܸ³gÏVz+é… àîî®ô·¦ºè ÿ÷ÿ‡ 6ÀÍÍ £FÂõë×5¾*XÞ1-%Å_ÝØhR—ò[Tæ¸×¬YÆ ömÛ¼º]½4w„püTr ôuüÄ„œ¨ êܹ3BCC˼#F $$Dáݯ¯ûúë¯1räÈb¯ÄËår¥Ó‘––Vê²åç竼å-**ªÈ´áÇãŸþQù:eïf9r$BBBpùòå2ÇR“8\¿~]å{䉨ú´×ãÇÇöíÛqæÌxxx(mó´ÙN¯n¡ÏÌÌ,ÓçÄÆÆbÅŠøþûï¥Á¹¿¿?fΜYdYmß®®iß——‡cÇŽaРAhÕªFމ=zÀ¢Ü÷½¶÷&ûSøkMꢱ….ã^R Õ1yòdüöÛoHMMÅ­[·Ô:ÙÃñ“æ1Ð×ñr¢*ÈËËKeG§ kkk|÷Ýw4h®\¹"MBà믿Æ_ý…+V¨\?-- îîîøûï¿!„¦?þk×®…ŸŸ_™êxþüyÀo¿ý†Ö­[£Y³f¸ÿ>nܸµ>gçÎHOO/rÁÄÄ¿ýöüüüн{wÔ­['NœÀ;ï¼SìßtÑ7|÷Ýw9r$fÏžœœÀÐж¶¶ðôôÄ¢E‹Ð®]»rÙÿÚÜwšîOuâ¯Il4©KYƺŽ{q1ÔÄ”)S0zôh„„„”¸,ÇOšÇ@ŸÇOâõSDTe ÿþZ»ò „@LL ž>}Š6mÚ V­Z­{÷î¡V­ZhÚ´©Ön|ñâ"""`ffwwwµN@äææâöíÛ(((€»»»ÚVILLDTT5j„† –jTR|||°lÙ2´jÕŠ1Ûki£’Ëåxðàž?¥m¤6>gìØ±?~öíÛ‡ÀÀ@¼xñ“'OFPPP…ß¶“ 9Q¥“””„àà`ÄÆÆÂÂÂnnnhß¾=ÌÍÍƦT„ˆ…­­­Vß$@Ur""""""¢ Àß1!'""""""bBNDDDDDDDLȉˆˆˆˆˆˆ˜r"""""""&äDDDDDDDÄ„œˆˆˆˆˆˆˆ 91!'""""""bBNDDDDDDÄ„œˆˆˆˆˆˆˆ˜1!¯ÙÙÙèß¿?®\¹Â½FDDDTŽ&OžŒ;v0DDLÈ_ÉÏÏÇáÇ‘˜˜È½FDDDTŽN:…¨¨(‚ˆˆ 9r"""""""bBNDDDDDDÄ„¼RÈÏÏÇ “ɸw‰ˆˆˆ^sûömDDDhm{ÇÇ‹/X""?~Œê“oÛ¶ óæÍƒ‘‘÷>Ñk‚ƒƒ1vìXÈåò2oëÆðõõ…KD¤Btt4ˆôôôªŸß¾}Xµj÷<ÑÆ™L†… –i;/^¼ÀÈ‘#±xñbXYY1°DD*ôèÑ]ºtÁ„ ”ž ­2 ytt4úõ뇅 ¢W¯^ÜóDDDDo011Á?ÿüƒÍ›7—úFFFˆvíÚaîܹ *Q þïÿþ<@@@„U/!¿|ù2Þ~ûmŒ5 ³gÏæ'"""RÁÑчÂòåË1wî\n_E÷îÝQ»vmlܸ‘·«©¡V­Z8xð NŸ>¡C‡";;[šg¬ï•[µj,X€.]º ##ü1÷8Q úöí‹Í›7ãÌ™Hæ¼ IDAT3øßÿþWâ­çGÅðáÃáââ‚F!00A$"Ò@÷îÝqôèQ´nÝG…›››þ'ä<€©©)’’’••ŽLDDD¤¹\CCC<|øyyy%.¼¼@›6m°cÇÔ«W¯Äu&L˜€ÆcÈ!6l‚‚‚H""5eeeaäÈ‘ÈÌÌÄÙ³gѸq㪑À¤I“àêê ___˜››ÃÇLJ{œˆˆˆH‰—/_¢oß¾ðððÀo¿ýccõ‡ƒ=zôÀÅ‹ñÞ{ïÁÂÂóçÏg@‰ˆJPPP€áÇ###çÏŸGíÚµ¥yUæ)ëÞÞÞØ¾};Fëׯs¯½A¡C‡ÂÑÑ7nÔ(/Ô¤I9rßÿ=þþûo•ˆ¨Ÿ~ú)bcc±ÿ~…d¼J%äпòIëDDDDJìÝ»÷îÝÃŽ;`ddTêí4oÞüñ¦NŠœœ–ˆH…ððplÞ¼û÷ïG­ZµŠÌ7®jž={6„ÉdeêhˆˆˆˆªKKKüôÓO°°°(ó¶ /„<þ .‘ùùùX·n•ίr y­Zµ°xñbîy"""¢7ôîÝ[«ÛûÏþàÃÓÓžžž*ç2DDDDDDDDºÇ„œˆˆˆˆˆˆˆ 9òÊUPCCØÙÙÁÌÌŒ{ˆˆˆ¨Õ¯_––– Q93B†ˆˆˆˆˆˆH·Œ"ÒDAAV®\ hÒ¤ †  ¨A.—ãÈ‘#8r䢣£a``€ÆcÀ€xï½÷``` ×õ»ÿ>þþûo„††âùóç°µµE×®]1|øpÔ­[—ÇU ÷ïßǼyópíÚ5ÀÃÃ?þø#V¬X´´4,]ºM›6„††âË/¿Lœ8|ðX ñ 9i$''5kÖðê´dPJpïÞ= >¡¡¡JçwéÒ;wî„“““ÞÕ-;;Ÿ~ú)~ùåÈåò"ó°uëVx{{óø!"ªDnݺ…Ý»wãÖ­[ÈÌÌ„‹‹  ¤7'‰_?Áûºš5kâ­·Þ‚««+<<þüb—ñôôDPP”ô–·Õ«WKÉøìÙ³1xð`¤§§ãÙ³gÒ2®®®<‰ 9‘®øûûKÉxïÞ½ñ×_¡víÚ€gÏžÁ××ÁÁÁxúô)f̘ݻwëMÝæÍ›'%ã5Â_ý…:Hó###ˆõë×3'"ª²³³Ñ³gO\¹r```€V­ZÁÉÉ ×®]Cbb"víÚ…>}úà£>Ò›zÕ«WmÛ¶…éé鈎ŽFrr2BCC1`Àœ|áááˆŽŽ†««+úõë‡éÓ§ÃÐPùË$d2~ÿýwüûï¿GýúõѱcGÂÖÖ›7oÆÙ³g_}õìííÖ¿qã~üñGÀôéÓáééYä3BCCqüøq)a=pà€BçlccƒcÇŽÁÅÅÉÉÉØ³gîÝ»777¯n'\µj`Æ ¸~ý:~ÿýwÃÎÎ=zô@`` LMMKïÂÏ000À† ˜˜ˆåË—ãÌ™3044„——‚‚‚`ee¥°ýØØX¬[·`nnŽ .‰QóæÍyK:Q%2wî\)oÑ¢öíÛ'ýžYU«VáÙ³gz•ŒÀÛo¿­ÐßÈd2üú믘6mär9–.]ª“„<)) P»vm…þ¼¼¼x’r‚ˆHÙÙÙ€ ú÷ï¯Ö:ááá¢Y³fÒz¯ÿ=z´ÉdÒ²óçÏWºÜëÿºví*„"..Nxyy©\nàÀB.—)ÏÝ»wEÇŽ•®Ó°aC‘••%Ž9"MûᇊlcΜ9€011/^¼PZï/¾øBÚÆêÕ«UÆgÉ’%Òrß}÷4ýõ2Œ;V)¯———HMM-u¼_ÿŒ­[· ;;»"ëxxx(¬#„?üðƒ4áÂ…åzüQÙÅÄÄccc@Ô®][<{öL£õ³²²ÄòåËÅ| š5k&|||Äÿû_‘““£´ßŸ8q¢ð÷÷r¹\Ü¿_Œ1B4mÚTL›6MZ.##C‰¾}ûŠ-ZˆaƉmÛ¶)í»KÓŸtèÐA¦¦¦Ò6Õ-›&u¾qã†øôÓOE½zõ¤±ÁĉÅĉÅãǥϜ8q¢øßÿþ§´V6Ö(kŒH?0!'¢rMÈsssE‹-amm-V­Z%Ž;&æÎ+ ±k×.!„gΜ‘¶=a‘šš*bbbD×®]¥dyýúõbïÞ½B! „»»»077&Lëׯk×®...Òv^ïø„"--M4jÔHšß£G±fÍñí·ß '''ñóÏ?KÛvvvD‡¶!—Ë…›››”ô«Ò¯_?ésnÞ¼©r¹×ë=lØ0¥5Ѽysñå—_Šo¾ùF¡Ž_~ùe©â­ì3êÖ­+&L˜ >ûì3aee%M?tèB™ $Í»|ù2r"¢Jîûï¿—Úß%K–h´î7TžèmÙ²¥¸uë–Âò¯÷-—/_666 '˜ cuO—¶?ñññ‘–ÉÍÍU»lšÖyË–-*/\¿~]eâ]RB^Ör"bB.–/_.CCCqéÒ%…yþþþ€èÔ©“Bˆ…  ÂØØXdffJËKŸ©°[·n‰¤¤$…iaaaÒò ó¾úê+iÞ¨Q£DAA4/33SáŒóëËÞ½{Wš!Mß¹s§Êº·oß^Z.--MårÑÑÑÒrÞÞÞJ;j…˜ÄÆÆ SSS)ñÎÏÏ×8Þo~†‡‡‡ˆ‰‰‘æmß¾]š·téR…muîÜYš—’’"M—ÉdbÓ¦MEþÝ»w 9QzýDê•+W4ê÷_O ?úè#ñË/¿ˆ±cÇJÓZ·n-%¼oö-NNNÒ ì€€±wï^O—f<’››+}vƒ Ô.[iê|õêU1gÎakk+3331gÎ1gÎ[ª„\1"&äDÄ„\´iÓFºµúM»ví„………Ëåb̘1€¨W¯žÂr>”>óĉ*?«  @DEE‰Ý»wK·w0@awww)a‹‹+¶ì±±±ÂÈÈH‹/–¦/[¶L*÷ëIò›Z·n-‘——§r¹gÏžIõëÒ¥‹ZµBøúúJó£££5ŽwIŸ%Í›áüÍò8;;yÙ›Þzë- 0°}ûv!ÿüó`àÀ077W¹¾µµµô¹qqq*—‹‰‰‘þ®W¯žÚñuqq‘þŽŽŽÖ8Þ%±´´”þÎÍÍU˜W·n]©n±±± óœáì쌆 òK@DTIdffxõdõ·}¨cçÎSSS|òÉ' óadd¤°Ü›†Š!C†(LÛ¶m›ô0¶Î;+ÌëÝ»·Ôö»%9yò$5jgggXYYÁÑÑ;vìðêuœK–,Q»lÚ¨³6h;FTyñ)ëDTnär9òóóyyyHMMUšô€……üýýñûï¿ãÖ­[èׯ>þøc¤¥¥aÍš5€Q£F¡~ýúÒº2™ ãÇ—Þ¥êàà€qãÆÁÓÓ³fÍBVVV‘…ÔŒLž<û÷ïǽ{÷'''\ºt 0bĈb×mÖ¬™ô¤öÈÈH8;;+]îîÝ» ëhr£¡¡¡Æñ.É›O¿››Î;¸sç5jôªS16Ftt4€W¯u{}QÅyóDja»]R?àÕÉÖ7ßMnmm GGGDGG+ôe¯ëÒ¥K‘m¾yòøuÒ „””ØØØ”XΜœ<~üXaš……¨ro–M[u.«òˆ1!'¢jÈÈȶ¶¶HJJBÛ¶mÿ¿öî>(ªêÿøÛARE DTZ–@!M%E¢LÑÈLÓ2-k̨О°ÔÑ£§ÑÔ´ÇI|-±L@eXÃ@y64t…àüþð·÷˺‹ìÂò ó~Í0çž{î=Ÿ=,|îÞ{RSS›Üçí·ßÆìÙ³QZZŠ¥K—Jí,X°k֬Ѫ»~ýz)ŒŒÄ’%K`n~ëmMóipC2™ ƒFVVòòòPWW']ånLPPœqñâEÄÅÅA.—C[[[é ucÆŒƒ76n܈   ½õ¾ùæ­} •ŸŸ/}ïááѬx7׸qãðí·ß¢££ÌODÔ5¼š““cPB^__šš)ÁÕGs7•Z­†âŽs5mšòâ1 2Ò>ýúõƒ«««Ö’©†j>7çL#ê¸xË:µªÑ£G”J%²³³ïX·¸¸/½ôìííqñâE$''ãÈ‘#(//Çúõëµn¡€?þøÀ­O»ß{ï=)BH·¬ßNsË{UU•A·š™™™á…^ÄÅÅI늇††ê¬1z»É“'K· ïÞ½Щ³cÇ:tàää„ÇÜ ¸^ºt ¿ýö wïÞÒÕqcâÝ“'OFïÞ½¿ýö6oÞÌÁNDÔ=öØcÒ÷_ýµAû˜™™ÁÁÁÀ­Ç«n¿=º¾¾^zìÊÑÑÑ ÄTsñ¼½½QXXØèW×®] :OGGG„††"44ÁÁÁðððhV2ÞZ}nÎ9˜:FÄ„œˆîA¥¥¥ˆÕû¥¹JóìU]]¦L™¥R)í_TT„+V ®®À­gÀ®_¿Ž®]»ÂÚÚ#GŽ„¿¿¿ô,öí:wî,%×R›!!!¨¬¬Ô»ÏÛo¿-}*¾páBlÛ¶ 7nÜ@YYV¯^­÷9³çŸ2™ ™™™Ø¾};€¦oW€®]»âÓO?•þ€O˜0‘‘‘HKKCJJ """0mÚ4©þºu뤋 ·‹ŽŽFll,®_¿Ž¿ÿþO?ý4Ôj5àÝwß•êï–°´´Äºu뤟çÍ›‡°°0ÄÄÄ@¥RáèÑ£øàƒZ<~ˆˆÈ4¦L™"=FØØX:UUUذaƒVàÖü.š áû÷ïǵk×tþ¦´ÕÅãæj>«£ÇˆLˆóÚQsgY¿Ó—f™+!„ˆŒŒÔÚÖ³gOáèè(ýüÝwß !„8~ü¸V½~ýú ¹\.<==ŨQ£Ä³Ï>+¤Â.Ë¥iWó½ † ¦Ó‡?üPk?ÍLêšïo_ZM!&L˜ ÕqttÔZ.íNêëëExxx“ñj¸–xc3ÁêûòññÑZjƘx75ÃkII‰´í¹çžÓÛ¿Õ«W7yŽVVV"55µÙ㇈ˆLãûï¿—Þg;uê$^ýuqàÀqêÔ)±qãFi™­÷Þ{OÚçÌ™3ÂÂÂB..."==]ÔÖÖŠ””ñÀHË|5|ßnj•””iû AƒDzzº´­°°P,_¾¼É¿³Í]µ£©sknŸ›3Ëzrr²TþÆo˜úè£&߃—.]ªµÏš5k½ @DGGôsñ¸­òæô¹9 yYY™077—¶½óÎ;&1!'"ÒQQQ!RRRDNNŽÖúÕBÜúÄÛÌÌL >\¤¥¥‰ØØX±}ûv±uëV.ºté"777­ýþùç‘’’"JJJŒ>ŸÒÒR‘””$.^¼¨7×hø©BFFF³ûá‘””$’““Eaa¡Áÿ4|þùçR’œššÚh"nL¼MíÊ•+B©TŠƒŠÌÌL¡V«9à‰ˆ: D\¡Pˆ¸¸¸Fÿ&õïß_«¾›››8xð`³“^C/·GBnlŸ›“ ¡}×^PPIcDw‡NBpñ:"êÏÀÍÍ yyyسg´þwC8xð ,-Ònܸ¹\Žüü|Œ1)))mrÜ_ýUš½|ݺuX¼x1 ™Lyy9rssQSSƒ~ýú4ózii)rssáææ&M~ÖR—/_Æ™3gгgOôïß¿ÑùTÚKkô¹¡ .   nnnÒ„©w[Œ¨ùøJQ‡IÈ+**{÷îEpp°4ùšû÷ï—–k™}{÷îØÛÛcüøñX´h‚ƒƒ!“µÍŠ^^^°°°@ÿþý1cÆŒ6OƱ`Á€B¡à!"""ºÇðr"êþûï?\ºt Ý»w‡••BDDDDLȉˆˆˆˆˆˆ¨åd r"""""""&äDDDDDDDÄ„œˆˆˆˆè®RVVÆ Ð=Kòòr‚ 9QÇ’ššŠˆˆƒëÿøã aà¨U˜b|yzzâèÑ£ÒÏ E}}=Ì„œˆˆˆˆ¨cBàý÷ßÇûï¿Ï`´¢ŠŠ ¸»»cýúõ F;°··Ç¨Q£ðË/¿0Lȉˆˆˆˆ:†¤¤$ 0®®® † $&&b×®]ºÉŒLXZZ2Híä­·ÞÂgŸ}®¦Í„œˆˆˆˆ¨Cؼy3fÏžÍ@˜Hff&NŸ>­Snkk‹cÇŽaΜ9 R;éÑ£ÜÜÜžžÎ`0!'""""j_•••8vìüýý ©®®f:°°°0lÙ²…hs†€ˆˆˆˆÈtŽ= Èdö¥T*qøðaÁÇÇ&L€¹ùÿ5OKKßþ‰K—.ÁÛÛS¦LA·nÝ´êìß¿èÒ¥ <ˆÄÄD€ŸŸ‚ƒƒuê7ܧS§Nغu+²²²Š1cÆulãÇ#99999èÛ·/¦OŸ®÷Öýëׯã×_…R©Duu5|||ðÌ3ÏÀÆÆFªSVV†ŒŒ dffÂÜÜ\zVùþûïG`` tþ~~~°··×ûZ$''£°°ÞÞÞ BŸ>}ôžwÃØ%''#!!555ðóóÃôéÓïøzÞÞ†……˜˜kkkL™2~~~nÍ/ Ùfkk‹‰'bÈ!MŽ)CûÑšã«1xã7øËß ü„œˆˆˆˆÈ„RSSáëë«w›Z­Æ+¯¼‚gŸ}UUUðõõ…J¥ÂèÑ£±oß¾F÷Y¸p!/^  6 iiixøá‡‘““£U7<<ÉÉÉ Ä_|899aË–-ð÷÷G~~¾Nûááá())AHHNŸ>|FûÆX´h¦M›†ŠŠ Œ19r¤Î¤_?üð¼¼¼°mÛ6ôêÕ C† Áž={¤}4Š‹‹qèÐ! ¸¸ÈÈÈÀ™3g´Î?77W'Ù_°`æÍ›µZ¡C‡âøñãðõõŶmÛôÆ9<<xñÅñå—_ÂÃà ÀªU«0mÚ4ƒ^ûððpäçç#,, ›7o†B¡€……°sçN¨ÕjÌœ9qqqðõõE]]ÆŒƒÝ»wëmÏØ~´öøjŒ••,,,¸Zs"""""2™ &ˆ´´4½Û-Z$ÆŒ#®\¹¢U^^^.žxâ 1iÒ$}/^,fÏž-jjj´Ê·nÝ*D}}½T&—ËEß¾}Åï¿ÿ®U·¾¾^¼õÖ[bôèÑ:íËårñä“OЏ¸¸{ñâÅ"00Püûï¿ZuÏŸ?/²³³µÊvîÜ).\¸ sŽ¡¡¡âã?Ö9×^{M|øá‡zc*—Ëuâ½páB1iÒ$Q]]­U~âÄ áàà RRRô¶ãîî.~úé'­òÊÊJqÿý÷‹ôôô&_{M»wïÖ*‰‰®®®büøñ">>^kÛ÷ß/ÜÝݵbÙÜ~´ÅøjllÏš5K$$$ð ÀHLȉˆˆˆˆLèá‡ùùù:åÙÙÙÂÊÊJœ?^ï~?üðƒN¤R©„‹‹‹¸zõªNýºº:áîî.222´¦¤¤$½íWUU‰ûî»O(•J$rîܹ:õ9vVV–°±±EEE-ŠÝ¾}û„¿¿‹òS§N‰îÝ»‹òòr½õ7lØ zè!QWW§ÓNLLŒÞ}æÎ+¢££ JÈ·oß®S^[[+,--õnS«ÕB&“‰Ë—/k•Û¶_%äááábË–-|0oY'""""2¡ŠŠ ØÚÚê”ÇÇÇ#$$...·µgÏLœ8÷ÝwŸÎ6™L†áÇC¥Ri•wîÜYo[ÖÖÖ ÂÑ£Gu¶Í;·EÇNHHÀ3Ïúô郼¼<Œ9²Eñ/++kôSe^½z¡¤¤¤C#cûÑÞãëÚµkèÑ£ߘµ;;;üûï¿: ¹“““ÑIàÀÑ·o_¼óÎ;&9·¼¼<Œ7ÎäÇ0`€ÞOÞõ¹|ù2}ôQDDD`Þ¼y&ÿÀõÎ&ßйsç0hР=ŽŒíG{¯+W®|ÇýŸ!'""""2!GGG\¼xQ§¼ÿþ8uê”QmÉår¤¦¦šä¼jjjžž¹\nòc{xxè}6]ŸØØXôìÙ³U’qxðÁ‘––Öèö²²2äççÃÃãC#cûÑÞã«°°ŽŽŽ|`BNDDDDÔ~† †'Nè”OŸ>ñññz' µ¶¶FXX”J%ÒÓÓ >~]]Þò­[·ÂÉÉÉà[Â9vXXŽ9‚ãÇ7Y·¶¶¶Ñ[±Ïž=«·¼{÷®6è¼g̘¥R‰ŒŒ ½Û—/_ް°0;:cûÑVã«1'Ož„¯¯/ߘµ½I”››ž{î9Lš4 ÅÅÅRyUUæÍ›‡={öèìÓ£G|öÙgxúé§‘””¤µíÒ¥KØ´i“Þ$k÷îÝBHe;wîDDD¢££!“–sl{{{¬\¹!!!:Ÿ”ïÝ»³fÍÒŠOJJ òòò¤²ššDFF"22Rï¹x{{#11555Mž·Ö®]‹É“'ãØ±cR¹Ë—/ÇöíÛ±jÕª?ŽŒíG[Œ/KKKÔÖÖê´sõêUXXXðòfà3äDDDDD&äçç‡^xµµµ:³—õÕWøàƒàîî…Bsssœ={+V¬ÀÌ™3­ÓÞ´iÓЧOÌŸ?2™ žžž(..Fqqû&•n IDAT1^~ùe!ЩS'©~TTvìØ7ß|r¹*• ÖÖÖ8tè¼¼¼Œê‹1Çž?>xàL:ÖÖÖ8p ²²²0`ÀlذAjS¡P`Ù²e>|8FމÊÊJäææ"<<ÉÉÉz—ízê©§°iÓ&xzzbðàÁÈÍÍÅ©S§`aa¡÷¼gÍšL:666pvvÆÉ“'1dÈœqâD¬\¹Òàù ˆ 9Q«9yò$V®\‰˜˜˜6=nÄœ¨-cÆŒ8|ø0ƒÑ |†œˆˆˆˆÈÄ|||P[[kô¬×Dw›O>ùK–,a ˜uË—/GTTA÷¬þùçÏŸçóã-À[Ö‰ˆˆˆˆZIMMM£µÞ²N÷ú¿×üæâÛϦþí5IEND®B`‚vispy-0.14.1/doc/_static/frustum-matrix.png0000644000175000017510000002353114507104216020212 0ustar nileshnilesh‰PNG  IHDRñkg¸»ìbKGDÿÿÿ ½§“ pHYs¯¯»ÇKtIMEÞ Ù¢V IDATxÚíyXTWò÷¿@Ó ¸¡‚0jEu€ÄŒYˆFŘĕg(î —Iæ1qÁqÅŒJ&F'‰qA0â B Í¢¨ ²hm°û¼ðöýÑÐÈÒÝÖçy|9·ïísªnUõ=÷œ*cŒ ‚ ƒÃ”D@DO"—ËabbÒ®s~øáãÍ7ßÄsÏ=×e}+++ÃÏ?ÿ X²dI»Ï¯©©Á©S§`mm   ðxmÚ4Ãâ·nÝBIIIc¶±±éö¾deeáÌ™3°°°@ff&lllðÙgŸ¡wïÞt„Ž;;;»vÓÔÔ[[[Œ9R£=nÛ¶ +W®Tû™_~ù¦¦¦˜3gŽÚ ^YY‰É“'·{\>Dzz:<<<““ƒ!C†´«o¡ÀÅŃ Rj‹‹‹Ã­[·Œc:ÝÍÍ ÑÑÑ=Ú¹\ލ¨(ÄÆÆÂÔÔŒ1øøøàƒ>ÀÞ½{é.$žIcÜ“fËÿ·ø@e{ë¾d{Äõˆ{÷îáÃ?Ä… 8Ç£;;;8;;#&&+V¬àVÉfgg+9%mŽã½÷ÞCXXçøÎG›C%t§ªª ÞÞÞHJJ➢bcc1{öl$$$Ð+Š3SŒ1¤¦¦bçÎ*ðÖ××ãôéÓ‰DÉd033\¹r¯¿þ:÷Ùääd¼öÚkœlSRR0mÚ4˜››âããñÒK/ÁÒÒ’»NËwÎ2™ ¦¦¦àñxmîù“'Oj\Ø–œœŒ•+Wr×mhh€H$B||<† ¦vüµµµ(**‚›››ÊkªêA¶×LèÖè\îܹƒˆˆœ:u #FŒ@jjªVg©©©JŽ$..~~~ÉdœãˆŒŒÄš5k°fÍ„„„p{kËÊÊŸŸ©S§rOÉÉɘ2e ššš”¾cÊ”)¤¤§`ûöíðôôTš Âõë×qîÜ9P rssaffGGG•ÇüñG¼øâ‹4hvïÞ͵4ˆ»gpâÄ ¥§áÖOÇß}÷fÏž«W¯"99píÚ5¼úꫜΞ×ÐÐÐF÷7oÞ„££#,--Û\·3úF¶G¶GA¼‹¦z"""ðþû¼:½sÎÍ͟χ““÷]RR‚)S¦`Û¶mZŸÏ‡¹¹97©TŠ1cÆàðáÃ:9TB7Ξ= WWWåé,SS 6ŒK B4sõêUøúúª}B1b €mÛ¶)M©¯_¿wîÜÁ¾}û°wï^üöÛoJA;;;>>>ÜßNNN¨®®FRR÷Cö…^@AA"""0}út¥wÚí™M;v,Ž;Æ=Q+žæ###qæÌ>|{öìÁĉ•ÎÍÎÎÆ /¼ òºÑ7²=²=Ntkt‰“'OÆýû÷•V¤‡††êäìüýý9g7`À¸ººbß¾}Ü´›ÂqÌ;åå娮®ÆŠ+ýû÷ÇæÍ›±uëV >R©ÇGLL <==ur¨„vÊËËQPP òéÊÚÚš{ $š Fpp°Úã^^^pwwGï޽ѫW/¥'q… eddÀÖÖVéýqNNŽÒ;ÐmÛ¶¡®®Ni…÷¦M›PWW++«vgƒS ð¿ÿýþù' ÀµûûûÃÇÇ2dˆRß»>²³³ñÖ[o©¼ngôll‚x'Ó»wo”””tØÙ)VÙ*œØ…  —˹wÜš„……¡¶¶|>K–,D"á›6‡Jh§ªª T.¢±²²B~~> ©Z?cee¥ôwff&vî܉S§NA.—c×®]X·nÒOUòWµEK—m[nnnml©%<O)€·|:th›vìß¿yyysWЖ2²= âFîìø|¾ÎŽCAËÕñfffJŽB‡JhF±ªYñê%}úôA]] é)±¶¶Æ_þò;v ÷ïßG`` fΜÙeß§jáÙÓ°råJ¤§§ãóÏ?§Ld{Ä BŸP¬†VUøO±÷žx:F…]»vlÿ¯¸²½î‚^È´ <<<þéøOU*@cF1³!•JÛ«¯¯§)R²%²%²=zïiÇCÆD¨ÃÁÁ–––Üû¹ÖŽD—ì|dKA¶GA\ ÈÏ|>*0VUU©ÌÚE-‘-‘íu%4ÞA|}}ñÓO?µë'''lܸr¹\ígc8pà€Êw?º¢¨üÃ?tèü   <ÿüóX·n~ÿý÷N훡‚„„455qm"‘b±ddKdKd{Ä cÇŽaúôéí:§=5Ÿ†Î¬Üò)§3úfèøûûcÙ²eXµjª««‘ŸŸÐÐPœ8q¢Ý5± ²%‚lïi¡ét5¿à;ZYS‰OªllÙ²¥¥¥¸xñ"är9„B!ú÷ïO†C¶D¶D¶×íð˜Ï磶¶gÏží”ëu´²¶úÇðÖ[oÁÑÑ&LÐZyüøñOUÙÍÍ 555J)u­,“Éàçç§¶òÓôxv ["["º???ØØØÌÀ™?>›>}z§\K$±øøx¶mÛ66~üx&‘HØÍ›7ÙÒ¥KcŒÝ¹s‡>|˜ÅÇÇ3î<™LÆ&NœÈcŒÕ××3ìîÝ»Ügž>žÊ÷<wïÞeÞÞÞìÞ½{Z?[[[Ëär9›9s&ûôÓO¹öÍ›7³õë×s»ºº² °½{÷²½{÷²£G²'OžpÇÇŒÃNœ8Áý=oÞ<¶oß¾6ßqøðaƒ—ï®]»Ø;ï¼ÃcL*•2¹\ÎÊËËVZZÊ}.99™YZZ²Ç+U&+,,äþ>|8ûî»ïT~Ï·ß~«öX]]ãñx¬®®Ž1ÆXUU333cyyy:õEÕTµ—••i½–\.g666ìÖ­[Üg¼½½• ²;v0™L¦tßä51mÚ4Ä >ˆûúú²I“&•rœÙ–-[Ú´»»»³Õ«W“5wÂÂBª««cŒ]¾|Yë9ªœð¸qãØùóç™T*e‰„`EEE*ÏðàÀÊË˹ë <˜åææ2™L¦ô·oß6xÏ;—û1Ád2“H$Ì‚=zôˆ+Å9}útväÈî¼ììlfiiÉÌ3gΰI“&±¦¦¦vq©TÊÌÄ­[·²Å‹3ƘN}Q5UíõõõZ¯•““Ãìíí¹q=~ü˜ VRR¡¾}û²»wï²Ý»w³‡’¡’_Ô‰^xùúú~)R£Ê=\^^Ž‚‚•…¬­­¹DûߥEFFbÆ (//Ç;wpâÄ ®…:rssÁçóáää yTII ¦L™‚O?ý[¶lQY#%%~~~àóù077ç¦óããã!•J1fÌ8pï¾û®ÖB!†„ª¼à¦¦¦8|ø0Ö­[ooo¤¦¦âwÞá)¦¿]\\ðå—_‚ÏçãÆøé§ŸT¦cÕ†™™6n܈­[·‚Ï磺ºš[à&´öE]nóÖíVVVZ¯uõêUøûûsS½"‘£F‚½½=ÀÄÄnnnHJJ‚­­-­y!¿¨3 € -lÓ7D"ÜÜÜÕ«W+›1c222PVVFVÝ$ Fû÷ï+µ‡††â‹/¾Ðxî‘#GpíÚ5®vee%fÍš…9sæÀÝÝ^^^HHHÀ7ß|Ó¦ †"]gDDžÔ:ægò‹m¡…mzŠbñŽbqOKúô郺º:R;éÝ»7JJJ:ü#qáÂ…Ü߃ Â…  —Ë9ùûûÃÇÇGeA  Cmm-ø|>–,Y‰DÂm½ Fpp°ÑÈ[Us077Wèššš’’‚O?ýæææ\pM¸¹¹i}Jo¹˜Q×¾hƒªvM×jýÂÄĤÍÓ¶‰‰ pò‹†2¶é ¦j‚D±ýˆè>,,,¸§(|>¿3QW£e@áóùš§{[] …+Œ1lݺ3gÎıcÇt®­íææºAÉ/>ó~‘‚¸ž>ÅH¥Ò6ÇêëëÕ>!#áááàñxôï)ÿ…‡‡ë­¾LLLޝ¾ú ;v쀩©)é«›ôE~Ñ8 ét=ÃÁÁ–––¨ªªRy³>KOáááäÐH_A~‘žÄ >Ÿ€€•ïp«ªªàïïOBê²²²pàÀœ;wŽ„Aú"È/R'Ô‚„„455qm"‘b±Ïœ<c8pàzr#…¾ÿþ{üúë¯:}éëÙÖùEæÍ3ÆΞ=‹Ç«<ÁÖÖcÇŽå¶Ïô4Æš;=22ÅÅÅØ¾};*++±zõj.÷ó³Æ7ðöÛo£¨¨¨GS+:99á›o¾Á‹/¾ØicЗ±‘¾ž]}‘_4L4n1³°°Àõë×yóæaÁ‚èÕ«‘––†ùóçãïÿ;¶mÛF7r±eË”––ââÅ‹Ëå …\~æggµ´¥ºëé{iKÒ—aé‹ü¢aÓ+¼ÕJGGGàçŸÆ×_ ///899a̘1xå•Wàææ†•+WÂÁÁ=:€¼¼< 0S§N5:åXYYÁÕÕnnn*÷G;999Ø¿?bbb0pà@äççÃÝÝÛŽ%‰°sçN!66b±®®®šK[~õÕW(//Gjj*nÞ¼‰/¿üÞÞÞÜV¯öðÓO?áúõëàñx¸víbccÛdâRÕuc(,,ìÐØvíÚ…ãÇC&“áÚµkÈÊÊ¿þõ/H$âòåËøá‡põêUxyy‘¾H_ä”ß~û­¹¨Žº¼¬¬_¿~*s‹D"€öxþØùóç³éÓ§Sba#¥©©‰õíۗݹsG©===¹»»³ššîsƒfUUU,//ÅÇdz¨¨(öꫯ2‰DÂc,44”íܹ³CýXºt)[ºt)—ûõ×_çŠ.hꋦ1´wliii,>>žmÛ¶ >œýñÇŒ1ƾÿþ{fkkËRSS¹ñƒ "}‘¾#Æ××—ÍŸ?_uîtÆ’““áåå¥2yEvv6ÀÓÓ“~]JW”¶šËƒ9rDãwûúúbÑ¢EÜ4jw”¶Ôt=™L???½.mIú2,}F€ªŸ——ǰݻw«¬êäããÃFŒÁÊÊÊèIœèRº«´¥&º«´¥.cÓ÷Ò–¤/ÃÒaøOâ*·˜¥¤¤&Ož¬Ô^YY‰U«V¡¢¢çÏŸÇ!C4þ@(--E~~¾ÎÿîÝ»G¿ª%®]»ÆUÛ¾};ž}gΜc =‚L&Ô)S°ÿþ6Õ}TqäÈܽ{·]ÆÝ¡òƒ„qò,•¶Ôelú^Ú’ôeXú" •ûć $%%éýŒuŸ8ñtGiK]è®Ò–š®g¥-I_†¥/Â0Qìoó8}ûöm”••áµ×^#)zAß¾}UÎüh+méååssóNsŽýúõS[mL—Ò–ªÆÐÞ±µ®ª¦¥-I_†¥/°i3žœœ ÄÏ;×®BíóæÍ£ÄD‡i]ÚrëÖ­ôzÆõUSSƒS§NÁÚÚAAAäô©T 333£Ô“¾ß‹*ƒ¸¹¹y‡R¶¦±±í6j2X¢£ðx<|üñÇ$×WPP"##€—^z©Í¶®Î ¸¸&&&ÜÖ/¢ýdeeáÌ™3°°°@ff&lllðÙgŸ¡wïÞF3Æî¸ŸÊ†Z¾üø1† †Ñ£G#55Õ LïÄ Â¸xøð!P]]ªª*­»`:ÊÉ“'ajjŠ9sæÐ;€\.Gpp0bccajj Æ|||àâ₽{÷Ò½ØÅ(½¯¬¬Ä¢E‹0uêT444àîÝ»X¼x1ÒÓÓéN%¢[ùå—_0vìXôêÕK­ÓTWE¬e{gTk}=m×Ôt¼»úÜ]ˆÅb…Bܸq£ù‰Çƒ··7.^¼Øé²í¨ÜŸV¶ºÜ‹=­[S4hŽ=jïââ‚G‘ç##à‹/¾€P(„\.Gxx8,XÀ퀤¤$$&&ÂÙÙ555())Áž={4ïÝÎÏÏÇË/¿ ‰Dsss¤¦¦âĉêË®]»pëÖ-øùù¡¾¾r¹YYYðòòÂܹs•>›’’‚¸¸8Œ5 <Àœ9s0nܸnïswÒ·o_TTT _¿~\[~~>\\\:M¶šäÚÕ²Õv/ö´n'L˜kkkÕûÄ ‰¼¼<ÔÖÖ’÷##`íÚµHNN†¿¿?–/_®tìÂ… øè£péÒ%„……¡´´››‹ &¢££‘žžŽÂÂBî)±½äççÃÕÕMMMX¿~=ÒÒÒààà¡PˆãÇ+š„„„……!55ˆ‹‹ƒH$¸q㺵Ï=AËž™™‰´´4$&&vŠl5ɵ;îM÷bwߪøõ×_Õ—"%zžÂÂBDEEÁÉÉ fff¨¨¨ÀæÍ›)éÆèÉÎÎÆ|Цý½÷ÞCXX—[½  €Ë*iooÄÄÄ`Íš57nbbb4Oe._¾b±˜»^QQLLL  ¹6DGGÃÎÎÎÎÎmòžgggcôèÑJýÚ´i<==¹Üî–––X°`ÁS÷Ù¸wï>üðC\¸p#FŒÐøY]e«I®Ý%[u÷¢^éÖÐóÇcîôÊÊJ6|øp¥jIÇg¾¾¾\g‚tcŒüù矌Çã1±X¬ÔþàÁ€•——s5Ìrss™L&ãÚlllØíÛ·uú®o¿ýVcnvUyÏÇÇΟ?ϤR)cŒ1‰D°¢¢¢6çwEŸõ‘ÂÂBª««cŒ]¾|Yë9Úd«I®Ý%[u÷¢¾èVcît¢gÙ¾};<==•Þ¿áúõë8wî ˆtc´Ü¼yŽŽŽ\Þs|>æææÜôm||<¤R)ÆŒƒÃ‡sS˜fffptt씾´Î{^PP€’’L™2Û¶mМÌÅÞÞ^)çyCCÎ;×#}î‰Y©ˆˆ¼ÿþû(//GFF†Nï|µÉV“\»ë~Pw/öÔý¨Ž^áááá†lô§OŸFcc#‚‚‚ŒÆ‘…††bÊ”)˜2e ×fbb‚“'O¢±±äíI7F‰¢VÃìÙ³•ÚcHIIÁùþpÚIDAT½{÷ð矢°°½zõ‚»»;sss¼ýöÛ:}WNNLLLÔ–‹‹ƒ@ À›o¾ÉMÉ_ºt =¤I“0|øpðx<¸¸¸p5._¾ŒK—.á­·Þ€:½Ïú„D"§§'ÒÒÒpðàAìÛ·111x饗àïï¯ñ\m²1b„Z¹ ‚.¹t½»ê~l/ÿþ÷¿Ñ»wï¶¹Ó cÛ'^^^[[[ìØ±ÿøÇ?”ŽMž<UUUÈÉÉ!oOº1*Iž/^ ___ªü\mm-ø|>d2$ WAL‘XJ]ªÕÖhÛ'Þ:ï9МKC.—sïA455¡¢¢C† i“!°3ûl,è*[Mrí*Ùêz/ö´nÕæN'z–ªª*P¹HÊÊÊŠ;NnŒ \½zyyy˜1c†ÚÏõë×|>@sµ2…ÃT8Ëö8L777¸ººª=Þ:ï9Ð<Ú:€€©©)†ª2ÐtfŸ]e«I®]%[]ïE}Ñ-­N×3«gU9Š>}ú ®®Ž„Dº1:V®\‰ôôt|þùçÝ–²ÓÍÍOèŽHA܈077 :»"aAºÑêëë‘‘‘¡s6*777ØÚÚ¶i_±bÝ`¨7cÄÐîE âz†b:F*•ª4¼–Ó5馧©¬¬Drr²ÎÁÀÒÒR)„‡‡ã“O>¡›Kü1ºjÝ1éÍxôJA\Ïppp€¥¥¥Ê÷«õõõ:¥4$H7Ý…££#"##;|~xx8 |ƒ éèQ ~aÛƒŒjAŸÏG@@JJJÚ«ªªÒºuƒ Ý5558pà¾ùæøÒ#é±K¨®®Æƒ ?ˆÛÙÙÁÆÆÆ¨”‚„„455qm"‘b±Xãv‚tc,áùçŸÇºuëðûï¿“@HD+;;;Úb¦øûûcÙ²eXµjª««‘ŸŸÐÐPœ8qvvv$ ÒQóðáC¤§§ÃÃÃ999xî¹çH(¤GB ôN\OÙ²e JKKqñâEÈår…BôïߟCº1zt­'Îãñ4¶«ûL{h}=¯©é;»«Ï¤ÇžÑcOé’2¶¡7(j8Ëd2øùùQ=qÒ£Aè±'t©ÈØFOâAè TOܰꉓÑ㺤 N„^ÑõÄÁCQ›Z¾¾¾X´hÕ'=ê¬Ç×%Õ'âY¬'þ´5¯©ž8éQÑçžÐ%Õ'Bï zâ¤GCÔcOê’ꉡ7tg=qmP=qÒ£®zT|Owê’ꉡ7ôD=qmP=qÒ£®}î ]R=q‚ ô†ž¨'® ª'NzÔµÏ=©K âAô8†VÙ =ê ´ÅŒ ˆ‡ê‰“ z'‚  âAAè?4®§"** NNN033CEE6oÞŒ>}úpH7D'#‘HpèÐ!\¹rB¡B¶GAœè8UUUðööFRRW4 66³gÏFBB‚ÑT9"Ý=Mnn.¶oߎ!C† 11ƒ&¡í4®‡lß¾žžžJU‚‚‚pýúuœ;wŽDº!:‰±cÇ"66QQQ6l „l‚8ñôœ={®®®Jm¦¦¦6l~þùgé† ÈöÈö(ˆë#ååå(((h“s¬­­‘œœLB"ÝÙÙq}¤ªª T.Ô°²²âޤ‚ Û#Û£ ®gˆÅbP™°OŸ>¨««#!‘n‚ll­N×;ÌÍÍ4hD"Ꭴ¢™úúzddd@×ZNnnn°µµ%Á‘íQ':ER}©TªÒYµNºOnžu*++‘œœ¬s·´´¤ N¶GAœè`ii©òO}}=\\\HH¤¢ŽŽŽˆŒŒ$Aí=“Ð;q=ƒÏç# %%%mŽUUUÁßߟ„Dº!²=²= âúJHHÐÔÔĵ‰D"ˆÅb’€H7D “É”ôJíQ':„¿¿?–-[†U«V¡ººùùù ʼn'`ggG"ÝDEE/^Œ€€dddàæÍ›˜>}:-Z„û÷ï“€ÈöôÓu5ˆžŒÚÚZœ={Öè”SZZŠ´´4Èårøúú¢ÿþdɤ‚ Û#ÛƒŸŸlll(ˆA„¡qšN'‚ …‚8AA(F±O\.—«\UjjJÛà ‚ ÃFU|“ËåÆó$ž333¥cÆŒ!ÍAϘ1cÚĸÿþ÷¿€ÿ?òÕî½ÒIEND®B`‚vispy-0.14.1/doc/_static/favicon.ico0000644000175000017510000016334614507104216016627 0ustar nileshnileshvy Ðæ(vò ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿbÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµ5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^Ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²1ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ €ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿWÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ {ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó wÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿPÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòrÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïnÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîiÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿF¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëdÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿB·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“ ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿç[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿȦÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ;®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽ õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽcÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãWÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿiAÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8ªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ4¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³•ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†gÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿW7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ1 ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙIÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²ªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{wÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿDAÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×Dÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒ@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^iÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(“ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ 3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿzåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏ;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$Žÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿg€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)AÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊ6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿê øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬ÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿo„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈ2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ+?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃ-ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\mÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿiÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾Èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüxÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿy‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿeËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ4=ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸ þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúsÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿª°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿekÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^Âÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøjÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZ½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿW¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ aÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿoiÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿS´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ*!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó \ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ –ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿP¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[Qÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿL«ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿG9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿI¦ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3 ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿE¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©”ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ! úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿP7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæAÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƪÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ;”ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿeÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ7ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿß8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ( øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ4‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡Üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿž{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝ3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0†ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿϨÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ.ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠcÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ-ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€Õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ ÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÿÿÿÿþÿÿÿÿüÿÿÿÿþÿÿÿÿüÿÿÿÿþÿÿÿÿüÿÿÿÿüÿÿÿÿüÿÿÿÿüÿÿÿÿüÿÿÿÿüÿÿÿÿüÿÿÿÿøÿÿÿÿüÿÿÿÿøÿÿÿüÿÿÿÿøÿÿÿüÿÿÿÿðÿÿÿüÿÿÿÿð?ÿÿÿüÿÿÿÿð?ÿÿÿüÿÿÿÿð?ÿÿÿüÿÿÿÿàÿÿÿüÿÿÿÿàÿÿÿüÿÿÿÿàÿÿÿüÿÿÿÿÀÿÿÿüÿÿÿÿÀÿÿÿüÿÿÿÿÀÿÿÿüÿÿÿÿ€ÿÿÿüÿÿÿÿ€ÿÿÿüÿÿÿÿ€ÿÿÿüÿÿÿÿÿÿÿüÿÿÿÿÿÿÿüÿÿÿÿÿÿÿüÿÿÿþÿÿÿüÿÿÿþÿÿÿüÿÿÿþÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿüÿÿÿøÿÿüÿÿÿøÿÿüÿÿÿøÿÿüÿÿÿð?ÿÿüÿÿÿð?ÿÿüÿÿÿð0?ÿÿüÿÿÿà0ÿÿüÿÿÿà0ÿÿüÿÿÿà0ÿÿüÿÿÿÀ0ÿÿüÿÿÿÀ8ÿÿüÿÿÿÀxÿÿüÿÿÿ€xÿÿüÿÿÿ€xÿÿüÿÿÿ€xÿÿüÿÿÿüÿÿüÿÿÿüÿÿüÿÿÿüÿÿüÿÿþüÿÿüÿÿþüÿÿüÿÿþþÿÿüÿÿüþÿÿüÿÿüþÿÿüÿÿüþÿÿüÿÿøþÿÿüÿÿøÿÿüÿÿøÿÿüÿÿðÿÿüÿÿðÿ?ÿüÿÿðÿ€?ÿüÿÿàÿ€?ÿüÿÿàÿ€ÿüÿÿàÿÀÿüÿÿÀÿÀÿüÿÿÀÿÀÿüÿÿÀÿÀÿüÿÿÀÿàÿüÿÿ€ÿàÿüÿÿ€ÿàÿüÿÿ€?ÿàÿüÿÿ?ÿðÿüÿÿ?ÿðÿüÿÿ?ÿðÿüÿþÿøÿüÿþÿøÿüÿþÿøÿüÿüÿøÿüÿüÿÿüÿüÿüÿÿüÿüÿøÿÿüüÿøÿÿüüÿøÿÿþüÿðÿÿþ?üÿðÿÿþ?üÿðÿÿÿ?üÿàÿÿÿüÿàÿÿÿüÿàÿÿÿüÿÀÿÿÿ€üÿÀÿÿÿ€üÿÀÿÿÿ€üÿ€ÿÿÿÀüÿ€ÿÿÿÀüÿ€ÿÿÿÀüÿÿÿÿÀüÿÿÿÿàüÿÿÿÿàüþÿÿÿàüþÿÿÿàüþ?ÿÿÿðüü?ÿÿÿðüü?ÿÿÿðüü?ÿÿÿøüøÿÿÿø|øÿÿÿø|øÿÿÿø|ðÿÿÿü<ðÿÿÿÿü<ðÿÿÿÿü<àÿÿÿÿüàÿÿÿÿþàÿÿÿÿþÀÿÿÿÿþ Àÿÿÿÿÿ Àÿÿÿÿÿ €ÿÿÿÿÿ€ÿÿÿÿÿ€ÿÿÿÿÿ€ÿÿÿÿÿ€ÿÿÿÿÿ€vispy-0.14.1/doc/_static/carousel/0000755000175000017510000000000014507104250016304 5ustar nileshnileshvispy-0.14.1/doc/_static/carousel/signals.png0000644000175000017510000052775314507104216020477 0ustar nileshnilesh‰PNG  IHDR úY=#¼bKGDÿÿÿ ½§“ pHYs  šœtIMEÞ !$ØŸ¢A IDATxÚìw|Õúÿß;3»›Ý4z/¡†$Bè=ô*v)vEŠŠ`•bÁ«XÛµ\¼×v¯½] E.U@zG mû̜߳™ìf7!xõû»~¿ûy½æ•Ù™3gfΜó<Ïçyžs‚,Kb`ïLÑ¥} ñ—yc a³*bÙÂÄUÃ:ˆc[žgv,â×· ¦O"´ã¯‰é‡ˆ„ø8ñæ²ñâòÁí#ÊÝ;a°8¼ñI!ü@ˆS¯›ûOÎ-1éæ~bùc7‹{' ÷N"^|ìf1ù¶æs”l3ŸX(¬6kÄq«Í*æ=÷¤|ÕebüŒ©âª›Æ@´ÎÉÕkÕã¦Oï®ýRâš[o÷.xXX­F=6›MBQq׬S¹_⪛ƈa×^)Š„€hܼ©¸iòx¡X£<ƒÕ&æ/^&1ñ2aµõʲ,úôRaùq§‰ÅO,ññ æ±Ù þ"œñ bÜÄibÜÄiÂjµ «Õ&=ö‚Y‡>WÉ&ËŠ°ÚJë-» »l‘p8ªˆkFuŒ¹ñuѹÛm}ûß'úöŸ!dÅvÍÈ+Ÿƒ†Î}ûmÓ*­¿sãëB’1òÊ'ĵcV@ôø€sã_ÅÈ+Ÿ4ëxÿS!ªUOM›u’$ϨØÄ5£—G}¾ò6ÅnÔg±XDëþ]ÃÎI²Q^RJÅ%Æ‹«O+·nI–Œ{„ô«hu–·ÉV›è6jŠè1öžrêWÂöC¢U·ÁB’ä`=“EÛþW‹Aw-€xç1eåVÑ4§§Yfø´¥—ÔvÿMÛÇb‹h?¸«xǵFüSýQ|-öˆ§·­y7 ‰McçO‡…cæŽ}o&>;Ä;®5â=ïâk±G<øþcâE«DÞ CÅÓÛVŠÏÅvÑçú!b½8.Þu¯½Æ cçM‡…ýn.Væ+úÝ<\¬ÇÅzq\ô»y¸x×½F¬‡KÇ¢bKǽ º¦÷«Û*6ýe¯8ôêyˆÉ#¦G}ß¿Œ±7{Ìb1~ÈѯÝ`18w„Ÿ•ŽÉÀ‡BL:Eâú¼[ÅäÓE|\¼¸}Ð$ñÍâ blÞ-B|ZZ~ÖuóEœÍ!q׈{Íý’m—¯‹þ3'‰OsO¬ ~Ý,æÚ žT÷‹綈çÅiÑ~ÌH1êåGc¿nsO¬íÇ\&žgÄÂs[Ä㾟żSÂŽÏ>ºV\öøƒ¢fó±Ä½K+ÄêÄCòDZÃzB–¤ˆ¶sû1fÜ\C¦»Yôv³P«)?oò¤DŸÁ×›åþõƒ&:öa–-Ñ£o›Qÿ»ß¹÷q­”#/eY— T©²ÿé&_b½íû·7e´z›"¦­˜ö‡>ÃÛÖ/s¨%¹RÇ>çÜÙ’R©úóœpÚœa×Hã{,¿}¥°)ö?]û}*V‰ŒÞmD·1½Äkîw‚ö,ºí-Þ5L(VÅ<ÖóÆ<ÑóÆ‹jD;Þp|Nï2–Õ‹Ö/K ÓßÛï»F:²¤pûòkñ«¾?UÛI²Ä Kw,’Äê7¾e×·ÛÍs5×䳿|ˆPÃìG’3Âî Ó1Û6áÅÛŸÁçöc³°|òÓœ;vY‘)>_„#ɉl•Q*¯ÝµœË¼ºÜ:gÝv3ö¸8šÔ6lä½[¶±ëÇ †]FwÆ‡Ž—ïV¡å•V¦œ®ilùnU…ퟔL 1(;NL)îõÐT=B`!HLpàrûMòÇ++¿ç®™¯ã¨Ø¬ ) krÅ-OaUˆ·‡i:U’J^'t¬ŠÌžý'ñT@ i‚Ï¿ÝÎWßï S»fȲTéÁ½àÞ™(ŠÂß–6ÆÓseÿ;âøá›U8(ûÝuMgêµH’d‘uÁz–>²ÐDVýUpùý>Æßz ã&N£n½úXBXXy¤A’ejÕ®Ëûï¾i L¥Tñ|÷ÍçxÕ Ñ5 ØûX>þRϼom±âÎBö„x>Ÿû ŸÏ}{‚aT×JmÊò¹÷ThZ,ª8ølÛüåPâ°÷ä1.ïÐ"Ñï!ãuþ¢qÚìa×ôiÅÛ?|aÄ«ºV.ÑÁé »Y O¼÷)«wüŒ_U©šOõ–M¹íþE%Ï'¶’ï]ç‹ÍÛ©|o_ ñN¾Ü¼ƒ$gÞ@ 4]°qïâZ4eÔâeÜùìkX™ª ñF_Õ)µkðþÚÜųÙèºÎ‘;ƒýWppß6î߆¶‡Ðuvm[qí?þò[ÈÉÿoT–,üÙIÅ%µI9䡼ã_üôñoª¿"’Rѹÿf”èâñá’÷°H„&ØýýÎp·mAJ"lJŸ7¦½ŒbSøé‹ÍdôΠZýêh~‹ÅBãL£- ™DèžVãË­ó_+^açú ”Xš[CˆÃ¾ÝÛÌ}»#Ž93î ' Á±°eݪK'v‡ƒ%îB’¤èvp¨"øjÕÎ0Åðù7ÛÙ¾û ÛM¦q£šT F@tMÇíö‡—ýv;Í›ÔfþSÿäÑù‹œ”÷€%ÂWßï@×þ€ÊçßlgÏþ“Ü2ª—I@*ƒuß®BUU6 „éU**fîÔû‘$‰3'N2{òô¨‘MÓÐuݼ>ô™Kö/&žf ·\~ÿżÝfg؈k8î,~ŸÂ‚|Ô`´£„´È²Âø[¯1ë+è²¢Þ¬JÔw(/â²k秨šŸ];?5½ôÂåÁG"§Nî 3¢%IáÕW—cx«dÂju°{ç§æ±C½‘] »ÿÿèºö‘*SYGé3e#&º¦þiÛ)à ðâÔÇi‘œÆ'Ï¿Ãü¿Ü‹·ØcŠ2:,b¡ë´ìØšé·ß‚ê ŸdDN«•y8&±îƒoºŽßëÇlùb5“êðX·ñ[æÍ¾‡´.mÙñí&þ>ÿ¥pa¯úY½ó[¶þ²‘5“ˆ³Å…EH•¤fzì’œÉøÕ?ÜB‘§ˆP}¢é¥¨„ÈX°Ð F#Μ “ÑÛjõ®ïЂá]Ó¸päß.}Õç£fóƬ9Î$ ÕS2ÑRŸjÔAW5œU’Ié”Íòq· Û U³îåðÚÔ» gI&‘Ké˜Åò…ÓKû—˜€³ZtU#.9‘S;÷ðxK=XþG6þTîwþü§hºNg<»©0ª^‚b¯‡÷~\MÄd$I"+¥™y®I­:udwîÉ·»¶Uªît8Šû^^‰"KhºŽ/ ÒgÆ|ž~áu:¥5`Óî}ܽüMœ6è:÷ J|< Ž8¾Ø¼¾Ü¼ÏÏ—›·“àˆ3#ºG7n#«ic¶ì?ġӿòÒçßâ¨TIpš÷«–MQÌÈJ¸ìV±Ç9ùyÇz„Á¾¯EÄôÔ€Ÿƒû·qø ¡«U5Àù_Ophß¶0'Ó®­‘D ø‰!†?’°ü_F ÙóýN„.Ðu½kv—ƒ*oL{©’¶­êWùåÇ}Ù~€·f¼ŠP£ÖÍ9^§cxnN„={]z¶éh×5Ú FØ¿ŠÕÊÞ­Û.zP÷‰Ïãá½ç–sgÞ ‹MçËïwúû‹o·³ïà) ‹<|öõV´`ªÔW«v† MÓùø«­,{å+–<û ’bDÔ©Ágßl¸q•$§éüêû$ÄÇñÕ÷;ðTYâ¶i/á÷W¾“_Œ$èºn¦\ýQ^5~©T9‹ÅÂáC¨R¥* ‰I|øÁÛÌžÿ~ð6v»=L)E‹n„¦eESdѰ{ç§hªŸÝe€ªúÌTªh$£²Ø½ó3Ô`èT’¦Ýåˆj¬ïþ_F@b¸4öþÂŽï6—àdÃǫٵz+Í)58ë £k¯Ÿ¤êÉÄ9ãxáÎGñ¹¨Ó¬5©ƒ» ˜Z)uùy½!¯†K¹4ÍJ¥fÃ:œ>xœ¾½ÚsáÔ9®¬ßZ)õسn;ÇöÂSäbÃÇÑÃÅ~ÕÏžc»˜ùÚ<ž|QDä#Zù)/Œ£aF8µ€Ÿí# ¡äEÓ5üªŸé!Q•ìñ¹Ø#í(ÉYšú´fçwa¤¶$ Q ØýÙwÔÏJ'.1ÍôL©j4oÌúWßÁïr㨚ŒÐtd«ÂûSçrf!›–ßr µ4C×t>¿M)¹*;?úšï””NÙÄ%%˜¤ÐžOÕFõð{<üüåêrÛå‹m›Œ4¦„ÄK0ftÖîÝE^ëlê$WÃa³“àpà´ÇEOßJªrÉýnãÞA'Ž¡·q¬Û³ŸzÕ«â2ǽÇORàróÑ[ (‡›Ô©ÉÚ]{ë55ªñáú-lØ{€½ÇNâ ÊâO7”¦^}ºa«áüòù¸ïå•´¨_‡/7o7"+å@è:÷mãä±ý]#.ÎÉ®mk̨F¨ =´o‡ö†À¡ý?™ú­l¹bˆáÿ©ˆT†,„â“'>ø]êñy<ìÛ²­BÛ7à÷óøS#RÕ@€%ãïºhjâe¹9Xmáé´{6n._ö]l³Yñð=#ÅÌ)#„ͪ9Ê.ÌÉXƤö’ßOÌ-Á‰ÓŸ‰©ãF­ÉC×þ©'q¢ÿÀáB’¤K¾În·‡ý-¿m•?ä¹%Iù¯¨#¶ýߨâââ¾,[@’es€Yÿ|B4ÊhZÚïÙœ4:ù凅3)¡‚…d!‡,DP?µ±xàÝ%™/: ëQñø’äKìó²èѦOÔ:zgö½3ûUê~•½oÚÀžB¶WÞôqâ#!İEÆDùg7‰Z©F›]ùô#âªg犚-RÌ6-Yœ¡~Û4ñ†ˆäzµEæÈþâò¥³J'ŸÞPᢀÈÒ§Ò 5(—Øžâÿ®T¹G®º^8l¿}¢ª,IbPû¶æïÐ}Y’„ÓnÓZ”ÊçàÌ_ÿþ|Øo@üí¾I—ui/ÞºobØ}åõ>9ÎXÜå¯÷Žv«Ra*ùÛ:»ÇÅûߟ|âsl‹m±íßv‰ 2ü\]¨ Q9½}™HmV÷¢¤åÏÛð1#<¶Å¶J¥|Ae³]RùÊÔó[®¿¸1+—{üR ÍE N%œL4î˜e¬tz”¬—6°GØï²[ãŽYÂꈊÝ&Òö¬p\ŒüÞ[eVJœÝ!ê*R—v)ê~EÇ*º®äy¢”Û¶BâQ‰m±-¶Å¶ÿ­›…‹.óûÂn·âóEÎÁe)bõ«bˆ!†¢CRä°Uß.v<Úù‹•ýo„"ɨ¢#E–¢Îûˆ!†bø¿ŒÿqC 1ÄC 1ÄC 1üß…k‚bˆ!†bˆ!†bˆ!F@bˆ!†bˆ!†bˆ!†‰!†bˆ!†bˆ!†bˆbˆ!†bˆ!†bˆ!F@bˆ!†bˆ!†bˆ!†b$†bˆ!†bˆ!†bˆbˆ!†bˆ!†bˆ!F@bˆ!†bˆ!†bˆ!†b¤²,ÿÇuØìöXˆ!†bˆ!†Š YúÝËG+s©÷‰!†bäœphšÆS+–a³Ù.ùZ›ÍÆœÇP¿aý¨Dæ÷ 71ÄC 1Äðg'’$ѦKÛ‹–±H$YB’£—/‹¬îÙX$£{œý¢÷‰!†bä%„#!1§V, ¸¨¿ßQÒ iKWJïþyHRd“dµÏ®±‰áƒ"Iøu¿õ1Äß %ú¡2Î¥ßÛõgthµéÒÖÔ’,ѦsCfZ“èšÎø…“°ÅÙhÖ¦9uSêѶ[6Š¢Ð¦sæEïQ½N nyè6:ìÂÜŒ3)žìíÌóŽxãLB×ôXŽ!†ÿYÝn²Ú­—F@åÏ% ÐuAqQ1²¢°eÃf^}ç ž{ýE4M«ðZ¿ßÏóo¾d– ȲL ˆ(»qÝ“ØÄð Y"ÎZÚqå Ò”ÝÚÜ/ùkS”ˆëíÖðcCsÚTê¾6E¡k«æ±P‘°¸A“þ‹œ‹ZÆPú÷ÄsË^6åE„¤Wϼ PvÛ¥¥îöê™÷§#!m:µA±•ÊV{\ƒ¯Š3ÁI»ž9X$ îBI ! YXd)ÂH %3f;çN㺻GsêðIÞØö7í>H•UuÏ Î˜¿+kɱˆÉŸJ~Æd÷Ÿ©ŸHt•|ÔnX‰‹Gaµ)' ²,3p`¯pÁœ˜üd¥D(Û+9£0¿E‘‘$ ï¹·Ûͫ֒þBÔzC‘•“MÓfMƒçZe´â•çVðÐâ9ÿ+£ЬTøû÷¬û?Á•ÛQ5ÁÉôýy↫¸¶k{:¶HÁi7¾‹¦ëˆÝß’Ù¸>‹Fä–>]xꦫûWS¯j2Ü{OÜpºXe™î›ÈW³¦ðÀÈAÜÔ» Û–Ìà§%3ylìøU•oŸšÍƒzóÊ„ëIr:bDÃë £nƒ®ëåB˦¦µúCIH‰±R™ ]Óµün›•b(‹=úPT\dþÖ4å/¼FÇ#ÊöéÝ>½û•«³ê×oÀüyKÂôFE£gÏ>—>­S9•_ȧ[v0úxò]n2S eÚ¦M+zg¤Ò²n-((¢OF*7޾œÆ5ª™‘–ÊxÕdYŠªÐ”°q#]”Ü*²l–‰V¶2JS ‰ ýB ë:£ncÖ#„Àãñ˜çãââLB’šÖÊ<Þ2-‹Åò»zÀB÷uMçkqá¢)’,Q;µ!çœF þ÷(€ Æ«ÝjûÍu]lÿRå„ôäŠ$ý±J¾¬,·Ûmæ‡Û²ek֮ЏÖãu“×§Ôú$I¢OŸþ=z!Œ1ûÌÓGàö´”+çme<†²$ѽSÆfDTšª±rã; »e~¯! ¶![Ú÷ÉeÔ=c°Ú­ø<^¼n¯9î„0È‹=ÎÆ5“¯C×t«BëΙœ=~Ÿ×Çø±;‚O›W I–pãqy‚Çt/Ë[i9Ö±]&ûtC²DÊë¾=ºü®„¤2×ËeŒäÿdÌÛÊè\I’+,)ã8§÷n³„êõêWø¼¿§q®kû„ nã¦Qï#I2)éé?ê¾È¶·ÚhÔª=Zµ7Ÿ7=w Þ9†®©QŸ[’dZµëkOËíoÎA*AI=ÿW¡Xzu}ÉbáÁiwÀÖ,»åZŽÏçðÙó`·±ôá©ì=q†AY\p¹ATU¥I«æ\p¹°[<>?ŸÍ›Î-ýzðØ­×á;p˜÷fN†ÄxîÚ—ÂSgi—Ó†Î-›âñx˜÷à†å´‰ % &7«-¹ÙFNtÉwìܾý{÷0Ë•þòzßßF‹¦MHJˆçõçžÄ鈌°8äõ$91yÜÃSóŠ(Ó·W· ¿›$IT­V•‘W_DJT†”hš†ªªQÏ 9œü ÉoظÑDv¢õÿÔ¼¬0¥4jùÝØ\ÿÚ úYkиCjH4DŽP,ÉBóm𺑕ÿÞ+Y’èÙµÜósÆL04Ê’’P¤wf¬A¯ ¦k¼xçãT‰O2ïaSlô îWtßhD¢efÞ%¿_Ë̼?”„hšÆüyKp8œlÚ´¹ó2õOÇq¹\-²/Ï÷#¥iT– c­Ôà.(È/—µh‘JAE½ä IDATaA˜Þ1°¡ŽÀzuª3{ÆhlAY.ɥ䷛Uáɹ·E7"*ÖÛ°}ÛO\>þ*ÆL¿ÁL«e ŪpdïÆÜ{vüÂÔ¥Ó°#V›ÂÔ¥ÓØÿÓ>šg¶ Zíj´Èj‰ßëC±*tÖƒý:¢ª%ŽÒ“3활=~†iÏÌ@ ¦äþ}U³z5jV¯F Œ,“%™ÙmHkÑÌlSUÓxýéÅ8q¿©TMã­gÃVNÔU’dÚgôB×5fÞþy), G^ÏÒA¼6àpÿÜ™lþqc%\t² XÒ[7çŠk2`pwÚ´mɨ†£izÄÇ·ZÚf· ë/j[”<¯Õª0uúM*÷Üw uë×âö טÏa·Ú™7nûŽî'Îf'+5›³ÎR¿f½KôîT@‚*—Î8'/Ü÷<)u‡)õÐút!(ôx‰·Û”.7Š$aWÞ›9ïž_м^Úådâ´YùxÖ¼GOå¢Ed‰ Åne·F’,¨Å.T¿ÕãE–$T—t㞊,$aSE’Ð…ˆ¨S‘eCÁ¼ðÝ:µ§e³&èºNnv[ÚµmÍÌ{î$3£ï½ö<ª¦Ñ½S.7¯¢jr2­Z4Ãç÷³{ÏF²Ú¤sòôòzv ‹pÌ`Íš4bø€<ÒZ6gû®Ÿ¹cê23Z…ECåõŠð®†¶«Åb¡Uz:çÏ7mF‹¥4çZ’hÙ*µ\ ( ƒ‡ÅãöpÕuטQвÃF^†Ïkx!oŸxGØ8Ô4-j½•õ”Y$‰–}²##º èl>ºª‘5²;–à*;iýsL¯é¨åw£kZ@£AVsœUð»}•îã¡$ù·¤,š†²]^„á’ HÅF»fÆ|¦W¦,8?}ô½¬¸ëq4Ýèó¯¿?\FéÏMXlæV¹ôhÝ™]Ï}Ï+S–òýÎuÌ{¹- ò——Õ¼,ƒP÷ËîQ /™=Øÿ$R+ Š5ºq—š™áyü½##<8 ÇP’o¼ù*ùçfß¾lÄ•pìØ9îœt7º®‡ÉÝã·$Rq•%jäE’$zöèCaÑ4÷>^ËÓ Ça³*躠w×6»¼a±»}нSF¹‘ÙhÑ“¡IA‘ë7·]›Î™ø<>¼.®ÂbDp|¶éÒ–ý?ícÂÂI´ÏËE¶*¸Š\ Œ{ºðûüxÝ>²{æP¯I}ñ¦?v?ŠUASU&=:™Ž‡É W‘ I’p¹°ÇÙè6¬'¿äDµ*Éhjt#¹Øå¦_.acòÈñ“ êÓýÒǧÍÊœ{ïdݦm”t³9HošƒÝæà̹cíÛÕÏæ•?Fù–‚S¿£Sf?¤ˆs»´|ýâTé¨~¾yѨ+·uïJ+!‚©tQdŸÏã!§ÏZwîþ»FBŠ aÆ ¯FOIïHÀïE’$¬6;iŒ”ÈÔœ>ž?Ű[æröø~$IFS4l‘M«ö™A™qÎDf½¶£TÞää![mhªŸÇHÅ|âÓc´hÛ³”¤ä‘«ÝA£ÔöhÁv•*tÊtIëú·ÃïMbÊN·Ú\1.Â!aµ)ÜøÀeÜøÀeas7žœúWæ¼9©Üù…ŠqºiÓ¥¥i›–͆0ÓµºÂ¬'B’•\P5„Øí6ââìj¹¤"Úï’Aùî;Ï£ªÚEÓ¯dYæÜ¹ T¯^•êÕ«FܯlÙrm`Kø ½|8½ÿ¯Èä÷ EQÂŒ ›ÝÎ}sf²ï> ò ÃÒY.†âbwYðûü<4ïN.œÏ'·S&²,sæÔ¯T­–L»ö&QP™öÚЬy#žxæ³c´i›Šªj^ˆìvéH’L1Œ,·ÇKNn¿þšo>‡ª©¼õÅJjT©A¡«ˆ剿=I‚#¡Ò$O–dv%raç£ÇÿŌ맗{íЮCpy\ è4]×±[í¬œ÷jHH4 j ÊÎÀ¦È Ênæv›JS ’ !«•ó.7®b—AÊw£sà̯ ̪@Y»Ü ÊžݯÀ»5eÜÍèBàóûyíƒA.OÎEŸîY¸d.I 4oÒ˜”¦)ø~ºvlÏÒùq|×>|ë%ÖoÚJ‡ìL†öëmx¯ï¿›;o»ç›Ï7kÖñá[+ /š×K×íYþÄB’â©W§6?íÚÃß_ _nZ×uÆÞ|C)Y’²¡— ã×3¿"I’™*e±XÈhÓšC£ëz11”Žöÿbx„…@Ó4R[µ ! ³?zÜî±cãªë®Žœ+"K´ì“U9º"ã+ö„‘,¼.ün¯©Ø%E‹áu6ÿfS)Ÿ?ršÚ© >åõ㬚X)d·ÚyúîgiT»1»“×2úg(‘è—Ûÿ¢JadÏËyñ¾—iTÛ Ûv«¿>ôš®ñÒ¯òóÊ_LrPJVâ*Et<~/‹y†›ú]Ë[ß¾Ç·ÎÆaw„Y ëŸø„Í¿là‘QÓÛûJ3zbU¬´¬ßŒÓùg™3f:5’ªrÛÀÑÔªRƒN­rÐu‰×ßÇÙ‚sÄYíä4ËĦJëÀ©ÃäÓ–:Dºwʈê‘v8ì̺çZjרR. ùO²%YŽÐI2‰‰«ÐAȬ6… 'Q½NuEæô‘SLî~ñ\š @Œk+ç'ž`‚IØâÊÿÎñNýzF~þ@€·>ø˜Î9YØ£¥´–±KXø$M5¸ôh“€ü¢r휙·?Oíê ñ¼ ìz Þ=Ÿß‘FŠä„j\yÏõ,¸ëp]¤HŒOF–ä0=zEß[Y³uw)!î`æZ§Ì¾)iå1³%Q(ãîë·_§fý†œ"j$äRH‰Ýáàî§_âî=Éÿõ|¤l½c6#n›bµãq’Ñq0ã)ÎÇçuä#Ø×L]ÆŽ6edtDÍúÆ"3µ¥¢Xm|†ÜôãÍ1lBw©ý Ð$£3Õë6Áë.¢UN ³Ï‡¦l•% 6ÅFÛ&mI©•‚Åb‰™•%š®ñó û~ò"ɵVcòãcJ Yâäá³QE„§Ø‹»È6Þ«ÕJ¦à\qT¢©…ç]x\>ò®ìXþ¤ó`ºVÑW霰è¡? àryHIiÀÙ³ç¸çîÛÂìîÝûY¸`öàÄ`UÕx{å2ó7€ß cÇlóüÂÓ?~,þ륰r¦6 H• ÉGßÁýYòì“Ü~×ø£)ú ËŠ‚…Ì=t„fÍ›ÒoðÀ0á'„àðÁCæDøòÒPÊõø;Œ¸¼/>5£ÔÖ-v›Þ„’¨FËÔÚå¶&Puý0šµh̨†Ó(¥-[5 3«.—aàµh™ÀÔé7•“fx÷UÍLñr»Q…®=Ú¡½>š®±ëà.ª$V k+‹ÅB@ „aóï˜ËÜq³# !Y–ÚuHˆ''Î4úŽ9Æ£¯/áÝ…ÿÀf ýf4ÍÀn³S³j rÓÚ›×:y˜ÔF-ÃJn,(’DyŸÓ{ì$ïÏœL\I”I’9ïŠ$}Þc'yoædìå‡f4·Û\Y+tß :Uã¦0’e™¶i<¹ða>úâkþ²|9"¿€÷ÿú‡Ž§G^OŠ]n.9F^ÿÞTINbÅóKÁáÀn³±âõ—X¿i ²,“_Pˆ,Kx¼>øðÍ蚎Ëã¡I£†¼õÂS¥Q/«g_~‰Ï¿ùެ6é?y”§æ?ÄúM[ÌH$I¤4mÂéS§KÛ!ø×çó!IMš5¥n½ºhš†E²ðûMRBbJæ}Øíqìøi;v»ÝŒ¶¤¦•F.·mÞJëÌ̈ÈÈÙ3g¹îú1Ü6ñ“Y$‰Fí[R;µa¹©S¦Òôø¸uîCæø5 ŸòÇ[ZvŽùÎÞ­úçЬ[k¶½¿Úœ[‚F9-¢NJ7úäAw‚,IyŒHì’‰Ož’N‡ôŽ‚]‘óØ?L£ÚY»} ƒ;A×u|¯›ûÆ>@ƒZ yõ“WÂ̹O hß*gœ«bå¯AâS–„غÿ/G8pò0‰ÎD¦NzÏ j­§¢y\´k–‰Ãf’ Åù4•Ì&é´o‘EËúMЮ‡ÏÃãóп]/r[dãòº±)6zgvåÜ¡=¼8ãçæáò¹ÃÆòñs§ð«þrBô>Ò²úãp&!xÝÔªŸf´(бwý•ú)mùöÃ¥Ôm”að¸ò ¦ŠQWàõqÝÄ»#S'²` OŠbcäS¥zÃ)šÁTXä7 ¦i$&‡«?]ÓøúëÏ©^Óoÿúð=ºv1¢?½z×ué–f¼ÁùX°Ûm~c’uv{23³ÈÉÉêYŽf¨ª!žz‹ÅÕ²,¡ªZÔ¬Ð}ÇGR‚“§^ƒß¯"W¥ M*,vÓ¼IÝ -—’ºá.2ˆC¨N •?BÀ¯'ÎÒ¢mKÚvËB±)¥dCrÕ•‘ƒÇ£EûÝÅî¨F®"Ë êÓ¬Œ4öîVWçœ,V~ð »÷àªað•YÉ2 ª(ŠŒ?låK•ƒGŽs÷¸Ö¯w¥IH‰¬ D±dYaÞò;ÈIïŽÅb¡Jb |~3d µªÕHÅ’$™ìVݘ5î ŠÎ³}ß:fOx g\7]6ƒ;®~˜êɵÑtÕŒtÌô*O½y?—MIçá;^4ëtG™.Ó)³ª¨ð}Ü…ØN&=ö¬1$J;/ýãM®q—,C ’ƒÏãÁUXHRÕjQçú<*^O1§¸€šõ›óÆvAíF©ÈŠ_¶s·¶È¢QËv¤çvfËwïï>O±Yozî,AùðypE¾ÿð[çqÍ䇙ñüzT¿«ÝHźn¦l…~¯­È‰×ïÁªØ¸¹ß-!˜~ÅŒ°²]Ó»U:ª;eùdž÷ q åF[#ȆE¢kz·ˆÈCÇþm9uøWsxù<~Í™jȹ£Ë„SRµ,Âæn˜Ÿ$(“BѬM#„ìÿéV›rÑt­°g‹vpË–ø|~ž\º‚÷-¢Y³ÆÜ1n L7…¤,Ë\¸Pfü›Q“·W/U¿A‡ó.jÔ¨ÆÙ³ç#½)‹y¬¢ |V›•î½{°wÏϼøôó̘ý¶Có⮞¯?ý‚C¿dô˜±Ï’þ»·ï4:k›ÒÉ?ÝSNØÍhF¯ÇGšU™1ëvÚd¶ 3à‹‹Ý&IÈj—†¬Èx<†t˜8e, »‡¤ä²s2p8ã¸yÜU4nRŸuk¶ðþgÏ‚!(QdI6½r-RSHJNà¡yw"I}ðMIïˆ0¤4]êØBðè¤Åaž€}G÷1áŠñx¿wÑ!#Y’Ír€“çNâøÃÞ9¯}¼~/6ÅFà"ÊL®–ô)AH¬–+/¼¬˜^ÿðÿ’Ô .S_û¾ qµÙ¬´ÏjÃñã'Ö?ýì1ò ÉÍÊÄn³‚bx…1 •Ay=9}ô¸!¢…€âÂðE$™!ýzóê+Ï¢ Anv&²$a·Û¸_2qZ€¿§ÃÁ‡o½Ä™Ÿ¢Èå ‹ÊéºÎ¡‰GÓ44M£wß>X$‰ÇO ( C/ÎÈ«.§FÍš ½lxÐÛ½0ŒOdg)ÓÇ6ý¸EQ¸kÚTΞ-õ¢¤e¤SXXÈðËGðâ²çKÉ»üÅZöÉ2ÓlÒúç„ÿã²ÐT•±$*ö8$bk-`ä'Õ®JÛ‘ÝøeõŽˆHÌ£ëßCõL£¦l%£]÷ÞÍø‘¹söúæö'¿8?¢]úu€¦kˆ|Á;ÖR·F=ª'×àí¯WòÈ-sxaú œqNÖïZOäšTIw¬<úæ"†vÆwý…€àÐÉÔ¯Ù€¡]‡‡) ÀUȾ7èzj&WƒâRÙ³McÅ!IæÀ©C¬~ôŸx> ÝEômÛnéÙ±|-'ÏŸæå{Ÿc߉ƒ(’ŒŒ –Ì/qùÜäŸ=N߬aR»bcΘéHr±àHHF’dŽýrš´Ü>œ=¾Ÿ;|€bµ B_ü^ƒÀwqÛ×îã¶¹ï"„àÖÙçÜ©C¥Õ; èo ÊœB✉xÝ…x=2I·Ç?94Þ‹ÃdžE¢nµºL>ň\9“Íç½{ä´p’ÖÙR9R;¬ãpä`»g4̈µj“’eÑ…NÇÔN‘Žx9Ò±[„¡ÿÓ4 q&i$V'©ZBXÙ’ßj@CÓt&?>†8§ÍŒ€ÈŠ„»ØËøù×íP=‘‹­I#E3ÌJÒ‚l6+²,³nýV>þäß¹£‡Ïì6.˜ÁîÝûM/û… aeBÏGÞSP½zU, gÎüÊo¼ÏСy‚Ùf³Ñ6'¯§”ÖŸ/®S…‚BRš6!à7Nzuè3 /Sî¿ÇL]ñz´¥Ûn¸‘æ-[D<"¡6 R£f¹2qE_~Þsœ­q{¼(V…”¦F(÷ØÑSÌœ3«UÁåò•“ŽÝnc有¦•$ «U v×ãÅf³šdlÿχ¸fôÒ[aD{œŸÏÏõ·Œ4£ n·Y–HJN(÷ƒ×¬R3¬#K’Ä€NP5•êÉÕ8|ê÷Î O©²Ê 6«¼Ü>´º&ëú]‹ÝfÇå5úC¢3¿}±’nm»†EOJ¢vo «+9!™Ÿì5ÓQB½f—‚R¢ñ;¯tTFÉy<^V¼ñ2Î8;}{u£ °(ØGär£tå>“Ó%¯'Nœ¤sûväf·-‚¥,s!¿€üüSñ•­_–å0ã¸yjK$‹…ÄÄDpáüyÜnV› .àv¹‘% ÇKí:µËõ®x<jשÆêÖ«ËÆ7²mËVZ´lÖ—RÓRñº=‡[޲ŒbˆKr2hÖ’êTÃ"Y°9íŒ\2.Ì»¹`Ô8n|ã~” þ‘‘->Žß¼Ÿ§î¼7ìxb­*æwÑᎠ/ÁÚ DÄJZ%ÁbO1²$sêü)n3ŽEã—ïˆ'³yädÎM3œ+Gþ}‚M?o¤JBt¡sêÜIyé!‚݇wóï7¾"ç¦LÔjîÕTTUeÜø[Œööy9¶ú(Mê6 # Š$Sà*‚…ïv¬%É‘H‰Ûî»Eï,²ŒÇï%»Y \Á>4X5Ÿ—b¯ Ÿ« ,Rj° !(tÑ;³+’T:·J:ŧóÏ’×¶[…©‰Áþi 3Z\…¿šŠTW X`ÇÆyãc„ø.̾Ÿ™fåóºh–Þ=„\ˆ yµ˜÷IiÙ» âÙ¾úú “xòñedµ-5Äü>oýí¯8ð Žà%²ÓæY½æ{3+TÆé®6›;'Måè±#|ð¯wøëko“˜˜U_ôì>¤¬Ç7­ei?Ù´n¹q]œI· å—C'ñù<³èúôhÕËTPè¢C»– 3—!ýrƒ)™’™ò6ެ O̽µÜÉéŠUáÚ)£8qðDЛ¬pÏ3Ó9{ü,®BW˜“ >¨wÎ;M›.™(6+Z0•ÛŒ|ÿ‹ú¯'Î’Õ-ËXá*˜ЮWNâdð^a$ÑÉÎwPœ_Õ3¡( …E¤µhj’%“X Fµª×»Ü<¹ü5²2Z…Xé¦ ôࢥx<^úõ _-«o.aã£mÓti·ÇSnZM‚3°`<¢0ÇѺŸ¾âóçsÏõaµÚ‘%US û¢uoŠÝ…x}n’ªR5©&…® Áñ­0í†Ç±Yã8|ro„gönÞÀîàýÃ…œ>zØ$šªâqÓ*§£õ‰Š•½—@à*,Œ:×D’dê5mƒ·¸ÀŒ¶.ŸuµÖ!$PlvÜA§…$+øÜEhšÎÖïÞ%µ]dÅŽÇU€»8ŸÆ©í±Ú´î<˜cî§jÍxCÔ•§¸€ô¥QÖ’¶‘dðc䎳#m`ÅF¡»0øUÓûÍO_“Û¢ƒ©'mY­Ð¦Ø°ÊV¿å &<{ž½ƒÔ­Ñé2t9F·¼ºÙŒLK’Lv³v|¹å‹Ê¥Ø™ßºlôØBƒæuÂõj™ åKï~¯Ûo~W]ÓIª«ÐC猶Üùèhý4~Ÿ‘®eŒM©”€È²LïÞٵ㫠wÅÂÞ5>ÿü;4UcÐÀži<¡„£ÄˆŠb¾p¡ ìX¨— PINN 99‘@@eÛO»ù裯ÃBÒFJ—Ÿu«Ö2hÄ .DmÌ]ÛwRXPÈ]3¦òÊ;¯³kûNæÞÿŸ~ö%@I’ÈéØžƒóB’’“ÃHFÉsijåæ H²DÞ¹ìß{I2&ûZ‚€ì`j”$IS·^M ‹¸$‰â ¡{uù;¼òâ;* ºÈj—nzºŒçLàÔɳ¤·nλ/£Ví!é]†¡á.öÕ.=jJšÝfç•Y/qÅýWáõ{Ã<èݳº1môÝhºŽÏïcÆá+ê¸¼Æ S—O¿’C'#Y$ÜÞR2ê øYóÓZ®|=ï-~Ç|ª®Ò¤^>[÷9>¿Aø\^7 >ά_";¥!¸Ü¿™/ç]nø#WŸ(.D½_¡©Nù…áÑ›`š•áRî‡(L»ÝÎ?ßx‘3›)¡ÞEUÓ°„Œ¿3ûðÏ7^4SeI¢_ÏnΑ*éÃN§ÑÆÅÅÅlÛ¼…öÚ3|ä¥4&·sG|>/Í[¶(wé\¯ÇCËàÜ»ÝF×Ýx`ö,Š øýìÙµ›#‡Ð"µ¥ÙÏ£¯’‰àõÛ6ÅY-‹,Ñ(·%Z@Ãïö1zZxªM|õ$\çŒ6,ûlŽdCzò‹)8qŽïžù€1/—’;Ï›à`Ûû«IéÐÊôŽ'Sáóo&×b!çÚÞaаuÓ6<ýÎR^˜¾‚8›US©‘\“bO155µböÒ‡X<á1_^ŸÕÛ¾§ZRõ°È¡U±b>ûô+F?r ›ýwsÉjY’¹ªÏÕl_µ;Øtp'±‡¼¶ÇïååéÏa³Ú òa½w¢x’êIU)pdäÀ‰ümúó8í‘k¯ï žóú}ôÉìZn€%xî—S‡ÍhŠ,Éä4ˤFR5Ä>î<%­Aÿ^ãðð{Èé1šœîוÞK×H®VâÂsaGÀïaØè,š:€‘7>NÕê†Q~`ÏÚv¼<$âžöc‘$2;Žäè/›"æüzö 5kÖdú½r÷´I¬üû›´Ïé`Œ¿`5_~õ)|ðµjÕ¦IJS, EEElݺ‰áÃŒµî§L¾—}û6ɵ,ËäõéÏ‘#GŒynö8 ¢FðdYfØÐ‘ü¸a]¹‘þ°ã%U :¨ŒosðÈi¼^?Í›Ô5õDµ*‰¦ã°k‡Iôð½ IDATtNž>oþ/×o’›Ð9"6›Â±çHiX I2ÊÚËw‘;d‘øõ¸AÊF*Jƪ«ÈEÛnYtÔ…õ_üµ(©3ô¿«—ÈrçƒX,Û”iÏÌ0WØ*‹üÂ"úõèÌ(•«~€Q¦+T•¬¨¥Èæó^=n*o=ûX°“œ#ÎncåŸpëh#µ(7«êÞ1‡ÙmÐuUÓøË¼Í UµüÌ8›ƒÆu[ðü?fP}ì?ê*&4Îäo‹W2àŽÆÈ’ŒÇ[lê Y’éÑ›b“P[ˆw$’à0¢2û²ÿèNzçŽOYò{9ö¥àôyiM²¹çúǸâî6̺}1«·|ñ|™]{)6w?ýS®ƒÅbáÉ»n¥^3ÃA{æèaSn”8hK¢;^· «ÍF³ÌlRÒZS½N=Î?J×aWж{¼Áh»¦ÈìÚ+"Ë ©‘2É"É´ÊÉÃã*@’<®ñÉf4Çãò’šcÌQq&UãôѽŒžþ,²,qöÄ/´ív{·|ƒ#>Ù ,Áå|SÛ5æüé#(6{Ø0µHi¹ý9ú²DØ*Yû·¯¢ÛðqÚ¹°I‰¹s«Î|²ñc>Ùø1u«Ö哟00g q¶8&ŸÂS*t˜.¼a-À…âÒ•\:¸6|Dv³vfû[e+÷4#g\9³0 Ý@¶ØAVBS¨JLBÔŒ!¿Oåá1ÏðЫãÍg•If³^¹‹¥9³^¹Ū`±@F‡æ|üÚ÷xÝ~þùÅ¿Éî™~ßàøiÖ¦Q°¿øC ˆÄÕW eÄÈÛ¸úê¡Ô¨Q5LªªJAaÝz\n€êÕ«FmÄ@ ñB,Ã0Rƒÿq|`ÌȲL·n¹¬|ûC:wÎá¾ûQPPTîZùÉ»Ì{p6oÿõ-N8IµêÕÃŒ¯’μú›U$W1þ1Ý…À“}9œMë ¯|r•dsIÑÖ&£\Ò{@ž)8Հʒù/òÜ+sPW±ÛXÞ-˜.f(ßµëÔ =e O¿ðŠU!;'wp}s‡3‡#]׉w²æûMdf· ûGosg=Cr•DEÁçó“Û)3¨ð}¦ u¹oLŸÉÕ×ýƒø:ñA^£p(Í+bð˜Y1å<½üË i«²bˆDG•P°¤ä}O^¡A\$³þ\e—ƒÛ³ézÓ ,+÷-™È´»¤ø´¾oêׂ¢.n¯¯ìeç ¼²—®­»aµX¹uèm|½ö+TUAUÕ úŒ¤¯ÓŒÈI¹»œáÏß@ûfpøÈõŽ=i¨šÊ±3G1û ºÏž1”ƒ$Š´ÚŠ¯Ö~yαæ)+6VyˆsÅràd.“™3Eùѱ˜D…¥EÁ^n L¢ÉE)¢Ðᨮ@QCkXLfz´êBai1¢(2¸Ë*¤KßÞÎ3Šì¥´øŒíðzÊyðéI¸+ŠqFérý̉}ôr/÷Oø’åK_@ÕãÝ;\µPÙHÿI o ºÝn@0¢ÉÉÛ¹îÚ}QqÁpr%%oçúënBï{aA’$¦Ïx™3gôyUPp»ÝÁwü“C‡r)..¢a£ÆÌž3›íÜ-\/¤±Éð'2yüí¬]ŸÆµ—÷Æl’hÔ 6ï@rú^.î×»ÍÊÛÓá‰çb’$vdî7Þ¯Ùl¢g—VA²¾QýÚLyæ.#Ráöðð?¯BU5†]Ü•‹Ú6eðÀΆ^qF;ÔE€²’Pò’Þj6Ó÷ŠþlûmkP´QUU2~ßÃå8¯E«zéK‹JÃËM& MÕ#o’$Ò³KGn»þê`š‡ÝÆÛÓ^0ÒŸ ia!=:_Äêõ›±Ù¬II –Ïvxyö|ƒ ÜzýÕ”8ËaZµªóÊnsiDµcœç#Š"Ã/{˜Ç^£ì£Ù¼>î« º@Ã\ SëÞ,øúe<Þ $QâàÑlVm]†ÍœyüŒ“d¢o—Ë8zê€Ï '΢Së>AQÌV]º‘Š¢³ùDÅÕ6Hþ‘=Ùüsâ+¼0w&žŠrd¯7ÄÁá·Ýe¥ÔiÔ„¥™Gñ¸+0[-´îÒV]º,48”n5ˆŒ$I;°—.#„[ìNÒS†ÏÛÇÈW5(/-ôµÔqFÅ!JgŽ6ƈ 䤬Áî¬Òìà %§±Ú]˜Íú3./)D%:ö»†{žû“‡­’´†²¢¼ç†FûÆí‘æõZ¼7™ä½É¼õð\~Úö#¢ b’Lú¿UTů)¿²`ôBìVG€­ä%m*Wt¿UUDÉ )M|Ž™Ù¾‰Ãê0œGU˜BåÇ]w=ø¹÷GLо±ñº0.Ì+ÆlÑ×yñ®y”—êÄ«(¿Äø·¢ÌÇ-óÞK_c±™qDÙxñÎy\Ò§WÈ=ôÚ1d܈ދ½{Ò¦í¥¼»` :´ö†V¸2x ?ýø±íÚ±•Ý–dA”.\k?üJèŽÛ¯çó/~Âd’èç+N—$‘Ûo»Žuë¶P^vî†ÒS8zøˆñÿ+¯»š'<RŸ‘•‘IûŽxkÆì°ae¿1÷ÆÔ™œ:qÂð›ŒÔ+…¾ú2‚=Ͻ<)Ä«STX‚ÙlbÒ+£9røš'ŽŸaÚ¬q,œÿ…Qt_PPÌŽÔ,^ÒÓ(0×|ÏoGj6-Z5&)q§á=zø—]9€:ukóüKräð E¥¼¼‚µ[>eäèÛ8rX¿§ÓŽªªœ>•Ï•×\Ò­:æKrNŠþ[+ʸeâ­,›ñ5š¦Ñ¿S?®x ²"³-s;³Q©W«.#oxˆÆuû¼Â| Š ¸ûÊ;)òye6¥elNßÌWÜî3^«tT¥+*°[Ö_ Í1Iu[·ä†»G¢(ŠAL’„¹N¾»st&ï«ÙðGCºwëèèú$xäá;yÿ½™ÂH亮'7÷™™»yú™ééZº²]ùkÂ9_PãfMHOÙaüÜ#Oê]Ššvò,I&²,W® T,Æ=·—ËeŒåò²rn¼öL3ƒUÛóô“TTT/tJKˉŽqQ\\ÊàËúÒ¹[[–¿‹/¬­©ɉ™ÔoX§’¥ɉ™ìÊØÃ›3?¢aÃ:•mÊ$‰’½ª´´ Iiܤ>Ó§¼ËáC'(/Ó{cœ-búËïÒ¸‰ž»÷öìÅAÝ¿ÌRõÏAC Nè JÕTêÄÕ5&šÇëá¡és¦à ï}¿ˆkÇ_o,B˜W˜‡¦iÔ­U7$ÒáÏÒs_¬ú²ÆFÿ•ðE@Bª®…“§OóÓÒ÷©W7ªünYQˆ‹!ÎׯÞåt°*aygÏ^ØÊ¼šßØ 6\ËJ˰ÛíäçåaµÙ¨¨¨àPn.‹ÞY@·žÝD‘ŠŠ ®»éZ¶jEÓfMüQ“ÉÄC> Àûï¾GÝú•ïõ䉓X}m1EAô#àt¹(.*R^®èɶOW£ÊŠÑB70’á$Úw½iE'ÏÆN ! D“î­)9]™¢ š‚×^¸èŠ^œÈ9ÌÙçHú|-fû¹Û¶Æ¸bY—¼Æ’(Ñ¢A ¾šö-^7’ŒïÒÉØ—Î/[W‘ Y‘ƒô_Œ3†ß¶ÿêkò`Q\xàéCÓ4žþ×8ðÀ¾£{¸óŠ»‚¢-Š¢ãŒÖ‹XˆuF£)  äQ;*ÎúþT,«ÙbÔtø=ân_Á…¢*AÑPUUX›¾™hGTX)«JµÆ| ±¤*нþà\š·íOŸ!÷RQ^LNÆî òpæÄ>J 1ö½ö£¬$‡«¶O•ðá·òìì/ A‘õücMS)-ΣS¯k‘Ã,†*I7ßtkµ˜#GóÁ¢%ÄÅÕº éW›‚†½œÃGŽÑ¢y ]Ò‹Ù‚"+DEEáq»¹ïÞ‡ø`Ñ_”E«öùY,fž3Üg¬êéuâcøyu"Šªê…é& ¯¬íÄå´WK"6gøÞ‰ z£Œ=\»V4^ßüÛ¼=‹Y¹ä-¦°¨‹Ù’>-™$vlLý·É‚ì‘™õè Zvl…ì•ycÌL´høo‹^³äŸ-ÔÓ „ÀqzÏI¿|CÓF |ÆdQ ¾V>¹õóêõ,_•@L•¤cØ À"_û…© UÆåŒaÏ¡ *<ø"w‚/ [A´3ŽGs(.- sÛ¾Øm® SNÏ$1£¨2Åg|‘ÙàT/‹ÙÂÈá/Ðm„“„¤ycñ8¾œµ€ño<Ë©üÐõTJ}æ^Ý&Íxîõ©˜ª4öÉÍÖ3C*JKQEö"‚(²dÖN>ˆ+&Žfí;RRp6H9EÅÕB’$žYð)ÇöïeÛo˹õÉg dÓ˰Úá—OÞgþ3cxê­E˜Ì8øÒ§Ú÷¸Œò’ zÞv—.C=*Kf>H½¦-|iXP\ª"㊩…»¼$ĹR¯I#MŽÉb#+q ‚ RVRVލªJútÒ¦úÓe ÒÍÕdK¢žÖ­ew–o_Žª©!ºß,™±ZlôÛ“7GÎÕÇ’¦ð{ÖfîvŸQˆçŠ ›+&‹‰&ÝlÌ MÓÜ·7enΞ*DEƒlš±Eæ¶½ô¹¬3š¦asX‘½jÐÀTU•¤µúZ+ªÏiS[ŸC½†t¤¬¤\/Oj qq1¨:ym6+ã&LÅjµP§npÚS Wàèáíœ<•Çà!ý™<ùI.»lO™úº??ÅäÉO²üǃòMù¥zb±Xø×Ç xüþG«ä«¹ cV¨Á+ûXpÈuØÔ+UQ8zäß|ö·‡¬ŒLZ·k꟥{ïžL|âi~âQ–ûcõ×ñʼòÂ<\.?¿Î(DOKÉ2”~ÆuyõÅùH’È K{q(÷8÷Áf·áŠrbó¥cæÎ)J¥ñ¥ª .—Ã0Ìf“¦Ž¦àlQµ†}êî4#"! P'¶Žñ]Ø:!ŒU’D†ô§+—¢ªjÙwt? Ÿ[Pi4ØA*ÂwÏὪª¢¨úB—š1ã… qï@öÊäçåóÖ‚±w÷¼^/mÛ·3æ‘æ#àþý5U3jQDQä³ÅKBŒÐ©“¦0ü¶[‚ŒÑœ5©¨²‚ìñröð)4U3ê3 ²èÜâ´äA”D¼½»˜ª]/.(2b¤Á¹ìÌê3šeãÞÅl·"p$mo¥Ð—DêµoBt==]4¾UÃs„xS÷¦á²»(*-k¥nø¼¾ô5JËKÀFØhžŸlø‘_”o´Ë¨l}}q×KôÔ+ÿ¤öu%øú«¯øà§EAç¶7lÎí³Fáöz|çÓßQ´#ŠuéÁóÉ䋞Ȋ¬·¶ŽŠEQU¡Q|}^Z: ‡ÕκôßqÚÄ8£Ô+'»ÂÒbã<1Ž(6ìÜÂ¥úAiQÈ3kÓiˆqO²ìfñ›wqÝÝ3øç“OPQVHyi¥ÅgÈNûI2#Iföf®ûügq4‹á¼´2qFâO4kÓ7$ PQ¾þæs^˜8‡Ãr­¥Ÿ-¦uÛ†L~ñÙ ’“&?͆ ëp8œhš†ìS_OëÖmhÙº>‚ räÈaòóó$ ¯×«¯ ^%%q±NÖlHcÉ»X½> YV¸áª¾œ>SX© <^|¼‚7æKt”Ã÷¬ÕÂà×ÛнsKî}üÍỀ݇‰q…îÉ;örôD9{[éx <§ ùPg”³†èŽf´ç ‡Ö]ÚðÉk!{½8¢Ï#"è)Ì5îP‹ZIñãëå¿0⺫Œ÷àñx¹eä“,þê{V­ßŒÇã õº0e‚ž&uãýcX:ÿuÝ u?ËÞŸkÈûøZçör»ì1””1øÁ4®×*Èco·9Iß½•Òò"|õ2Ë×´‘Ÿè«ªÊ®ýÉdíO!0È$«²ÞL¢ä,ž2²ö§U›B_–kÖ#Rô|,pÙí÷bwº0…"š yÚïêëÙ²òGãÝiªÊáÝÙDÅÖ‡¢è÷àõz wYóŸM“6í9‘{“ÅŠÅb¥(?/$J°üÃwIøösTEAözøéƒÉÔmÒÂécü+ Õ È÷&ß‹ìuS¯©HâêÏ=mº ¦¼´âœÍR¬vqucøá½(Ú®›+ª‚(\Õój÷lky/ӾлóåéÎá8§~Þ ™ë©U›±7ŽgüãBÛ¥ûæEq~)'¡ë ötЖ„o·#<·’B=bíïn¥G¨åŠŒÛëføÄ,Ÿý#ËgÿÈð‰#Œ(F +¿ãòÛ8]pUU c¤œ•1j棆—ÖéÆu1þα!çõáëÜôì-ÿUeõúMÄDG)>¹†BÒU ›À¢E%€¼ÄDG±:a“>¿…ØØh¢£\$¬YOLtt¥ÑçÛ¯n‹æÜp÷H,f3.ýЧÇ<2öN?Ž¢ªÔŽg×Î]¤%§Ðµ{÷svâÂB=ŠÇŽMÂEÉDIâ™'Çc2™‚S}Øìvç"y{RÐXTÙã¥nÛÆüöÚxËݾú ½kZݶ)Í/¢¼ ²$?÷$¯~o…‡Æ][Ñõ¦AÜžÚ†3À8Q[Œ“½2Ð|ûI&‰èzq¬œºQùl䜠cªÊ¿¸è8<^«¶ÿÀßâffß‚¥GÏÕßy£ðÏsU•hG~@µ¢*ü°ñ{ö}sˆ^úpèD.øoÅŸíÑ F>øH¨Ñ¬Nc¼Š7 MJÃis±9¨èÐ-»¹cÖ(Ú7nÍ€½¹cÊÝ´¨×”õš2òí |±áì){ÓÙ”¹‡çM e›n8­ÖelFN|Ä=i†‘æ°ÚY¿s+—tê7 q¾ÉleäsßñÑ·"{ƒShK‹Ïpæ$H&3qñM‰‰kHvÚ*4ME¬v=/Yï ^ùœÑµ)+©<‡90óÓÒçc3(-ÉÇã®l_“»g]ûÝrv»ƒ¼ü3dffT»Ð£×ë½`‘P\¨†5zýz3::†M›7 ¡1tˆ¾róС—‡ç¯Ì£O¿ÃÌÉ÷qã5ýظ5“å¿mç—÷ߢáH0›MdïÑk‡¶M(ò53±Û,aÏ;ìâ®FdÅ$Iè-ïëÄÇðÓ¯ÛxiÂLë+Ü9Ȩ¯Š-òƘ™Èžðé(f³™ ÿz6l'+¿, ÄRƒÛaç³ÙK°;mzd-ÚyÎÖžáÞI`›ÿؘhÌfSOvy¥ã( sÖ=·\Ï΄Ÿ8qª²åxIi)ß,šËœ…süÔ.íß›%Ë~ä–‘Oñă÷°æëÂ.|èw 䜠ÂSF©o%nUUIÊL EãölM_(JX-v&̾E•éyÑ%¬KüÁ— «°k 'ò³-#%X70Wmĸs]È6ÝOE9ó×oå©»@vW±®˜XTUÁ£€Ùb¥ßUד•¸%ô<‚€d2Q^\ÌGS'â./Ó»#ÕŠgûªìØœÀœé“9{ê^‡fí;VFL£c‚"9é›×ßj÷a%äY椬¡Vý&¨ŠBF®Xßq¾,ž†ª"SV”r絛vM_ºÔmÒŽ…/ÜËÁ¬m8£kS^R¾a§FPŠr¹§œi_NE%Æ0޹ÏÓ£ƒ×«xÙq`/Ý1»ÅnÔoÔrÕB®ÆAk³ØE‘q>²0뛙حv Jò«qŠW¦lå—pùmývK_NÎÃïÖöÿ¦˜ÚQ(²ŠÉ,!ˆ"1µ]¬ÿ~»±’úƒ®aǦldLæ¶½ ¹¹O<ó;?®o2É×骤° Ù«pöTª¢òæØOŒ40Q¾rP®¦ “·ÛÃÄçg2òÁÛ™ñÚ;¸ÝRÓvU+/ ʵìß¿¿oI2~ãb£Yé‹p:¸ÅHÏèÞ½#……ņþg@’D®½ù:–û£1 2RvCYYÙ92i”e£Åb!&.Ô˱''EQÉÎÜGQaIÈ"h³gèµ3©É»°XÌ|°àk>ùð;5®Gj²þ,7©ÏÓß`ï½çtç®íp:í?v¯W¦q“úL{q>e¥Ì™ù!§Oå‡í2Rn›Zѵ‚;,=ÅÉü“Õ q£F‘©%©ê(-/åŠ'¯ægæ„ì»pò»çðd¬ªüÙUÕ˜÷`r: å¹®i’$öíÅç_,#1%1:ʈ„ âbcˆr:‘EUùù·µ”>@”ËŪ„Jò"FG±*aŠtÜ•[ IDATª ÒUë7…¢ÈÄFG#:¬Z¿ ¼bc¢qºœL™õ6«ø\ö/úe_~MÞ™<ºö膆f¼µšÆ‰¢(ìÙ½;d¿’®]'žÃ¹‡È=p0dqN‡Ã‰ªªØ¬zŠW¸";«+8]Áâ´1âíÑAµþÔ¬A\Ká±<Êò‹Q%(­ªFƒ%ÆÉÉœÃ¨ŠŠÅaåXÆŽí<ˆÅa­ÖÛX“1ù½ç9pü¿l]¡+ì¬í˜MfvìICvD¯½™>~f5Q/ÙHK‹r¸pÙ]ƼQT…õ©ëÈ+8Ã}×è+¸ïÛ~(ˆèà…Ä]ÛÃVAËúÍØ™›C´# USIHߌÕl5R«A ÖƒËîDVd†u½—ÍÁФ5ü–ºž¦u“Íisp4ï_oú‰û§ÞKƒ6]È+:ËêÔ ìÉIá뉋ØsL¯Cr6o‰Çë¡eýfAÊQöºYôÚM˜-vÜ¥tî}½Ñ'¿¸à¤oA+VÌcã/ï°+eªª Ë>›ÿV{ÌšJiÉYžœ¶¾R™+þ>3ª"ë©f^}Î#R°Ù£ÃÈ}sØ¿eYF–j׎gWÖNƒ”lÛ’þ§Ë›˜]?8NÜî ŠŠ yuÚë[Jýf-‚Œÿ/ßz–º1gÌXú\ñÌ–ÐV=È]ä?ƺo>#+q šªP”¯{ìg/ûœxA aÙç¾késíùdÆ‹$¯ÛV%ªáÓmV;‹^‰Ùb#'e ²×8¢âðºËÙ¹uŸ¾öuµ"'y vW,²Ï¡`wÅêM1|SãÓ™S9{êV‡+ˆÈÚ1d%®"+qf‹E“‡S¯‰^÷áð¯ßä#&áp´#:,i÷?ïƒ' j*._#¿c§vTmU¡¸¼˜8W-ì;/CËú-ÃÖrxd+“V2 Ã@cÛÆÌ (ŠR-iñÏ¥#{O²+q‚(„,!`±êéY¯Ü÷.^ ‚@IaÃFôcçÖ½xÝ^DI$J&’©úº@Å·žÑ¾ŒC´îÜ„Vš°/ã0&‹É *Æ™üõJ5Q“I↛FVF|-M&ÅÅ¥z!—$òÖ›/rˈGÃZ—~RóäSS(((bØÐÄÇÇUÛQCW&’†=ϵ"DIâò>ƒÙºi‹! 5m6EK@ &6æœ5 SçÌð &£ï}„áwŒÔãÇLgømW2ÿ­%>o°g@šT òo³ÛmÄÇÇqpÿCPøCß ÕÕ:›•ô´#ŠR^^A½ñhšF|8·¥ãpØk¼EQÈ=‘Ësó'E$,& §òO…=&¯°’Mè<€‰ï<%©ú[4lÎÏ›Ö ¢n¡ÏúÉ7ÇÖLV¦î 2ÌÏÕ EUY™ºÉ×’¶Â+sÓ´¹Ø7{M9ÌBW›·%‘œ¾—Ó^/[“R‰kرNcÖlØŒÓá€âbæ-Z̸§Q·]7ýy4k‚«iGÌf«Öo$6ÚW#bÂn·år‚Å©{ÊŠo!A•v­[qí’°jM5·G÷ʉbµÞIEQ8¸ÿ 1±1œ8vÜG dgfaó)‡C¹‡*†Õª§ V«•C¹‡ÈÞ•EIIIÐ43™LÜxËM,ûâkvíÌ !§ƒºõë2oÎ[Õ“@—ÏFÎ1 %³ g­hnÍ6æ¬s«ËÎöOW“òeª¢žWás¥áª@¸ˆU‹a«!þ¨ÅÊ-?c]VdUÁauðì;xî§Ù´cCÍ„ßëæå_âå_ ™7ÍE—kô6…‰»¶DG1z ˜ñžÌ¾}þâ.õyµu£FÕt’»:m#)ûÒiX»>/|:ƒ¦ šsëkÓ«uWÖ¥ÿNŒ3Ší»SyîãWIÜj˜û#rþzfuënP`mºNŽó‹ *N³Aý¸ºXolнIó°^ÝòÒnyèm#2qóoñê—"Ë#]ªª×TöV`¶:رÅGUM1jEºö½‰[¿5"Zሴ$™±XFI`Ú¡‘"Öº-Ó^}Ñ“þqœš–6¥"|×$Wcbbƒæ£$IL{õE&½ð mÚèí©<@×îm)..»ºùqÚ‚î»÷åc±ù¢UI¢¨(ŠjÌÕZ±Q5Úöý{µçè‰<¾úz*>õ6>õ6ß/Ÿå{ž ›3°Ù,ìÌÊ OÔ=^Þ3‹†-¡*jPj–ßhTUgthŠ–ªªdlIÇárœgÀYï–åŒvúƉŒì•Ù·s/QqÑ!dáÅ×çѯgW²÷îç¹ÇGât؃Үü§ò š_PPÕ}rÍû\Æž¹«ga–Àwi³YC")ÕE?w®ós•샩ücL+£I„~x|2CUU¶¤¯&ÆU oý+Ë^^^02$­Ï_ßårÆÐ¨^ &Ì™T}zšÕʨAévÉ0j7pòÉŒiزM€‘iÂb³ñÝ‚·è{ŵaÉ¥"ë‘MUIݰÚHÍ%“!¿7þºÅØÿðžlžzý¿ÃÐ ›×#™ÌaS¬Ì;µêÇ›“LVÒ*$3,~õŸä?€ÅæÀ‡âõphw2±u¢øaÑó¬Xü uµ&;yÄQ»~3<cP¬Ýi#sÛ ²’~ÃîŠÁjwéQ—€,{Ý,š| µêAËŽý+u™(ñÌGO‡¦>  ¬€üâ|¶fo¡w›> î¬wmR§ kv¬f÷½Œÿ`,å¾ô8×Ö‘§ª ‰{¶3üÊ›*G›§œ—?ŸB³:ͪðªªJvò~òOÍY‡/—8¦v…ù%AI ²G&*Îi4m Là ŠŸ-­q.[ìÜ^dY !>ÆHò×_„«ýLªÚ×wâsQPPDRrŸ}þ#Æ :§pYþó#ìÝ®]+ù,n·§FA?ñB,Vëy /! }ï#¸}‹–—/oLIóV-Br54Ca½8þù %ðýW߆ýýv#=-«Õ ¿fù÷k«*¿|eÒoò ˆõÉá»rŒ}ÿ>Waz`¢’h(5–ü¢SLâÓó:§Ýê$)3·»œ¾AÃ:ÍÈ:Zíù3·é2å‡÷Þæ­'õ•»ÑÑúÚN BåêíR ÚMúæ„ WñºÍádÉÌ)ANâ>‚@T\-}ý#Àëñ„.hªÂÙS‡ùdÆX$“™ŠÒ"Þx7í{_NÞ‰ƒlYñ‘5¼ù°ú‹7*džÙ¡œÊŒ»ŒÃ»S°ÚM† bsÂέ+°9¢0[¡i»ž,™õwv_DDQä›ùó8v`g¥.;G=«Y2eâtái÷$òæÈ·èÒ¼ û`ÜûcIÞ›LÛGZ9á7gmFÑÂÏAI” !¤sCæÔœuJ@VŽŸ^ÖO_wòóèØ§6‡Õ÷.d>žþ=Oÿ>ˆ¨%|-ï‹òKjüýCnêM·ÛQ•Ð{«ŒÀÚš¿¬U/6ÒøËÍ=B§Î—‡5P@£–¯@Ë­&½Îȇϯø/×·p[ÍÄCfTõÒ*Š‚ÙbÆj³rÇ}w1é¥)!Ç®ûuMˆá¤**Ö$„(—ªWNf J[Î.=Ô¾7'Ø›d·ÛHó¥\Ùí6öä4^pZrªª÷awè†èg‹4RÜr²öc±˜É޵܃G9”{Œ¸Z1ÄÆE³#5»Z"WÕ¨òx=Œyã ?–Œºy_¬þ²²>§†ÞÕç"5™±‹¿&ºa=v¤eâ²Y‘•Ê”©pdÃÖ¸7M›K…W®L¯r:Xžœªi!ÑEUÁùˆnÜ „­ºe™•©™X,f¶î9–€´nÑœg_~ A°˜ÍlOÙA÷ÎѤQCŠŠKØì[W懫xå·¹ãá'˜Â=³ÇðÂ'3˜ý]e£‡ÕÆsõ(dâžTêÇÕá¹cã^Z: ³dbûžT¦õ6GóN°è×¥ì>ºm9)üš¢ËÂ#y'X¶!èyɲ‡OçÞƒÅêàû' È^ÌV;Ùi¿aµ¹¨®âSUƒlÈÙB—~7!Š&££•3:Ü8÷ðã§Ï2ðò‡9qxW@ƒøú­øxÎÈÞHUUY¿~--Z´dæëÓðx<Õê9“Ï [PœSÝ»W_'kÖV’ÏÔ´d¢Ã´ ÕÀ˜ú_L·®=¸¨CGF~³Y딲Ù,ŒjnÈw­š7`é7 Œl66›EUÙ{à8»•ƒ‡O±bURÐâ‚á°Î×kÝæ€”3Gh*•ÓncïãÕ:TEeÇæ4öìØM³¦ÍÈØšAyi³›Á飧ٽc7uÕ 9®a‹†¬úü×°ÆŒÃåð-ˆˆ¸+*˜=v&«…¥³—`óuþŠŠ"+iWо²¢à°Ûiߺ%—ôë¶6Ï$IÜùØJËʉ‹‰¦"@W–•—óĤWƒº;b͸Ýb¢ô\³Ù$SüÑ»ÍÆKãGógBQe\ŽVoýö¼"G»ö'sðXŽQfµ8HòE]jr •—”°/C—Ÿs^z-¬íçˆvUK@ŽîÛì,ð‘Ùë!uÚkÊ%…z$*}SBXâ«“¤œ:²›]'‡r’Pe/f_š’#*ÍwÝœäÌ ¢ó˧Óùð•;|D(ЬÄU”—r`ןCºOf<§¢Œ¨¸zÌ5„ï<Ëgo{øÍ(w{«¼gøäÞ™!å¹ÃšªRpø4+§. Ùï\ýB¿¿ðá.ÿ¡ëÎÇË'ÝÆŒ¯ß6jXüß­J­L›ðÁËúÊТĨùÏ´é_½ÍƒsÇ…çUdRö¦s¶¤€qï¿Ä¦Ìí$íIcuÚCQÖ”Šé_ûC‘=,w?ŠâåÐÞ¤ ªpF¥×YO§Ú¡+ŸÏŸƒÅêE‰œŒ5Aí{ýQª …)ŠBÂú5ˆ¢ÈÚµ«Î­V}éÂÑQ1AÙÔ´dÜsäççár¹ð¸Ý!¿iýzý7$§lç›o¿dðàËX¾ü{^™:¥Zã¾IÃ:!Ñ|§(8¶ …ïrÙÈÌ9ÄÞÇðxj7Óßú“I2ZôZÌ&zö{8äú·eVëttFë¿åäÒ⢖Ì~f&“ÄŽM©Ô®Çía§/ͪjÁ°&}CœQ¬[Ò±9m¤¬OÆî²ûŽ×ôNYªÞÄBˆšW7w¢\NZ6k‚;Ì|÷“×ËäYosøè±ýV]S‘…³^æÚËóâëóðx¼³rí¢\zQüÉÓy†c/1-ãO•ªª’¹7‘Å?½qû*$e&y˧½7*lç«pǦo®™q™[W ?úÒ´¶þ²Øˆ˜Ô®ßœƒ5xŸÌ¸Q’P¼rs’8v ó½C1z…§¢Zy~>ÛÏW7„Ú¾•Q‹};söt6‡Å˜—Êh±­ª*Û~ÛAýfñ5º÷®ì÷0v—-¬ê‚WnS…]»öдi#,X¦ÍI?qŠ-[õB¨mÛR±øúŽoÝ–ŠÝn#::ŠÅŸ,CÅsœš®›w&Ìj•¸†Ñ·:ÚöüA^§¶­ycêL£ éùàãEË‚&_8#,-%Ëè\øwUᬄù[ Ê·­ü;i[:»³uïñG‹¾ù·žÛë1&üËв ß'îÀf6“¼?—~£žcÏñSPṪë~'úÒ~lصSí8p{P5 —ÍÊîã'yõ½¥€Æé¢ˆ¯’Ħì½|—˜ÆIé4©eÌ]±–ŽwŒfý®ÝPVÉfeë¶TÞ^¹Ž=ÇOq|›¦¾xøH¬UHHe±yõÑ0EQ VU‘ý²f}@4©r¥šýý)•DÑc\K>Ï5TΉ‡ÃNEE‹ßÿˆ‚³4lÔˆ“'N¢iV›•S'Ožó:¢(’‘–ÎÊŸV Šây‘ª„A”$–<ð:ŠWK&‚®'‰ âιšª¨dý–Ìÿª*" ÞóUzŠªâ1ê1ô–ÎÿŽ#bwÆšóòÌÔªÓŒ¬”•d§ý†(šøîãñ©©j,:šC\f!!ܵ_}BÂú5çumZ·5"%çð“¨LÅÚ»w7/L~&Ä–à‹ºx<¶lÙDŒ/Rb³…OÑñxeÆN^ÔÐÅ¿ `ãÖLCîK’ȓϿÇÚ ;Îë÷$lÎ@–6nÍ4λ#ó@•÷¯߇F01{Ì,ÜånƒÜ;£ÔiT—SGN±nÙ¼¯¡÷jÕ«ÅÎ-(^9Hdøž™3Ú‰»ÂÍ;Ïÿ‹F-é ‡„ek™ÿÌÛX¬2¶ìÛ2Û嬯éå¶GƱø«ïÃ’‹§ÒÚçex™$‰QÏNá›å¿fÜ”™:zœ²ò >úò;úöèbÈíUëÿ“ ˆÂ¶Œ5ìÜ›xÁóÿBçm Ì5R¢‡oìÎ2‹ŠÒ’pÊÄØ'ð¾îÛCFM²FMUÉ;v”^™ˆìõV+ïIHNÊšJ}1ó!ßv}L椬A”L|2ý>} j~›ª*hšÆ®Ä_øîÝg*Ï9ë¡y£×6)Ƶÿ ™üWA’D:öm&%ë qÍN<îÏ©¦**'ç3wü½p½d'íÇë‘Q2[ª˜æö1™$ ÐDQÔ$I2¶ù·WÝЬV«öG®å¿NϾ½´žýz×Óüy]a>#Ÿ¥mÎL Ú&I’ÖoPmÕöíѱcj¼žÙbÖ^™=ýßïßác’LÙµ$QÔÍbª¼¦Ù$iÃ:µ×fÞuSÈþ?v¯Ö¹i#­a\Œ6¼ow Ð.½¨­h]š6Ò>}ü~mHÇvZ×fµ®Ík×õêâûM¢q­Gß«5©§]zQM ÐnêÓMA3ùöùoÿˆçù;:vî´ouÿ׊|"Ÿsʳí<Çœôÿåú‹Eû×¼EçÐfíÕi¯ÿw8Aßz…¡«üŸwßùP³ÙlÚ°¡Wü19)‰ÿ¡÷aÒ|q¤véMC´†-i€vß jL©Úý“ÒíîgîÕ¬›öà‹kµëÇk·?u§ß ^ûÖxíˬeú;“DÍb³h—Þ4D»öþë5@Û¦¥h}¯ì¯}™­ïs݃7h?Y©Ú½ï×Lf“f2›´GgèzûæGo9ý%ýЉRµïÅb1ÿíæa·K†Í1³ÅzÎy'VyF¢$EdÚÿÏ‹‹ÕÌ“&J¢Ö÷Š.½-"‰ZãÖõ´'gß­™-A¶å‰1%IAƒ¹&Ò³o/íÊë®k­KÙd’ù¿ý‘DQ³šB‰ÍlÖDAÐDAÐL>¥ì' ’(j6³Y“D1dŸpçƈéÔˆŽ‡È'ò gXJ5~W‘¨zlàÿ¥ÿ2£Ìd6i¿þ”vå]WÛz í­žù„A<­ëÅÝ5‹Õ¢Ý;ñ~ Ю†‹útÔ^^újÐùFŒ¹M»üö+5@k×½½Ö¼C m«–¬Úå·_¡]sïµA$ðØ®wŒÏ¿ÈæŠ<‡¿ƒÿO82ÌÖ°$ý¿ïÚö ‚(Išd ?y^™==H Uç›ûþüÈà|"ŸÈ'ò‰|.˜ ü?cf=â´$¢$j&Ke¤Â¯‹Mf“ÖýÒ!çë1¸§&ˆ‚qœÿ|=÷4öytƘ}-J‡Iäùü>ç\þÿ.$I:gÇ®ªrŰ‚ó?óüDADðw„(‰!-ÎgÛù¸½ºï#ˆ ‚¿þ« È_EZ"ˆ ‚"ˆ ‚"ˆ ‚‰ ‚"ˆ ‚"ˆ ‚þË FADADAD! DADADA„€DADADADHDADAD! DADADAADADADHDADAD! DAæž»D IDATDÁ’$ý¡ï"ˆ ‚"$‚?A ‰HRäµEAü/‘!Ã.K6ôï.©TêbD?DAÁŸŒÁ}»¡(*“F߃Ýf½ðñ”ýç']„@Fðo¢áþ>×¾Æõ"øÊ Qä×]Éè'F¢(JÈwCÈIÿ}øæûO˜8i‹%òðþô9(^Ðö"ˆ B@þÏ-‹Ù|ÁÇ[Ì&Æ?t+MÔeP¯Î\6°'åî &í.ð‡ àªÄÅd±„õ´ÕDpL ‚(þ­=tçól[¶i´ï¹Ž‰–üD`pþH¢ˆ$Š éÙ¿úýE‘Á=úÿ[$â\׈à/„û÷à_sß•«& É$d¤GÏ®´mßšòòr4´ "˜ÿë„3ˆàûô”µŠ¾Ú­»ñÝùlÿ;Ã$Jçü»¦mdŸšeÖ¹·˜¬è’qÆDÈ)Eeî‹ðúÄQ|¼¦ جú÷èÈç?­='Ñ D»‹ÐþÒ‹Qõ‚#!ª¢p眙Xþ9o6ñ͛Ѵ[—YUîœý»Í $&‹…»ç¾N­&©Û¢9M»uùÛÕþߣ**N‰ÉlªvßO—¿Ï­ÿ¼Y¢H·^]ªÝwáóP5BHþçå‡ÂÄûÆpÍÀ¡(ª>n÷Pýx%®8”ÇnùgˆÇü¼Ç´(q©ŸÄø +I’"Q‘¿Âø­êð‘$DQ"#;…—¦>‡ÅbÁl6óþÇó(/¯"*•ïH8'ÙLÝZðþ›x¬ü·=ߣÔlfḱA¿_QUö¶”îmÚ‹!Ý»‡u¢U·½¦ùõÿU'ýÉç¯J$QäªvCþ¸¶Ã¥!dìªv‘D±F’áßçüȆ Q±™íƶ‹šôÄl²dAÄ g±îåÃ4ˆkZí9Í’™í®K<´»ü/'#¢éï-kÅ@õ:ÃF”DÌ3f‹ ³¥Ò°2[Lˆ’hx_ŒÉl­ÙÃïß_„ ãk aú…‡$IXm¶e(I¢(ß…NF1àúÁÆ¡Ãé¬ñ:~øÿ.!ãÿUO×ö­Xµ)‰w§ŽÅRƒ‘±Q.JJË‘«( ‹ÙÄœF£T1Výh{±.:_q#^‚p¼ŸŒ˜,f®û8O~ûEÐwþïË q—–Q·UK†¿2‰f=ºF÷/šÆ5ž ÿèQÚ`Að–WТWê´lNÓ®þFÞQéܽ ›4 ¼¬<|tHéй ¿n¤¬´ÜØV»N-Füóæ Ò⟋-Ú6¯T&NBH"øß@AIûâýI¯ã°Ù))/­Ñ°ÚôóŸ*Û|”óA…§‚;¯º‘^u`X¯ ë50ò2þ?`ȰKŒw¤( Þ‹Å‚$I\vÅ:‚(I¡¡áõzéÞ³kÐ9¼/š¦—pç÷ËêÖœ·§ãñxÿö$~ḱá34ÂR}>Y샹˖1ü’‹ƒä¹I«P»š·WëÔS~l&“ùß"Õí«ª £Æ<öü «É‚¬*,»gV““(¡¨j%$®n7€ã¾¥e­FH‚~_6“ž8 Y7UEVJ§m {«Û 2Ž«îwJ¢NÆ´»ŒuÛ1¬Ë<{ã~u^Ùƒ¢*L¸~Ã:ßÈ´;>@UÚ6èL×féÓz0ÓïúˆÇ®zQ·cM•iŠÝZ `Üu3‚ˆ†¢*L¿ëcš×mËã¶òüÍsQTåO% vV º\6$h{Õkˆ&ÓõœýÆNŸaÝP•ß{"ÄxîÔ§#_¸ÛÇ\ÏÏÝ Àô¥Ï ªz·¥ßåÝ}DŸ~O¾vI©JJYáëïкS3:õiG¿Ëºc¶˜è=´kµDDQ4MCQbâbyiö ¿çnÚuÒ ¿>ƒQ§~}Ú\Ô>¼dÅ îxèA6æd£ª*KV®àÏ?C‘eã¼™ygóÜsÜóÈ#¼4{6ýÖ çNhÙ¶-s>üÐØ7&6–ìÂR­Ò—D‘‹}íA½:Ï ÷ŽÔ‹¯E~A1šv>^ÿ³ÐDZ1QäA•к¦AaqIõ»î%h; ?ùGŽºþèˆ JxÊËiÖ½R©µ»x ª¢pÕØÇ1ùò‰›vëÂECc¶Z©ß¶ 1õê’ŒÛ'À=¥ei{ñ@Qâä¾ý<üñBÌ6»N>ÎçÇW牑L˜¤ÿÌÄ GÒEQ¤sýÝvîÞ‘¼ÓùtëÕ9„„ˆ¢Hß‹{³mS"ªª“Y–±Ú¬”—½RUQ¹ýþ¸\N†\y 7ßy’I¢"ÐÛÁÿ$I¤°¤ˆÂ’bfL§–aÒ¤¬‹±¯_.ù÷kÙ¸ÝÛuª‘„øIJIyãîz˜~ºÓ©U;z]Ô•îí;Ñû¢n‘HÈŸŒ¡Ã‚ ÝC¹GtÇ (Ò¸IC~ðVkpMGN:È;“À‡ŸÌ7ö±XÌô (UÏPé ûøDöìÞû·|®)T…¥¥ÕêQ©G´ÓÉGÏ>£ËgEÁ$IAÄâBU—ßpE1„ ”•–„èòs‘“ªèع;kpZ‘Édææw³oo6 ýáhˆÕdaÁÍ“iߌY׌å÷Ü44MCV~ºov³-ÈXèØ¤nك͉hY«1¿?ö o™Â¬kÆêNߦ«‘;Õ“Á¯eÔ/ ¨ ªª0¨ÃU´¨×ž‘—=ÇÞ™¾÷Oþc&ÑÄîãôi3˜Rw1}ÛÁl‚2O ›ö!çè&Ý2 ×Ïà‰LÅe‹fP}μrû{Ñ(./Àj¶Ó¡QwŽÍ µI,Z÷êñ‡ÉGëÞ=hÝ»G érÙTEA4é¥Ë°!ÁãD–yÿä¾›ýg ˆ(ÒÿrýáU”{˜ºx|'½sŸv<4évns£^º‹'fÜǪ¯6òë¡ÅôÒ…‹¯éMÝFµé:à"þ9þf®¿ÿrœQvîzòFžŸ?šg探aóz•Fm·–4kÛˆ&­Òm@ú]Þ“ÙÄ¥×õåŽÇ¯ëÅŸ>ÿ_ì..BÓ4rŽgʸñÈ^™_S’™8cÓæ½MÆÑ£ü?öÞ3LŠrk÷ÿUèÜ=™4 qÈ9 aÈ9+*¢¢‚ ŠYÅœ* b³¢[Ý{«°1¡JE‘$9‡! :‡ çCõÔLÏô¾çýŸsÞëú×—™î®ª®®zžµÖ½î{­'¯Uk2€Õí uǨŠBf­Z|úÃ,ÿô3fL¼Šg^V¬0®%#“ﺓ—^½»v²ö‡˜4}:ßþñ;Ù r¸{Êæ¾l8úã'O"Ý ú$¥ª­ ª¦Ñ/¿õjgÒ¡U34Í0.}»µç·m»L0qñY…ÝŠÕb!U˜÷êR>YþfÒ‰êóiš[?i`‘ž]¡wÌÀŽ`™·Â "-û÷ƒ^„¼>¢Á Ù­Zв_õÛ´âÄÎ]æäEÑ@ì¢HË~t?Ž—'MB‰F ÐQЋh0hN.Бm6Be>œi)qªÔlô¬ç×hî3’Á}Fž'H“k.ÿÕï”-çgÊ»Ínã–YÓɬ•™”‘$)áoÿûUNžNž}‡Q•P(L("ŽÐá%åý_~û/ùMUÉë٦ݺTKpº3Ò™öÊB4EEšuïLNë– þ§ií&t>á¿Xƒôkî‹Æ@†ãO2nòì+E'‹©Ó w/˜jf}-¢!¿²‰6ön;Dï]‰„côÞ•?~ÞÁOÇ?£sŸ69ƒ®éÜ»h:·>u-㦠áÐîcÔË­ÍU·¥~ãºÜ½`*áP›ÝJþ Žœ9Q„Õfa×ûyäÅ;ƒz›÷Wü‡ßÖ­Çj³QˆN 0fÂD#Ž8ÀÀví D#Øìv,@Ó-èT/I’q¸\ôìßwß}—ÁÁ@€ƒûö7pºÝxU…¡cÆàÞxã dIfùНøiåJÎê:EŽàðý¼÷Ÿå YE²ëd2÷ži¤z\ôéÖž`ÐÈVϽg×FŸ+ï`Ök,B·Zdî˜|wL¾,AžåõÐãÏ `ÞýOÓµ]‹jÇ{Aú$ɼ´ê߇âã…œ>{”io¿Zp…VýûÄ÷éKØç#äõ’ÛÉWEC!Æ?þ×.~ŽÓV Y"äõa±ÙÐu«Ói^`^dÊ p42¤X’ˆ3-킃òÕyKÏûy.}éÑ¥oŸè54©óWT…²ÝÉð‹O½{þ‹Ò9/û ±X-¢@Àoü1LJ“æ‘eEQ P~Á–蚆ÅbaÒô«Èªñ·›òsZ]ŽäûTÊœÈ6kÍç)/t­ò÷|ÇýO6§3á}K|® qðÝf@ߤ`Äêp\œTH­ ¯/tœ$J ñÈÄëDzô©qÚ«?×I#ƳäÁgpØì´i’GLQL»Sê÷šc¬í¨d3DQ¢S‹¶4ÍÉ5ñú} îÞQQTå$à0¯\»USP5…ù—¿„Ãê<ï=ïßÒÐ~«šÂ+×.­ŒØÔz„¹ïCcž6ÔzÝ÷ú[רëúyÓ뚦²zÕφ”hÿ>x÷c&L¼„GœË¾½HOO‹Ã*7ßr6­$·Qb1Å ÚuMÿ?Æb](À9ßç:VUUÞšu/›;Uû|Ö”)̰ëñÏ$QÄ"Ë´íÞ9SoàÉ>DÑT¢ŠbHÖ:vÂ[™ûܳ„"ÕÃZ´jKÓæ-iž×ŠP(ˆ¢(ÔËn„%¯¸¾@ПðZ–-Ü|Û}D£&]36›6í:Fhß±+EE§‰F"Œ5wÌ|Y¶Ptö í:t1Ç×3î!ZéeQâÚΣñ,Yøôš(ñ¬nZ=–O~Q‘D ‹(#‹QÕêÕjÑ›C%…ŒhÑ;Q6¥ëø‹Ó»QGl•ê1Ü6'‚ Öþ’¡Êó×"YñÅ}pañ‚?=šDD‚Hseâ ßo•mI0H"øBe¦LQG'ƒ†YM)h5Q8V¤²hòg<=i®ñ]%G±HVÐuŸÝÇ¢Éo'½Æßl£u¿Þ vÜ´ñrõ&>·¾÷:šªrçÇïprÿÁjÇM}é96-ûŠëo1X#5¦öè9áÒ„8¡ü¿IÏ<‰®i{I¢ÄÀ®#ÿï‹ÕB«.ÍØ±i/wÜ?…_~ÉáÝÇÌz© ãæ9z‚ÇÞ¼‹‚]±Xeêädñä»3) xÉÜ Qð—é1¤3~oÓ'Îñì«’î¦èT1?üŒœ&õúÃôÒ‰½[óÄó3)+öñÂ{Ñ£U­2¥çбÙm 4h0häȸvN¬1+"É}*I¥èXm64jÄcwÝEìì„ýI(EGG§cJŒ/–-'¬¢$1tìXŽ<˜à,¬V ýó;røÄ)ö)äýggc‰ƒˆã§Î2¸·aŠË|•¦…žµ‘$‰@0Œ×8/ÍûÐso1ý¡çXüèí&P‘e EQQT{’`P” Ù“(I„â ˆÙ«ÀÐq¶ìßA‘,V.yd6=®¼MQiÙ¯a¿ß¬Ñ31õíWMP’Ó¶5¿›ÛE4D²X}ÿLŽlÙFÃöíhÙÏ ÿ«fù’m_­úœ_\ŽÅbM:i›};§»!‰-›µ¥KûX,Ö*àÎÆ‡/.ãþ¹3Þw:\¼·è nºÿjºwìU3,‰?Åå×^š´À<Ò®S‚þ u³kÇ ýÅ„ãE1 _᱓ Ù[¼•²ÅbáöÙ3Ø‘ü>Ýøuí&3h‡ÂÈ5€×ÊnÌ“·0dÖõ¼à[¦jt¸t —>}'½o¸¤`x®h ZyVÕfÁžâb‹÷#Å”—N6éÕ6ÃqÒv„¡õm58€>žOFúç".‡5~¿­U˜'1\íVY±•Ë6-’97dIÄ^©þ¬òÿ•Ïe8qÁ<&qŒˆIß/í†wŸk'2ùÅægY ðȪ¯ÈëÙÉb1ˆ ŠhªÊ-ï/aòât7Šæ=º%åÏHSUn\òuš6fÒ‚¹t5 Ô$Ö]{MÛ‘šNqYiÒ,©Ãf7€F%æc@üïš?~!ÅéF– ·^¢£Vq\›æ½ÂþcF°,à£OÇî†4ë¿´&ce“ŸªûÕT¯òw» uÊí†U¶! "þp<{Òm„¢ÁƒQÑîRf22¯ÞP ºnÜ«í.AÓ4fz €Î¹ÝéÕ¬?v‹ƒü&¸m)Ü;üñ„®;IYI¢G¯nxË|æ\O·R\~¬±zÕZퟘ\‰)¦}–$‰ô #Ñ£áãðá£D£Q†ÄE‘ƒú¢(J9ÿB‡²yl²€æB !¿çˆJR¤Ä¿v»‹Öm{\Ô±Õæ¼Å³7MgãÎXd™Îyͫ﷙^s ûޝò»ŒßSvêXñûD‘¢¸ŸÜü<0«,#IbµÀÖj±ðÁ³¹êšk“€FCöÒºMdYÆãI5?kÜ´9‘h$á·•×m<òÄB¢Ñˆig%F4¡cçî¡ëz½,· áp¿ß,[¸éÖ{©[¯>W\5™ÂÇðùÊǦòîïËøüº…X% ¾ˆ®îïýštÅÐ1ÛHvþY¸›]§òÅu/˜,†®k†ÏŒ9™bwC°»låµéoQòŘʷ{Ö“æð”[\よM¶rI›Ü× »Î¬îÛD‰^-‡p²ä¨yï_ºñQŠ|§ÉÏ€(Jˆä7ˆ?¬á {Éo>€@ÄWÍŸ>«³hʧæœpÛ˨ÁŠÊ’D»Üî–ó|ñ P7-_Ø‹$JøBj50qÇÒ·¸½YºŒ‘à÷§,~†^\`úØÊ7Xj<‹@I)–j~SGE,6§ T‡I­U+é\èØºÛyç`¿NÃ’Æ7‚ R¿V.÷]7«ÅfÆK•Êßfs/’Q+œ¤@÷ ùà ±ëè~ë.F\ÝŸ'Þ¾›#GO˜ÖWê¯ÈªÚ,øJýøËthL`]× ±OùÏÐm@üeA"D±Ú,ˆ¢@÷AÆ÷í=zíúwì=z(i¨¬´„q°Qñxt$Y6C0>¤+Ä+”ÊFvèØ1œÐ5æ¾ô \®f\$Y6Ie RV’HÃ…Â$I¢UÓ†H’Hq™¯Jö]KÂxX ÇËÛê¶jš{Q¯¤¬¢Ž$Ž×¸‡Ž¤qƒzå ´7êg„ø³ÊëÕ+n€uÂþe§ÏТ_‘@Ð4‚ Ú·%ìõ™@¦üÞžÚ³ßyƒ6ƒÄÙׇ‰Ð¼ '²ÅBÈë#¯Oï¤I¶mËŽœ+9K,e`ïŠIS¾OÔTße»uÕÈPq Ÿp$Ä’gþQi"éœ<}‚×?\ÄÛÏý§Ãh< (<} €K†O¬1 Ý¡k;dY"à$½~]×Q•KæQÐr0µêfDÍŽXšªqû웱ġ$IüÁ„‚Ñ` XÅ0hªFÃ& 8zðXÍÙQEåÚ·cò‡s8¼i…Û÷ÑÔmd3esõýsí‹sÅ ³pçÕd5©o‹Ìö.ãõ3[8µë V§+_ºŸº-3ô¾ÉØ=.ÚŒ0HzúÌX¾˜¦}:“Ó¡‘@ˆöcúÕ¤>7|4Ï.åÛô Ýyî¾QÜ1©o<1¾LÞ2ˆÆ9œ^û0™é..܆ŒTÏÜ3‚yw§}žlFömIã|ôìDÞ|ò2ÂQ…ПsÌó|¼à*"Q…yw cì€Ö¼?Bœ ý9Ç|=´w^µû×¼Gwûé[²R&<õ(‚ ríÂù8lúƒs'N0ö¾»ñdfrÓ[¯`w»™öúbCÃ+IhŠÊk'öâHñP|ümõçÜñd5lÀå=HÇ‘CÉiݲÆg<¨[[´ÃðUÍÑ$æmšäá ø«Y]»Aޚ߉Ÿ¯7훵‚ý'ª9+¥¾2‚>¯9÷TUeíCbпJÍÉùlU–YrOÒµCTUᇵ4Mcî³_"ËTUaî³_âr§¢ª OÎÿ–J‹Åbcþsÿ1|Mn ,q™kßþ—2ç™/Ð4•Y¼I8à×-kÉkÙ%ÁX,6²²²iÛ«Õ–4¨‰ÄbÜ÷ÆZ5ÌÅðôã$-6÷€¨4a*·ÎmÓ)y]«E¦sóæÂáä‰A]çœ×KfЧÆy …L;[ôîh>¯™j‚Œ——üÃH¶nÇ5×ßÄm7M42ë;N¢k3n¿ŸÓ§ ‘-Ú´ëH$F×õ„ßS>Ξ9‰(‰hºŽ,ËA`ÓÆu´hU]%qÆ_‚?dt+>=fÁXY”ˆÅO‹$!K%!o‚í) y±ËVŠƒeŒlQÀ§[¾A¶:¡Ò~èea?ivOyƇÜôlZÖnÌø¶ƒè•ÛCÅÇ‘„ê]² ùUžœø&)#;Oä«ßw±pò'8,.ÏH¢„(ˆœ,9JA«á,žò/vÿ“`Ä_3Š‚Á€ÄMD s÷{3hT+Ié‘7Ø.å÷5VéÕrþpYò_Ñ9ŒTs×Ü|ÇþÚIý–y‰Åä’Œ7~L°¬Œ6ýû"J¢9¾”ò†¤â"½^]S±bÔnU̵j0_ÃÖ·ÓPó¹WžKŠÃj±!`Ì Qɩ݈93^Ånuжi:æå'€’ 1õƒºŽºxb I+Ú"g ‹¸õ©ë8SXtÁEvt]ÇŠYuzÒï R0¢+ ÿý° dÊ¿C×uDIää‰3FG¬$H«¬¤”A#G&ÍИ€ ži,#•Y’jíeA:Ö\ýílž,'J‚1EEÓ4~úm+)nƒÚOq»ˆÅbIÏáã'“H…ª/4 …/*¢±ëÿØÁØA½.ªàS›7Ðyì(b¡0®ôt"¡@…<ÄfåÆwß =»!_…œªEA/óuÕ |ÐëO@ƒiÁ¨‰Ö!Ÿ½’êö†ÃîäíçþÉ“‹î7ò¾£ æD”ÐËÈï9Pˆ+ÛÍ{‹>àÆY×óÁâeX­6jeÖ63”þ /ñÙÇ™—s¥g † =k÷޽øòÛOâ,’\#9sõÔ+‰Fb!ÚujcÞ_QÍBò{»“½í33_&¨ˆ•Ó'Ï¢£ãö¸ÐÑñ{ýxR=F78Q$àO Û7ï0’ʲ­š¶á7LcĤi¸2R™±üEvÙÈ;úÊ ÏÄ‹ESVx–ËÞ @…Œxx/ÌøwsâÔ>f¼º˜P©’c§q¦§0í™g°§¸+’N;º·¡ôøiÚŒèEÄ uœ\±h%ÇN›ÓCEÚåÕeÎÃØsè,óËžCgyþR=v¸g$û|»ÍÂñ£/ K"OÝ1”=‡ÎrÏÃøõÓÛ˜0¼=#úä1¤gsTMcÚìѬzçFìé†óù÷âk9yÖkœïé+ù÷âI\wItýžºc(+^lî«ïz†ö-ê&°M»w¡v£†4éÒ gj Y¹ ضòº˹£Ç –y¹ãƒwÉjØ€™ï/Åáñд[švëLãÎ ù*ØÁ´ºuh;¸?EÇŽ£ëš™åÒT•ot`©—E»ÿ$Ž Éˆï8|‘PI¶µZ·N7›FTÎL[d™~]zPV€x>ò6FE¬+Z´5‹Ï«DA¤{R‰©Ç´U IDAT1¼?»0°{ïŠBuÉ äÔ®ËkÌÇV‰E<[zŽmûv!*µÿ•D‘Kzu;¯_Ô°^Q¿v­ë½—„# îd¦>yÝÂÑ(ßw[E I.1ÅÂ;{ŽfÙuì¡U–™sý•ÔÏÌ0µrÆaD»q|µnëŸ[CL‹Q×qO\8…ûÞaÊL$Q" 2ûZã»ÛÖïHmO]öžÞÅœñ/0¬íX?ÇŠsiç«È¼] I­æq»­ ÇoMAQcÈ’ŒM¶¡j*r»“W·u?g‰öæÇµFÀÞ» ŸÕë¾2ÙŽ2¯/!#Ü6¯œ1àñ¸q8¼ñꛀ‚×ë3ý¸,Ièè¬^µ•ÁC àòÌܪ3-²DYiYk‡´nïfT0]×yø‰Ø±mµjÉ Ÿ·¤ §ó׎¬ÿ]gÁ ˆ„ ûÖ³` €1æŸ;-žµ‡üØǫ́ªB,aÓö,|iápž½G×,Ã*ïJä÷Ç»êÅæén#øÅATi›žÆ9¯ÑaŒ*ÝÂÚtéBçæÍ«É¯lËEUt꺎,[¸{Öãœ,<÷ ~Âq`д¹È32kqõµÓIOÏ4æÞY£FðÌ™“ž8ŠËå¡s—žÌþ b±(¿Ïd~3³jãry˜vóLÊJK8|pš¦É,Mã·?Ó=¿ Æø NZ=¦tÎT$AdD‹Þ,F’M%–Üù)ERnì~SºŽãë=ëØrtdå$éò¤Z9ÃÔ³a{ZÕjL“ŒfÞôVÉJIØ—¤¯€U²á •pèÌžœ¸·-%$ô* Ǿ80q‰…™9f>7žÍЗ™@¤Ôôh>@Ø‹Ç‘Šªó*¿ù@ü!o’ ZHôª'€ª‘o@ ´ŒöC$øïh(DßIW’‘]°?ÀÄ[g‚®c±Ù¸ü‘û¸òɇ°Øl(‘(wêÏò“¤deÒ0n3#ÁŠq³~ý÷ÔmÚ¤F"‰Í´"òcµØyñÞQ«ÈhU!ÝcÈ»5]cü€kñ½„£!&¸‰AÝF]ܺs»‹6M:1óê'.ÈŽˆ}«Ò}PDQÄ_0åVºn0ÛõáÈÑ,øçƒXmrµÉ'J"¾²À̱C'™ÿñ}Ü»p:«ŒŽQP%Ÿgñ¾ræ¢2©Ì’\è¸ÿ®­Iƒz”ùÄb*¯}´ŒœºµâE ­†^o:„òâM_ È¥Cûß±u™—™7\Áñ“gy}ÎL-¼¨ë“tC“d¼gÎ2î‘ÙDÃaZôéEɉB³kºNÈk0Ljޅ¸ŒD×ÑTµÂ˜Ä·pœá,òúô&…íå€%.K¶ é;ŠP8ȹ’³ `aHŸÑÄ”¢$1zðxîšö í×' pæœa˜[6mCqÉYA?¢(1y öÞ˾£Ì1˜Ìi]6j¿mÙ0V“eÚš·jJÓ ‡Â´ëÜ!.óéÒ³7Þ9…Âc' *\ù×U6ôgNžA’$Ü7‹…çßzš™Sg‹Å(<~’{¿3á;·þ¾í›w_ÐÍü¾óm‚„  k:¡R/²Í¹³'Ìk:}î0·,~‹ÃFÇápÇÕœ;q‚:y¹ävkCØ@”D‚qXÞnt_îZµ„ƒ'·®,õ‘ѰžÁ†ÔéMñÑS´Ýã[÷Ш[ÜYi´Ö‡`Yõ ½Ón5˜]çù7¦°jã~^zhDÂþ0V‹„û1þñòu\1¬=#ú¶„p MÓøôÕë¹ùªž,~ôÆnKtg!ÝÛ7 ød)w^Û›hL¡ÄbTß–à ©„Ã18x‚VMjSÐ¥"úÙW). 0ÿ…IÌ‘ªrà·?HË®‹(Iþs+KdË7ßãÎH7g*6O¼_¸˜¦Ý»Ð¬{"Á‚(šµYJ$ÊK“¦2hú „ʼDAäJubE‘b¬N!¿AÐuˆƒ´êW@þøq„¼^)¦½¼˜h¥ÀDUUî˜xN»Ã”ã$$h>úwé…(J„"a>œÿ*ÖxkI]×Í‚å@(À¢™±bí*TÍèî#‹V‹…G§Ý…ªª¨šÆ°ý9zªÐ´Œ6«•ŸÞYÆÃ¯=K$MÎTMãßë~5ŠªÒª˜ªÒ«ub½Úö7žK`fP®Л鋗p÷øQ<ò¡y?ý–Ü9ÝÈãçxõö©„£1úµk… æwéèøÃavhƒVéþ¨šÂ7Û—ñì7qÇXûëNUA%z5ëÇÖc¿£j*15F§ÜîtÊíkÆŸžÍú1þå´¬Û–’`1ZÖŒRFw¸Ì&” Ç\^ˆ¾z÷*z63XÁP4Hýô†{ ˆ^¼N[”hž× E{g/aÁüéX¬6fÞw5š®qç=/™E,5eA±XI””r3n_À®¿~Åa7{÷@ZZ-Dd‹•œÍi˜k°€-[uÃï‡m[üL»y.3n–¦Í8¥ª6yNN5»KÜߦ{*Š!]ºVÈ5ã_?ýfÖ,y›ÅBèÐá*‘¤Ì€N) &WﺋX%ií`Á€¿¢³• ˜ÀéÊk¦ræTaB nʵÊ3ñ~?nO ‡íGE7É#ðˆ'$¬V;ï,]NV­Úœ>UH:Ù>¸EQpÅÁ—·¬ôüµfv7ï\³€Ò3 IU‹^”„}lGÐ[«¸ZÕiÂðÇû¢/ÚËÜßdhǨšÊ²«¹yá|7íõ„æ U™SAʼn3 ‘ù Ûa“¬ ´7\ :q TÍ ¹òç::ÁˆŸ"ï)î5'€ˆ¢D~Þ KŽ&$$QúÛ1¡«¹}µÇ_ɆǪ·? ×ÄËP…9×_Mv‹CÓ5Óߥ¹3Hs€$ óì‡òÊ}Ÿâ°9/(³ª›QŸzY ¨“‘ôùôë4¬º«ûÀŽØVüóÁ–âïé¾D|¥ÿ«X¬2=†tfÙ»+QU MUYõõפZ.®Èõ¿T\ÌfµÈ,~ôv|î-Ε™†­»¸ý‰™uãDl6 –89 Ÿ ì@–$je¤ñѲÕ?߬©jTËüADIÄëÖÀU¬,žì¾ËV£ãôÁƒI¡»lµšC^_µ•Òk7mÌÛÓf˜®Œ McÏÚ Ø«PÎ!Ÿq¼d±Ð¢À4ÕZî B<°KÜÞÇËsß7¥Q¦®±wóZÙË¢ys«yfZ˜ðª¦òå·Ÿ0°÷0¢±OÝ»0®ñ­n$ž{äuŽ8”ð^L‰™¬KBpŽÐ¶SBÁ‹lÖl8œêÖ¯[­«‰Å"3qÊœ.<ƒïøuÏãwRxü$žT¾2>¯OŠ«ÕŠ·ªlOQرe'ù}ºÕ˜±’m&8·Ò„–•z“ŽQ–âERÈä̾£H -‡ô R‰} Å|ˆ²Dë>½ûƒH²„(KÄB^9ò;™³9¢ídÆò¨“×ÉïÏ5š 퉄…`‰×”BªšÆŽ}§ ÕÁð‚<"Þ/aÌ€ÖŒЊˆ¯"Ж%‘pY«E2öõ…Ðu—…‡c„¤rY$b³Hôì˜K~û†xýFõo‰Q…f¾à ò°Y ívøx±QƒRJ*a³ÙÜõÙ‡Úµ UQâ,–1®b€3-Q’8}à·}ø–Yœ^΀ºR A²X˜úÚ ÔÊmXm¼Š¢HnÇö„ýº]:–LwV-y—“{÷ƒbW²xÖSœ).P˜…”ºŽ?0ƒ¿"rùÇø#8~æd5 S©Ï<±˜ßwm3²ò’Ñ¿^fmVüøÍym`Y ˆ£’lJEvl—¾éѪb1·9Ù‰Ò-A vj*?mÛÉÂ9pÕM÷R+5…GŽSì÷Ó¶Q¾Û¼§ÍƱ3çÌd“¢ª ìØ‹$ST2<5K`~Þó‹ožÇë_G×5²ÜµÛi»Oî@Ó °sn>sóŽËte±ÿÌnV'Éb&sMeݾ«9úúé¹Ìökv}G½Ôsü n3Šc҉ªd1yñŒŒ4ÓÆË²Œ&mózPZRfØ‘*I#³žXWgü›R©K™€Ínçû•«±ÛmtîÒØßàþ•‘ø|PZz–ZµëÓ»` ' Á[ZD0è§n½F=²›y/^Çð, _ú»Ã‰ ”–ÂïüFë6ù¼öÖ+|õŸ·Y·Iç\Q”üø¹ƒ/wÏz•Ž[âr§òðKYýÃÏ‚ÀɃ´kßœû iÃF\—Ù,ÞŸ=¡N]ô-›9zú4¢(Ò»m[Ö¬_oÎÏr»Ø¤^=þ3o.áh”Xü>e¤xˆyûâHM%‹V žªÚÕ_þú‹Oy„@8\M¹4”$r7£NlÜnmÿÓ¸ž¦y&0).:Ë+‹çóÉGoqå5SÍzF]×q:]>¸=¾©!y¯8‘ˆQžš–Î?–¾É'½E½ì\.à‹Ï󪀩òëhÈ[!ƒ+ì–M¶²òÆ78X|œï¦½ÎŒyDƒd¹ÒÀbãùŸ?MEÑT<67µÝ™”½¦"!Y|Œ…yþ¦· ¢$äeT˾ÜwíB‚± ;Ž…X4ùí ®`^3ìӱȶ„:‡ÅÅÂÉŸrøÌ¾ ÏÁ(^ÿ»¡åµUQÈmߦZ2\ÅdWZ*J4Š,óšñ‘¿¤”N#†ðãÛ°÷—_2x,ÛXƒ+ݨï²:ìä_6ŽsÇOTKWÝ2Së€,`ÿ±]Ü}Õã8lNTMetÁN¢¨ 1%F~›¾|µþŸ¤yÞì9²ƒAÝFcµØ1¡p}æ5OòÌmorÿuóɭ׋lዟ>¢KË^ Aé› €”g£|¥f)þÎ&àIsÕ¸@^²-5Ãþí‡ÑT UUÙ±ùORùgS…ÔŒtÓèºQQN6ê`¶ß-.ó% ÚQý{ð×¾Ãì:p„qCzóÖgF–«~ÇjteyöSÅxG-½š4«O·öhšÆÌ®Àj­ÔfNª%â’¤hÕ‘š‚¦ªfm† ˆÕ²#š¢âHME×TóëuMeïÚõ8$OÂ~åVS5Ö/ý„[?ýÀì¡©*J4Êòy ˜´ø9äJ¢R=©Üzßuì9¸3ñ6¸*€$É BG«ÕƇ‹—qÝ]—àvyÌûu¬ð×^9ÀGû69 ¿Çép™çžtÙÜrý½ñ÷+ýœY‹L Ueci±È<ºàANŸ{†1·Êü8vžb1Ï¿õ4²Å‚ßçGìϯk7úåO^ª”˺\ng§—ƒ!,Çò‘íVóºC¥^$dóÅ"Q<–'¿^Žl7:+w@ª¢b±Xy1ø+k—þ gŠA’h5´'ao€(aÚŽ( \ægóg+ñÔÊ0Ú:k»eÁÊ-¯½H4X¥cXª‹l8ˈ/Äð80P {ª£Z”,‰LdH’Ȩ~-iܾÁpŒá-Mùñ&C¡jØSÕš ”ËŽíÚ…¿¸Ølø×êŸq¥§Óº6­ûÞ|¶!¯MUù}Ù Üéè:(± Í.ºNÑÑcdç5§v“\FÝ}Í{vA0ëAulGÄï§ûø±øu?îÌ ¼g‹p¦¦à"I²%=‹ë»‹h,ʾ£‡ytÚ]¦DÊf±òÑS/ŽFhÓ$/¬z~³fCˆ¶•¥9z%%§v=c؈ëpº<Ì{òz.¹ìvïÚDÃÜ´lÕ ‡ÓƜǦ‘Y+›æyøkG”W–|¢èÄb‘x=øý¥ 5› ®štÿúôV,‹:u]”–a·Ãì‡_¢$¬óõŠwd$::Åñà:xòb¼£Õ%½ùrݺ ûo·q÷å—s¢è,ý:´OÚR7‹q÷óÏsülQå(Iª`;ËŸM^ƒN•Sê÷óåºõt‹·ï­© ßåö hÔ¤.·‡Û Ò¶]gRRÀrë–MdÕ2–-ðû¼ ÕÊMJ‚ÁmÛw1æ{\]Pž4±WZDYUUœN‡îEQbÕZh—†}toÐDÉì‚•,”/ z±IVöž=B]O ®üA Uí&ܳâ9<6'Cš÷À­°ÉЦ”ÿl²•¯¦½NÉþßóôp¾š³›dsÉkÍck3Fƒ!>F…„Å+×wT3¾PiÖ’(Å;k•qøì³xÝ.£y½¶µ¶ŠKJ#§uKTEIMMxüAÂñ±çLMåÏo*ü‹;#Wz%'O±èÊÉXl6–Þÿ¨9 ^œ4_[Ä”ž%¾¸uÁÒ+ I’ɩ݈P$È3·½IŠËwÑX„ßß?IFj-~Ù¾š›Ÿ¾œ&?ƒÕjÃåpsðÄšÔoA4AÓ52Sk3ÿÖ7Œ„«dE–-&³yìô!6n_Ø>W¢išY¯¨±ÄÅPÿ? ÔUXTaÖóÈmXÿâAËE¾÷kkêtñèw%´³«QÏÔU & ¯â½ì:™ éÝ…:Y ?¶UÓ\-äžiªÕI¢HAWC7íõWÔÃzÆëtMeϺ 8*±åÆç­©3¨Ý´±É\€^áGŠP{Ön0ƒr½P”OdMcïº_ÌÖ¥º¦²ÃÆ Ë-ìY»‡Çcþ4_QQB€yý7óé›ß"N‡ «ÕÆœûqõ%7T d†2Ÿ¡åžvï>Œ×ßøþk½» H ¶EQâ©Y ©ÛQâ‹o?ÁawÆYЏ¬*¿qr}l½Ú<9k»¶íæžÇîÄérRVRƦ ß§îÛÿü+á˜` h¶ïÍnP—OÞý—ñû½~£]¡/€ ŠYg&yÆ÷{*=3I’ÈΩÇü$åHŒw®~€ÿèAŠŠ -2Þ3çp¦¸Íqt×ò%(ÄØõýF4T$YÆ]+ ?%f”.É¡Ruê5âÅ‘·â;[Ìž7áÀƒ(‰´Þ›FÝÛÂ`G4EeçÊ8ðàLõ **ÁRo<àIdœv ·Ýø6ሓYDU5–ý¸“)3?ÆÝ¤öEÏ?EÕÌa*K""ÓaIÌp©ªÆ·ëöbóÛťÓ߈Rd¯Ý™éüµúgtME‰F9¼y îŒ4ºŽÍïËV Æb¬ZòO|ý»×nàÐæ-;N ¤”¬† Pc*µåb±Û¸ñõÅdÔÏÆ™šÊ¤çæÑsÂxž9žºM›ãP’ŒÎ+²Œ¦(H™ºÍ𲿽¥$u³šJúõÑ4ÍèèW)01Úâÿ÷ïbtrÁ\¼µœ1q8œ8â@ûÒ#ðúý”xËL¦¤s˶ ê^À—?}ÇæÝÛÉLM'‹±içV\ñ¹ýÞœ—ð;Tíòò[5gl®ïæŠF©—‘Fý¬ ÂÑ(²$b•eFtëÈË˿㪃°ÈëþÚMºÇ… X$‰-މ)æ5+ª†ÍbAÑ Ý~‰/@šÛIªÛÉ‘Óg±[-ŒéÑ…Ö7ΤC“\JüFçw&Z¥›TT‰ðûáœöž$7« %â„}Údw@-r*â)¢Š‘ ï×®-í©Ç7.§–§ßîXŽ®kd¸²L»èô@NF.k÷®Â*YÍqšî2€³¾Ó´©ßñ‚cüñ‡ç› ¦¥¥òõÊÏùã÷?Ém\Ψ…j‡ÛmÆ[šªáÉÊLˆSÐ K,IœyæðQ®™z !à ®#BÂ}‹©Æœ¸ïÉiì?n¬Og·Úéz}=Æõ½ÊÜï\ÙYdQ&‹òæÿî1®±c^>‡ ÷›1a9À9S\HáÙ£,üø±xŒjLè]G&´`–%Ëß †S»8&CS5~[µwª Oš'ö¤ÇþsþOÜFÈçëÕÍU.}„DÃâv:ùò¹Ì{u)ѨbÖq(ñ‚90œTZ …±Y-X,²iLG è‰/^ø­©…»öžSM5 ä·û==¯¾ÒìÌcXn=k7Äë=@FysÊtj×jˆ=ÅÞµëÍÓšªâHI1shJ4ÊÑmÛ9ºuZ\6gs¹@D‰æ½z°õëï@ÐÞµë VpØ|²ü=¾]³Œh4Ÿ|:çJŠHñ¤&E¢»“³çΰcÏ–ŠÉRr¶Â ÉFøâ›O¸tø•¦¤IUU¾øöôêjè³zæNBñâHc-—~b~µûDØúûvêåÔ啱÷Ö¯ÙÈÑÙ½cù}ºáò¸Ù¾ù/“JIõ0lì`ŽÇ%É14;äH±ˆ‘Ew8\2q /?óº¹ßуÇPb >o€aã†Ð©{‡©ÜYŽ!J"Î7ÅGOñò¨ÛˆÃ:ÎS='ðñì9lûÏO/­†ö4ÚÇ¢  G9½û0·/~#›vàLó$´”mZ éAÐëO`½b¼4ò6òêuaƲX½ôcbDhÞ¯‹ÙvVÕtš4È SQUïÖíÅV?ƒHLå»u{iœ“ªf2‚ Ýâ¯Ëÿ*ªVq|N&v»UÕ”^g²~‚öGüü{ ÅÝmã·wÒ¸~ÍÁ–3%…í?¬FÓ4ê5oÆgÎA‰D±»]hŠŠ¦ªì^»pà·?H­SÙb!Xæ¥AÛV¤Ö©ÅW _2²oqPQ+·!§¤ç„Ë8¼e;ªÃîvawA·ò×êŸ9wô8Z<£ëJvq±(o~ù1ÚÁkI¥všÍ4fôÝ“±Y­8ìvTUÅåpòÃoëÐ4Íc/òO.1$‰úÇð™çÑuRŸOz&1%f´êu¹†›¡AlSÞUu<°—­nÂîpšÁwUçYκT•)É¢'ßìYGŠÝCD‰rÍC£xáþ¥Ü¹üY¾{ð{2Rjûó#‹Fï»~‚ŒúÕ@Àg[Wâ²:Ái\çÏÿà©ëóùŽUK«@M׈(J4ªÝAÈ΀^fa“íÕHÈKæÍÉm÷°q凞ä_•ë;ª2$5e“ՄèèñE …¤þ\‰Æ­Vz^q ±p«ÃÅfeâœGˆC”ž6jWW,z…H ˆ¿¤„X$‹“¦1qÎ#5·²¯*Ÿ¯ŽºäÖ Ú˜ÏiîL3æ¶Yì¼ûè ®›t«É$>üú­X-6î|þ#±9ضÿwÃÅ¢|³áßÜ0ö.DAd÷‘m& QT…¢Ò3¨šÊ®ÃÛT*‹îþÃ…ûÌë0Ö…H¹|J‹;~wŠ“ßVm©q5èrƒgv\øq+—X4ƬóhÔ°~5°q1çüß’€!ÊåSÂyø” }þw·Qý{ðëÖ]&ðX·Éx8).'kßž0¬Ë[÷ By[_£K˜€`ö¯ºyýAúåw¤_~| —“Ï¿ý‰ß·ïI½=í\ééÀ¶o¿gÂü'Íöº ²ªŽ”l.G·î@ÅÈ6kªjdmu¨Ó¤ kß_ÊÑÃFWœºyͰ:Û¶U‰‘•Ûµï/E‰F%‘æ½òM –©'\»Á|檪’S¯!ï}ö:Ý:ô2↑©,ûî3ºu04ì©)锣žh4ÂçßüƒÔ”t,qíleCQÐ}Mµ  cµÚèÝm_~û 1%ÊúMkHñ¤%¥\Ç ¹‚¹/>Xí³¥K>aßî8N¼eFjY’ظv²Å¨1¨è~Õ¦C+t`ãÚMæïq&‘OUt(ÑùuÝ&²jgrªð7ÍœZMf¥iÛ7ïàî‡oãÏß¶&Ì…(ÎÔ2©Ï–/~ÄžâFWu¶¯XË©]Éjœƒ#Õ‰Òb Ñµhׯ±:픞Á•™ÆñÐv¬]‡¿¨‡Óm6‹ÝƎװõ»°»]8ÓÒp¦¦òÎóó’èär à úMfUUUu+`@—ž¬Ùü §ÏåÁWžÆítá´;˜yÍܾà¬å°ryÂBÇ‘šŽ$Jüôî2ÆÞz5Áp(.Ûâºá·>óá”Üž$}|Œ””²aç2/uêÖKð×å@QÓ46ýº–nù}È®ßÐ&¦$×å!‹átºÐuÞƒ:u³«Êqª–$ð¶Ä3ŠÑög‚€1_ñƒ%& ïî§«üzt;ó—ÎJ:¾ì÷®xl•Âg«c¥§øê†W°Çë:TMƒLðØ\¬Û³.Q:Õ| ¾P1Õˆ ²32«TW¿î[¦©qæ2‚Ç‘j¼Ž#Q©–͆=+îyùç5ÅZ¥e¼yj?ÑPȬùX|õ ;Áþ_çêy%,j»å» eH|~úŠŠñŸ+Ž·à¯`qmN¯,~6á»Ê*ò‚ P9ºè=ñòj‹–³Fq¹Àþc»ðÊP5Õ,.Æ"DâÝ®TM%§N#~þs%š¦¢j*¿í\ËÛs±yÏ/”úйåÙ l|÷8ßþò9_®YŠËî6Ù ‡Í‰¢ÄØlgB @FJV’„‰ˆFfM˜O~ÃNü²r3·ƒ_¾ßŒ¦é&ÐHÊz¤8ãÙ‹›VoÅæÂ“æÂÝšfÖ…¸Ý.~ûqk5Íý׉„¹ü²ñ4-¨ñ‚vwZ³²¡^õõ×ÕêM*ƒ’¿ N*ëÝdYbm€Ô¯[‹GN˜Ù(H•ÌSA×vâ±—ßú(MfWëp¥ë:`ˆqƒ{ã Q5Œ´ê:áÚM3ø¶›8þ×.4E1¾‰[¥rp ©*J,Æ›S¦Ó²eG%%hhhŠ¡[oQЋ°×gN !.ÓÚµúgbá0Ëç- ·c{v­Y›«Û¼kß[Š™Ò­Ê®h,ÂM÷_Íjcê½WEĘ̀ÍÉÓ'8u¶ËF]ÍËs?àñçïeÁc÷þü±­‚]*)-6 ù6oÿ ‡ÝÁ÷?­àŠQ×ðÛ– æs_½á;¸”;bEUpØíIŸã×_|‡ÍfÅåvã7ŃÏëÃn·±cóN4MKÞT7§Nœ"™Àè…o›²ŠòLâŽ?ÿÂér iEgΑ_Ð Y–«µäMг'Ye=ðÚ¥w±pÊTÎ:3̓3ÝC4BS5¬N;?¿öOžl=žF9FqðÁ [8±mŸÝñ,6‡ÃÃî•Mƒnó¸øký:Ô˜‚¦ªD!¬N;¿Û@^f¶~¹šS‡qpÃV¬N;™ÙÙüõÍzÔ$""«EâÇ_°mïI~þãíòêòÀ“_];…·²ÒÀ" ÖOgß‘"ŠJƒ|°l3+¿ÚB‡–ÿ‹½ó“¢Êúð[¡«Ó$rfÈiÈ9#¨(ˆ(ºÄ,LQÉ¢¸*ˆ®˜0¯$JRr 3ä4y:WÕ÷Gu×tM÷ `Ø]ýúÌ3OwWߺU]÷ÞsÎï¤[í«ö f{9 úNfëžãÔ¨œ•Ó†T¨–ÆO›2)(òÓ.£BÅ{YþËÓò øü¹ ¤'W¥0;gØz©ôÖáŠËÃaZª2½l³ÎûÑu\©)È6û~^ârât&óOF=gæ™ÔO«Á×_Ä].ò5ªs`Ãf6ÍÿÓY‡ùjü»ßL ä'¤…HuF UW-á¶°À-ïª@·G.ˆ²æª|³ñ3½ô¦.xI”:ûZ~Øö­Eæ|þéרlrŒ<•$‡ÓAö™6ªÏs£'â÷PU¯¾øŽ+]†$J–„õóg0-ΧNžfŲÕ\yÕe(v…Ü¥xA¤AÃÖ¤*ÿþôNž8ÄÖ-?±e³¡(îÚù3°c›Á;Ünã½lعý(Žp\¸¦©¬[=—˘[•«ÔbŲ¯Œ11’ô:MY±ì'‡÷ñù<Üox”W¯^=üŠ V®¼F³pî\ö9ÌúU«Ñ4Íì' €ßÏâyópI$axr¢A‰ßïcèðá8]®ßä‰ÅGÇ¿n†ŒìÜ—IŠÛÅ™œ<‹µ¢UÓú´nÚIIv»Œç\BƒPl6–Ìy™‹nz4ÀX,v;ÏvèAö¡Ãc M3*Z9S’ … HoÓŠ7Ã;‡°gåj\ii4ŠìfB;—® \jì^±Ê ÏRl.v¯\e±ÒD£3%Ù'qÁbxï@0ÀMä­©ïðíÂÏiÚÀPšG¼p¯M/YeÄ’:.NeŸ`ÛîMØÂ`³v:„T#ìjç¯V¡µpùw­©*Ÿ7‡¯º³tÅß®ðÁ›S©JEÞ›9‡I3ÆQ­V5öï=@(¤²uÃv\nc^Üqõ½L™5žúêb³ÙøÇMƒ˜ýÚû¦•SÓ46ÿ²O‘×<À¦ØbrlÎ…tvfóWK¨Ý®)ÛæÿDR¥r|4l<Ù‡ŽðøxçæQˆ’Háé\†UoCRÅ4~]±‘yÏ¿ÁÁuÛðzÈܺ›Ó`,ûÖnæØŽý|÷ü,òŽb×âu¬˜ñ–ïÉ™ƒG¹B0ÌQu/ãו©Ó±9 ÂÓ9lݳ<3SyâÅù\xÛ›œç•V1åíå<4á[áú\8‘c§ hÖfÞû’Sç¡Ø$f}¶–ç^[ÄS/}o¸ñ9À¼å»˜9ɘ¯´†‡&|Ëáyôºù ²Žç"CÈ:–GÏöÏPä ðÅÂm\qÿ{qwB—…;;¶[ù“PC*A€Ïnj绗þIö‘£ØvthÏ¿^šd*qkÕdË÷‹ÉpWæóç&  Àçtòâ57 8dnÞÆñ}F¼ô¬IøvÊË|ùÂ’+”çÈÎÝlþ~‘%~øåko&ä ª*Û÷ïáþ)£ÃRÖ‰$I ìÙ—Ã'™Ö̈q( ª¦šàZ„¸—¢×gýé†âžsßyQ3¦Äª’–”BjR)‰Ða/Æ—Îä®é³xþ–k¹kú,DA úà»™1ÑÑúqóv<þ©n²$±|ëÎRC•r©<þÖŒÿøK*¥¦°jç<þN»Âæý™l;x(¼¨e´–îúO ˆTW9A@‘íôhÔ‡Â&)ŒþòaÞY9Ã0H(±|~CæZ6d®5«gEJ”ŠØ˜¹ÎbQ]õëR>Zó6N›Óâ%­¨†QÄcj”Ò ë:WºŒ¯¾üŽ€¿ÄX„B†çÑR²72N©i©ˆ‚î/V¸FvJ÷yXúã2{hš¦ÕukÝÐ`ÐϨ'a³ÙÍ=‹Ö®žO(ªJÔK“ßbÏnÃ31óÕY<8¬¿ÊÕ—×àמ0y¶ªª¤¤òxõOsY¹ìKæ¼7‘”Ô |ýÅLÞ~óYAàPæ.Ó‹4e¼óÖs|þñËŒþ昢*%ÃÁšõ䨱¤¸Ý¨ªJª;)\W£BŠ1—ߟ:™]YY´oÜ˜çÆŽµ†9ûüÌ]½:¦eÃHÕ«ˆŽðZI00æ^B¡ öï±F.ä唜þayº}3_|ƒW_O(dMÖÕ4]ÓͶFŸ{©X©Š¹ÇXDž9]nŽ=dä‹É2ÉÉ)?v„ÒJ8¹lÞ[ÿ ž€‡¾™L«jXµs)v7 vÿd†K®Û½)–0«èh…°5Ç<&‹×ÏA äÅ ³0ÛÌ9ÊsÃð’dwá úxü»iQ›ê¨šŠ®k¬Ýcä*¤8ÒPõâ5&„ÿRœPä˧EzCygÞ€·=™µ{—äHÆP™·ácšÕ¬Éá3ð<ª:•òð;Ãð Ô©”Î3ÓªNgÒ\åjå’ùeßr®›Ö™×¿K0Àf·3rîg¼rãP>ë¿[@þ©Ót¹æJsƒgMU9¼cA¿MUÉܲÝÄqÑù‘cj(ľuë9²swŒ …T$› MÕX1çS*¥×ÆávãNK%è÷“wâyÑÅ0’Îï™øî?]×6éÖïZŦÝk ê—yq½bÑ@$š6í]g=VŒ×Y¾é Ÿ[½u VÁž¬íŒ½ç5²ó­÷&Ú™»Ÿ¹~^²Ÿ×+ÙIJùdüá÷;7üJƒæéàÖQâN¶ÄÞ„T•&u0gÁ—ÌûéG(áýKMN‰!þ`€!£ïÇi·¶Ud™ö·Š…P§’(" ¼ñ^ØÒnh í¯º E– †B=“ÔϾ!Þ$®Y>ãóýæý™ìÈ<Ì¡Sg˜øÉ׬ݵ—ûžÓÚ0ç”àHN¿E ä§À—Ï?Mbüܧª¼NÿŠËžd;AHqùC3×±1sÉ?A vù:,Þ1€FU·RCK,zi@pñ¢e,_ös¿ÿ„Û†¡n½:T®RŸ×gåUjˆÔ´Œ²ÛWºŒýûšß7lTŸ›®¿ ‡ÃÎ’ÅË-ò@ƒ€ IµQU•ìì\ä˜séª\N^~¹7‰% )¬tyÖï§¿ø€ùþ•iÃ%‰>Ýä> úyfä?E‰U+ç²jå\SIš:á.ÿð‘yNä;MÓÌ÷>_QŒ Ø~à åw¦ºÝü{ù “·nس—`(Dû&?g`@0ÈЩSѳOÓûÖÛ¨W­ƒÇŽÃY"yÛ u80È]/Nãð©ÓfÿSf϶À’c»mËF¤pYk€»ïÁô©ÏóëÞ‚á%ÉÊ<@~^nŒgÇðUç‹ÏÞ7-ÉÕª×dñsÍï€YÒ7ÚH©†B¼úòxþ9óÃRç^HS©ä..~sûgÏ’™sŒd»‹<_aØßî\å”s¥B(À‚Ý?‘æ4xÊüpØÔßL"9ÎSZ5­5YáGßíZAÝò5õ¡{[#窺ƚ½?b·9Ø~x½…CÖîý‘$g Ÿáoß…Cqòô‡R1¥ªi ˆ„d™ój÷f &R5—ÝË.™žÍ€ª#"×u»‡GßL‘¯€GÞF³Zm9™W¼MEÐïg€kŠ=a‚-N$Ŧ‹È6¹L1°eÑÒ2ÛhjˆzíZ3}ÈÅ /^žpd­DŠ“H¢Q--2—müþìù0qú3 ÎvÚ_Ñ-îw‘uýÌ÷³nÇ FϼÑ3ï³êUå+§ñä“mGž¤{ê?xeäl–|½šƒ»³â»u<9xžC‰Z³p#²"sxÿqzWºžÍ?í åxwʿټj'îް%UÓtÔ‘|ZžÊx ½l[V!¼Ô®LîéöîÜg‚&¿/ÀâyK©^ÓØÁ»R•J¥>‡ W›ó5ú¾KÒ®Ek ùƒh!]ÓØ6ß ;V‘í ~B ;¶Î]u5eælm›¿Òrç>ÖF®YIeÒ •*ñ 8âõQàãŒÃev ™y»±÷MäX(dë¢%qABdÓÀ’Öª­ —^Æ÷:¥}Žå–mXŽßM!-¹x‚½YõÜdCíb-ÁÎ$#¾85)Ù*d4•oW,事ëüø:^ÒÆrîÝ/]ºU3ªÀý¸i#^¿Ÿ¡S¦Æ–È=|˜òÉ)¨š†Ûá(-]†1ï¾ ~¢`ô¿pýzs~Ž~{öY¯®ë&H¸updÙÆÎí[p¹ÜæïŽ”ß–Ù¡Pˆ-›~9«‡oãC„žýgNŸ:ëºP$~5`(M½QÅt¦½ñÓÍÊ}Ô IDAT–¿j‘ç+$Yqa“dÃSL^:Ûá*ƒìþ —ÍÁ³†â ø¨W¾&sw.GÕÕ¸ÏíàÉÝ4¯Õž'Þ¿)*¼Ví^ÈöCëIr{œ¸É,ß1ŸŠÉUxuþÒ+52ï^ÕtS6¯ ïߣêÅ•/kWl@·§kS1¹ŠlöüHRØÛ“ìL56†t¦ ÅÏÓ*Éã3·lÝÈ݈6rm\°(¼7TüZ¯•Òk³1 TÊ¢êòñ¨±€l‰'/c|c=°!~+‚~6í]wV°RÚ.ê"€+ɉÃe§z*l\iX^?œþ53žÃ¶Ÿ÷ ùÕ?l@Þ™ò9[×ìbé7FÌg¹Êiì8¾—#޳cý^ªÔ¬Èê EnÇ/{ùiÁ/4h^A¨Aæž#ÜmÔÑ[>w‡~=JÆ5¹µ»±ÃÖ5»Ù¶n²­„ÂV£c_™Î¯» +èü¯¾â§QT­^ƒ ™©V³&«öýJµš5Le±¨ €S «{­:éÉÊâö+™ýæœ9ÃÈa÷R©JU^yß°ÖÜ·a×_Ï×̾]»¨Q;MÇŽ©Ÿã-òÄãÐ9&ÒG[jDQàè‰ÓŒ~émÌŒ~ém¶ìÚÇŒ9_#õ©Yµ2ª¦‘’ä.ÞÓ ¤’›_ˆ,I(ÉIÆ´â—-¥^[”$¾›6ì¶‚}‡3ùaÍ2Kå¾Ô¤ä¸ùs¥)E%•¿åYüÖs­ý„bÀHl¨Ö±ÂºŒ½üq@FYíãÑæMÛŒÐ)UcÉâ|òá¿Ã–ì°×Ðãá–!÷DYÈ“ùq±Ö:yÚó1ŠyzzMÆ}‘€©Ð(,ýqÉÉI„B*î$K~\iå9qâ:µ-u/‹ÿ5 ƒŒxc‡OŠ„$ðô¿Þ¦Mƒ¨ªj>Ïh€f!wR© C/!×·oÜ„Ûá$-)É2ýeì†]Òbò™PcGát¹Ãº€HûæÕLÞ®iû÷ífßÞ]1¥s#Õ°Üî$ÓØ3óÕÉ1¥w‹ –®c™§ºÊü0ˆ0ær€‘ó§[Ú$ÛÝf®GÖvC‰_°Ç(ÙkõvÄy˜NÅâqñ«ÖdmÅe³sûgÏp0çh™kZÓ5láÝÑ]Ž$ª¦ÁŠ ¨’Z]‡dgš¹ž#^ U ¡j*!5ÀŽCHvˆ|ÍžIv¤ à°99–sˆw—¾DíŠõY³×ø®$ZSbóѲ¼…99–HMUœ‡žˆ 0B³z"\±,Ù!àˆÊSŠžÓ’,S˜ý]¿ÀˆÁ@ˆÑ·¾È¡}Çxý™÷ysÜG¦…> Y,ªj”tܺv7[×ífÍB#ã­>æ¢j†¥Æï 0í±·8qأر~/Ç2O2ú–Aÿ¡ØÂüÍ{‹xþ®Wxæ¶iH’ˆ$‰ÌóË箉é=qô(+ÿÈþ={™úÖ[lÛ`¸4+ ë׬¡Sº¼8æ9º5hȸO ØídîÛO—ú °Ùltëݛѧ // NìtêÙÃ`*›6±fÙ2F»×7_|‰$IdîßÏ€Î]X¿f mªW§[c?‰Åóæ³r8 cÒ4¥k7Å/(ò† UÓ¨_»:<7ÝRß^×ÃB(záêñC°"@!rß®p*mZÕª}>v-3„ÐÌ›‡’Þ¦UÜß·sé R*W"kË6Ì|øÈ„¢rO¢=(¬¡·}¿< ˜97­¯§ár1õM³<®á=ÓÙ¹u7•*W´ZÇ7l3½'o @…ŠåÙ½Ýjɺûú¢<%ªÙw‚þÿ™'÷Eë ¥sH¿A Ÿú,¿ìÜBª;™w2KׯBÓT¼~¯|ü6U+Væù7_" šù#G‡çÔ¯»LUR1[ºaµ…‚A&¾óš‘°þwz¶¥€„óV‹ó6*T4<¿±dñÊÀÀ*OOÅ[ÿ;¼Eú×-ë.‡(º­”0,%(A ú¡n=:"IŽì8póWB)I++ 7N…¦ª–cW?3…rÕkšJKãî}%‰P Àص»¨×¡+B e¥vËv8SÒ¸jôdó\Q’ÐT•ùºn^C”$wïó? Jk#Š-»ôûmÏT”-=úóoé#Þç³ÿ£Tÿ…ùÏR¶ÿ[àB(@('W<6Ú²Æã=‹Z­¨•ѪÔçr¾Fƒè%‘Ö}ý)@æïlJª¦2ó¾q(² IéÒ´ ë_þ†¦"‰"½[v>+h‰Pï–‘D±ÔóÎ.œåßì—ÎGG·‘þËF!1a‘ýßàqæAÉc‘ÏŠÝñ»æ-lL-­ó³œ0Xþqã+Ò aZµi#~/PS§?‹Z†<,y/Ý{vL?ƒt¼ùy1DzdÑ´W_d4éÞÇ|ï/ô!ˆ XΛ¶a-åkÖæÈ®­æ¹šªrÑÝp:Ýý‚(Ò¸[Ÿófèçê‘mvîÿ9š:kÛ–]úÅœ‚(ÒºûÀR¯_–âqÆ%¦°ïZ¿—4ïWê9ÑÇ"ï%QââŒK,JÃ¥Íûc— F(„­vÙN¿—ý¿¶(&/Š"-7Š9¿d_¥õÝ¢q£?„”Tøþ,ë²(Š4îÒ“JµëÆŸ—§<_f‚$Q+£%µ2Z"”X“ç`b×j1è%V¿€D€†Òxðƒë‘ãÛök‚¶‰²h¾ÿ+“"Û˜rûHŽ}¯L@‘[T€6–´­ßœúÕÒMãÉ-:Ç̹Þ-;£È¶8¤‹ z·ìrÞs¶KçËhÙ¼Í:ŸE‰N/=‹²à@UCŒýv»“.ÿ\~Wš<’…ëÞúgÌ(+ àogì‘%ó5òþ¿Ûö¸8æxÇÞý,Ï¿ýÆÜªž^Ÿ‡'½‰M±£ª!ž™ù9§ûœAÉÓÞäÑ)ÿBUC&˜=ó3Ô(ýbÞ>?j(ô7o9îûxŸÏ¥ó•iÝztÄSäÑ3UUeêôgQ[°pöy©ëŸWp^÷Ò½g§ùS­®—2Hz¸1ø²bG Ëì34ëÕo~žåÜ!S_$kK&Ÿk*òŽäd.¸ý~‹Wäƽkx‹òË.‘c-ºô‹{m5¤uËÍÏo.÷[®ÒB¼>dVÜëw¬Ó % F]ö,½õæó{¾"¤…EY”MÁ~q³KÌð‰~-.C%$AââfQ5ý;JÉ•ñ‡üäL/ââf}yýÆYøC~j•¯Í-]oãÓ»¿0Ê˺÷[Î(Ë­š61ß— A B¹4n¾êJdYFš‡H‹Æc¾išÆ=C®7æŽ,› %šªW©L˦Ãçü Ršr¦È 3o™‰ª©1ŸÏ„HçJ ™$‰&]zR)½n\qãø—¹P¸aì‹È6å¬ýŬkŸ—¡qLYã]tˆ’pN#fíÛe|ÿz´†nS”ãEVd»„еÓ%Á<š(vùœ¯ñ¿Fª¦òîâsaënæüi[¿yœ9" AבJŒËîdä5ÃÌ9X%­"·áÂÖÝJZ1DÉâi)Zdt¥u«^d4ël9ßf³3ôö± ½},6›=îïÔ!>±Ùì<÷Ì'>Þý×VNŸ9†ßïåŽ[ÆP¿^KèüÑMU¹î­"…•›â9®ãÍͳ’Á³_·AiܷвÝFÝÎMŒñ¾¼-.ï·]iç—e¨D±ô>Lã‚õûÂü\Zuém™óúô·àÖ]z³ä˜NÍz8žµßÔu r³ñy‹xî­¯LàbSJ—÷=tûvl¦B•êTK¯Ç„¾ÇïñXÚ4­§ü½À‡$Ѩ_ñÚÔB!®ÿüSd»=滲¨VçNÈöߢ—H’„^Š~š—WWuUU•—^}>.8))"ÙÙ¹çy/ÃÖ_âN5U審³P\.|E…1±¤rqêà>nyyöY•YQðäæàÍË5™‡¿ÈhçHvqÅ“ãú¼œøu77Ný§å~O|½I§(?çÜx(HË®ý£úñÀ¤¯ÃBP%Úöº›Ý‰ÏS:ÎèPÅ|_¿yì} »ìN^4—â²ï1ÃGã  ªo‹>Î9D†=™xõTBZˆK›÷GÕTÆ^9Ë[ Ä¥¸{åj—OG$tt|A/­jµaÊ5/q|W6o?ú&“*’VÍE—z]ù~Û|&^=•TG*¾r<Ùèè¿ hªÊCF!Ûl¥*¦eYüZwípΊ¼,ËÜ3äz4MãÚËúQ©ByA@Z5mb@aWZ6m™Ü\B¡¢("‰†bÛ¼q#ìŠb ȃ·ÞD(¢eÓÆÔ¨RÅ>ÔIGŽä5É2õj×:gQ`€ƒi«ë:yÞ\K™gšÊ匛_Ç&Ù °YZXLÓÞæ«"+qÁBtÈ”(I‚`yþ²MáÆ^æàæ †âž—‹®k¿‰_Œ¹l7Mü§e}6ìØ­Ô9¦ë Û¥s¢,šž Åi³(!ˆÓ‡ó(W-™6ý[xBaŽ›CŽ0Áœß™'” „§¿8‚¬Ã{Œ~+ßöx ðx â{F„Ò„$Jœ8‘ @³¦Í),0ø‹­éÒ¹?RØcs6ʹ*ØÑäÍÍCq:8u\”LÆ@d»BÖϸðÉGãùëšFÿñÏü)Æÿ¼*¢…T†¼9Åí äòæŠÅ&i}eWteôkZ èó¬Í×p»ú=šSµi-Z\Þ)&¤ í5=òæp´j‘S;7¬æ‰—ßCÓ´(ÅR¶L?OQ>ͪ‚Ýé¶ÈF›ÝÁ°g_fÛÏ+ àÒ»Á€Ÿ;ŸšÌØ·¿# P˜—C³¶]èÑï4oß‚¼lë|åïE‚$Ѩ¿5ÝŸ_€®iÐxù9õ3aÅJB~ÿy__Ï“PHõÔ¡ ¿0V.+6zì.6mØVš]ŸÏÏ «ûÓ©s[ °ˆç=Q…`0X*ú½ô[ÂÉÎvNÌ*ŠXY¥°Âdã–Š±J‰¢å}äœH_Ñmä( ñÙúŠ&O^.Þü<®=†sWYø&ÝûnyI¤Z£¦ÔiÛ‰ÜãGb˜ª¦ª ™Rì 8±/ÃÞýšaï¾OÐçãŠ'ÇqrÿÞ0³‚Ë1ðÉq¨¡ ž<¯ÙO6YýÉ;hšã©èºz^ƒÔ²KÓ{rÍ}“Ù»e¥)\Q¤rõúÌZê!à÷Æñ¸(dt¼˜5?lºx3egá xøðÎOÐ2²„‰WO5gC®'‡ÊÍʱåð&ôy: «4 ß›Gž7I”ÈñdãTœÔA5HǺÐòrò½yH‚ÙðÒuÿDÏ3±l’Baxˆ‚Tê‚ÓT•;G>„Ýá8§çV”_h>§ˆ¢Û°E3¶h @» ‹•dÌÖ];DYœ:Xl³qÝ=·òÚ·sˆ§¡‚A®ÐýY‡h›Ñ UUÑuÁ/3½æ\õzÑ1,½§Îd³íû¹¢DQ$ 1{ʄ؅,ˆ\œqq\å(×cŒ§Œd䀑tiÐ…Žu;ÄWh^Æß2dG2ËF.åDþ ‚j¶ém¹(ã"`D+y}"¤ÉBÞºíM ±„L•¢êèx ò-ÊÏÙÂt]«,V®SO^ŽE×oבkž~!nh–TKÍû0èì²%TË›ï§iº¼¶ï 4UãîYW3ø…KÙ»&‹kÓúÒ&gUšL¯7ÈfýcR*%ÑúÒFø=Á¿ `¼<;øAŠöí0êY¡w‹Îú¼z‹(ôyÂã&’‘Þˆ`ØSk·)<{Ãpnê=ˆ-ûwùhQsA‘.hÑ™ûv°dÂG¼zÏsæk[?Ãl7~Ä+x¾8 8â…9ÐuI2¼½ããûøs×ç÷0üþǹîšGp:0I çœH26›B³¦¿=4B ©<“µ=.oòæåsû£O›‡‚^/÷Žl1´ÙœNÊÕ®aá—MZwåŠBõÖ-þ#S&^ØgIÏæÙ¼°¢$R·KS*Ô­ÂÝCŸ!Pä ‹;CVJ²Ä‰=‡4évtM£í5=èvç¥\1þV4UcФÛ{à$›ÌðŹþX@h!•;ÿ= MÕ¸åÝǸjÊÔl]æ—uÄ™êbÐä;µm¦áy˜7–Šõ«‘µþ׸÷Ù¥cÍ2GAn6Ž0xˆ–¢$qüÐ~3 ÂáJbس/³sÃjŽfî‹oPAǦؑ…Ý„‚A®¿÷In1þoàéŠj%•ÐÚÜrj0ˆ ÒäòçÔ§ÿ7Þ“×k̹¦ÍÄÞº÷ì€DB«ÎE©ÿäï¸t@±M’D.èÓ5ìT¯Q…±žÀïP§n­?$d;úþ.ìÓÃôÖ”ô²”ü’$!IôêÓ>º­ H£\ÌÃõââfxñš¨šFûôÚÔ«Tûûtç£;oÄ¥ØË[epS—öüë–ëxuðU¨šÆ§wßÌ?BÕ4Ò\NöŒI·uÍ µO¯‰K±Ò4²&F×uô¿@Õ4t8l6úf4Ž2ív®|j‡wì¥RúÔhj0ÉZÍ[Ó¤çÅHŠ‚ 4ë})uÛt¤v«ö–óƒ>/ýzš Ï°öË€¿¨On¾ðü¨Õ¢-RØBwr&wÿëßÙ¾™;ߘaºæQ¤u¿Aܸ.F™ÑÔ³Wëäž>Fº¦Ð;[>H1Õñd[,mj(ˆ$ËTM-Œªõ›waø”oyù±4lÕ=.“oU³5¡ Ìxà5Cá xèX·©ÎT о¼!žD#gƒÓ†Bê¶»¹²ÍU<9ô1ò¼¹Ø%;=öâèÔSÆÄÌèÉ€–MKz¾ÇG¿—QàõÑ9£’(²zÿ*f<ü—·H®7·T«ÛCãGá÷ùÎÉWR)%‘F-šÒ¬m+nyd«VA×4ÚuØiؼiüEvŸVªV•ï?û¦”kÁÉÓÙȲD·vmÍãHóÆñXhd{MâãÑ¡·™$`Žž<Ɉ{†Ò¡e Nçä KÍ5Âç÷›m|q¬4š®ñÜ 11cA“ ðçÙ¡nT-DÛô6$9’™6xšé³Xr}\Ò¼/íë´'ÏcXRÛÕiGÿ–ý¸°Ù…&è( F\Š‹g¯|–Ÿüc…)V,ôøcªëçny ÷í+*¤Q§n&£•m 7ŒÆü×§ PƒA³ÏËzŠ>· +µËVõJÏû(Êñ†ï¯Øs!J" :Ö¢Z½JÙ£û•Œzò]®ºîØõ éÔñRd›×Sù>Ú‚m1y   »„"EEùtë2¿ßËôGýF ¿ñjÕj÷‹[>}‡ç‡\k9çù!×rãœ7˜vú 'NZ,óƒ^™Äæ búSnýì½?`”äšYet—æiQ/ƒŒ:MÍ5Ý©Yû2)A©Ó±1õº4Ç!Ûkµ©e[•d‰€×Ïý Æqjß1|y¼ùFÛP Dƒ:-¸÷Óçùuå6ö¯ÙÅ Éw +Æø=Æ\9µï-Ûw¡Õ]È9tŠŒ~ú4È0ÀoÝÎMÈÎ<Éðâ?‹8ŠÅ §¾ý Kw0哹(Qa@Y{w0ó_Ó±…=W¢$Q˜—ÕÄä1Ãñ{=qŸ{‡>ý(ÌËÁ t¸àR‡O8Ô[ý‹òÙn§Vçbßäò¦'êÔö\2y"ö”nš7—/oj¶kP¥z©kêÊ7ß@ ºÜ¿ó•PH5½!«Œ,ˆ®1JHb\¾$9k>2# IDAT¦¼ü¬Ù·a4)ÁnšÑ¾ÃœK¢(Ñ«wŽ?×}Í诲¢Øhß¾OŽx€š5ª™ùúdTUcü¸‘8ð» WWË:½ WWl6™>½»S¥J%Ú„»¶¯°$Ô‹U¥gÃz<;°/ FçtaëÖâÕÁWñÏ®bcÖ.jÚMשU.•×_EŸ& 8”c(“©N'÷_?€ã¯M4ÜÑÕ«ðá7°fú xAë{µjU§JJ2„cÛTMãåë®ä¹—ðT¿ ã{Bto~.¢$á÷Ѩ›auøÄ8BMº÷!àõÒ¬W_d»ƒ¦=/2•TS6åç!+Ù¶rwÎúÙnçÔÁýÜýöÔoßÕR†×›Ÿ‹ Ixò£µG¡Ì õ2àÔÑ´ìv™ÙWÉäñó-™[¾JmúU¨T£~Ì"êréÜÕËÁ™ãYtî{C´DM:ÁHÏõäCîéu/¿žÜË¡œC\Ô¬o1Ø =Œø¡o³K ˆ7—~Í/£kópÚèóÒýé\¿+ª¦qS—[À3Ú™–vY” t¸ÿùÃàÌÁ.nv ‡s,Â(â(*(ŒñŠ<:ùÙø^±ÂBZ…=ˆ0¢ Š‚€-¼Ðn}ì^|^/ϼ>ÅòìBA#4ª]Ψ¡>¯—oÞû„G'?k íÒÃó±(K[èñpÝ€þæ½Øä²…mƒôڜα†æy½–6‘×ÓÙ9´lÚØ óŠx K9’(Ѽfs:Öëhµ& "íÒÛ’âLfh¯¡ôoÕŸ‹š]D¡¯dG2ßò0yÞâP9wÀãýg÷ñÝHáÜŸX´I2—4¿„véøº¤ù%&‰~Nyž<‹åº$¼^vêV¼ÂëJ …hØ©[©’xžQiÒµ—)„ttŠòrL«ï^2¾‚||…ùe¸‚cƒ\_,`j8q\°€K1,ŠðZæÙr8|…~nž:o/|}¸ø ´®œåópËÿ¥0šÙcÞFµ «ÒuÃÃñÞÈ×øeïV4]2Oá݇§òüC“ÍÐ,Ã¥ðÁc/±÷èAø…Õó<…æÜÖt½XÁž3ºFïcÖ_×ΗqøÈ¯¤¥UBJ fc.vîÐÃ5,…?wêP3êý%–\I2úII‰à6˜þâtªT®mX¹ ÎÿyŠ’Dà {™ë 0?4ìÓ“3û3©ÜÈj}uW(OÑ}ñÜS¬ÿàkx‰yGŽ™<6:—äÀO«‘l¶³ß›(Æ6ÙÆ˜ÛŸ²„}–<Ç&ÛhVÇê%ì’Ñ‘õš‘Q·‰™kجNleäôèªÆŽïדR5Í|>u;7A¦8<²(§]ƒÓûÓr`gIÄæ°qÍËwslG""Eþü^ZìŒ/߃l·1hòlúrÏïŸMÀãCD¤ùeÉÉ:EýîÍxüHØH®œFÝrMP\v<ƽψÉmá!ã.;öîO(DSUŽê:»öúظêGtݪ³H’„(Š7ÞCÖÞ&¯+(ÅH'J{÷§(?—B Cï–9½dénZvêõ›CíÌ\—(%Z…¸ÞÝßÛ¯EÉ÷ûyaÅJóóeÿ|=Ö&Ê2ÞìtMÃsêÉÕª™í6oÝL½>½-¿WSU¦ê: 7͛˄[o9§õØ$NHW§.m„boH„RÓR€ÀYªª¡ëA|¾²ý-ÝztäØÑØíJÌó$‰ŠËóñœ¯hÒ´=/èŒ Çwíø•¤d7¹9y%Bþ¤ðµuêÕM7ÁˆªªÌ|}2ŠbCQl<öÈ0{dŠb#²tÑ¿ÿÂHìvÅnðèÖ­ CÈȧǛ¿÷‚^]HJróü˜ôêÙQINvÓ¦usZ„£R7k^Bîša ñ‚ ²çä)æ ‚ªéôÉhÄÑœ<º6¨Ã=½ºR§ByÒ\Nr¼^DAdÓ¡#èsçФj%îØÍ° ºBH%Åagæýw°x§ÒD^>²(2åŽÈ:t”ñƒúƒß¸ùª)Éœ,(dÄ¥}èÞ¤c¯è‡ªieZ@# LÓ^ã÷š¥r#%xOgîç‚ÛîCVËä‘ìÙH6Ũ¨¥ëÈŠBímñy Ïj…Òu­Ì…ë)4Ú¤”‹ÎÉ肬ر;ÜÜúä›çœ .ˆ" [ugõ÷˜‹ÆÊœlÑulŠƒû±æ‡]Å}“*T ïIŽ'—_OîeÆý¯’êLöœØƒ÷u£ïoøŠìý…Fe+rŠr !#BЕ’+ѳyI!Å‘Â{w¼Ã™ã Bi‘‡:ëÉCeÜŠ› WMæµ!o˜ÀG”DZwéPªÂ¹aåZFLs¼¨ ˆÖ];"Ë6Zwí`*¦‚I©Åš@ÛîÓ½øÜ=[¶óÄ´q(;«Váæ‡‡™V¦üœ„b³Ó»ioò¼ùÆ]x#`ÅXKn{“®5ò„$QB‘Þ¾w6.ÅE¡¯ÆM›˜×èÛ¼¯𜢫°´¹¤W8D²„YE{3´¨ãSÖí‰-!$•+OÃŽ]ã‡jé†×³]ÿ+épùÕÝ»‹CÛ·”n­°BŠr}Ôk[ƒÓY¹aË›Ì#ŸÞH NhT‘×KrE·À¡ZFèbQ®/®@õäziÛ¿ ŠÃÆí¯\Á‘C§Lƒ‰XJjÞEºSaI”¸´s¯RA‰,I@¹mÌíÈÕë¿ÝWÄð·‘W”o`3ÙÈ%Ê)ÌCGG‘mtlÔ ýÔQ‹w0ŕIJmë ´ÂaSp(vBjPxå{ ‹«`Ùæ2ëçç‘”ÿ™kºŽ§ ß$`س/£©*cÂIêR”ŽdXÞ‹çm„Ö-™GRjšuLãÂ(´¼Àðж¹¨²MÂá¶Ó M:ÿÚ3FDSµ¸ý”õYÚ^l(¶­z7‰«ÕëÓ‡u»ŠÃÛÕ¨ELhA JïÞ ¤ÅuךOd»¾ú‚ÇO®»z}úà9uŠ_Xˆ¤(Üøí×f®T¬ÇM¢ZëV¤÷èn®%I’¸ï®‘ø±k&­\ì€~5ïºv7æaI^ežðyèÞ³“É«#|¥Wï.‚À©“g˜>cmÛ· ¾v…ŠÆº÷û–þ"¡P¹gvÓ§w7œN'N§ƒ'¿Ÿµë6 ë:@ŸßOaQšf\+Òºe†ewêÔŽçÌ$ûÔ. ÀÉÏ/ ¿>Üróµhº†Çã¥RÅ Øl6Òk× nÝÚ€3>£ª‹Ø%™vé5É<“ á >íÓkÓºVuLM å‘»,ñÕ}·sdÇnYFD2Ïä€( Gd1ýú+Ñg¿Â±ã'É™þ‡3£H—d4&ÿL.6IâØkùõäi%ò¼Þ¸QÑÕª,ŠjTø†ëkS¨Ý²Þü<ÓÍÔ¸[27ÿ̳fÄT;ˆôSÜ¿—Ë-*s_°A9)µ‚)È^˜1’€ÏƒßWÄ}㇞³ðÑ5Î}o`Ϧç jT–ßW–¿nó·w¾ävü¼°¸MÈÏ“ÿ~ŒjU+D)(2øàšv×±ë—}Ø$™š®‘çñšûœ9^À·Ì3Bž7ߨx1^£“çñž×þ::Ii ÷͹ÈoŽmDù<³&­2°…­pÏ̘Âþ{ylÊKØ›(ŠÈ6™€ßO»îÅVÏäÔËÂŽŒÿƒÿ¸™cáþŸ™1…Ý[¶‡›ÂüJ‡PP.%…ÇîºÃ,­{ìÄIªVªHͪUÂëGàÐÑãüøÑ{œ[“\.@ ¿°Y–LŠM–ydèm<6~ ½ã§NÓ¢q#<^_¸\©±]N[÷ì1-=¥+²ùìêUªÇº×ÖÑ6½-yž\\Š‹†}Äþû‚Üú;œÚÏÐ^C¹¬U9„(ˆôiÚ›0žÁ½òõšEq-ï:VЫį«nUP˜C… Õ°Û]fÉ߆ Z)&tïÝñ¼a¯FÙØlv:u¼”Â"£ïUWÞÇÁ¬pŽ—§€á÷O7= çU KÀ4¢mùâ[Z º›Ë+À¢3Ù&ˆX0f‚Åãá d‚/µ;¶cË—ßšçõ¤âÏex=‚¡ Ù9\Þ­UÊW±T&‹¬÷Ìㇸ{àmØ;²$Ó%£'sO™aU@õаf±WÚ}/y<«xÌšµ7ÌÙçÐYÝþ§kΠj}9ì38³Í¾²;ݬ[–°—>ùí] €ÀlQd…Ãj®(L¢‰®ý:1`T&}xí»eÐgX6ÝúwÂlyýúŒ¹fXâo·šé3,;á³a— 4`8í‚þ¨ŠJ»¬ n}õ¯ ¿lpJÃèM7P4{ŽñÙ[·ÝÂ- çƒ  ¢j¶Q¬ÃôµxèM7P¿wo‚óAUUüµÚXܳv-Coü»6—âÓp˜}y›#‘6ûÏÝ©5ëÖN2Y–±X,)§oò§ó~[Ê9cÎÄfµ I¼L*»GW"á¨!Ó‹)*Ñ'§V›“É„×çá™Ç§ ǾT€'ÆPÄ~{0æÅ% qüDC‡ ÀétòÙ'ïpü؉ÆÆ$š8ÿ¼Qâ2©ùýN?}PÊì]ß~÷3_}ý9·›ÇÄøq!Š&šýví* áDMkðibHVg[QGN«•Ÿ'ÝÆ#µúB­ÉAŽÔ5pÙ ~ˆ&“îÙ›¢Í,òKÂ5&šBa? 4‡ÃFºh2ŒF91í5ví;Hs8 qß%IšèWß#•‘ãJÏ$ØÔ`3ýÆŒ%ØÔ@íž\zß“ ¹ôJÂ+II’R R“(ÆN¿1§µíé•´A'6»‹Ç¦-áî«ïL`þ›C-ˆfí?“I$«×àÞåP¢(2ßO}§?^ƒÅ˜edÍHôëò3/bæÚO¹hÀ%Zðxë…Ód¦.PO] N7>U ŒüŸ&YPZ÷™ÄÝ5üí®[pº]\ù¿1í—¯ˆê^¸æÆfDÑÄчéÚ+Û0·9‹AÃNã¡×Ÿ§º´dzñ‡'ÍǡУM“(¥È2Š$¥4 L&ÃbKq Í~?ªÎ\´ËH盩o3àâñÌzk ’Î’ì=pA/”&˜œ;y[·¡**P‡ÝŽ(Š457ëEOø šô8 !i SÅý™ãõë¦@#\6äRöžØ§eº 4ÄiêÍ ÖË¥ƒ/eD3i7ëÁó"¸Æ`#‚ÛD} ^“æÅÝ>Ž0,û Í'jÒ….¤1Ð,?;ól‚ õløñ[λév|:Ñ\u‡ð…øéÕ©šáëð×kF[Mq!C/ºœ@cžÌvqdŒ@Ò°»=­Xœ$¥ÑÖ‹&ºÜÂ\* R4rÒ~;VSÏãúa2‹œyÕ@j©;©œªKÿ¼sÍ7|¸ø î¿ï-¤°ç´b2›ØÃAlN &Q ×°,úŒì†ª¨¼8ã.údwÃ_"­£G—š8íÒ¾šœ«¨¸`ØÙ4˜uÃÄfµ2nÔÚÔ¶Ùø÷ã¯pta>²¢E3Ý:tÆb6¹íŠk9ÿ ÍHù˹sÖÀÓÚ²&ü¡ *0ó÷t0,ÆúÅ#Ǥ¾ÜͧO|€ÕlÁf¶Õ73‹h1Œªñ:Ü(ŠÂßF£¾¹¯ÃÍê¢<#Ë•?äå›âŸfà5%èéx#!î¾üF¬AjjÀl‘é¤ìEë=#*E°¶J» ‡È[{¯'AÏÆl¶pΨ+)-;Êu×BJ' ÕjáôÓ!šDü~?>ð+Ò‘hÕ£œv€µ»<`ˆ‘)-ŠƒÍô1 Y’ZúÐ*5=s,nã­ë®ç©ŸLñ´—EOU”„Lt‘60/Áæ&úŽ ˜ð××áðx)Z±˜ì!g¤”²m_±“(pË»W D8í²¾Dˆré=gsxÇ1üõ)IÁ•f§ ?Þv.Lf“°‡8Æ"oP¢‰aãûë5PºèÀOþ·¨®Û‡¬#q‹ÃŒ+ݪªœ~V21›Í ë7ˆ¨$Ðc8áâçðë2rÀi˜LÆä‡W?à¼ÓG²²`#ŽaÚÃÏóÉS¯‘yÙp®½pŸ==……WóÂûï§dF ùümFœuQI¢¼¬Zwf(F ±ûÔ×%Ç:Z,V-P\4E¨©ÙÏ­·\Ÿ˜ ߬§ÇMœ9l(þ2Žf¿Ÿ¦&?ãÆ_l8|}¹ñæIøý-kÍù:à ‡ñz=Üq÷£Ìúì=**«UKú´˜óý lV«æÓ4ëlÀ#µü2é6lfÑ>§ƒåå;N›ñ?<âKC ˜€ô}!a=FC0™rÉUœwË=ÙYeœÓ9g3ïúr$’ ï7z,'@´Xé?f,Á6¢UE¡býJOÊïƒõ´Ëî…ù¤dv§Á$2ôœ+P… ‹¾aÀ™ciª¯E–¢œ}ÙÍÆÄrÎxþlAÞ@s½Qñ|Wé&^û¾„ü?¿õdùŸ{dödSþVƹ’ÍÓâ6ˆX›O*™ŠÉ¬bUÏ!þoª¢²aé*F\8†û®ºÙˆûøòÝétí™MS}#²$3'o ÝûôäõŸ%rËyWrÛc÷Å¥ªKÌŽÕ¾sG6.]e,lÞtŸaЄ‚A&Oz‚Ïü˜¬˜E‘P%›Nv»ÍðZ¨qÿ &=»u5²_Å£÷?ÿŠ÷_|–Ìô4£½f¿—ÃIþö"÷Ëap_­ây슢 YVCœ6P3Äze OOºÛƒÅÒŠo>N¦;«háxóq.t M¡fÔ&Îë{¢ b­œ‘}†8KÐ øfº2Q›êõIµ@TUÅö·HðLÚˆÙqx_Üñ9V³EQس}+C/ºÜh3óå£O`±%¦Nt¥¥ãoHÃf«áã'0|ü¬‡aÔUmZÃíihª¢ˆÙjå©_–ñõ“÷±kk>C/º<©ø!À¶¥U¸Ò ëÒψÛP}ÃÕÇxæI-c™¿>„ÕnÆæ²¢ª*r´e=ÿ¼QZ\  …yé…Ginöc6‹ôèu&%%thߎ—_|œÓO̼ùK3z$cF¤  ˆ¹¿|Á’%«ˆF£tëÖ…ÃGj)..ÇçóÄ` /¿ø7Þ<‰p$’1%šLÔ4 >tØvV”ï@þ¿Tä$Œ˜„d·@Ψó 57áðú•ôÎY\ûÒ;X®„ëð¦áðú¨\¿21pU÷ h²¬Æ6ï®hªÔ*ƒp˜÷ÊvR»¯í…ÉæôPœ»“ `s¸xîÆ4?ÄÙ—Þ—‹Þ’°A¶6žTlwòâÕTÇQÒtºÓøø¹¿±§¢ÀØÈšŽ·= i,)] ÿuÜFkY–ŠŠ€€ÏéhSòŠÅ9ÕùŠ¢°qéj.þëtî–EÐà™^ç›÷?!ÒÜÐHc]=}ôtºŠ,ãöºošCûöÓk@_TUeOeµa$˜-N5‚ÚCGƒÌX0‡û®ºÙ#1/DcSêxEU)©¬Âé°#€!)¹ý‰gRoJ‚À~} {A oÏ4ûD¢ƒûæEUÈßVÄ™C†P¾s'N‡ƒîY]Œkcq&Gjá°Û©kläÍŸÏÔõèÝ3»sa-@uUù*2Ü Æåââ%ÜýÕDúwéoløcrFó[áoxÞ$&Mjå¡ÕØ“ ÿ„lÐL} ¡M@llÀáñ&` àð ›»æ‘ø‰†CFAA)¡jÓÜ陸ÒÒSªo]iéøëOà¯?‘@å·å|Ðô×)•Í&ƒ•Ô9c\2ºx4„è6¸“1~4$d>2ÚñÆ_ * ²¤Å4 CtYMxÖŽ½3Q$…f5@ 1ı}’*²·¼•cºTÍj±&xÁ~\±Eg殡g—nƆ¬ªšQ‘¢ôîšMCs‘h§óqû•×a2‰L¾û~^¹EUéÚ¡Bs&O SF{ê›S‰üáéé­?ƪâ<ÂÑ0Þë‡2¬÷ 2Üi)çÕ¶Â -¬eŒaÖŸWVdØäêÖ‡{þý¸ÚHŠª¹ó©øÇ…ŒŒVc/¸ž_~[È_ÿ2 UU‘d‰}ûª˜óMëÖÿÆÅö Ð|›ý \2öFà‹¨§¥µG–¡©©‰ôôÆ=ûy&×\ ëÖÏ%°is!ž¸ß*ŠZLIÌØ¬o8Öv ˆÇwîæ’çŸàt£N‡JÀ0-˜Ì"Š$#šÍ lE$ÐÛR§›¶8øŸàPqr=Ñfãò—ŸaÎí÷¦Ô¿·fÈýÈŠŒWù‘e™£uµœ5ðLìV;²¬І°[íTí«& S¶§ÓC†W›¯ þF¼./é­¿vì×€rN·Þ¸nrKó9ZŒ‹†ŸJUEéÖÍ(’’T°Ñ›=ù4o¤ÇYýZ@}} [’ T„ÔÀÿ•‚nþF•Á›ž™`d»}6òW-D–5¦¢±î¸‘m3þ1´y2vÂMTðÚÇÿ¡öà>o é>$YÆá°SPPDÏÝ9wÌY4ÆÅ¾žqæ9zŒ—„Ù,b³i{Kaa 55ûÙ˜»…«¯¾ŒôŒ4¢‘(Ì[Ê„ ã¸úªËxþÅ·¸xì¹lÌÝB0âµ)Ó>â|n¹ýA:vhH²ŒÕ‘Ήº;Ë )JJ›Ûn·³´´R{•,±RUƒbuzÓ9vhá`‹¤¹áX‚—¤õ‘éÿ<~#¶ H!®þh™<§piuFBÑSŸ¯¡@o§}ʲ_çÓÔÐHv_-HqWy•.YëC;6Ù,V+ÏO‹½Õ»hnhÄdqº´ÅóÀž½¼ýý'†äjÈÈax|^úHûÎ-Þ5—ÇÝ&0Ú{ð³…Ìô4~˜·@{¦½û´wáp$ÍQù Š&œ;ÛÊÊQT…`\–«x€ %•UØmVãZØ^^ÓaçȱcügúûTíÚÒ@ˆHšÃÍ ¬,_…ÏîÕÚE@QVW¬¦{f·ù‚S`UÅj–—.×jÂijv…{ ‰ÈcŒ¢ƒ±~×âKêãV’€ @Õ¦ -ã´î87ia–"‰ÌÈÁ:³Ù__´­äŒ<‡ôN](Z±XÓA×á¶w>¦÷ð³ä#R$ÌÛ×^“?/KÈ„×ÖáÎp"4ŸTN#˜Ì6‘a áh3[–ãÄž$ðàbë¦ \éιþ4ÖÏ.D‘UT~¸¬Ní“îÐ|"€Ók§xeuHIs{tÇLü:‰F¸ÿ½—éÓ5‹ÙÌúí[È/ÝŽÉd"òãò¤é,щƯŠðåüŸYºi?Nùˆ@(HT’˜¿~%ï~?³hfí¶|‚á¿ÿªn,63÷­OxîÓ÷Îø?šM˜E3²,³ãàn~}ù þùÞ£FÚmÕdwÙ¼ø}œL ‰±%ªª±­ç–¬È„#!þ³n®NÝAEÑHû»çè~\·Y‘‰FÃ|:ëYÊÊ7±pñ— ;ýB:´ëŠÅbåŒÓsødæ3ÈŠšUTjöVðó¯Ò³§.…qn»ìófb2‰¸Ý>öY}Ø´y ’!/1nwÍÍõ€Êž?Sß^ˆÕú犫šÌf¢Á·Ìù‚[-<Û§®ÌžÊ»~¢ÿñˆ–Ô'67RƒpµÍôÖ5;ÒÜ>õ¾öº¼ ÚŠª}Õ”í© "Eøqå\fÍÿÚXŸ®{áŸÔÎÛÉõ/Þb\á‹ ñ ˆæè‰&0¼mIéD‹™Ò…›²/)Q‰@]3n|ìÙT‰ÿX#=FöojÛÕå { • H‘dœ¸OI™Ì‰L„ªKebY,SäÒÁí…†ãZìlÇ®=xìùû¸üú±eíRâÍ·ÏÆ–5K9t/ò–ÏcÊ}7ðãš%´ë¤ÛNu-ÌÀ‰£‡ð `6[û54·­”°XÍÜôÂUL»ë+¤ˆ„‘˜v×WÜúÚ5Ì;ø)›S½µ&É®jõÊl1ÓwDO|í<Sóy÷ÖYFŸÆ˜–ÕG©Ü¼›g§ßc¬«»¶ï£žÆSʤÚz_¾nÝ8ï駘=áíDô’ ëG« oŽô4¶}û=j+›¯u*êóŸ{†Ñ=ŠÙnOdѲE=rÿ‹¼?}rBpvFf™i)A‰¹ g˜"˾˜9‡ç^|ˆû'>kÄÃ.[²†Þ›ÉGïnd¾M&Æ]qùy…IïB–vìØ…Ïë¡WÏîtí¡% èÑ£²,SW×€Óéäûï`öw_ƒ ó,ãÆþªeÆŠFض½ÄHd6·¬Ç’$a1›ººz>œþykz€·ßý˜H8‚ÓÓ“óν„H8bÔ1ɪʊòøtiC˜PU—Ó5«3k*ªt¶±P’̵3¾"«cû”àä¿& AM†% ‚$×»ÛÍ5Ø E>é8Äá‘"ᔆD‡^Ù̼ûï‰9ÑuP¢‡AQ®ü/ŒöXŠÝ˜÷" ž*Ðìœqÿ¤¹á8+-àê;^¢ÏÐѨªBsÃ1¶­ŸÇŒM¬þ5%RxS=¥ºÖçt@Nõã„”¬…€@š3½% \ñ2I‘¨=XÏü†ÝbOm 4sÁ³M&|N7Ë·µÓH,0ÝéÖÏmâ¡gãÖ‰,ËȊ̪¢ù«‹óˆ´*Ú‰„p:½<üøÅü8û#žxæn ÷4ÃÙ#ÇaµØyçÔ7Ô{ŒÅ.—W-ãÔ$˜´Ô¾‚&—ˆ1Ñh˜~ŸÏf,@Íx<.üþæSzÓM¢ˆ‰eñQiª­E‘$†^s5¢­@;|^Ô8Ã8¾Ý¦ÃG5 •`jÓãD¥D¢¶í¥wÀœ{š–Æó‰/3áÙ›ˆH0ÀCì)n2‘ž³iô7ÑØ˜húI‹cˆ¢r”§?y‰[.¿‘4·§ÃI£¿ A(¯©J©œ‚@XPd…ÊUÛyê—8Ì^½þЊíP$¹Í€b'.JZšN»óÁO#…¢'y&,­(2å[7RV‹¢(x„ä8#¾I„³/¹ŠŠÝN8—-k–´wn 'jÞVC‚Rë¨t+<öîüí¢+ˆ„ÿœdZšŽ'ì{Š÷3ûãùü1}KJP5!óRÒü—dFŒB )ÄÃ/OÁê°hAåªJM‰–Æ_@4‹ãìò:ص}%'•IµuH¡_^t ÷¿ñfàn“©·X-ìiiìY³Æ°Abà"âOì‹£¥eØÓ|Ú\m#Mu݉zTN;ïOŸÌ}w?Ã}w?“LbëHª_%I2ÑH³Å̼ߗ&Š”Ê³h´©Ù/j’ʼnD9zTsR?ñÔd_M²¢P|à^§4§‹Yd¿¿ÐÀ„ªP ò꿳±z7‘¸I.+ ËËwh†¬i(ÉÅçt°¢bGµ­Lk ¢}.¬ÀéãÿJóñÚ?#M%3óî;èØ+§ Cìn!!ÐŒÃãÑe[ v”¯]†Ó›FßQçljÒæRÆkÄ4•9CG“·t¶¶ÔÕâIoo¸:ZË£TUÅ›ÞE‘Yþã‡üãÑ—éÒc&“ˆÓí£kï!Œë¦lË Ã Ó<))8SÃënIȈu2öâ–ÏoçØ‰F­¯O"ò:í,+]’2è/þ8~¸If{‹ä§d!5)X룇^HkOeµñÙÙÏ'¯jûwÕбk½/….ö·ÇçÅåq³mãfÍ@“J ¶3ú²± 0„_?ÿÞè8§ËEUQ)ºeÑXW7ÍG8bæÓ°X,lËÝœò·—W Šbeª( N‡ƒp$Ê'ßÏ!ÇYExðå×è™ËáЃÐv|<.MþíÒÓ(*¯DQTì6sþ˜½M^TQÉ¡£µë¿±ÿ¶õ7¼/8ôïÒŸYk?Gø‹ÀÎÚ]»!)2²"aóÚùaÒHG¢:(iiËmsã²¹XY¾ €^í{2qÆD²ÛeóŸüÿ )’–Þ7®€¡’xóoopΫš‘òÅ _pëÌÛ€6†l¦,tÇB‹q4—ÝÃÓsW0lÜ_(Z±8 ˜­VÊÖ­ä¢M¢¦¸0¡Sf×.|õØ= ‘TeA°»m̸ã'ξvˆ!Še¦*óï¢÷™YŒý»&Ûpá`ÏöC†ñæiç¢áH3v¬È’Œ„ÌþNΨ얩ªÆÕ‰ý¾°ÄW|I—þÚÞ\õ>É//âæ—å¾ëþI×ö´ Dú±<½1G¥V}¸lóB‘0NLÍ‘†q.ŒÈf`ÏþzþeäoEVd"R”›_~4!Ýn8áÐñ£<öáëÔ55PXUšò9=./ÁHˆUE¹øœ©ÙÄT˜L&ݹ!'ز"³º8O?·™s çö÷Õd…Åy¨¨,Û¶ôöWåb·ÚYW¶Å˜Z©Ö*'Ý7²¬ãñ¤䢱×P^‘ÏÒåß%Œ/“g§}¾4ûë9søÅ<öÔCdgD–%œNªªRYµTèÚÕÉè ,X­Žyb[˜|?ÑfKÈôãHóq¬z—q—¾öÝG7Öÿà‰`4áý×ùîæ»ÚL!ª* Esç3øêq ¬‰æN£#šDÆ ŶÅ<÷ÏÇi6Sµ¯šŽé†C<ýÉK¼1ñe6‡ÁVȲl0$I{~ÿõË_2ú¾Kù}ÝŽœ¨¥ro55• ’¯xcÊ|’TÆŠ¬°kc9E…y¸ðI\AŠLéÂ͸pãÜ(’¬1‚Æp8q'³*’Œ3Ý­}fôÛ5‰& ‘ônË r)ßš‹Åjæ±;¡{ÎÀÔŽ+‚‰M+à1kë_þÊ\Þ4"áOþëÁ?&ÜÀ²Ÿ¿!«G¢‘ð©/¼™nê&3üð<ÿ¾ï;£oí.E«+ ‡G’̬!Èë×Ï ­ƒ‡åßnDŠÈz¼¶Äé±3oú lèŽ,—cûëøúù_ÛH'±!bûÉ©áñ…¦“ GFÆI’wôûö?%*q°°ð@N%¨ÇÉ:œv˜Äl€ôŒ4cøØ¬n¿ë£ªùóhÁ -†®U\П9<^7ë׿'ì±§ŠEyyl)ØÎÚµy¸=.–.[Ã%—]Àá#µXìvV¬\Ÿ vô>ÌHO###;kŒ}‚6•Z!àóy »aT’¹ê£ÏyvîBFöìqF–ËjåÙ¹ éש› IQˆ1'ö¸lEèÆlšÃA0eiY%öŒt¶ìÙÇÒ²J|áuîxòÞzô¤h̃eÛÆÍmz… ·ÇåÄãv‘W¸]÷ú©øA¬ [ŠKX³i3]:hì ÝfÃåp IßÎý)O<Ìâ5ëG"d¦§a·Ù¨®Ù‹ÃnO0„9JºÏGIåŽ$Ru¸Š3{gâ ­¶J¼>‡Uå«èÝ¡7gL¤¶©³Ã¢€²"Ó«cožûå9²ëÁúªuƸñ9|F›±½'šOèÕ˚ʵì8RM†K_Èý)¼ noÛÌœ¾0gfu§*C3Pz¦¬T™Ì¤H„]…›9纓 ®Êñuì”°Qué; µq•9²ë£®—eO`èÅ9-«¢:›——iRÈ8 Eâ‹ç¶÷¯âã—¦sN{ŠVhïé›Çæ“E‡“QŒ¸Ò Aé'óDœ3ä ®{ö¾/¿t’jyþzãÚÖ›Ä=o=Ï¡cG“2ƒµn/þº¶îuß¿Ÿ£æè w•áuzÚ|×F*ðT¿QUXU”‡EÔæ‘Ùd&ÍéÁ"š©:°›\0_7.1ÆLDвtë:vªWê{Š¢™Og>CT_‹}r*=ºHðüú|PZ–ÇÆ¼ ›¾ª¶Hƒ{æ1ã\­Æ‡ö>uÚ¼°8lóó)Øy™Ýës±Ç¥)ÔÕŸ’ÍZ_”KNÝ0™LZ6Â`3—?~ Ý;vÅãt³ûP ;öíLðä·e,n,Ù„[›Ê·àvºq;\Üóú$?‚I0±±dÝ:tAQÕ ¡7Ü«KOP´r<†¢¸lЮ-+'jŽ2õ¡'‰†¢8ÒÝ”.ÚŒ]p¢ £È % 7cÁÊ×>CoË :Zº2óÎ)DüaœxÂZ*hIo[‘Y1€‹"+ØqPºh‹Ö®nX‰)âÈY& ðØßÆröÅW&©bCîÕG^"ÐÔ@ÞŠy”Uúqº=ä¯ZˆÓåÁ›îÃù'Ø\ÃÑð3÷‹ 47œ< ^ŠH¼{ë,ú,¹øšüü¸5H¥º †ý•‡“ä>šòÃÄ?»?ή¢ýdf¥qxW-Ь E$f>þލÃj·Ð¾{¡@„¿|«ÃBÑjMn»uYYÛÃãÇ“ÆVŒ­8XXˆÍãá™sÇœ¨(²ÌlUåhY™î óÃu×'°‚‚ q$·,œÏwÿÐ2xEõL±âIêѨhõ:^œü(Üÿ¢D¬ ïOŸÌ#÷¿hȧT, þf-ÊkÜQXP¢õ“3M±i‘žá£NŸÇY];±³zOÒuÕÕ{øë„ñ Ž€ÂmÅl)ØFEe5Ýtçn\)KÀÍš5˜ €’²J$YÆérðÏ[à“O¿>51‰’—¿•¯¿ù‘>}z&Hw:°Š"Yi>V–U™1bÒ+A€Œ‡Ÿ§kf²¬à¶k´ZD’ˆÈ2Ûõ"ƒWMŸ…ªÂÛ‹WÏ‹ÝbaÞöRþùùlºvËbyYù»öÒ ó䜹,(.ÇáuóäÏó±šÍT=Æ–=û¤[±MÁáI 0G4CJ,@cP±ñiu›ŽÕì"ØØSµÖ IDATÜÈgw^‡h¶bu@å†UØ=>TY¦|]9¡æ&Ò:easiEÏ Æ€¤Öëè«ÝÉs“Þh‘0Ø]Èr”c‡ö`wztfDCÊNOÁæºõÑŠ î«Ú†C_d¾zóÑh„âÜÅœ}éÍ)ÈÙ—ö§|ËÊÒ,XZº˜ˆ!wù»óPTEË6¢÷KL/¹òGü<ýés|¸b:e&|'+R’‹ ÉhçæÊÆ“Ó!‡ˆ!¯¬ŸÃ‹¬(TìÞEauI›5«g6ï>ñV›)<•4Ù\7»+«4üt6,Y‰ªh‹Š"+s1{ú,²z¶¤vté àµûŸ&«gw¤H”ƒ{öQ°>¯M9ØŸ9$Iâï¾åÍ'K’¿)Šb†SòDz•¼ññ§YT^©3}&.ùÇí´ÏÌÀé°3uÖ—„Âá„8(ARjï ¶Qg:∬HÈŠŒÏé£)ÔDDŠàsøpXô~¨7½Û÷Âçð²ºb5‘h˜Ni Ka¶Ö´íåÉpg°ª|•Ö®ÃÇg«?cÖÄYØ-öv¼õÃËOè²ÈàÕjå¶w>fîÛ/S¾ne‹÷F `jw{©Ú´Þp$µa±òÜùg$½ÇŠÜµ îÙÅU?Ãà .I˜ËЬ°miU BÀ•îà§WWpd× ¾{z“¿¹‡Ô¶\«Ë©Ö|»•§wFŽHl[ZeèåºCp§;œøöø×”§ÿ«ó¥SŒçÅykRxÌþû£{».†¤J@øŸ5¢êÎ’1z«ÖÎg×á½Ä4R²¢°ÿøaÞ›; ¤h^w)6öxÒ˜?cmqyR—46jó)+«7lÊ_¬­¯ªŠh¶†ÉË_ŒMgœ:ðÊßœOÌ‘ÿS>˜ú{ö–'d@mVÎh¡†F—q P¯±z&QdÿÖm‰k“€Q\/fÉT.]™Ú"juìX¹¶%ND¿>¨†¦ŠM"Ÿþ¡ÉLëš4Cçxã ¢’D$eñ¦etëÐE N»§Ý™’¦ÿè—Oè’©96çáv¸JUáȉ#¼ûÃGD¥(‚¡Hˆ»ß~€ðÊcdµïŒ?èObšô-&ÑDõÖ"Á(ÛWUí·ÅnÄâ@Çã>ÆVˆf ’^D°Íg‹FÉ=š‰Y]¨ßS“à芗kIá0³'\Ã¥o½A¨¡œq—“Ö½;Ås~Àîó!DÒæt:xôɉÔÖ&ǬAêNÔ'±%Bœ}ÏR(ŠÂúµ›Núž#-Eï¯?ý¤I«žœ2iÇÛo¾À+ºš$„Ȳ‚(ŠÜÿà³)c·Û•ÀdÄäŒ+V®#»»V`¶²l}[=nüÕ³gw.wƒþÌ‘db¶XèÑ.ƒ}u ÜóÝÏ<üÃo¥-+Ê5#ª`Ï~òªwѳ_of¬ÞHùþC¼½xC²:qúä÷(;t˜;ÑûÙ)T>Êy>KÏvôl—ÁÜÂb:O|œU•Õ¬¯ÞM{‹ª#µ|°|-ýŸx…ã~?‡åÙ¹ ¶¡±³{¼TÆÌHÀÏÌ»ÿŽ;S› ._z‚|coQ6—‡×G4`ÖÄ;qx|È‘¯Ž½ûòÙwR¼t>EÑj´ïÑ›?Þ|‡·NHŠIDýƒåм av–䯽è(ºõã—Ož#h⣧&ðð{ °ZíFhŠscµ;øà‰« à¡Ämž9CG³âçé4œ8œ ïŠ# Ú<––-!*GÙ¸sù»7%©Õ'’(¹êÜ)“·½³˜´ïÊ–à´:øcûïôh×S÷’KFrº3$Hs¦!éÆïÒ²%XìÚæ’»s¯-x%Yꕮђ²,c¶˜Q»Ó™pŽËãÁl6óÀ„òí¯ó‘e™YÙ¼f#«…êÒ ÌsˆˆwÆuÌê¤y²…í¹›±;üo·Æy©…$¯uÌ‹<ë??ѧG¶qʤç_N8OÕ“=Øí66o/æÌ¡-…'v%UU)ïŸæô!9ÂÖ=-ÕÅ——®`yÙr¬[‚=âsúØU»‹ˆÁëôQ°§€…ÛòÊo¯ šÄ6%wV§ ·ÍÍÊòU(ª¢Çù6ûñÚ=¬*_•çï,HjÏîvlÉMê/‡Ûö¥ Œ¿«6mhÓP–¢ö•'ô»ª(ÈÑ(;ôxѬU‹/]»œ’Õ‰•€Yeû’*¸¼-ãÂå°³{«¦]ÞU°Ÿ&‚˜âÞñö]ÚuzdðÎ_¿!–Pdå¤õª¶¯¯BDdÉŒ\Ží­O”zœ".+‰ð¿yHÿ%ÍßÖ‘æöãFû71Ó—1¦ŸË“¬¶f|<§qž¤Èdê”blGšËC$úßõ‡,''ãˆFàý k×¾x=xÒµµèĉ#ôè>€/¿~…H”ààq§‘›·€ÜM èÜI[ 7m^‚Õj§¨x=¹yÉssSrÚóh ˆŠJ§!š,Çd1'd¸Rd™+Ö$4uuZ<ˆ‡ÒúÑl6$VñGåÒ•FÊÞ˜d÷yµû¥´²"c~_¿€ÜÒ|Cú'+2Š*sø¸$Y"*E™5ÿkfÍÿ:¡p`ìX½m=ÏÍœÌO“¿aEÁÜ‚ àŽcÌãw÷ÈŠ‚5ÿ¢(Òй)Ÿ)wýú”÷X½&×&«ÙÌè×§ñÎ’UÌÛ®é{+Ô"\q3¢ÉÄr€¬¨ÐþõƇ×þ ›ÙÌÀßbæº<6TïAƼ5µÇ Ïí¦Ý5Ì/*#O—.nl¢âðQv;Îͳ¾g~Q‡R}´V«}Á_OšrO0tâ*—Ê «°ÚÈQm :Ó3Pe‡×Gûì^¬þòßdvøìŽë(]¹˜Œ¬î-F½ E"d BéªÅÈÑN_’¾Á—¯Y†ÅáÀ™–¦N¹@k?±yQî¢lÛú?®9´§‚H8ÈÓŸÐ (M‚U]’‡É$Ƙ棧&0­•¼+v|ÿÞ¬“læ2=2{0ûz¡dE"͙Ƣ’l(·t4i–ª$’NÀó·ÿÁeÓ.Æiu1yÞKe¥Kt镄ϙƺ²¼ýD ³¶,׈/X\²0Á;~ÁU—ñÖ#Ï#E¢qÒ9%!Ã$мûÄKlY«Q‚O<4‘C5ûùýë°ÚmÌù÷çZQ)ÉøÕâ}´„³§N8ÒéMù”ûö(©ÔŒÐí帩3 º~òTÞf‡ÍƱº:ÚéúT“ÉÄÇßΦ°´<¥‡ò_ŸßÃb§kzWƼ޲ ”*gáö…™lTÄßÏes±¼lyŠïÀmwã°hù£_=BÞÎ<‚Ñ ªªˆølõg8:¸ð:4–åäF¥’ÀlJ˜Ìb’6^4›±»=ìÙ®y^AÏŽ%œTb Ê2ûJ‹ØSTˆÍåj ðVö•±këæÅUcÌ¢=o\ý%Q$dIÁ³{¡ç¢E4˜“ÂEX–6[¹åºƒUǨޯÅóì->ŒÃk×c`¶¯«âÿµC4‰Üóïç ãqUQnBÒŸÓÍ*Ýã¹ñ퇴ÚqN9ŽùP™…ô›x1½:%uϾ­UhŽ¿'ÊŸR‹ax[^Y—νøå·éŒ¿òvzd §OGÃxs¹âˆ 铉ÕÛšxùÕ0™D6å/F’¢¯§¨DÛ€Ý Æ¥-›Ç•™‰ÓeˆæE¯Û{ Ñ‘_#Bw²ø§jå^oÊs—¨«Oªl¾cEKÀ­"IHÁ‹^œBfïžméUU*öî dw¹Ñߛʶ +JRÀÿ©@eŒý8ZWË ³^# ðò¯§,†•¢¼0ë5L&ü4ƒ£uµ”ì.OJˆ›»~Y8y6&³HéÂÍXV~zøS:öëJ4Áê°RÜL·a}6ø±b#XïgëÏëÙ¹¡4Ñt¦»©­>Èì»?DÖÓÂÎ}òs:쎊Bñ¼MXì6m^gx°{X°áÄsÊ~ˆ„Cx|éTmßLÞòyD#aƒ5Œ­6/Þñüzjø-kÓ¹{oÃYœ»|^‹Pƪ+—7¼e$H…[»tÕ%…ÿËÿgÇÿæu)Û’$éˆ ËžyŽßï¾ÇˆuŠ}׿^Ó¥ƒ‘S¨[­t3š_n½ªE‹9cÈiÆwóæ§N $€×çaú|šã²o©ªÂÂù+xõ§Äïh¨oD‘ª*wqÕ_.E4™ °Ò˜H @þäy§hï)-,ɶm¥X,㚘l«p[±Qbàdí½<ù]-ÖO_œìsõš†D̤ˆ›vï5(íØg±ÿOðÄŽÄ; ZlIie\;2KK+ÙRÓ<[P³EÅåF›Šª"+j›÷ÑŒYGÌ-?Øær³¯x+™ÝzðÙ]w …Ã8½i”®\Äo¯?K»l»:Ó³ºQºj1ö¸*çÒ•‹øñ…‡©;¸ö=z“÷Ó·”®\„`2iY±d™p ÀWÞJØßlȱZ>ëçIû.½â>“Ú$1¶¤¹á„ÁžåjâÜE'õNh›AêÏ¿Ÿú@Û^S)Ìsîeï‰dU&$…øëÇWÑ¿óœ™fêuü¾m.vÕ’·+—v^ ;­NîþàÖåÒ-£;^»—ÆPƒAñ/+_ YðÇößé–ÞÍ»71¿èð >zÛqY펓¾‹}¥Û ù›˜ýÂcF¶­ØçûJ·'õ£·½Û¸ŸŒ‚ÃkÉÎ9™|ðØì„ssèΔ»¿h‘4èÃmK«Rnº1ƒ¦Kÿ¼q‡–1lÆ?±ù7Íà1™ML½þ;¤°„"« ò­ÿWŽxÐjÍ,Û¾!!HÕçòWQÈçK¤o—žø\Þ„k´Ø–àuYQ(ÜYB(n š×­½d-|~\F­¶³Ù¦͋qº¼†qöá¿_ F¶nÌ›¯O6›§³EV9úü±deõA‘e"‘>y7v›3a—e E‘ÉÝ´¢b €<úÄÝFÌIiy‰‹s¬ »ñ:¾»ùN*-Çl·Ò¾ooV¼9µÍ`r¤I2R(œÓŽÉbÖ*D§pÊÄ@Jû¾}øíÑgã˜åO;6•mùK÷>ž;ó¤c(Õ½cã ¿¬ ÍûÜdô"µ8˜´x(Y°™‚ÖðÍ­š¡µ;ªyé‹çm¢xžv­]wؼN~˜4=ɘ¶:mškS»óÊ|åHÖ}²ì}™ÿË·¼7é±Sæ2ù«áIË }Vwr—ÍKØó'¿= »Ã…IÙ¼J³Ö/žËûOÝ…(šQ…¼e-ÍÇn¹—Áf-}ïÏÝ‹ÓíAæÿ_G»~}YòäÓ)³Oµ4%O>}R•Š¢_§D£X\ÎS¯g‘³'\cĆ˜NÁ²ÄÆèöÂ2ªw$Æ`ȲBõŽÝüìw|ÕúÿßSv6»é¡HM-¤BïU˰,ˆˆ•jCÁŠ4ET°×k¿êU¯½\±*½÷R¶—™ùý1»“Ýd±Ýßå{÷ózí+Ù)gvæœó<Ïç)g‚ªÊömÑú9j;‡ƒôŒT~Oôßÿ^õ§<çÙwóB’$&\3µÎêWá´­†àÓÏŒÚC¿?À¤É3ÌHJ$ik&¿öF}m5 5B©šNð7^o×ÚÙ¹z¾ú˜ ßdQØñÓw|ýü4Îj‹-% Pì‰ ¥D„çt0+^z’ò=;kB‰‚@FËLóØ`ÄÛÓ7­0qøû)í:òËÇï1gp1ÉM’ê°_MSÙ¹é'ž¹oÜq„NDίl¬V¥ëš¹¶ÿÏß¼$[êD>baÉÌåæûE¢ÓN¼Üž/èÝ0ü’¬I¸ýn.[tÿüù]ÞYó6›m¢`F!O|ùTÈH bWìüóçw¹ïƒ¹ /8×ÿ+Ä!ÞÁЛ†#‹2S_¿‘&ÉM ¨f½5 —ßEN³\n~íÜ~·¹|km YvïBŽ8\clÇ`ךª"J gÌ E2bcÍ·ßã÷ùxhêì:u;k¾ùMÓ,œ1ç¸ïu‰DmQ{Ÿ,I¬^¿­»v±ÿÐ!ó>DQä±_1WϲÛluV³ŠœB-áô[”üÇë>ÁRë3Ÿ¬û{h%·ßÍíoÝÁÎò]Ø;m›f3ûíÙ8¼N6¬ëyO³§¡È «vdpâs× È Ÿ¬ÿ„ÆI¹ý­;ȼ)‹’,cñÚÑ–ÚÄ ’~>^þ•‡ö£FÔˆf-”IE±Ù#V2 žÆRVµ(Qéi) Α`D-FãÖi &Ñ"•ƒÛÊ#Œ wß»Œ _ïˆá¥‹ÝO[í1÷Û’­!ÃSdõ›Ìí²b¼¥Ö–lÅ–b5s®OFhºÆ§kVAýÁsþns Š›b%-1ÅëI ‰|þËʨâõ‡”j¶1aɭج &1ûQ¹ã…'üm‚ àõºy|Ù Z·êˆ,[xöù9œÒ´&:кUG/¹‰“¯Àð!Ë~Y÷ kÖ~ɺ +Ñ5-f-‰)‹>V}oÈÈD{2ª¦"I.»²-jÞ®©*k^5òúoÚžÖ˜ËUŸÍG¼ª™dKIaËg_ÕO*j­:ù­¤ó·â·DMz]AyõÚG£îÏžaŒ“uïòkÿ¯;Yûö·ˆ’ˆ(KLVΈ",ãú&!ÅŽ-5‘½k·#ˆ¢y¾(K¼qÓ2º2¢$²ö­oxþòùX–œvËFÏ%³´Ù=s£ß3"ØS°'¦ i«>%ÁÆc³o U»ÔÐJA’$³`Ú8¼—IT"u¡ª£¶ä—õaÒ­óXrûdü^Ö;öÔ£ÃNBy¢«*ßy·ÞùlMN>áù¿¾ú½n˜Â¯¯¾†êõ5\–…Ó ]ÐXñul"àt¸˜}ë<³V#Þ|í5®?Ó,PÿMä«ïÌôó?‚pz¯ú'¤åÖn#V›âÉ2wÿü#‡¶obﺵ´.(æÞá]y`DOÓ¸×Ô º¦±yÅç5)ltl^OB’áé´Xm|°hnLã³ÁˆøþüShÓº®ˆx# H‡¢>¼úÈ-f•ÖÀt Q’yæ¾qø¼®ßõÒ3 "‚@v£l¾Ý¾QY`ïýüDAdãÁ \÷ò$càè*ïÿúOZ§·Æ®Ø¹æ…«yw­±r×Ý¿—yÝÏW›¿àŸ^àÒ§.4ß´þÉú̈‡Z‰ëx4 ¿½!J2L2NÔŸ m¯6‰ˆ¹/ôVÓÚ+UEA -%…Ûç/Ž‘d¤rià ¼IÖ¤:ãȯúÍeu?ßøþò!5`F=>ßøy¶$Qb³ðýuVÖúuï¯<ñå2Öî^k®~ÕÓãPda3"cã7_Emü¼rç-æü4VѪÿüßm,5¾~)ºàÞŠ…Þ ­’âWYûÑ–¨ã­v…}GŒ=ª“Òm¼5ãdl£;Qì‚þ k?ÞbzTé[a4kߘgnx‡¦¾E†N6¨šÆ¯»6³jóÚ(ÉƬçæñÒ—ïpÅ¢iøƒ$QâÞ×ÅíóÆ´¥?ÿùÛ(±ÇçeÂ’[£Æ˜¦k||‚ˆ±ìn;Yùš¦aµÚ™róœ(bûÍÊ÷°X6l\…ÝžŒ€@ àcÝú•¬[ÿ öPÁ¹pl´µbå{Øí)æ|ÿfå{ÑÑŸù³ñÃOxùŠI'pŠèšFÙeÔœ¯×%(JR"‡7m©_¾éÔªƒ:¹ý溦ñó»5E»A_€e£æ Ê¿¾_M¿ß£¾‚ñ+7â­v›çêšfž_û}#áÈHÀ[£·^·ˆ=«·Fðûxê™<õÀL~j0hʱ¦-2YùÉ;Qï“©=†ê[ 6;×ÔÜw^iO†f 1Э/Åzr94T•Í|s_¬U¬b¿ã‹/Io“ÍŽ/¾ü]ã|¿aö݉ w?Çê¨ð9µ÷ý&Bïp²º¬ô“é#J’è²b5·]²àI½en¡>|òtÝ’`3··éÒMô[?]uní¶ŒöýâyODí—,}ôéýÇNŒ:þœÛø“îE6ÿŸtÏëºl±þÇžãÏÒEAÔÝ*×\Wå˜ÇGù9­p„.‰ÒqÏ=½pDcÿ×?¢(þ¦í ùïÙ†÷èïoùÔ7&þª9/ˆ¢žUØEÏ*ìò‡žÓ‰>9=³t«]ÑýºÎ×e«ñ[D½äôNõ~ºž‘§‹’xÂã"÷w=#WÏé™õc| ¢~jI?ÐÙ¢?6i޹oHqo]ŠÑW;WòºûtE¶4x\7xnHr½ÛDQÒ{õYïñ={ŒÐÅ?ðÅzÂßò‡Æë°A1u[ΰA'œW':&þùëdZÏ!gÔŒkÂ×áþ¯olõr†.ŠâŸ>öN¦œðûô•(ÿyÏì£/þ!,úíwݨ·Én­ú÷?ø»Ûí?°§.IâÉØ/'ÿDÿŸÛohÔwQ’tÉ¢è—.xêwµ'J’ž?ðT“ÈÔwÜŸIDþc“Q”ÿcí„ù³®ÿüo·¿’| GŒú[ßþú¾ÿ–mQÆ„,F‘–“ýS›8œˆì† Ëÿ—ßzüßn´Õ§‹¢£þL=ÿüycîd¾VüS¿î4¤O …ÑOÒïìß“s œ0)æä(Iõ†×Ž·ïD)Oèú Ããˆ#Ž8âøý8ÞòÏqÄG'½Œ“¤?¥¾âÿ þO8âˆ#Ž8âˆ#Ž8âˆã¿büÄGqÄGqÄGqGqÄGqÄGqÄ HqÄGqÄGqÄGœ€ÄGqÄGqÄGqGqÄGqÄGqÄ' qÄGqÄGqÄGœ€ÄGqÄGqÄGqGqÄGqÄGqÄ' qÄGqÄG' $QúK΋Üÿ{¯GqGqÄGqãô¸ÆäŸeh*²øÿ! Ìé‡(ˆQýW»­1úcHî@,’å¸í†Û‰ü?Ž8þOIŠì8âˆ#Žÿ&È’Á!Ç —ÿ/ÄC%zä÷5·Yd…{Ç-FÕÔz Gü¾ˆB;|^¼þ¾µH–^¼€úcSl,½xª¦"‰sú°ü²¥Ì8íf|A?K/^ŒM±! "¹Í;qóðëéѶ[¶Ãc¢Kfg”Aé’YŒÍbûKÈjqüiD’BL\j‘$‘ÁÃûÆŸ^±U\ÈÅñ§%1êïñ•üÿv@WDF ÿSŒ¥p;qƒå?cœô.€Õb%¿M爽:?o_ÍijnÄòˆ×&=óú" ¿ì[d…yãó5Å;ã/êß9ý¨œ>—±M‘D‰Ýå{̹; SvÛCYv)¯ƒ9ýÐtÁ×<—›†M‰j{@N?TMåö3fòÀ¤x^?§ÏÉ£-¦DDÕT–^¼(íŠã¿‡€¨ª†®ë¨ªÖ “Q¤UëæL¿c2‹%‚˜HµˆJ\yý/BÓTÆO˜Ž,[â#Ž:Î Cá "š¦Q±s%š¦ÕÛVe¥…õ¶û¿@T4]ã¡Q÷þñ>$23ZsË)ôkß+f*ÈÞ¶( IDAT_k°‰¿»¯NƾU5•kϹ…©Þ‰Ççæ‘) ë°w5 $¯/ JB4~ï³– ç1š¦5‹ ¨¿È10$o[må¢îça•­T{ª¹g܃æqá¨cµ§šÁ’œ„'६M©é„qúœ Îhö€@¶ÝYtë÷o?µ*4ôöW Ç>æuª<ÕèºÓÑó{¶ðþ#Ω¯ÍßâhŠãÿ1àhðIÁ@Sš7A€i Ü+긃{!IÒÿù=÷ûÿóÅŠXüÑ(†ËåŒ “8ꇪªñÆs Ñt™7]E–£ŽéQÖ¹Îy­³íçÕq˜Œ{.Z-Å ¥ÅùÿgƦU¶F}oW\ÄÕ}.göÈYˆ8$ÈVöWä³g¾ÂôGIò_žžÕ¿ I‘D‘þE¿íÜü¢“’„¸³ââ‚:Û ‹ò#äóãËæÇt‘mÔ!$……yhšÆecϧ¤¤(n7üoßf(§§§’žžjn·( ý÷Œ:vÐð>È©1©Ó¦,ý…Ó_w-I’E‘=ûý&Â!ŠÝ{üçSÚdÙÂø ÓÑ"ò‹Ã((,ýÝÞŽ-³p»øý>&^3ËŒ„4„Ô44 °á}"þ©ÇèÞ£…«ô—¤£l)n²,3ó¦«QCÊ+7§g>ˆŸÖ¬' šÇÜ>í|~?c/<;Š˜¨ªe ´Ïάsn%…uŽ‹õ½[iáI­ädQ¤"Y­YrÞC¤$$;#mÃéuü.›RŽð¦{ÆO½‚·Ç¿LrB2¯^ù,A-ȰÜÁˆ„6„D„çRÿ¼¢µ#‰"[´¤KÛö'mß_éXd =òú°rý¿Y½|G«¤=¿/A5ø»®ù÷;>àúÑ3P,'oJN¤Ñ/‰"ÃKò£ÈŰ’<€:ÛÃX0n /O½UÓÖÅ8ÞnUxyê•UÍ$Äa /ÉúnµÈæ±õÍ+#Ñ›ÅÆìñ÷„ '¸nð5fÚTMoëØƒsp¨ò•ž*ôPÌ*[¹ûì;p<\ÞûR†åÁfI Ò]…ËçfH®m GRu€Óç¢ÊSUï3lÔ8ƒÉ׋"‹ è\\óÌÚwhKff«(¹~Ψ‘œ3j$V«¥#a³Û¸ù–Iœá94jœ¥'eY¦]û6(Š…òò sE8þB9#ɱí IB<Ž},ÉòoÚWßu~Q ,¿×4A@ˆHwèRZ€ÍžuΘ ÎÀçõ×!&µ¡U6ìùú7ˆðÿVEi0ÉÒ¯W”±ùg‹E¡{¾hšFïÞNx|~~g,!¡/I"……Åd·íð§)φDTt]ÇëõD ðß‚‚®õ„K¥ãFLDQ¤Gøý~Nq.Û¶®7½smÛæÔû[¬Vc°œ{Âß>¶!:¼E‘ëÚ„£¤k›:ÇýÃ_–dÚfw¤mÛó¹–PTXò§ ‚üâ“ÊÈ ƒtjŸ @Vë|óÝjz”ufÂç1æìáX­ Á`œöm°Ûl8]nôÐX*ÊÏáË?˜måt0Úù÷ÊŸ(Êω&|}ªjÓ¯¿ ‹,#Š"¥!EzÉyg°ïÀá?èþçȇDP òÒåO‘jKaDÁpVïù™ç.{¢ÁäåxŒáyCc¿E²fKå¨ó(º®ñíŽU<ø·9œU4âw‘ E–éŸ∆5"uÓåóÒ¡yË:m…£$‘FO¿ü"\^ï_æpˆ.ÉP Ï,0r£‡öé‰(ˆ&ñª*Ï,¸E± (n»~"·]?E±4˜Ôôî=€¢¢š7oIß~ƒ£îµ¶g`íÚ üQ¦G>1Cœ5ƽ„Å¢0~Âõ\=þzdKÝ Ùbáî¹ ¢¼ÆuÉ’%äYÒ£§á-Ñ4×T™×·Ôj[Q¬\|éµhšUã!Ë&^3ËŒ¢È²Brr* qøðDIÂb±pée×qþãëUÖÍš§2ÿá qVñqú_æ¾ùçѲuFLB’ŨcgÞq&-[§ã÷9æ}ÌTP%]ÛPÒµMT«V>Œß<ŽßBÛì1~“•«¯¼ ¤}»N”Ô‚RŠ Jc´ÀÂès.åÉÇßâÜÑcc¼.'$ µ=â Æ¯ø;‰x}çÕÞ^VR@b¢ŽíÛðÅ¿¿gÔ™CÙ¸y;Óo‡Ïg̃í²E”ä$Ó¨ìß»+_Eÿz€¯Vü@ÿÞ]ûÜ/:`‘eJŠ EÚ´I#@7¯Y¯¬$†~ƒcÛ,²õ¸¤4úÜ?®t$QbxÞ*Ý•T¸+‘E‰–iÍy{í{nŸ‹y}qº«Oj¯î¸ F2ïÊQ(²lF7&N»€€ª²ãŸKÙy¨œÎÙ­øeç>F”ÔDABºN‘%†—æáðxqx¼fdÞ•£HµÛèÑ)MÓ¹eÔP4Mçõ™FTwé¤ ÈjšQ/! ¨ç`P§¸|.ª<ÕØ]2‹qú\ì>¶‡9ýP$ÅœçUžjæô7‹Å·ÚÆãž4Ó°¶ÚÆKãžÁ®ØÍȈ9£è½ÚSmÊÑ!¹ƒØsl¯áä ÄG¢gÏ2ÜnO­hsMú|»¶me¯ÇGZZjT¤$ÌY</ÎØ®]4Mãü ÎA–d$I2÷ ¢HAa.GŽ”›DgêôëØ¿ÿ bQj(º8=dD[ëwÚuëGNqwd‹RGfwé;ÌÔW;w£´ÿit|fý±Èˆ$Jô-R/!Qd…ÅFŸÐ1}‹‡D,½,Ç$«„ìÔÞ¯(Væ=ýj(Ûà§oWà÷ù¸}Á£(ŠI’èÒ½—^3….Ý{¡i]zYKýO5œMEÍRL¢1ïé—±Ùíu®[Ø,šÔœÕD¾ÚzÀ7C†™íEeÿü–ŽÞ²yÓn»¶ž½œ!€¦ë+¯ä”SP~´"ô ”¨(JEŹ ú)~€o\m v¯—Ù7¿åºY7pxÍ¿ c§¨ :¤¬ÃÞýùÛ©Cð‚‚Aºv. 9)‘œvÙìÚ»t#’²gßNW¸ÞÏŒ® é{ü(Nž}ñù¼3âôÞ¦UU•ü‚b ŠM^XT‚Åbá¾–`±(H’LJJjTÊ„ÕÑ4]×CF¾Ž,[p»ºÞ @GuõqÉG£F§páExîÙÅ\{Ý=b –sFåÀþݵž½ñ¦×x&ÝÎ-ÕÙ¶u=çŒ2Œåó/Ïç5ï?, DQä§Ÿ¾©Ç ˜ró©ìÞ]ÎæMC‚uvl;Ìi#Š¢òÿEfé“cQƒZÔ±•n¬VC¹\=ö)ž}e<6»BQqkJÊÚÔòJ:b5^üO>þfLRRQyŒ~}†âpV“’œå• “ÄH¡‹FÞ¥(Šü´z%kÖ®âÅg?Œ:¯EóÖx}žzþ° ,zªIH¯ø14U) e‹…ë/E‹˜“«5BàJ1ÉŒ¬(f»‘„#òzÑבا ×K¢ÝƨK®gúìTW;¹zìr:dsøH9 œ”e†åÅÓ®aÜu·›Žƒú¼Ø²,3öÂ³Ù³ï ¢(Э´£!¤ª*mÛ´®ŸäZl\yÞÜ=ìŠó†Õ(±Ž˜pÑãhš!È/³€œ¶=cQ”Ìs£ÿ—ë(‹†¦êI‚ÄðÜ!æø6Œ› AM¥sËj€Í߯Æô™Q9ó7 Ÿ1Ƈ‡R¨¬²ÕT„Ф0÷ÌÛÙrd›9wÏî{!»ŽíAÕÕßmî9z„*·‹™ã&rÃëePa“ÔÔvrU»Ý¦·6¨ªèè4NI")í›·À勎txü>‚ªJ~ë¬9¾ÃòBF‹Dßa†WwðˆBî_~1š¦³bçÜ(y0íÞ³éÜ­ š¦SXšEai v…ûž¸5¨™¤CQ$z ê!s%Fž×•ý»Ä~–•Î B¼;Z«ÆztÇåqàúI¶§ Äôë8ÜU'|½ ûÓ»`n¯“Þ…Ù_¾h˜áw¼”éXÑåXç$$XÔ^ƒáóÑ+·.¯SKCiCNOr{±Zdztʦ{N6nŸŸá¡Ô,D-šÒ#§-VYF×aÞ\3¢?çö-e×ác( )D67‡ÇËÎCåtmŸÅÚ¡9^zမP$…—Æ=Ãö#;¢H‚UVøe߯è¯èì>¶§N¤B%T·I”pú\8¼Näô£Úãˆi‡í Ih™Þ’…—, 4§w]û¥W^¯Ï”Ÿ£ÇœÉþý5ºxêôë8°ÿ þ@€gŸ_B0XCôÝnmÛµ¡¨s^ŸÑƳÏ?jtÓÉ/¿¬Çn·™r‡˜ò^Õ‚<6ýõ¼¶pÇU ÉHmƒ“ŸdÁõÏrÃwÖ!$ªdþ”gb•XdHG§º²2štõèÅÞ;ÐÑ%‰Îe=èÒ£ö¤dfÍ{˜wÿýeHæ=d´q®³ºŠ~ÃGD9Ù;S;þ7tä²Û [tíÝ1cÇÑ:»mTÿþ&"ˇÁb3¥EEÊ*Õ<»ü‘l δ Cqœ{á™ôêÛQ¨ªªŽŠLL»ýZšµ8…ÏW}BÏ>]c /E±ðì‚ðê..óAWV…&^ÈsÐ$¹ká£xj…â.ón›JÏR£ÈõÛ×дq#:dgQQU? C›,ž^tŸI:$I$³es–Þs;#‡ 8AИðA5È9ç\Àõ7ÌäœQ²`Ñ“””t£  ØÎC‡  r¬ü(’$áõzŽsÍݬYK¦N›m¦i½øò{”–vGDRSÓLåcµZc„¤ƒQŠ(…Ы÷lvÃÈKMMÇé0ú$)Ùè/‡£Ê0fkEn—³®ºÓ ãÓí6úeÜÕSq¹œ¨ªjNÚ0™ §{Å´<ÐÛŽ0ãö3b¦MY­ò [¡é:×Ý8,j‚íÞUNÇœfõ¥M›¦P~Ô®étî’‰,‰xÜ~$I¤G¯öLš²œ^}:0ü´Âzû8ò÷‹¢D0`ùcoRUUA¯žYñÍghºf g—ÛE~®Ñiß.Ç$Y™íh›ÝK=«„Y, K»ŸŸý¥O<B)Èï‚Û튕GMUY¥ëhªÊÍ=…E±âª2 ’Ën›mŒ›ónœÆ·Ì pôÀ~ZuèˆÕfãÆ%ËhÚ*“N]µæ» bþ/[,Üôèr4U%è÷ãq:Mò¡©*7=ºœ` `n„¤¥’#åÇØ¼ug„óR\hl‚`|báhy¯¿óW\|‹ÃðiÛ¦5Á`§Ómæm7m’Á½wÜ`Ž×ã)¸mºaKHÆé:@çܡ澇ný„ Ïºš±£ ¡KÄí¬¦´ßil_¿Ú”£JzQÒ÷TŽÞOïSGsßK#L¾MÓBÏ99­—O{Gþù Á€Q”ðy\äw5Æù‘ý»™¾ø5“ttìÜ`À_+Bé¤cf>Ÿ›;n4–Iöý:¶Ÿ®¹½¹vÌ ÖïX˘ÁcI°Úýجvn¾èn|~/s'åfº÷Èç_~†b5œßº¦aQ”Q§ª¢œòj³/ºŒ®…íê´q`ïnf=¸8Ê!ÙgèpªCócöâÇMÒjÝ6 ÛÅ¡ýûX}Äsb+MJ×uþ6æ4^6—+/º·Ëu|EðX´2 ©¿ðôëœñ·aH’•Ê$ÉUÆäy÷Í8óœafêO”!©Ceu4Ë?m`®Ú)a¡I¾g=÷L»¡Î½Øm ôïòXÜÂ/ÿƒ7žXD“Œt¼õÆ]8&ŠõK¢Ä©ú²sïþ§¤¥¥³sçvÚ´iKUe‚P“/,Ir”gÒ4jäP^ž( tïчýû÷šÏáûï¿eè°‘øý>Q$³M[c2tÌcê´Ù¦‘ZTTÂ'ýM«ß›Õ¢e‰ö$3¤VYSo¾”ëoœC۶̨Çq¦G@UU’’Œ"Ø›o¼ˆvísÉÌlOëÌv&¡Ñ4ƒ÷Ò¾}^ïØ+˜žaG–Eª*=uÂÊ={·çιãÐÁ*–.þ”Ußm§[¶æó©®ò0pH.¢tüüQ·ÛO0¨’ž‘h ȃsytñ' ’ËÜFןRa䛂Öåv’—kä:´Ÿù›‚ž½™²x)›ú‘’ACAð8äuïÁ˜)FT±dàìÉÉ\8u²EaÛ/k¹ié“\1{®eéPlk¾üœ—,cû¯?sÕœûêF\H²Ûùaõ:³ø<¼½MëÜ:g1þ@ýdø¹TW;c:kEQ 4”j®# _àœK¦0~ì¹(¹ž¶E²[sê€kpº+hÕ<ÏLźèlÃùátƒ(¦í+ÖoVŽÛÍß1R†l )\pæÝœÒ¸-þ€ÇT„—šÇÎ=«ùÛðé\qî"óØ©ãߤi£lÅNû6ÝÌ4Ʋ¢3ë:´ ×¼†}•ûÑÿ®ã ­µ Ù²Xw`{â"zf—q úPÔãIiÓ"”¶UŰÜAfÎø;¿|À/³V†Ž×Íô%Y’Qµ É 5.yY’idÏ0"Ú®c /Ý8UÓ°È2Œ›„ªiôË/ QþõÃJZ5j‚(ˆ‚€Óë)SÓ“°Y­L>²–'¢é:·Ÿw1¾@Ý4ŽDQ䌲®ª<á*=½å„rœEŽªfŧó9°·‚ªc.úæäÑ:»1}†ä’€…œã¹%Ø>9ç]yGV›òO’DúÏ¥üƒE/\ÁùWõfþÃióy¼õíTî_þ7Ï9Óˆ¶êáùúZ­be‡Ò’Ò ¹ðóѼïðú=ì9²‹Ü¬BRÓØ´g?I¶d>ùé>ùñ}²›µÅ“&žu£™†¥j*÷Ž[õíÚÞõòê£Ìu®q\ Ïê°Óû ªü{í?¸{ÞT~r.­2[˜ŽÉðü,íVD‡œl†@RR"=û–ríMWÐ*³9~ŸŸµ;>å±EÏqÚ™ƒ<¼~ŸŸ_~É]G»Ý ØÏ¾¾5¨éÓ¯-bÚ×¢O‰ ë•°! ¤'ÙYr×d>ýv-CŠ;®óÉš¤&ÚH´ZY<þ<ØsÐ ÌbMLIסÊé"1Á .7è: ™ÅãÏãÀžƒæ1 œnŽ9ÜXd‰.íZ×½?~TΈphºvÜ-USÉHÌ0IG$‰‰l«ÊS]'¢Uå ¥îÉõ=>Ñ$a{Âíö ( ­Zµ`ÞƒKƒ¸Ý2³Z!ŠII‰‚@»öÙ;VIaa®™fe³%i]šQ‹Ý躎Å"³aÃftÚµÏfþ‚9Ürã1#û¿€ßGëv¹~ÞÙr˜ƒ{¶s匇شú[ì‰~n_¶ Ÿ×Mãf­8ã’Éd¶Ï£´ÿiÈ…k*™ûüÇ´Í-æW]G×5–¼¿Ž¦­ÚP~p/~¯AqVWÐ¥ïpt]ã­õ~îÙŽÇå ðÓ¤E&ÝÔuN¸¼Nîÿ—¸–›Œû¼ù¢»™qÙT’ì)Xdh”Ú„jW%>¿—Å7½ˆÇçÆéq0jàEì=²Ëp Í]Êò<Ç“³ÞÆfµ³nÇôÃ:ç½’×îù]×пÓÙ{x§9ôí:þ€E7¾€×ïaÑmwÕ;~Xø -3³9x\nºôèE0¤ÏÐáfýKue%.G5Óï[@, ð¡æÒ:»™Ù7º®#IŠ¿–)“¯fò¬Ù<÷ijDJ9M7HMvã„.] Ì:ˆ0qhß1›/¢c'ð|÷­(ífD’IáÝ·>¦gŸR3B"„¼@Á€á‘×uÝHãºýZÅ‚Çm(Ñü‚Á ’,áõú꼓Äh~£g«æÍYöÜc4kÒ\ÑäDÈHÇãõòä¼9Ü6ebt4$,œd‡ËEUµƒVÍ›qóÍ×1êôa¦ U9´Œpe­´¦X5!Á`#çî­7_fÁü¹øý~³­H‘\«Èï÷qË͸çÞ‡±Xrs Bî=÷>Ì¢÷˜ Åï÷1÷îÌ_¸Œ³þv{öì4 °!ÃFðóÏ?Å\Ù*\¯ñÈâÙ,^t'¯™Åªï¿2ï999§£ µVuØ GHN9¥n·ÓÜž™ÙŽÄDƒ€”tíÃÂù·âõºC©*‚9øÖ¯û‰nÝû×›~Ô¤IJ(í@5S§lv…y‹. °¸5»dR]môåÊ[9óœ“pèz-Ò¬G{ÓƒA5J‡ ÈôÛFrÏ]BÄ壸SA] œ;ê2~øéÛÒ%* ~²l¡Y³–”àr»p{\ø¼víÞÆ¹£ÇbU¬èºÎ?~CÇyH’LF£&\|áxdÙRÇ»¬£›Ñ¥¢ÂR¼^w…£÷7Ö†¿tÖìݺÅôÍzöe¶ÿ²–Ó/¿Š­[÷Ò±ÄðޏÕ´/.¡Ev;l‰‰”‚ÏíF”eŽØO—F×ãt 3.‹] IDAT$ØpUUâu»(:œ¼n=ؽq]B©oý°Š3ÆM`ü}óè9â náY–ß:@ ßVb¢Ý{˜cNUÕÐ{ Åbö—$‰ƒÁß´’“ªj(!ÆÝ·^gFI4M£e‹S¨v8£<ëÑα£çÓ©]ÜîJ“ìèºÆöÝ?1çæ¯©rcö¢ñ躊ÓuŒ?l¤W鹄Ä×^ú4GíápùN&]úv›1ž/u5Šbçȱ]dµÊæ¡[×Ò¼iÞ‚"[±X¬ØR˜p‘á99x Ý:ŸU'²àª%l>¼ ºY4ì ú½üse¬‘KÏåñ»žŠ~1]èß7Öüƒ‹ËÎ3C÷9MÛóªWL¹RܪÈ$*ª¦ñ¯õŸÔD¸ÝU4JÌÀ"É”»kˆ?àÂù÷²tÂuØ+ûŽ•#‹=:æòâ Ó9wúõÆY¹‚‘eÝQd™—–>öƒûñø}uÒV¶ØÇá”­ ªRívQœmèœnùEd„œá¾Ôu=J[-õ¿¨ï°<#­/h¼ûj‡º IÉîx —O|„ó®ìb•Í4­ž:R]é¦ü°ƒ1—÷ä«­w“`WEFM’Ù¿·‚^ƒrðº8ñ’ߥ5;·æéÅŸ3}Ü \0®Ž*n—ó®ìÍ.}çqÇqyõf.›Â#SžÁãsã÷{IMLÃåu¢é:붯1S1êó ÕNÃÊÉÌ' °„ÈÉ΃ÛêD(T°c1S±îž75DŽ‹)ëTÌÁý‡éÖ«Ø”­;­2t¸$âq{¹åV#Å÷ëÏ¿cßžf¼¨Í vïÜGYb:åµgÚ“·ÛÃ䛯àÞE3™;:/>õ·ß{#~Gµ“ý{’“Û‹b‰åÖ4HMŠ6é)tËiN7¯|õ=ÝŠs‘$‘ZOZ¢Q;Qát(·€Q¤ÂåF±Èl;xI¨pº‘Ãéf™wMfÕæ\}j_¸ü³¾D%ò[ä5(JPíq0 Sÿz£ í›¶ãÂecÍ4ËãEHY1­Hµ¥â ø80o;Î#GbF0 ™\ãÀÛ½{n·›‘g CGÇÊûw»Ýæ!5Äãñ ëzÔb6…Ey5„Fin·‡ÂÂ<üþ@h>ª5Q–Ú]«ªìÔuZµË¥´ÿé!³ÏŽ?s÷sŸâó¸¹ù¡…„³“ßÛ¶‡Â9zp/%}Ê%øvU;‘- •U0øœËHLI£ÛÀ>xBöLIŸáÞ·‹’>e‚ˆÛYÇUÍæ_¾gÂKxû.ŽÔÊ—%AiÞ¨³Ç-B Õy¹<”{èÓy’iIH¢DçÝØ¼k9[-2¤'72ŸÓŠŸ?£ÂQŽ"[yúÖw9òXÎPªÝÞC†M¦ë!Ù—>‰_¶†j%Ä ¼ûÞ§´ÌÊ Ì|p>|ö1½¯ÌؾiO<²ÅjE±ZÍn‹”U@‚E]»Ñ¹¬»Y `Ô‰;pa”a|ûŧè@ãSšá:æòö‹ÏR¤~òÈrÃÛ÷ýÊU¡Á!ÑpO</¿?À°>Æ[røn-£/És¯=LÏ>§ñú+ï2úü3xòÅù\yÑø}ZÞgr(÷Oð*††ŽÕªðÜksÞEgEúg„b±0ùò‹€§§áõùÐö4 ÐÍz £ÕË/ŸDEU5WÜ4 ­: oµƒÆéi–Ê'_}ƒ  ë×½ºV¹Ðuw(̨£óÝOk™~ïüñË·u ]×9¥Ys’“SyîÙÇ vزecTø,--USIKËˆŠœTVÃãq£jª娨,:&üwÕwß I:ä†A𑇳F1-}t.U•ÇЙº)‘æáÉ™g]ŒÛí2#$ª¦’‘Ñ„SOÍ’‡ï2&zˆ†##‘ÞŒú°þW£H.;»1YYí(ëڛʪ &^} ª¤¨ ”Ý»·!ËrTdC 9)‡ÓL2š0aÜÍ1S·Š{Qge%¢$⪪Ģ(lüa7>ºœ®C†³ê_Фe+f=û2S—=CÀë¥Cq ¥C†Ñu¨1§÷nÞĬg^&!¤ln˜;›²¡§"„KqÿA( tî;€»sû‹¯Qº®³Òˆ¸TWx¢[Q~G**«%)jå<€vÙ­9|¤œM[vPQYMRèº>ŸŸw^~¿ßß`òÕŠï)-Î#)ÉŽÛí5Ÿ}ë–Íøð“¯ÉHOãÓ/WÖìU:äÄFF42©qH‰TÒ%ÿT :öaìMhÖ¤=I‰H´¥òÁ¢XlìÙ¿™gçcÕÚ·©¬>HãôÖ\uþå4NÏ gxdOG×u=ByÅ®¿âEŽVìAE÷ÜÀM㮦yÓȲ•UkÿaÖšDZ‰ª„VB”ÜP$…`ä±AxáûWéÕ¶{­ÔÌ¿ØÀ–Ã[9rÿ>Ýô%÷¼$k"’(m„úGÕUÖÜå5]ÇvTƒØ•è"ÄcÕn÷\w 7=šJ—‹•›7Ð$%•¦©i¼µr‡*+èÞ±‹܇/$—u‰Šrèº1Wœ^-5æÝïW²óÈ!¾yém2›4åãïW𤿾Ô(/n“ˆš‘0™µX$&Ï:ÝÛ ã+ÝØ¶ñ ºGWÓ2+ƒo?ßLrª ÐÙ½íç\Òƒ§å#JFäæÕ§¿¥g»ŽFŠ•öD+^·Ÿ²ÓhÕ¦"ùÅ­©,wѹ,‹äTÏ=ò%åGœT”»8´¿’ÅsÞgÀ©ùu é°c!-)ƒF)ù×÷˜FÇÖy‚a ‚~Öïú%Ê(HKŠÖC¢ âpWsÍÙ7*¿/Îúq¸«êÕ:F½Píý¥JhÜ$ ²mË.^zæIsVzQTô..ÃáprÅÄ hŸ“5Aaæ]×™ó¿ïÀî:x„ûÍTÞ)ãncæ 7“–‘JYbæÌ›ÆµW\Å®{9õŒtëÙ…Î%ù¦³IEuäíOW2ø²,¹æðH±'0éÊÛøðÙûÀ"Ëãz½F}ˆ®ƒ•N7‚ ðÜ?>ã­ùÓŒ‰Ž=yÎ:¶<……Ó®"ÑjeÁüilÙwØh¯Êò‚û9«GgªÜ“‰‚ÈÀœ~¡ˆÄ (šJµ·š9ýê<9éP5•T[J”¢0" UæÛÕ­)i\¸l,Þ€§Ïe:+Âõç_p{÷îgÍê_INNÄëõQÂpÚ˜®£©šé2樈Ïç7IHmÇYrrn—Û|áô¤P}ŸÛí¦KIS§MŽÊ:©ÛA×þ=°&ØBiSðá+QÒw8>·‹nƒÏäð>μl [~9Œ 8ªŽk…é=Û*¹iÞ3Ø“Sñº:٢Хï0\Õ„¿ÅšÀœç>dܬ…üðÅ?ñy`ïö\;g™á÷ø ’Õ¹C{ŽUåŸ+Þ¢wÑ`,²B¥ãýЇ¢X ù©é:-·bÒ˜ÜpÁx}¬ ÜóÌTfŽ},˜}©£SíªD!ŞʿV¾J·2ÆUP ²áï’»¤Ð-¿/»íàšQÓ!-F žÕÊSná¾é7ðÈÜ;ñ.§›û*xò '$H²Lu¨ÏÕ`°›\Æß2‹¤”D Ú3\L÷~ƒ¢8¤í¢ƒTW«Ì¸.ÀírñᛯÒV> ¯tõþ¯Ó³OW,™.] I°Y#<×ÑKá+¯$£QjPE’ï‘n•`PbPéé©FÞn÷Î|ýÅwhšÆÑ#ÇÐu̶"•Ë;}Æ%£Ï¢{—Π99u`_ªªNöêÎà#Yxç ‘i×\Åëïÿ‹‡—-†òºž¦*‡ƒSö9•¿þ†¤ÄD>øâkF\6¦í²A RYí Sû¶X­ VEáí'ƒ†WùÁ[o‰*’ O⪪JŽ•¥U«L¼^^ow.#£1’$SÚµ;W¿ž™3&ãv¹˜9c2÷ÜûpÌ Žr<ðàR”Ð@7Tˆ¸„#+áˆÅ;ï}ÃQ]oýlj ]ÃdÁQŠ,0oÞ<§£*$ljz4°`þ­Ìºm‘i¨„£XII)¦#¬áäzVyßÏáCÕ|ðÞZæ>0š_/Åï †ŒÒh2iO´2ý¶‘x<±ÔôŒD5N¢ßÀ¬6ÉKR²•±<Á ¯MD@àÐÁê….sï¼sùûKßEEá4MãÅW–1iÂ4SÉFÞÿÂÅw¡X^ëyZ4›ý‡“’]ž—י‚.Lšr!Š¢pß¼YätÌÇf³3þª›h”ѤN½Î /?ÁC÷-7£+®Zu8¢$‘ß³7?4Ïn‹«º ‹bå¶^£q‹–|>ŽìÝM¯‘g±öë/°X­têÚ WU5›Wÿˆ£²‚.ýqdï^ùr%j0ˆÛQ…ŠPxÂÞ}qUUšJ:÷ˆ/D`<~cœÿôÙ'üøÉGlYýS„RÕøyÝfÖüº‰~½ºÖT^¯>ùšÌVÍÙ²m§4md ŽœöÙ8]ž¯\MÿÞe”vÎGÃëõ™¤nÉò—غcO½ç¿ðÖ|ž}ãf<ž*||çŸq€‹Î¾Â œƒ~ì¶T’Q^±›òŠ=L¾#‡¶­Óùò»ç騶'I‰)‡Î¹FÍEJ8\倎ªønÍ[æä0㚇i”ÞŠÌùèèlØ ½JÏeݦÏcþ¾[žšÂ®c{à =ÝóÈì«ÜÏy¥£B]K¢ÈVç´á‹-_“¨ØÌw„Ó¬ÂÓ;²ÐÙôñÌÊ—ÈmÖ±Þ÷N|þå§œVRÆû?~Çe=±H²™5¬¸” çßËõO.¥Có–,¼rbˆ¤Ég·£ÚíBÕ4Òš5§On>‡«*™}þ¥X»ç3nèiL{n9#»vYðk·ZQd™®í:FÕZtë×@@¥ºÊcÞW0¨¡5Ò%aµÊ¼þÌ­Ü?ãm~üfÉ) TâF’E\N¯I¾”U)BÿíØ|ˆçß½€/ÿµŽ€ªâ¨öÒ¦CSî¾ñum@_ÐtŒ„D;Ÿþû[ì6$$@j*¢ 0°g7c5, ©e °%°ðάøþ',m²ëä¦W‡ ´V¡UXΟkæñ§¦¦Ñ¸qS5jRç¶*+·Þ`×®í¦pT+×NžÊÌ“ÍcÒÓ3Ìž}þ¹'زeC½ÞpÄB©g¹ºÄ¤DIÂé¬6¯/J€ŸÉ×ÝÁë¯=i¤™Õ-|àçµß™©ho¼þ4Í[´FE22š°ê»/C5û˜rýì˜Õ3©iv‚!’WÐ’òr éSŒ•e6¬ßO÷ží:¼I å‡`zÃ7ì-S¬2ϼ|5W_ö”á1ÿ|£ñÂ'»‚Í® ©ϽòúôïXç×ýºÞH{رs ýs5G"ã°ØíIFþ®,cK°±ê‡Øm5ÂÖfKÄá¨6 „ßïçСýäåv6BØ^E…¥uSú€›~åâ Çã÷ûBJFˆ O¯ûv+Þy—÷·oÃJ7ô8têÚ]JI–£V¬²X­ÜuÑNi‰$ˈ’D³6Ù¸ª«8%+Q–~ÉX&ô@Qi>#®4–PNL±°öËÏ-´.ø¾­ÛYôÚ+üôù'üðéGlYý#R­4˜y?Í=øaõ¯u]%¡ú,«UáÙ—ÿA ä—BŠ<<šSR’bŽ[«Uaô™Ãزm'·Î]L›Pnúî½söp¬fÁ¬ß`çî}\;î¨BöVÍrI´§bKH6 <·§ ÀÃÜG¦Ð2TŸûò;·ÒªY.¶„dÖ¬ÿ_¨8¬C_|{AÕO‚Vüø&¶„d¦Ì¾–ų— XlØ’¸ÿ±kiœË_¹–fMÚÓ¦U1’d(°Þž³=»QßîX…‘Xk<•ÆIêí§Û â› [Q0œµ?~2® Ï"ɦ䯽lm£Äš¨lí(G˜Œ$Y“ë](àÝVÒ¿ï@v9LëÆMPµšå”åKM²^ë¶—žaù¤ ƒ|³ì|…Ym†ôä•{ðñÚŸðƒÜöÒ³¬Þ¾•e=b¦¨l=°ŸçÂ…ï!8«=¤7NB’Er Z˜†wvÇS¸æ¼e¸Ý~B•ŸØ…¦é”û(V™^sØ·«7iÉQ­äeÔ‚lÝ·‰‚6Øe8²lá¬>çòóöÕ¤&ã£ÊUY7G|£|ºàÞYñ:ªf8/½j #Î>‡ÓÏÌÛ}HÉ$%'¢¡“œ’ÄÔÛ'2­2›Ó¹$Ÿìv™|¶ê5SGéÀ†_7óüòÇë\¯Q£t>þæË:d,ŒY7Þ"%•ȲŒÕúÿØ;ïø(ªõÿ¿gfû¦Ñ{ïi¢(DñÚÛUE{. QDš¢¢bïHd„@ ´`è=u7»;å÷ÇìNv²»IðrïOïw?¯×¾’¾gÎyÎóyÚ±1ê¹×DhEÕXš¶[ÏÕ~ß´-“{.ïFq@¦ùõC¡V5¦fâvرH"¢ èSjæ~2X˜@ïÐë í¥нñÅ™›^yÄ:ÕÙºï6ì>²L¥Bˆ¨ûxùÓŸ˜ñø½T«œBßNm”“¯RÚk¡¨ +²V’äLŒšrE‹Ët«r”Ðê$g"+v¯ ‹d0Ÿ[šhî=¹ŸWnx ›E_iÞ—ßS»vMCæž=›KëÖ-D»ÃÆ[oeÀ=¥ËÜx<ê7¨$ˆ‚QxÈb‘¸âŠ^¨ªFaa°XJ°ÈŒªª$&%F_UUq»]zñ–2R8ÈeìG¿ræd’J°B\écFÏü «Ý¡“‡üB$ÉB½Æ)Œ~øF¾bA0Œ¶‚ Ë’êZa¤%´_QdÓµ”rR‘dEÆðѳýe¬J[ŒÝ*2ôõÛhR§Š"ë÷Ty È+‚D·î±;ø=î:À\Ô¼O¾}/¡œMÓ(ò`³ÚéЬ j73ÊzÛ­z%®â£zÉî$·Ù R—Ë¥ÁœÍSêG:y UË µ¨ øŠ‹¹ú·àfýô WÿãòÎæ±zñ,À÷Ü;!‘·Þ{˜{"?7UQX¹ð7v¤n%|æ^³;'Ü€*Ò¹[ÜA7Ú˜—'Ñ£WŽ>Ž Á¤'™eX­Cî¸p ð@€“'N›Ê.Úí6>ž7™ìº52)9boqDb^iE%%1‘o~]È•—t×ÃÄÒ2ØœžQ"¼róJž!)‰Sû2}¶^™Ën+In¿ê²ž_€ß -} n½ï»ãßbà÷óáܯ¨S«Ú¦ƒ=­hѤ‚Õ}î÷ûñâ0DQbò;ã±X¬Ab"pâÄ1’“+1ïËcvZ§Ó‰(IT­V%‹~ÓcBƒÄÆét‚¦„ÅápÒ°ažzbŠ,SµjuvïÞÁª•K£æ˜­J¤ßHJLá왓(ŠBýúM Qév'’_W*Z8ÌÝšŒ$Yxì‘[̬[Uxöùר·/€{ ãõ‰ÏGxcü¾cFþ@ó5±X$’’œ´ïPA¸‚:u+‘œââPÎYEÁé²  ñOf¯¦q³êH‘¶íë"ú⃱&ø³gJ¬v§9 "Ë ²¬à÷ˬ^µ‡^]:Gx¸²³÷½C…T©RÍ(5l±XP5‚%˜ÈƒËå®›¢²oÿn¾úæ#ìv‡Ihz½zíõ*UªEÔŸ÷ÞW2w¥³go&þ5•;·árFÖàž3v”Ù3#H í.¹4ªá 4N6-^@µºõH_¹œÂÜ\>Sž|„ú 'åàNJ¦ °aþ/z?¶­\†¿¸˜ÔeK¨R«6΄DF<ø0'þ8ˆÓ@Àïè%{öäÚ[‡püäé¨ofâäYÆWäñ‡p"ùù…ˆ‚Ààûog¤è‹íù|~^|ú!ªV©DÖÞƒ¨š†¬¨8ì6^~v09‡™œ†õëðþÌ/„%¼wh} ¢ áv¦ž¹ÈĭݮӤ#Ë>öÜ„?à%¬P²ïàÙ°ý~ÆNL^ÁI~ž•OAÑVo:‰Õbç“oŸcÉÚY´iÞ›Û½"¶—ˆªÈzÞ’1vä¨JK(ÄÊauðúMc¹cöýøäH`ŠK÷2·¨ÞŒ¯RõòÑmk롚i‡·ÇF(—@L/Ç??~0ê=<>#Þ}»ÕJ«aƒh\³–ñÜþ(Æ‹F5jq2(ãÈÏGV^ûn^9èßG>œböx$&á V¡9þñW|ö“勸ö¢.z\»¢’±5‡îìB¥* ȲŠ×£Ÿë+”ì¤qó<;v&ë—gá+0ÿÛTúÝÚ‰>×·cäë·Q«n%£È>ßQs\~‚^mÔ°yÜÜó ¶¬}›÷ÇÿN ¸®dñûdÞ|ù'5«µÝôÐ÷èÚ׋ӇÄð\Zl,Þò j46çûe€ªªlܵ–Þ¯&%¡² À“‡E²ÊI4r®à bØÂvЬ0ﳟÈËͧ~Ã:|:ë zŒÞø(v*óÁœ×¸¤wWR*¥pÛÝýÑ4•Zµ«såÅwàt9°#"V.YovÅßU‚äfä³:¡É$%è ‰Ôê7¬c2*)ªÊÂÔLRÜNãû’´Ý\wY`ïÑ“à p*¯€ YÙ(ªFåÄ aÈãåL^!)nWØ<\+̈U)Á™ »³Ù°;TªÉ œÊ/4ytۼܣWY÷Zhšfx …}@Qîž1[ä¨rAV+^*Û"JäyòL$ ¤(ˆ€(Š4l¤‡æä“Ï> ÷\I(xíÚ5yÿ½Y|2g‡#ê3K’ÙkQZ‘y7b I)‚€;ÑͶՋPÅô¿áñ w¢›-+æãJH@~Ñê\(ªL²[o?I”¸4˜÷±"uàv$FÝDW2kÒ–âøPÔ’¹ø—Õó¨W³1» «ÅÊ=¯áçÕó¨]+Ê~¢DJBeV¤êyz.ôïuÁ"µ¬ÏXaÒó òréVÉUÖ,^ˆÝá ¹’KÏÿÑ`æ?rà˜n´÷ß|<ƒE?}ÇÍ·ß͉£§qç÷ùðÃº—#§½†¡ð@Îif¾ÿUkÔÄëñ°véBÓH^³dA8‘ðàmÁŠVÅA¯…dÊÛ( ;3ö ( §Nž!)91Ê ±º+*𺮪jäžË/©.T`åK·‹MiÛY°bµ^QAÓX¿5MiøJʼn''&òÐÀGp»`¯ÊK¦U³&Ȳ‚¢ªT­T èÒ¡-]{ N‡ŸÏ‡Óa§rŠ>ÀϞ˥NÍàdaµpý-÷rÙ­8}6´ž>Llˆ IDAT€#aµZQU¿ßœÄAOfMNNÆë-iO‡C'))•ÊU’(rÕU×±}{ª)ì „`’ŸÅ°,¹Üî ©]ºö cû6Óâ†HV+Õ÷¦ŒæÛo>¢víúFNCBB"nw"^OQÔ<ŽG}™iŒ7, áÞf³óÑìI¼<â!C1¾À•ˆ¢I‚‚} )ÙÉ·vâ›/71ðŸºÂ¹dÑN ò½¼9á7š5¯ixD }8VªUO"Pðùd&O»×ȉE˜n¼¥3?~¿U ZD›TgÖ´€£”'B6ÊáN?¾ý;£¨*_|²€'ŽR«V]¾ÿñsÃcáp8IKßÌ—_ͦI“–ÈŠÌþYœû3Š@q1[–.¢ ÷ßL~‹ÑwßbTȪV·i+—G­F”µ7»Ü&¼¢'.ßûð |ôþ8ÖmJ£C»–ežSXèÁårràà!.íÞ™Uë6£¨j°¯ni)´€ à°'pøø®`?׫A ¦IGÕÒwé$%}×"¬;÷?[Ť4kt13¾x„•R£j#¶ïZLõª°ÙœØl.&ͼƒ]ûV#Š“fÞÏ_D¡§bcwöwSI°'pº0:™“UY‘ƒUzhšF­äšÌÝô5ª¦˜bÀÍ“¨ÊW[¿§VrMÓêÊ0Rž É.7iÙûõuZD‘÷~û‰$— g)¯«ªiÔ®\…U;·óãÆµL¼oOL›Ì?ƒ1ÅÁKÓ³…ð¤ñì9z„3Ÿ}Çà&Gü†ÌÃ9,ضÅtN÷Ë›FšA Z¶«cÈR=ïü~…ôMykäÏ\sc’+»8—WÄeaÉëákù}2iG³Mϰ }‹I=LJvÏâN°›ˆ†ÕbãÍG¦qãˆÛ)öyLKޱg$º’¿ìgëž\ÛízØó¯AoGœ;ã—wéót'êUo€?à×C: æ/…†5kñä{ƒJ‡ülÝ´ÍëÓXøë ½¿,ÿe‹Ö²móòr ‚ –`(¶Š¬°ww6s¦NAAÏéíär9™2k6› N6oHcÉüU$¥èÖñ}ÙùíÇ¥ìÈYezŽÇO“R) ›ÝfŠ–Ãò ®ƒý}óRÜNv@#?¿ˆ*µ«3ù§¥z¨V™. Ÿ,ëï1à±¾dÑ7ï`ÕÝ–<5ãkÖoÜaèIv%á“}&rîµ°[ì|ñðöžØ_Æí’IÑ8™Éލ¨JLS2¶gaQíÚµ¢° ÐaŒ°T"àõx¹¨S{<‡MSyÿ½Yœ:u§Ó$Õnj×®ÉÌŸ2{Ö\Μ9k ‡<20lAd•*U*ñËÏ ¨V½ª¹ïÛì 7“?ö(ŽBÈ»!û}$$%Ä<.DVÜI°mÍB¿ñ4£mèØSGsyÿ5¤Tuóá˜aìJ4à4­Ó’gÞ…(Š$º’¹´£¾æÇ¹üÓÔªª‘÷*Éí$mï&lø´=†AÀãóðú§#øvÙ'|ðíD£XȺmùç5›£¢*Ô®*1î#½ZåuO÷á‰I÷PT¬„(\—È;W’×¢Ë_Xµh>ùùŒ}êiv¥ocì3â+†JUõp½P/º¨û%ÌÿvMèïÆ•`Ф”J†wƦ/¾˜1•Æ-Z¢(2gƒ}Øév›ªÙ‰~€§†Ž ©Ä ˆÅ`&…NUT¶oˤ^ýÚÜpó5ô¿©¤vþîÃzÈÅ+¯>C¿úP«vurÏ呟WÀM·õ3UyÈ=pß–­Âç P£nm~]º’@ @‡V-P•i3æàváøY»9•—ßxW¯dà-&c÷^Žž8IµöíøÌügÎòÂø·p:TJNbÁŠ5,X±†” ‹P”$îþÇuüºdõšv¤u³& ¾çf}ùm…µ¢¨lX¿—ËÍñcGù`ê$APMUéÃ-Få*Y–±Û¥âu÷åâÅ¿ÑçÊkƒÊµŸQ/?(J<ÿÜP~ÿc…ž)1!9ÒÒ¦Áñã‡Â ,¼7e ·Ý>ˆä”*äççÒ­[ï7gN‰  Oj/ÍÐg|81ªÒËCãtÚ÷êOôêÒŽukö–Ä•/) + Y¨ê5¨Âñc¹¨ŠÊÑÃç8{¦—Gÿƒ¶M†“±w ~ÛNJ%g‚^MƒÛ×íÓ³'ëVïEQTTEcÉB½¯¾8ò}œÎèy*ùùyœ>}‚&›3eêŠ}^Ó;UU•öpôØ!l6;ÓgMÂawòí÷ŸC¹¶m!I: üœ9sŠÚµê"Š‹º*Ó«ª*ùù:‘OKߌ3Š`í/óéÞ¯?‰•+£ª*Û´#uùR”@€­Ëcs8ؾf%)Õk`wè¿ï@F:;wîççéдc;|ž"îœ 4MEUTlN'dfâp»Yþí/Ú»‡ß>šÁŽõk©\«v§“„d]ˆÖ¨WŸôUˣʥœ©!dîÞÏÛ^dçîýœ9—Ç‘c'èÕ½SÓ®îÙ ‰ÌÞ—takZ¦i±¬ÒãÒÂesáv¥ð‰lÛYb¹ùšX³ùK=Üj×aÓ-C¿/-s(iÎåêVòââ¾_ð…ž³Ìýa8µª5Õ½Áq*¯œ¾k1>¿US‘b(ùUª`“l<ýÝS‰ÜÒX¸k)ª¦¢j*Š¦ÐºVK®lÞ›{WG á9*¹RX±w5wvºÅð8(šÂW[¿3HI4å&œ\¸ìvVìÜŽ¢ª(ªJö‰ãF(Vø™¿u_=÷2›öf‘~ðÃ?Í{¿ýļÕ+p‹ŠL˜95‚¸lÝ¿—M{³¨2àVŽçžÓÃp“’ƒIú*«-b‘u˲ÐT ›ÝÊ-.&{Ï E%)ʼnªh$¥8ƒD]eûÖeŸF(¼>âGÞ|ù'EÅ•`cýŠ=†g AÓjt«3<â^–`hDÀ/óÞøßiÔ¬§•áƒçšÎGÓ8•{‚̃lÈ\ÃU]®3y Jcäì§ ¯›ž« ËÚæu[qôôa³±E²2ë·÷ÉÊÉdÌœ9~ö(‰QB|Ìc\ŽÈ˜þ€_fá¯+ðz‹IHpãNp±oO6VoÅô>¸\L~}‹_e(í§ƒGŸDbR*_l5Ÿ|xŸÌüš‘ãÇãt9ÈÚµŸú ëbµZèØø*</ ~Yf„I:¨Ï[SG1à–ÇñûÎÏÈv(UW®¿[·!ïNΩ³¨šF¢ÓÁêéÁt—i¾EŠ–n' ·f·écà±'_Ãݲë·f‰¢ÄÂÔL]» ‘7ÙÏØ_^ãð¹£$9“"¼ZÐ`*‘"á!Zå­j¯“œdü²ŸY«pÛ]çÕ>’$DÓàâ]ðx¼Fh”a°´ðüð'¨Z¥2×0h¹\NŸ>Ë—_|o¼ÿð¨–ÇY”D6¬ßb䔄{ä§¾7‹a?dä±ü>Þù0ÍÚÕdÛšEºBQpºÑóÝ8T­V=¬6;¯ºuSp‡å{žr¯‡ÝÛÖS³Š®?m[½„$(ö‘˜\UQŒ°¤€ßÇÞ훩Y óÎ’±qŸ¼9ŽÕ¿Ícç–ÕH¥ Áx|¦|=Žë¿gî‚QT‚=ó]¼>٬螧ÝÅ—‹frÍíqXu}/9,ljúoѤnKv'—<܈ùë¾£kë^介ÅfؤçÈ9¡“úÔ¬ Ô¨\›µÛ—F¼ßb¯‡i3¦b/õ"½i×Ѥekô½ŒùßÌ£U ¾ù&# cË&}ˆ¿ŸôÍ8{úY' BðýÉ“ñû}œ9qÂH^W¯ÇCúÖ]ÁcózH%P¯òQ¯AH‹š¬[O- w‚‹®aÊ@(iÜérðÆ¸©4ªÚ•u.1B”ZÕÓs)ô{˜ÿó7Œñ'ŽŸ"?¯€+{èDeåÒõ&ë´…TjÛNm[¡(2žb/=þ‡Žêå<‹}>›·ï`SZþ€Ìî}ؽï€ÎN6`Â{ÓùqáRÀÂgßýÌác'ŒÁÝ nvîÙÇ‚kHNÔt“õ¨S«»÷pøØ ÖlNeSÚvö¬ú½Â[U²³÷Q³fmÖ®]UZ¸ð4ÌøN2B¢êìÙ3¨ªBêÖFRwie>÷\9^Ž` ÞiŒ7åq„<*…Áp‡pâPX˜‡(dggñܳ÷bµ™—:¯0¯r ËŠQÍ&53+¦Â˜’âD@Àï—þÌWÌ>¢hˆ¢Àö´CŒý3²¬Ò¶i3–/ÙÅ€;>dÞ\=çäÜÙ"“¤[÷'EeùÝòºå ~¿\¦Âk‘¬lØ´Š§Ÿ€5jm¢ªª‘/â÷ûøhÎ{L?Í8{ÇÎ4ƒ€‚H“Æ-ùÌýb#^˜ÀñãGLwt9]lÏØj\sú¬·£ؼèw:5¨Á;ÆкC ¾zç Öÿö3õZ6eÖ¨ìÚ´‘Ûqãµ÷*\Ò‹»šÔ%¹jU¶-_ÊÉÃ9Œ¾ëŽìßË‘ý{9´'‹M çóÆÃérU_¾xs¢$1ù‰Gðy½ü;8pð½{váȱF[¥¦gš&LEQô…?e…{nïOÎác&÷§ù˨W§Šª’à mRÙš¶3ÂÒå°'päÄn¶íüÝ÷?[…3¹G¨Z¹>ûnsÆV2&·Ò¡RÉFõ* Yµi.‹·fÞn"à©;G-µ²¸ª*<œÎüåSÈ:°®¤¼bˆ Yl|=èî˜}?~Å/à+Óʹ0s ¿—!_>Içz¹ï“Á|6a^dTŠ(ái·Øp.=üÄb(YŠª°îÀF)‰ô¸(†Gã×-Mk¼H¢Èã3§âÛ¦¨*ë³2ùvÝj6›QKŸ„Ý(ªŠ$мüùÇx£¬E´rÇvcû¦½»Ùñî †L›Œ? Qõš¼ñÃצû̶ŠuR,"ì?(‰x‹||ÿÙFê4¨Ì³?1æ¬ÒTÓ‘ÂöͱÙ-$HÒ6¤ ÏËŠ™$$:HJqá÷ÉXm¥ÃI刷ãN°£(‘ESY»cûŽd±çð.®î|m™+jGóºWIªBÓº-j<áû²ßÈ Øw$ Q±HRL+±ªÂ†«£ÌU K­`Ù¢µx=Å<3t4dfIplݸ=»ßý>?¯yŸiï~JA~aÔûmZ—†ß@²H ¹÷ySXä¢ù«PU ›ÝFK»ñôWc‡*ªaW=&%Ñé ÁagÁV}¼ï:tœ‹×ã·ÍxŠéóÒ;䜯9à§z8öÇ>Æ?r¯½ð6›MË~ÁfÕ=Œ~”õ‹`ëÊßq'¹©^» ¯

Y0mÆ›™O>ûÀ wCÏûæ£r Þ±ìlúwëÎÊï¾æbAÀbµqxß®° ÊÚENÖ.íÍ"sÃ:vmÚ`—ŽØÏ¦zîÑîM$‘/=ÏæÅ%^M‹~G‘uϪ(ˆ’d쯘÷´üþ0ø©ÑT«jN¬>çkÄ`gíÕ£+×mÆ"I x;ƒÞn¬ÿ¡‡ º E·U‹&Ìúô[C)ŽF@ôªÅ&×u¨}EѤ™wðÆô[E ¢háÝïEŽQ‡_VüÌúêqNùÃtLú®E‚HZ梘äüÛßÇ‘u`}Ä~MƒÓ…g*Næƒç[D‹A*Öâxéç1:wJÍQ±Â¹Œ>FJ"å˜ÊÏ›×óÅ3#زoII+Ëë•‘“m^X¹s;jÐ{ÛJ¯†…'YX¬ì¢jK3¶ÅìWÏ=ð …y^n½ïbR*¹Y¿bOP†(X­’iÌ[m.í6ŠŒ­9,û-ƒgÃñÃç°X%Š ŠysäO¼ýÊ/tèÚ0¢]Ö-Ûm²žK’éùÇá ÔžµØVp¯Oå/Çðø-Ïs]÷›Ê|‡›“=‡v±çÐ.š×k…¢*$º’LVvEƒ=‡£ËßP¤Â²Ek …Õãñ–“ÅkccaÙ"}¾ŽÚç÷ùÙ²!Më·Åè‹R3I †td&Ïø†ás~À'ˆ×<$c}™ YÙ™s ›Å‚ªjzžG°±OæP)ÁÅæ=”ôµ(]?ÅíâܶL\›©oþ´z+—¶ij"’«¥»–›Å}²;g 0¼#á‰èv‹ƒ;§ a•ú,ß½·Ý]’#|W>ÙOŠ+Ù0FȪ‚ÛæfEÖ*TMÏ¿L°»‘ÂÎ á1¹‚:Ò»ï|HÓfMDüËÏ¿+EVªU«Êm7¤Zõ*†Þóí7?S£Fuã^‡=b|—„Šé¹—o½1ÕH€/ M|ŽvÝz3vpÞ~õ}ŠòsÉ;{ŠžIs'ëÅ ¶hÇ©£9dnYKëν¸²–ÀMÂï+¦• phÿ.,VUjÖaÑ7³)Ì;Ë®ôBvo[OûîW°cÓJ26® mCøpŒ^í²FlY9ßô,á9#’háæËï剷ï1¾^ú1Ó¾Ý( °tó¯ ™x›¾èç—¯"‰²ßD []ÐOU¥f­º†4‰UU«¢HJv²iÃE þf{Œ PÒ~2¯ý….Ý—" áyÁ…“,_² -¨Ä"±¶‹¢Hçîu ªª*¢(1}ÖÛÑ«ƒJ&/Ï[¤i‡üAÕ*5Lý(cÇVÒÒ7—ÛŽ[/`OjIü¹ð“ºlIP€èý,}Õ ~Ô~©» £ßÊ¡¾vlÚJsx•ª(¤­\Î…‚¢¨Øí6^™`.O½yk†±øžÛåä䩳 À¹Ü|FŒL‹f Q5·ËɨñS —ÿ¨ñSŒ X±”bUUH K<7ï“Mÿ«ª\¡ßQ:,+-s‘ÒVÆù²ì¯°ç0 °”“¾6HNf‰ió©\òhwìAKYé*9ÇòOF­®U!Ø™s§fO;¯w½rÇöˆwm[Ùí,óÈt=I]QUVîØ^æ˜÷Üw¬X°“I¯üÌòßwF% ¡£©zµ–Y'8¸ï^o9 ðÌýs‚‹Ð=ÿà§Äbݲ,“!OQÔrÚO‰bq9J¼$ë3WG=wçAý÷>3u½;\eÌYÑÿ6èÕpØídlÏ4 TØívvdì2Ç1ó¾{áÔɲ î€\®žä)Ìç§îÂj³3}ìãx 󑃡ϼ2Q’˜ô콈’ÄøGobî;#%‰ùs§2°WC‰–~ý8Qbû†eÜÑ1Q’xý‰;Xúý~5ƒú‚À¯sßG”$Þzö|^O„Ç<|Lý¼j^Äóþ+˜›mœ¯L]h"¥ÿö½4Ö¤-Á_ªˆ^=‹ˆgM]¿–üs縨ZIŸÌ>–ÏîŒtTU%)%ÅÉùØC|óñLDQäà¾=F©aEQXµèw6¯Yi„u}úQœ.][µáç/?cú›¨V³Û·lbÛÉ"ÛÒÖ,ÑónÒ6o {o–nÀŒh;ö + É)IT©R)hy–H©”±øž,+$$T,ÞÐjµ’–ª |‹Õ‚Ëådé¢ÕÆz!«‰¢¨ ºç¾øaZ„âà÷xòÕ 2þÈ>¿ßE^Ÿùˆv^EÎ_úK†ÉÂ[š0D#룊ó5`T‰.]Q±[í\<´;V‹-ÂKbÈ=Q⊧ô…wý‘Á²m ¹ãŠ,KýWb™Å[~£Wû+X¸ùÔhd\C ¢ówÀ‰Ü|œv+ã¿Z@’Ó €/ s÷³õ9î~c6¾€žLî †\ @ž§˜[C“ZU‘§ÍʧÎ2â“ø~}u_@†šz2î¢m»p·ËŠjxbj¤$±4}·©/.ݽ‚m9é$;“Iv&Êh³êMùãC™Lr&±d×2üJÀ¤¸®ÈÒ×ìA…³EgI°»Y²ku*ÕfEÖ*CiMq&£j*+v¯$Ù™„Ûî&ÁîÆ/û¹cúš6lK²3™G3#ˆoh^«V­*½{õ§Zµª†!GªU«Â޽ط/ÛÈÙúÞ,RÃ’²3¶gùš{÷0í3H‡Ëiä—¸\.ÒÓwž¾Ó¤çÄÊß ø}eÎK¥cÅa…]Jc>2ù¼ž ÏkÑÈB42PQrñgïY¦±g¡Ù‹Ó­v2’Å‚ßçã¹þ‰U#Y,\Þ¼.›×¬4éŠ,G´‰×ã!{o›×®B²Xø×3±vé":×HÀfwðÜÿ4 Øk/$à÷³vI‰±O4«¢è5¼­¡ ¿¡_`CÆ|öìÚϸQ“éÜ­ƒÉ®¢ªŠ‘¿Q®¥*à‘z}ön¾†ËÖ]¬j˜›YgÿN§ƒÞüHÌŠ9áU¯ä vÓ¼Ÿ|Á•¦ w½ ëWÉÉ£^~º¤ä` ½™ IDAT÷Ó…9{ÞÕÓñÞ”1LyÿÛ˜–ùhÞÐsD¢-fõgΊx;b[ÊÔRƒÂÏðg¾2HLÓ:Ï!I" :?bs¡¨y(«Íÿ‚÷WG‚Û•è…¤JïW°Êʬ)cùúLj¢È‘£'ÈÚ›m^G€ *øW‚¢*¬Ø»šõ¬8Ù¹Ðþ!!7 ów.bÃÁÍÆÚÂGD¥,EU#Â8.yø¯·_)ySÂðïŠóÁ“ï §aÍ&$º’Øy0ݧˆæ%)½mñæßxùÞñ,M]@bŒœAðË6d®áš.×ÊLèZv®ŽZ‰ëb?²²Ñ4;&Î4Fˆ È1=(9§ÎòÃú4¾_§‡yÍß²“oV§òæw‹)*öÑ«ß\v{Žœ@hs¢(ðÓ†tî˜8ÓX´0ä‰Ù¸'Ûtïðwå°9¹uÚÝF9]ã ¡5vÜŒüq [n5+ív7Kw­0 4&¹’u}IS R©zmFþ8¯ßkä’„½xº4ìÄéƒÌ“hFØ”Ãé0ÍùŸÌ™Çñã'ùìÓ¯9æÙ7Èé¤ÄPW:ZÄf³ñÚ„É4jTŸ©ïÏ"))íé;IOÛô¨Øð4lX¿B ÆQQ™'GÑ›ä2ÿW+(ÏUEÑ£'‚ç…òcü¾â¨Ïþ,&Iã÷xùÙ×°ÛmÁð‘îz’ën¼ §Óaˆp¡Êß8\ÓëNú®¹+BÉÚ¶%#øþÿãE9orSÖ1¢(±tÉÏ,]òsÔý‹•;oëQáçÛ±Cóq8\ÁN÷×Tò‰IH8bÇ/ŒÝf3BÄf³òÒØÉ¥ÈœÌÄÉúÊð×Þ6˜“§Ïš¥$Ši©Y½*?ü¶´Â‚ô¯ ¿âªüç:Zák,Ì\S‰Ý­‡÷™ JÉ$a·ØÙpp3kö¯GÑ”x§ý¬nVT\t^‰Äkv,gôoêÖ×d1Á™È‚Mú<JºW”ð\¹\¢ù×%æ*‹R3ÿÔy¿mÖu…Áó¦î4–{²‘•ùÁüWÝÐa®Ì¢Ô̘d|ȧÃÈõä#*Ôî)®ddEf[N:[ÿЉP’3 $G"GrðÒôç¸gæ@ª%V%Ù™Ìñ¼AÁ²»è´UEÄ ÇEsª_¹žAX¢À4£"V(,+\~ÊÁøý¹Ÿ~ãü²e­,Ë{‹ù|nôj¢>ŸŸ¹Ÿ~ÕjùÛËí8ʇ«„:Ó®{Ù¶%#&)8_˨ÚVé…üEeÛ– ¶mÉø¯Y›þ¯ < º,8p «â$X‘éÇ>eîgïW(<(Ž8¢!kovÔDÅP’bÖÞØa={öŒð’†Š(м?ó Nž:ó·žÈ,¢DßVW2gÃçØ-æÜ¨ägëÄ\Ó#bŒ—!te%ö~ŸìcãÁ-l=”vÞJqç‡UéK™òÝë4ªÙä¼ÏÝshOy€†1Ϩ]µ.ÙÇö™¶¶kRŸ)ß½þ?Ñ~òŸÔd#2BzXÛÏ÷öŸ:@öélF~8œöõÚñÚïoP©Am>^û v.$9“¸gæ@£’™Oö‘ìÒ=' ör€±¿L KCs‰òdg¿—m9i8¬îûè!š×h†¢*$9Yºkyº‚jX„BÓ4ÒÒvüI]D­Ð¹€ÿW Hi¬\ºþ‚‘Ã#æ6N>þš$åÏGeakÚΘ!ZŪÒ$¥¬‰îa"“U…%Y+Ø’³-¢|®¾ÿ¿óûÊ#)q\„r0ü²¯dÝ·ó€ÝêÀåpGõId?£f?c#qùìK3Æà°9÷Œÿ)¬ÈZE¡¯ˆ 61î׉H¢„ÓæäõÏÆûVì^I@öS'¥6µSô<UU ‰¾Þ‡žÀ>øÓÇô‹ ¾öû[ÜÙõV.{ãj¬’·ÝMí”Z,ÙµŒâ€%»–—K*B$þ¼®×ñâ#´ñOüÿÄ?ñOüÿü5>—´»\Eé¼Ï»¬Ã• ýkÐÛšÕb‹yœÕbÓÚ7étÁîÿœßGŠÒÆáÛJï¶ïÊVWÿ‡ÿ ?öªVWh¢ j€æ´9µ«Zõ‰·üó—ùÄíqÄGqÄñ—AhUì?sžáÉø÷ø³÷ã¯ÙGJ¿qü•' qÄGqÄGqÄÇ ñ:gqÄGqÄGqÄGœ€ÄGqÄGqÄGqGqÄGqÄGqÄ' qÄGqÄGqÄGœ€ÄGqÄGqÄGqÄ HqÄGqÄGqÄ' qÄGqÄGqÄGœ€ÄGqÄGqÄGqÄ HqÄñ¿$$©ücDQŒ‹®¿2,–øû‰#Ž8þ7 U`^Š#N@âˆ#Ž¿1Ztï^&¹E‘úmÛR¿m[„ó$!âÿñIDÿ³¿?œt\{U$)>½ÄGÿa%V”ÿMÙVÁ%‰ž—]oèÿ4‘DK¼Eâ¸ð,n=øÿÒÖáÿ[¬ö Ÿ~lEßÝ…zÇ-º÷@^+Ú5I¢~›¶ÔoÓ›ÃA߇‡Ð÷á!X¬Ö2¯+ˆ"-.î~~ uhËR´-ÿa…½k˾ÿöD v›…k¯ê`|ïÞµ v›% I ¾[QøÓä¦Diãž–ÿï¤Öò=þo×R\‡úoÏwí:v¢]ÇNj²Úl¼ö·(ŠRFŸéqiœ€\²(F' ’(ѧM¿ÿ/B'Úþ8úß ›•Æ[Åè?=ñY¬Ü?r:ª¢JfënWû+Õ¨Ã}#?ÄáJ4ö‡¿3UQxàÕY<8æc*U¯mÚ>॰XmQß¹ÅjcÀK xyD§¤_ØÂ•{ÆŽ (ÉT-ºw)ÄDQMÃçõàó ”é9ÑT•úmÚÒø¢‹*¾%+*_Ž|»ÕR&a¸¶[ë B<úukƒÍrá ‚Õbc൯P=¥¢pa¯o·YøjÎ0|~™þ×v4¶¿<ö<^¿‰,ȲBщ٨ªÆ¼9ÃL$Åb£zM$I4‘—ÓÆ·Ÿ=¢¨ÿOËÚsô·•ù‚Hïöן×ü¯¨2“ÍbÿKÏùe‰ð}¥S™ñ/|†µ‚rð¯E&EÓ_³ŒºpcàB^KEÚuìDÛ°E™£b¿Ãà3hùù¹Æv§ËñnEQÄSTðaósY¤¢¬}ªªr׃°Ùìf"ŠWD! Ñ„‚¢ÊL蛨B$6‹é}ƒ¢Ê¦k… tI”¸¢õµçÞ~ñ}e ¤ÿ¶U=nÅ/»ÚõîŠEý>Þ²<ŠÂ'˜Š Z½&ÔjÔUUXêÓ¨V·±±¿õÅW‘wú87?6–†mºÐ¢óå¦kx ò(öШm7j6hn„8ù¼…acÖæprÛ¯¡* ‚(áóûn|dt™‚(D*Õ¬Å-Ï¿ˆd±¢Þ‚Óñ‚$Ñ¢{r·+9A©Z¯õÛ¶+Sú¼.ºº¯áa)S±¶Zû@Ögf£iZL‚òÕ¨iV§:ÿz ¹D%ÚvI¹ºsKÞz~Yá³÷ż–ÕR1Å&\Æjzr±ZlÈŠ?æDògo 8—«¿ÿ›úwàŠËZ³cãRAQ•¯æ ÓßYõf$'»È>x­ÉP•ïæ>Íf1~W·7þ¿¸kSZ4«¥“¶«ÛWˆ€„~KE~SiEæ’Ý.XÎÑß•ÌD›‹UMeÄoWˆt¼õp éÈ÷䢡EÕ+Bä伜Ðó Š=:]}üYí´i®ËÏ׆‰¢ÈØJ‘üÂ\}ÐýÝæUEÓŠPT5b_¿ž]°—ãe'±H†$ŠÜpéÅ‚ÆöŠ‹¢„§¨—]Q®žÍãzUßA±×ËKc_Çj³¡(2«Óöáóùxï­ ÿƒdâüdÚۣ砪‘ž¢ÖÍ;F½–(Š´mÙ)â^Þý~¿ÏL@À6èm;úÚ ¥‘ç9œ~Ì畞žôãJÌÇC¦oû›L§sã´¨ÕI”°YìÌò#óÖóÞ¢(ÒüâQ•›ó}±BVBÊWh›ª(Ü=z|¹¡ÿIÿW Y±ˆFÛÞ‘“tèzÅxLÛ‡¾7 E–Ë·Nü)Ë•ôoíÿ;£A«N´ z=οÏkz>o·>þ5ê7ÓoyIUjÞ Q’‚^…"Zt¹›ÝÉuçDÎ>>/^ú±—{G  e—Þœ:¼Ÿn}ïä–aã¸í‰áQŸMUUækªª"H¯»Ž³ÇŽ¢ÈDIÂ[_Jth^Q’°Úlh`L î¤d¼T­WV=/)›\hb< IÄ9Wà1ÈC,‘[èÁnµp®Ð•¨ˆ¢@¿nm"ÈÍg#îCVT$Qàš.º‡°yð¸ÜBoĵ¬Ãnš¤{1ÂÉ Åõ¹:7¿Ê8N’,(ªŒ(J<ç >~!-êDRZIVT…—Õb‹©@ @ÝÚ•¹ë¶îT¯–@ß+Ûñå·ë#Jè/Xèß·#¹yž¨$ãâ.MñûeSxUˆdH’H®MiÛµ©Þ–v³<Ž’%Š"—tèf‰òæ„ZUk0òÁ§Ipº¸´cwDáß' V‹•ží»þ…HEÅe¡¢ÊLúµ©/\Ò¦qÔã{µí‹$JH¢D“Z­9r&US9? û£ÎqyEçð˾ “›ÕÎÔq¿£(ò#(’(ѽÓ5Q÷>Ò¬çù¡“Éܳ€šÕêñâ£ïá°»þþ“Hᱨ$â¦Þ=èi·2ÚL¤_Ï.ÆêF2¯#I" jUçõÇ,—ИúŸ¢0bÌD.¿²/V›Ím!)YŸGÏK/ê½)=äååšrA­Úv`ïîô¾²/E……+iÛ¾ Eÿƒú‚ªªr ð÷†ˆ¢DV×]0âRÑçE.ÂÏïЧGÔÎfµY#Œ>ízwIt,¥,¼¡ûŠ¢ˆ¢(¼õö+XmÖó" ë6þ\f,gH½þæËƵ#ŸMÄc_i«N,+O´í¡m幟-’Tæùå)O«ú-:òGæa†L˜!|Ï÷ñÀ«³¸îÁáœ>vI #t«x‹Î½ñy õ¤>IBÓ4|žBãýû}^ú?ô=®¿—Â\èÔçf¶éŠd±qî䊋 °›.ZØÖÙÓg÷™X©’ABÅÅ<4ù]ó{’刬V=/!¡re Nóæúä#èãRD«VA þmkµ¬¨|7ú!ìVɘ4C^ŠÒd$Ô焨DEU5ÞzS©× ‘[è5MÞTJ*kÞäø¹ºµ¼Æ£‘&uÚ3ìæ·#Êðã|~k·£q­vdßÉï›>)Sötn~û¾Â®?6¡i:~÷l”RÄÅç—éÇ$¾üæžþ9=|/¾òtÌP*ú_k…sØ­äèmR§…ND®½ª¢(D´©(‰ Ú6øjöì;†ß/‡…xéž–ðÐ.I Åÿ²‹º—#?õc9IݵôrU·ËÃÜŠ*îálj¢È¥/æ¢íþ2äãŠÖ—×9í_L@öÊn-.gþæ­Ñ H›¾FrpV}È÷äâ²'0uØO:u€§n"ï>ò­AF44UæÕ{?Ài‹TðýìÇkÿŒ{ '½ºö3ÈS,ï_Y¡Uá(òB^ÁYrŽîãÉ'rEÏ›9~2‡KºöãïŠhó‹¬(ø×ýÂKïdÿácÜpi÷˜ó™ QU•)Ï àû7F!‡ÍÉIÂnµr6¿0ª¬,=|ˆK.¿’^—_‰ª*¼ö·F(Ølv@¹ÆÆÂ‚<7mnÌkùùÔª]‡×ÞŒÓé")9Å8Ö)À?&cç4oÙ«ÍV®ñª¬ïçÿ^" `!ÏyH^ý;÷ðx‹ˆõ" Ê1æUQ¢çøHLHâ@ÎÓñ»S+B@tWiŸ6ýbÄ}™C¶5²WH7¬šÄ”!ãKQ I´ ”cuQ9}(‡¾C3<?$W«nß²g¯˜UrBש׺ ­zõ6oa‚(P¯u[ªÕo`î 6÷Mœd"%›Qz¿(ˆôhÙ¯Â×?Š*3üŽY8lî%HQúÞ(,QëprѸc«¨ž}º¡}Ÿî&ï…Åfåþמå辜¯F‡>=JµŸ•G?ÍŽF˜UMó ª*?ù éi;ÑT}d´kß²bª]ÅrM>a)q:¼õö+¨ŠÊ{ŒzÞÕ]º#‰"6«•O_þ—I°šHvëvajɱ’(ru—îQ¿=¨¬÷íÖ“¾ÝzF(§][¶Ánµb‘¤ˆg7Þ¡«].Çç-¦äz òô1$Jœ;y”×ÝÃý#§ã)8Ç5÷>M•Zõ©^¯ Õë5¥U·>‚ˆ¿ØCë‹K,éhPì)Äç-¤Ø£pÓÐÑ(r‹ÕÆ Ÿs"g_Ø8Ê‹°â+²ô £iûS·rËó/Ÿ¯ ¢C$ôûZöèÉîcÅçsñ{½ø½úïs&éŠû¹£GYXÊž_R„BŸúukMn¡‹(âõèÒ¢­ê×àš.­Œwp&_·UNräå½ÇoÇå(™¨BžðÐ*ɘX$Ü/Þ}5÷ ]–é4 I²†‘•«:ݧXo»NͯÄnu2°ï+9½¿ñ–QT‹dêý…v‰‚H·–ºõ·Ð›kÈMÓ¿ÇF€w§-äýC3Q#PºwmJåJnÀK¿kÚ#I‚ÑG7ªÎðWCÞ3]èwu{TUãÚ°0«ðÉö\n9‡Î iûPÕ´]+5÷xŠõ¾òÇñÃLyvVK,ë«@›Æ-•«qìÌIz´ïb:âÒ ‰)ˆ(ªÂ[OŽÁj±b³X©W£µhû§»ÅÆ…„Íb¥SÃçu¯î-ë°åý<ü²¾]nãñ©7sQÓžmþ½MÃ.hä{Îq®àEÅtjz‰q\¯¶}Mç;›Ã5o¥¨¸D&Œü^¿'æïÉÍ?ö¤(2¯>=›o>ÜNí È~´SZDÿ·ZíŒá³r½(ÑK~a.š¦Qä-àù!“ÿ¶¤{[óûÓ¤W©[½*Ž'ÁåÄb‘ÈÌÎ1ö_Û£‹AR´+eÅä%¬×K÷€¬ÛžÉÄaº·#ä%9•›§·{’W–¡MÓ4lV=ƒ áùùa¡nšÆöm[xûÃ9Ø‚$¡Ç¥—G5¾äçÓãÒ+ ®i6»ü<‚ — ^OOí:v¢×åW"ŠbTr“èrеECÓ¶ë/n‡Ýj1ÍÙ±rm¢‘о]ô¹ãºn킺ž@¿®m¹¬½ÅгMcúwooì³HçGH<ÅE&bqÝ•·r׃°Zm´nÞ±\ƒgÍêuñës ÅbáÁ»ž0Í¿‚ ËãøÆ ZðÙ·Ó!6‘D ~ÙŠªO@Á·®K‚ÄUmo E­¶eR™Ðscç»yöú1<·à<¹ÕʣƢ* ª¢pÓ³/ÒúÒÞX¬6¼ùÆDóÆ·2ûð ã¼þO<ƒlˆÒaVª¢Ðwð£ØŽdíbâÚ-“(I ]Ó gÇvñ)Ý.á%áB{µùG™1×=ZšIž¬ðø ¹¨É刢„Õb§m³2o•l¼|çÇü02'B N¸Å~³ÓГWu0†{=žŸ4 ±ÏDn{áaªÔ©Q’ØÜ£S„Ò+ŠRXèƒÅfåÖçâxöáªðl^ÌvðúD!’$a³[õêÓŒmx)ÁË‚%†M*6G‡ ŒðvL™<â°ß àõs`ÿ|:w Cz!ê=®îª—ˆõ9}’6 ›BÓåp0êþ‡yø†[X=÷{þ5èQœv;Q"·°À€ýºëï¼Ñùö_o"+ ‚ P¿FMž¾ã^zwìL¯öaµX˜öìËÜØK¶ë¯Çp}KéÖªm)ò¦ðÁ¯`u8iÙårüÞH‡ñà×>çÔ‘ì`ŸÖ°9\x r).*äæÇþE£6]É;}Œú-/¢õÅWbs8¹å±qTªQ7‚È{ òEQ¿_çÞx óðæ•ëiÛ·e3M;w6¬dG÷ì1Ƙ;%¥Äº”†¡ UkêµjÏë1Ñ\IɆgôô™s¥»œž¤nº‡•x¼÷øí4­SÆæ±)_s2·€½GNQ¼`2§ó éÚ²šÔáÚ®­±Y$ZÕ¯Éc§¨’è6¤Å°‰â)ö‡ T]I †VI’Èî?N0öþx|~vågâôáÔ©šÑÃe@’«R„¬NvWÅnuÒ²^DA¤Ð›‹(JF¾GE0¤ÿã>][öA´ T$ š‰„ÐöâæÜx}g@$¹r‚>ÁtÙ5uÒý@>ÓÞy€A÷=aj‡~Ww )ÑÉ Oõ7%¶—S¶Ç¼ØïãÝáo îÙ‚Ü2= ºU z¥j izÌãéÕ‡žãÙ{‰ð…+ÑV‹…'î|˜ô½;Œg¸µO®ëyU¹Þ’pC”¡ÐU©Ë¸ÛGÅ<¶,‚íx¯¿˜×Ÿx§)±ñú]c©[¹NDžf¸·£}£$ãùŽ=D¿.w6rUFVK+ögòOpýÅ—$­]CÝ;õøcØl7¾@1#î|ÇD@Ôòb`4Íð΄ϥõk7£~ífämšw!ÿT$‰|äœ3m+(<Ó;‚fnÓæ;PX”Kõ*µc[²ƒïÇRÊ c„¿Š‘áßWbäu¬v³>VP$d`½(I¸œŒ¦—„cõ¶Å!‹S_xŒ»pÝ•|½dÅ~½]sŽŸDÛ¹’wŸÊÍ—ëúÈÄa0däë,ž:¯ßO›Æ B fŒ§s…¨š†¢ªÜpéÅ4ª­rŠŠ‹£z]¾yí¥¨áYR0|6äõWÏl6;Ó>ù”í©[D‘ÆÍZЮcç¨!Yº .{®’åM›·bÉ’õ\sýˆ‚ˆÇSDÏK¯@UUzóMìV cî¿ïìË“7÷aÝoïóÄÍ}Xõös¬{÷EšÖ®ÎM—t¤yÝa† ­`=ŠªdÁnµðž˜úøÝ(ªj™¾]ô' jTæ›Qƒ©œè¦UƒZÜ~YgþÙ§¯?t+µ*'£y7¡ª7ôèÀuÝ*nä>ì5ü?¢(Ò¤A ÒvnB?­›w(ó\AiÓ¼^¯GŸ´ G¥Œã;·ïÁ¾ì]/CJ¸µRQe>òö`|±Ýê0%“‡&£DG5º¸‹¢ƒ½†âñòÖàWpXü¸y1—´èU0–¶šjŠÂ†¿ÃR‰~÷?hœŽ„v¯]ÕcN|Bí5áþï°[´kГö u%³wÛ›yâïP«Rè1Ä¢(Ѹf[Ú6ìiìMîÞsôë<IÈ>Òf®2Û?ÃçÜD‡F—š„JA)C‘Íé@Ó4üÞÈßö²®.¢ ”°³P˜›ÕaC”DZöèÈ–+#¼"”ö]iàÉ/0ö'’\¡Áà´%D/tEV¸þÚzHVÐzÜ®}K\1ÏùÿQvÞÑQ”ßÿÌÌö’BBèjBïU@@ª°‹,Ø TTì¨X°#úµwT¬TQ!ô^ÒK }{™òûcf'»dþæÏ‘ÝÍìÌìûÞ{Ÿû<÷Þ'îÅátà«ö †tÖ@’°Z-¼ñÖ3Œ¿xl ˜‘$‘nÝ;²råzn½åaÃiÖ.Ü'6«•çn¾“Gn˜Ì쇦ñÜ-w"+ Ã{öåhé)|ÁD¢-=IïÂN\0`¾ ¾.ã²ÌÈÞºáG£,^·šµï¦iŒî;€å¥<ôòëlس“q‡pÛôçð&ºrìüìÚ4iFïöµ L(P…(ˆöJ8è«hG‚~:ô=pÀW+¬´Zí¢.¹ÒåWý7ôWa±ÚLyhX¬vnñ'ïC%“mI|Ÿ¦¦q¾V+×=?ƒg/º€Žƒ‡¤8Ĺ_?ŽñS¤ã Áµ˜´ë^˜Á°o$ ¥¥‹ÙMQØüÇZ÷è™’Õ)?~œ¯*ªøý·ñWTPзö¬MÓ8XRAŸÂTúCthÑ4°Y-Ø×á½ ZàrX‘$‘‘½ © „  ‡žº™I—>Jaóiƒ[›ÕBŸÂøÃª!#™ ¬ ‰“Æôg÷á“u§?I”èÑnᤤƒÕbgâ觸nÄcµ59MmÝ­N1ÿ¿W»Ži³½Á°üíißâLuõ„>´Ê¯ŸX¸÷ŽQ”:etÊRX¿ö_TE¿^Ý[2`x HY¹ÜœT)šÏ¦xß ^zý.ãÞD yZiÑ<7)Ø“jù‘€¯ê¿±¨mÚ›ìH¦'ÃŽ©{¦SëB¿iJYÁÁÝû—e|A_Šûgóvìß“öï†vl~Öf±1{Ò,TM奫žá ,¼? IDAT¹ËŸ@ê¹³™9á¹´Àç‰¯Õ 8UᥫžÁes¦¾:}i¨.?ʈÎCSü•(J&ÛÑ8§§÷GˆÉv‰d¹;ÂJÿ¼MáXÙAúà!æMßÄ[Ó¦Ð8§9v«ƒ]‡7‰ëŒ•Y_z–®Í6«»n|žÉ×LGU&_3'¿Îྃ…„ÃúõQëÚEæÅG¿Æáp›ë< ñÆÓOÔúÜs}¦û× ôî: ›ÕŽ €¢Äõ,ºM¬|x2êñËúZêk¨—DÇ];ÍHÈ(|³RcüõOPÐy Nx˜V…½9oÜmÜúÈ'\sçk ½ðêÕoJ—Þ5 ‚Öíõ½Ø¾Û:tŠª*¼øÉ²r1ýÝ"Z´éÊ“oÿƒª*´,Г?ÍÛt5P5~[¾`»Ý†Õb¡^†—H,N\VD‰Òc'ðCØ –¡Ò¤an6§*ª¨ô8@oÓΜӥ•þàq:øøÉ)ü{ôñÓìûé¬G…?UžeµÙ¸sÊ£lݸÉbáÀ¿{¹øŠk°Úm)ñ¨ÏE²XD‰þ‡ k%-ÿ‹è+ñÙ>òÇ‚_u"‰pÑPïþƒDMƒP$Fç–Mxú†±pè8çtlM§–MhÛ4ÜLY;¬ÙŸ][5…SåÓÞÉ`8:qQ¿.lú÷ˆžp4>ŸáÒcp—ÝÆ’2š×¯ÇKÓ'síy}ùò‰[iÙ0‡p4åUÜ1v­æ2¦ÏÙež‹…Û®{)OM4ÁA.ý9^r¤œ¥îM¼žL]&iÄK¡H†yMÓʵ„4¶I”D —÷½ž¸±‰LJ3Ta.MK-&F4ÍiÁ×~h@ƺÊP.›519ÊOë¿b\Ï (ªB,©MÓ4âJ<5»«ªÚ¶…î#Ç A”]5úö»èyþEz[O-åÆô9ð\ê5nÂw݇¿¼ŒFmÛÑéÜ¡\ýôóœ:t€`uõš4åÚfÒwÜ%œ:tа»Ü|?ãZtîúŸ4å¾p%Ñx˜NùýÔq} FÑ9H6«ÝÌ`¦,tAb@ûó Eý8ln¹âCäÉJI²"Š]ZÄ:ÊÆÌ›~K‘VÅå({Žndd«ë,FPe«ÝÆÀËGsñ”‰XlVDI¤I»|"Áp ;®züÕx²2D‘>cãâ8wÂ…+"š §^VmY‡ÝÆ”O_âÁ+®úOÎüÅ×ðþ/#J"²¬ Y$c^@ii9qC¶µiã_Äb2S½«ÍŠÕf5€Å5<÷Ì4ü¾…­MvcÀ9½ˆÇe*+«ÉήDƒ÷E²X5z[6ï ’¤Ù¬Vž»åNšå5 s¼¼”“¥% ì=€“L½z"ý:v&`fè5–§C~KÚ4iÆ´×ßfòØKùáÙW(èÕÉc/å·UËyæ;ôîÖ“Þ…ñ‡BTø}•ñ…‚úæ7 ›ƒ‹‹(Û½“öí;âr8ÎXh'Š5Á×ÉC{yíýäwìE4 °W 89Û¡¡™FE·ê@Uéq¢!?!•ùž^°®3’…½†Áˆ«ïeÙ˜ ¤¦i¦Äª®îg®¬,J¢ûèÑ)Œ#ÀŽeË8wÂÕ¢HÐW7ƒ¦ª*›ÿøƒn#F¤£ë’MݰGÚŸsNZy¦†F(CVSReœMÓEã|ðâ&@ ¶¦kë&z±u De Dƒl/ç÷­ÄìUH åï…o‰Õ؃¨,óÊ£ïRЬUI…ïé¾€Õbç‘ ÿKF`H‚„¦©f"B±áhjûHUSY¾åG:µâ`’³Ó¦©Dc!±:]Ä¢—Vž) ßS4^cÿ«‚ôïÝ—ÓÆˆaÙ°|3OÍø‘Ü\/“'¥uË<Àb„yõS3¿SJñ¾¥ .ïÏ¡‡(9™ùàà¡/kä!û˜ÌJâXTô'sœµHuíWË8tBw‚  ¨*^—ÇxFVÞxàyÆ>pe•u2(rðÀ5wÔd¹µèr8Y¹umÚ¿Öaù{hšJÀ5hhÌøåU—åDU [o£_›Þ)Žü³ÛæP|b¯ ^Ftj^ï{u–ãáIOŠ…Í÷ rZRKM#×››ðëòhG8¿÷•µÔ]Zõ¥K«¾I÷kAÓHÛ”AUvÚ@ç–z ZZ]ÂU߯œû »o&ÓÍž#[q;¼¦ìJÀis3ýÚwÓvßÔ€`ÈOÈW„Â~|JDQ÷¥ç ¼„X<Æ#0éÊGp»2E‘–ÍÚ³çß-f×+E‘™rË+$ä¢(ѲY{ŽŸ<@µÁ*F¹*½²á´ »,ǘõí!NÒ¹÷Hr´ û€±ÜöØgŒ»nc.¿Ÿ›ü€âíë9²³¾]Æ¡}›¹âæçiÔ¬›þäÒI÷Ó$¿#Mò;0pÔ Œ¿þ .™ø47ÞÿÝú_@c)ìz®äšpþ•Ò¨i;4mKýFz£€©¯,aÐèysîdƒ-²H·]r>Ó&ÝÍ›ÞÆ„‘ãé^КÑýt°r×Ìw9tâ$]Ú¶4ê·ÑÔ¶}Œ¿×mcy•ÐÈËÑã€},çõ)“©ôÈÉÊ ÒàÆÇîå²óÕÚƒ};œ¹ ]ÓˆF¼÷éwìßWŒÕfãØ‘Ãg”@JRj"QÄîtºL6%§$‡î燅+eE‘"mÁL²õê;€¥ 3»…I¢ÈÈ^ˆÄb„!FõÒÛ©Û,-ÔcöôÛ@UhÕ(—›Ç ¤}‹Fàr0ëŽ+Èpéör`§6+«âƒçî"rã ßåçU[xâÚ ¸x`w6í; .m!C‚6«… ¬öŸ(ÅfµœÑÔÄl"Ûkî Äóô¸2Œ=” &‰ä5ï6lcÂV…Ã!:ÖQ”žözU曢øìŽßqÙõ“µkÔ1%ƒ!‰ª b³ØylüK¬ß¿Š{°ïPˆoïYÊ‚¢ u²‰ÌdžGذ¿ˆ¨ÅewóõÝKxê²×Ù[²Ó;é²#)uAFÃ!$«•‚¾ý‰üæ-ÇB!33øiÔ¶Imö¿är²6Äjwöûûýôíd±ê…«¢D^‹|>zànüåeDCADI"£© U'KèzÞ¤ÿGiMÓ°Xl\Ð{¢AKŸ9u“ímhÂU<{íM:Bn9ˆÅ¿DUõ¬Ã¨× +q*ü%DbA^œø½ÚgñƯp;2$k-ÉUºëŠBD‚!C6%ÒoÜy„|3ã¬ê´äØÞƒ¸2<úµH(q_y%«9.#ZDâÑsg¼Ïï>•Zg¢iø+ªñÖûo ˆ×ëá·_—бc; [ã÷ÕO‹.£{w=[ðö»s¸öúKÈÈðèu"š–,ô¿¢¨x½úšnß¾-ýú÷Äï¯9ç=÷ÝDãÆ ˆEcgì!‰GN–0æÜá\=| /ß~v‹ -¢Ç.¼xïCì:t€á=ûêëßjcá+ïòêp°ä8øC#úuì ¡ þPˆ  F+9 Ã{õ%‹òËŒY”üó7¿Ì˜¥g˜$‰ ¿(¨mƒÉ© øÓf¨ AP›ÃÉ O¼Fv^ŸÙåê¿‘€Â^CÌYŒdäuSwûÓ)³>ÂA…½†"Y¬:ÀñË¢HéÑý¦40¥ \H/˰ØlLûî;^¼ô’c9ö¾û™÷êL¯!%¬€ˆ r$rF)˜ Š´?g`-–ÅbÑkkÆìÊÜe›ˆÅ,Éܽv›…³îgÎÇ¿#ºÖV–Õ ׋·Œc÷á“hì;Vʳ/LùŽÖF·+!Ô¤Ÿ!ËãbíîCÄâò52)õŠÇ"Ù†«iÛ¤¢(L iÀ  ÷]ú¦Ù•Hw.u¢Ê¼sï?-݇ÍbÇN{fžr^ƒ¯¿_Í9ýÚ+TU‡¸òÊH’^¬øÛ¢ÍüSTLV†‹j_˜‹ÇöÒÿ>TM4çÁi_3ó¹ I®‚ÆCÝ©µíÖ‹üŽ÷óÇ߯Ÿ‘€šÄA¢ˆ=ùXXô'£ú«3™”ð[Ñ »ÍFµÁzt/èÌí/>d~®Ò_M¶7ƒtKãïlVýï Z´FÂш)½Èp{ ?j=Mþd7ƒ€¸"siŸq5 ƒGUUJýeøÂ>.é=6<¯:ÁË¿½Á7¿ELŽÑ¥y'>¿}6ÉÊ‘ò£d8½æãyæ2=ãÞ¯MoîzÿŽZ` ž;+Å÷íw ‡Ní3ÁÅéGŸvçŠÌÎVYîœÙ ~ï© ¸ûg_Ƀ—½DU œo—½ÏÉÊ£¦s;¼<õÅmd¸²hÝȘ±#¹].àhÙ´ÅË’$áTRí¯0â7½~IUáÄ©Ã<ñÊ ôì<Y†¦Î aýf¨ªJ§‚Þœ8u~=šé2°‰/°sïzРu‹ŽˆF'¯þ=Gâ$ü ,]9ƒÆbµØj]M( w×a)Z$$¬¦Uû>Œ¾ü> :¤E›îÈrœ#¶“ß®‹ ‹ÕÆ¡}UÜ2õcœîL »FŽG©8 cÏó˜4e6õµ¤¢ô(±HA˜xÿ{ÔËkF(XÍ-S?âètè>”X,LûnCL9V<& QY¦Ð¤EC¦©ðÙü¥ä7j@•_ßg= Ûâ…¹îüaüºb ÑXUÓ(úk%˜écú÷¦Ò¤¬Z÷I¹™zðúùü¥ô1˜^ÚÑ ÛXOÇKèÒ¿¢XS´üú”Éü³yѤâq‹$ÕrSàóU 2|4ï~ú-‡î7m}UEÙÙöÓ¢-xx€—ßú9×ãš$`³ÙèÔµ‡ùÙd+Š#/Ï–ëô6ôd{]fÂLÓjêÔ$Qäü> ,Ad@‡Ö<7q_OŸ ‡K¨ô‡DyÑ{L™4žÖs¡ÒÏâï¥âÈIüán'ÏL»…¶Mòøjúm̺óJ*7¯Éaµðó wS±r3 ^¸‡õÌõÆz’îñx,å'+>>üêuî¸q*q9Fa›N&˜Ø°¥ˆ¦ZЩ {Jò9ù^O¦éß=î <îŒ:Ù'ÓŠí?YÌ BÖq ÕIήâù»6èÖ(UÁr$ABÁnqеEo>]þã{]]+# IªC(ªÌ†El8P„ª*T‡*iÝ €ñ[TJéÔ´;çuºIRn `Ò«oRvø0íúö§ìè‘”¹5A“0h,PY‰ÅfC²$$fzVW´H¨†ÇU§ÇkJADQ¢qÛÂ=P•¬6 œg!ðWÛºQgʪSê;F¦+Èr×O«½”$+™®³ÃÊ÷+‹éW8†Ñ½®£ÌwUS ÚÏšòí‘XˆÝ'°åÀ bñJvåì‡@Fn¶yÖÂþÝX¿d¢$rýs÷аU3lÙ¦jDC†•šwhC<cúoï GÍß*PQÕfCŽÇSw‚Ñ4µNTÞ­{GÖ¬ÙH~~3üþ`- ù¦MÛsþ0Ú¶mÉçŸ}ÏG~C0F–e¬‰ì‰q‹üM^^.Š"óÖ›qÿ·’ß²‘H§ÓI–T|Õþ”ŽWup4ÂìoPvª„/žxžx\FC£:`x¯¾”ž,á³iÏqá€ÁÆÆÔ¨4€ƒ?Q2e` !Š"#{÷£:!ap5ª~ìVU~?•~;¢ƒ‘Ö`ßÑ#|4£v±cA¯!¦¼ÊéÎ`÷ú¿QU…°_þÏÃSî9裰wX !  ˆ†ôk61h5L‰(I„Õö®=5Ö,(EZ÷èÉ–?–Ôªƒ³N£užlþc1þŠ CéÖ×YJDº Îæ?þ0™—ºÖZ{s?' 1$QàªI²dÃn¾ùk—ŸÛݜح­®cŽÅeªaº¶nÀ–¡ip‹$QábÅÚÓ¾­Ú4©Ï¶eSzíg¹,^¿EU‰ëÞb‘jÇf× ¤ PÕ–mý‘ ûßD8 aŽ.÷Ì0H"XËtç¤8çë,Š]Z bÙ–•g\…Íza·:9§ó8–o]_ëýc{¶°zÝ¿„Â1V®.fÂeýjž33)#¬ê÷/è,ÒÅöÂf½\^sï+©U ¯¨æžÛFÛ«Ö%«ÓjË6{JãˆD&Öj±šíyýŨþÃð‡téHãú q;]é0 PwW­ê€YQLW·vøeÅBî}õq´M•tjÓ>%ð¿¼ïx⊌¢*½m6/ÿ6‹c•'FCT‡ý\Ú{ª¶Þt äEÝÇðÎpQ÷1)`¦ž';5¨Pe¾[þŸL­Îµ1ãÛûÉöäòÎ]?‰…ÈòäÔJ>Î_û-} †¤ø@_¨ŠPÔŸr‰;ŠÅ£H¢%eС”&0Mìæ½³™ùøÇdgÖç­gþÇÁ£»QUX±æ7‚!?#Åô Á¹ý.Ò“YÏ~ŠÅbK”,Ð¥°ýºàâ[.g¸{Ì’(áVÑ¥}4Mý6+ôï9’@ J/\2<Ùt*èƒ?XUkÁ„C>Z´éNÇÑ,VDIâä±}ÜúÈ'Øì.Q¤°ë¹D#!ÂÁj#So¥ Ë`BAŸ1ÓËõ÷¼IvncA$в 'y[‘ß®‡~nQ"òSØu0ñh˜Þç^J,¦°ë¹”ÞúåâJ”˜åÍ_¦ðÒÄŸQU%¥CU$DÚ5éÀºâ%Œêq V))à šÞN03N#Li…¢),Þø][:ë2S+oZñ£ßçÂa¬þe)c﹞ң'ÌÏÛ]©Ò®æÚ È Õ¥t;/µ0^‘e 7¼0‡Ûi<¿v$Ñ©ö¢5z{‹ðá_ñôôW‰Åb)›Çb±`³[é׿k×lÆb±àr9¹qÒ•Øì6$I"?¿)+ÿYÇ˜ó‡™ ¾¬´œH8Âî]{yí•÷q¹œµYF8Yð#‰"U¿Ô% È-’D¥ß‡$‰z` éï5¨—ï/¾¼k#z÷Ó£(]J’JiZêz²[mü2c¥G¤|ŸðëEì¡ÚlWa¯!¦¼ÊáÉ`÷º¿Ïˆ'¼E]ÝÌzQJ©+©8¥í¦¤3"CÎÈ@t>œƒ[·žqe·#Ø·n=r]+ûP¿¾)ÓÓÓIϺ =Ê’¨ªzFäàÖ-4lÕ:M6¼f,Û²—ËÎíN¥?'V‹q…;Þø–£¾#á˜-’DU ̈ž…H¢@¹O—äxݵ‚ËÏíÎwo2;¼%œv¡$@M¢ˆ=¹-n«Æº¶7®6Y>3›ÅraÿÑx]õðõd‚€$Z°ZìL¿þ+žþìjbr®dÓ>½–ËissNç‹økÓwg¥ª¦Ð0»‹—ÖfþØŠÅ"±ð-fò yÎGâÙÊŠJv–+%Ü´åx‰ËŠîdŒcÎÇSÙµûX£Æ9ÿÒ™4le;Û×ýkJ½ÎªëEFõÆ¢¢? fYÔ‹A÷ĉËq:·ioJµF÷† Ää÷¼ò­·¨•µY­ÜqÙ$šä¥/?”œªäɯQRG%\Nq¯Æ5 pøØ>.»à6"1àÛñœÞC9§÷ÚµÒ p#Ñš5 ùyò¾XºòUA¡~øƒÕxÜYlØºŠŽ½é×ý<6ïX‰ª*ˆ¢D“‡|†×-©Mkt°¡ÛÛ®ƒõÏÏM-)¯%V`yéQ:ô¨aY¢‘]¥Öv6<2þº' 2˜Œ¬úüöõ˦ KQUþ\¿Å W­Çi·ƒ+ËT¼ðñ7´lÔ€c§Êu[•°¯u€ÂX<ÎÁ'ñºÀG¿,¢çuwC“†X,³#VIy%w\v-ï<|'š¦rðxIŠ/´Ùì|øÕOlZ·4Íô•’$ѹk:wë(ŠTVDñff""’ÅBfV6qYæð¡i“PÃ_]M0@²XRùX,Ê]“&Ò²u[Z¶iË]“&#Ë2ý%àóÕEŸyÂŒêÕÉHJU.á“Çnb쀮f-‰EѨaP4 Ýÿ$ “Eëw¢ªn 9é{V ³^¼—¢ûÓúÇò ½YGŸnñù«8züP­½*$IÍk°æì6;wßô8±ø™œì?TÌ$ð‚¾´ù{19ÓñÛÆ¹Œïu5N››97¿Eçæ=j²4ø#>ÊŽÂcrùß;±ªx-ÍsæmÄ•8ÃŒö¼Wö›H$¦q¶d: ¶:ðÅ?ï‡E°[d{r‘97ÇÞ;Ïxc—ÍK! öùpefâp{ôìzҳܲt1‡èíDEÉBØïÓ3 ýÒ®oö­_C,á£"¯EË”`@LÒ6m߃†pÁÝ÷ iܶI²Kêà (qDQbL¯Ùôïß,Û6‹d5Á@UPo¿1ýj½CU¤)JQ¹gìk¼6ï.A¤:TnÐòDA¤ÌwÂÐÁJô+?\Eu¨Ì\ [¬`t¯ë8§½ž-Êð¤Wõ»³3@RèÆ(E¢yKè5æ\2sëÕ8˜äl˜¬ (2š¢Rv´„÷îz&éwq±~Ár.¸ýjzŽDÕÉr½~¡Êg²#f0&ðL€…p8‚ÕjIÉ BÍe$Œ“ ÀÖ-; øƒ\yÕX"‘(O=ù*šgÖë°yóæ~÷v»®Ý:òÌÓ¯‰D‰Çãæ¹eYA”DúõïÉ¢…§íÜS·,Eeí®í¬Ýµ½&¡ÁåCG@0@ýæù\ôȽœ¬('›-|KLe5€ê Ÿ½ú¡ieHªþZÅyÕÁ€ÞÆWLßР¨ ¨êÙ厱:›Ž Ý:©†Ü«PÏª× 1‹*̇(Äe½Æ$aìãI÷-+*–䖂İîW˜’–DÀ1gÊ3\5ìA^øòsíýVô!Më·¥]S½SÝþã[k+7mE‚=I¬¦õÛ²rÛ¯4ËkWço°¡x)mštå¢iõÓvÁ’eQRdi÷ßU{.BVÓÆ<ðØWDR€C:äÀˆ¤$Oêe{R¤û=J;c"úوĈ>çr¼ì$ªª‰Eyàš;À°…o?úŠ.uy% n/Šªâr8uʨªÊç æòÎ#/QZYÆý&c=ݦiZ-‰Âé€!áÔX÷mêõjå Ú4Ô5ûã{^ÀÏ~7¥r `°l÷Jº5ïÌWwþíúòӆߌ$bmF(®Ä©VÒ¯Moú·íó¦¦0cÉϧxIsÄä(WNÌœûæ›ì@(À®f`ÇQgü ößn€^QÓ§+k÷ü}Öß- ÀY:ß÷ï1=A,2°×ƒßÞøè‘:%[¢ ³ “ +]-ik6EQdôŽ,Y>Wo5*€Ž]4¼î,~^ü1s^\D^njÂ¥Àªª°{Ërœ®Œ3ÚZ4­æ³îŒÛ~:¸@tûmØÝô¶\Ãb³›€¨ÎvÆmð¸œü´l¯Ü7—ÃŽÑ9š) IDAT¢ª>Fv†‡p"1hÖQ¦§¤4Sú¸YUQŒë/_±–«G ÑgY½~v&•>?¨'jù¼X,ÊÍWgá?ÿ`³ÛSX°äÑ ªªP´ü/¼ø}ÕdfeSPØ£‡ѪmA­Ä[½Ü\]ÅPU™6QëÍÌBQE¡I³æ¦í%_º:DAH²¢âöÔ$ZBA½6$aC,’HÐ:cMZB^U}¸$ÅV*ªJ$gÜco‘™Ä¼h@õÎý¼ÿâ½µ÷m,J\Žñ÷»yûã­ÿÛ›Rá¨Ê‹ôEÁðµýxâý³Åµ¸ê¸ÃauP8EE®ê_3ÌÊt´áC;vA$œ6_ü3››çÜA4)»–˜2ãªW¨W±xëz2\Y€€Çî­|hh”ùË$‹¼×>‚ÕUx2m++k4fÙõÈïÒ¥ˆœTعuéúŽG°º pg×CÓ4ÚõíO,FU$‹›Ë…ÝíFŽéš¸h0D4XÓm§ßøK¹¹Eyùùˆ¢„ªÈT*Ñ‹Ók ¦³˜`ît#. }Údò[Ñ«íy ê4Ž6»²xÓ×Dâ!3;¹ëÈ:|¡r2\õEýxœY(Ùn§¾£XºåÛZSâ6-òÚ3õ²9Œ}ê–Z‰]UÕpyÝx²2Rô€ký‹Âþz¿çòc'±:l|óÜ»\þèd³Ë„ ‰dåå°ö·?QUU9¸­˜ý›w‘•—ƒ ‰ÈÑ8{ÖláüÛ'°ôÓŸðdg¨ò¨òÕÉÄ$ûjél½«ììL*+«Í ðÔ©r¾Ÿû6«•óÿ4#½Wù¶­»Ø¹£˜h4ƃSžIÛv¯ °5šª‘ß²›7íHé_fW¥\,gí®í¬ÛµÝ,Îj˜ßŠ·ø†“•å *:˜tË›™…¬(F¶ÜŸTŒì݆ù­ûè}DOË.dy¼ÄeÙ0ÐBŠÃjÒºcJç«p Ú\¿ÉÅ䪢˜¾Ó“‰Ë“iHµÔ´Az]ƒ¢„ÿ×,ÉjåðŽ&øLWÇáôxسº(éZô{ VéA•¦èrÄÄ÷&îIŽÅ¨,)aéDZc…Þ)îäÁƒ\óìó´éÕ+­æßætR²ÿ_ó»ìV _?>‘gσäö¹hPé§*fÜz%ìi̬ªd{d{\é¡Ì0$ TÂômŸ’d2ŠªQáRÏë¦~–‡F9™Ìý{SŠ$kÏ‘SL¿~ 6«…¸ãůobÒ˜§±Yì”Tä¯×âœs÷Þ¼kf¯BñÑ)2«VçGw¹ÄSè2™¡Ý.gëþ8íÞ´¿Ý‘Òbn¾àYþ=±•×`³:ÒÕµ3Ò_Ï-J}îYn“å‹E2¦£ki3Šo½ŸHpT#Ë mZ50¾Û"çJ¸²n]LEíõìu{X³cCŠ|ªÒ_Åå÷\Á´‰÷¡Åã¾]ò“É|Dc1¾ñD y‡ªª¬Ý±\ª>ºtªÕÙ'±Or2³±Z,,(ZZgW9ÉQ{Þ\4›U%œÓ}$+‹W×êzõêÕÏóìO/SRu’Áç°n¿þ{WÄes!‰" 2óX°u Ÿ=ó5w~ò×;•ç~z™'/žZ«mo8N¯1KsWõ<¹TøJS~¶ê`9þpçtY÷ ?Ñjܯþ¬Ò ­GUTMI fÎïs%KÖý{Æ€¦ÏÁzVØåEÕTÓ?¦Ë)%ÔŠ"#ŠÐ³ó¹ ½rSo“t3h ]Qd,R¢¶%õùÄå(ó~Ègß¿ÁmNJ*ô¤Êî-ËpíuUUÁéÎL ÀOÿl]‡Ë•Á¡}›‘åNW»·,Ó™Óö¨þÞòÿf{=l.ÞO¦Û•:ëÊ8g4gèù×ðþ£wC+?Má`µXX°j½ig];¹#® Ù…£d¥•ÕdywãðÁ¼>ã©ÚlÃâ ;M;žÙ¼!ã§½E0Ãíq—Õ:‹Ã0Ù YQÍŸ.!¯²H"‚@ ÀôÚãµÄûI¬Ý•šõ7frì;¸›œì<Ô×ÜV-Ú±akQZ™¼d±Ðªy;¶î\Ç3¿Åã/Ý•ò~¢ÆÃô'Ò(¯ ‹—ý’VÞU'©–³·d'>7‹¦wYºð:3!VÉJ4Öc12œY,Ø“òcG‘,.Ÿ6óﺋÙâÍ-©Ç÷£iЦWmÛrÖ ,9k,Š£z^ËgKg0¢ûºääÉ Ÿ³~ï¼ðí$fÏAXW¼§ÍC(êgæ·Žù{Û&( DªÍÁ.5ÕÊ¿'¶1´Ëe èp!™îÜÓ2Äq>}ôU·ÍÇcL[-š¢Ruªœ>3»íX®ë»Oì;„';Cïºc³Ò碡ì.ÚŒ;ÓKu©žQÝ·a}.šäÝÙå"r›5D‚UþÿTô,Ë2}ûõ¨6’Ÿa<çÞ»Ÿä§yŸ›AÀŠåk°X,4iÚ¶æÈÔ”ÀÚ4jIç–e…‚ÂÖ8ìuK{ìVï=0Ãj»eº=,^WD\–QTÅb¡*ä$¦¨² h<ÆøÇî§~a‡´Ã 5MÓÁkê7Ï× \VVJ†­°÷P‚>½Vâäá}Ü:cÞzy¨ŠL8P#‰Ú¹æœî 4oÜǮA–cúNªã0ÏÙk(‚ "Ç£ÿibmܤ5|ñ8ŸO{«ÝÁÑÝ»Q›Ãqñ²gÍj´:ö”³à½wÉË×ÙÊÒ#‡yò÷lûë/v®XŽd±˜u]°þ÷ßèzÞðZÝ®4Ue÷ªU§I‹4ʪuIcùîCésyi¦Ÿß4n0…Í0uÎÏÜ÷îÜ>m6ïüø’$’“á"Ž2íšQ ‰TÂ&ÓQî !õ³<\>¸;E;¤°*9™n¬$½æ –£v¤¤òYÞ<|uô$«~‘‚€E´š¿ßÖý+8·«^ÿò—7rßeoã0¼ðå ó×~[çÜ·^ûàAÞÿâÞýô ¶ì*ªõùÄ=ìÚ{h˜×IÒFVF•¾²´–.!ÃJȲÒÅ.Æó´Ûæÿ'Þs¹3ÍÀ_UAÄåÉ$§~3æÌ¸YŽêï›{FÀéÉ$ ×ì£ÓØn¬6.w&V›ƒÙÏ_Gý†-qº¼ìÚô7»6ýe²,ú÷ ԫߘ]›þÂáôÔ BÈÎð˜At]ƒvêgeêƒ ³2õ̹Ý ZŸþ1}P¡¹>õóÍšòZ6Çn³ò×ü/™2k;¦iƒ–Œ;·?‹WoLûµ­ÛµãËçP^VZëù'Ò6›¦=Ã÷RQ^†7#“†›òÖÿ¾bÜeW™þ!n09ɾM‘e222O«FÌõ-‰"ý áô>™.»™/|È/E[Èp9t  ¨ä7Ìå·Õ[ñÀ«dvh¥䆹(j*ЈÄeÍh”ËÚÝpyj†Ú&Ô.‡ƒEëw€ªêo£–Ƽ·‹Eëw ªˆ…ÍÖbWòrqç£WšlÞ_¾ÆûŸ¿Âäë¤oÁì?´'%f’$]‰Ò¥°'Úu¥¼²¿¿Êìœ%J²,óÓ‚/¹ùêûÍç%‰ÑX„Ïæ¾›&f„˜s\$Ñqª2X®gÍ|MìÒ Þ¹íGò튥\;p2O}¿Y¨·)”Säƒ G0 ËnxûNÚ6ê AOÛꘕ#LzýÚ6êžÑtÄ3#ƒX8D^~KÎ~ÙXLN¯Q’¸þÅWYðÞ[XväXQ”è:|eGòùøzú4¶jM×óF°ÓQäÛgŸdØ7ñέ7²yñBF·’Èpef"Y$B¾jì.wíì¼§>•Ò”×4M57†¢Ä9tj—"|ÔÏlÂô/&Ô D÷—lgûÁU¦³ŠËQ“QQ”8’h1åW5_%âvd0ý' ïÖˆ¥›¿¥S‹þµ2QºÜJ_¶É3A¤¤‚×-êYËW¾û†7oyœx,Æáû,6§7oyœ^x fQùz—+c7X¬V|ezVÁåýÏ(#£æ³ñXœ§<Ã}Sn1§š&Žù‹¥8=Y–yäáÎ:X(ý¹­”—9ǸýÕçiÞ¢%Ùšàu¹œ.–¬_¢ª¨ªÆ‚5+É4Ö‹ :ØQUþX¿QÀÚ0'—¶C{S¿Q“Ó‚3=SäqºXµn5ª¦Ò¸IóT&D0¤YÑ¿¯ZÎe·^ËÜgfb3ŠïF͇ª*ˆ’„¿2IJæÛ¤•@†âôdòÓì§p¸<Ø]V{M&;Q·!ŠNO&hЦëæw5£]shÄ¢aíÛ‚Ë­˜›þ"#;^º‰¬œÆÌyñF4έÛyû»_h×\O,Xµ>% ’ÌLØmVf}ý%®œÌOëCõä:j<…Óþ6;ÃSnÊt ûó²Õ;UNÁ°sغï@|•ìz9)€ «^Û·è Å竦s×Xm6ò[µaÞw_‹E©ª(ÇëÕÞŒLÊËN¡È2’TÓð¨YC7ß}ù ±XT¯ =M*J×L¼•®›tÚºPÉöº EbŒyì-2›äé>ÛídÍîƒTBÜóðëx[4Bè}5­š²xÃN\‡ 0êy]W‘¼6+ŠªšŸq9¬ßs€ùk·ƒªŒÆX·û Î€x:“ùk·ŠÆ¸æ±·hŸ¦(^VdÙg&hMeQÈŸ’4H„ʪ2"Ñ£‡]’6I‘(øð’Ç£ °Hó{,F'6]E¤¤šâdUŸ÷!Õ0ÙU¡ Adéöß5°;s×| õ éÊ6AH–+I´pIŸëØvx¡O4|¡J¢ñÙîÊ|'M¼yªúxHAø™îì:³kv— UQ°Ú¬ýe•%%æ„¡ÿú©iäµhY#ÛPUö¬)Ò‰dW,aÉÿÞG²ZÍ¿ÓT•}ë×ÒqðŽìÚÁÁm[pZüâ5z@ž˜¶ì++å‹i§dŒãr”)Œá—§>Hyíùo'’ß°#6«ƒ>çæQÏtuª¦p¼bÚ{-Ú½ V–BÑoúšóº^i &Œ}\Ž2{þ£|·â â œ¨8€×Y{.‡;Ë‹7'ë ŠMضl!z¨0¾;Ó‹Ãí23ÌEó– ]@¥Fþ¦éDRä^g; _ DT¥‘oÍ|é½´²åÿ! ðûȲLuµEQÎØ†WO4@èц{ßxš63‚C ËÅ”·_EÈkdy<,X³J«*Í‘,—}ÇŽ°¬h§AôìŠ 0éüq=ÄÊm›qÚì,]µ·ÃA–WgC2\nâŠÌ²5«X·g'ùšp²¢Ü¤õíÚÈ¡]͉Äß¹æTUF”$œî ü§ˆ†|úì*OÃi4 ª)ì5E‘™=õJò;´E²XÈnДkþ PUfìls¿ÙìN·®IÇ8¼{.Ï™Û-«ªÊáíÛ9¼}{’ÔŒsgP¼f5NçŒçpzõ}yóëoPvähŠ“K¼—,›HÌiÒN¯qpx=œøw_Úz«a#ät‘½¢Bbøec’ IäÉO~Çi·™+]«»¡ø0ª¦Á©Jî~k.|>ÝlG+ËŠ>„0þÿÕ‹®»µnzö@TUØP¼7mvRQeü)lÞ‹¥¿!®èÎ≦c·:O“Ù)Òóö¶MºÓ­õ¹"Õæ÷î<¸óì­µ-’„/¦m놴ëñe¥xtUQY°dK­Y½FDлÉrMUQòr3N#ôàëÂÑÝhÚ¤_|³RÏ&mø‡ßxÊt´±x<%ÈÆbؽºm\¾i5’$1õ­gj[ªªJiU9ÛöéɤF¹ ÿÐ ì>¸—Š?ŠÍ5è~å°Ùé?éü³‚йkæÑÜèpu¶C‚*ÛŽîdòG÷š¯y§%Æ ó9ííS_¤ZþíGwñÆu/ñ˦úZMslÝ¿§Ýͯ«¿HY: ;ævxp;¼æµÌ_û-ÝÛ øÏlH•~-’Õô}‰=‹Ãüußr^·qFëØ8cû]“TG)“áÉ2ò¯‚! É$‹rãýã°»°X¬m\‚,Ç)Ú¸§ÓmF§ûŽšŒºvVàXQ7Û”*Š>7âôdˆÍîdáÜY|ôÊ­UXíNš¶ìĶu‹8X¼‘};VqêØ>‚¾ Ô_å)V.ù‚{6PZrÏÞ¼E‰³èÇ·8qxžÌ\,V`Þ'OëÏ(Âáôв '+~B^c7s?Ô[.÷Á4<¹Äcar´ )nL9^º[ZúëŠ5ˆ¢¨$;™ H4 P«iÙ¤!‘XL¯mS”´‚¯ËY Ìhhdy=¬*ZŸÂŽDvíýÏ>¼]{•<¸W]tž¾´}Õô4I’ÌßU0ê&Íÿ™Ü¼dfe³âÏ?¨®ªdÀ ¡Ô«—ƒ¢(ü¾`™¹&Ú´-À“‘É'ï¿]û~22k1=Ù^.n‡ÕE[ÀíÒç£9íl(>„E™4u9nnyxóþÙĺÝp5Ì%‰‘åqqbí¶$¶KcåŽY½SÿoåŽ Gã j()ãâ§ÞCDnð52;´Mã—U[hÐ(—ü9,Z¿³vKãÌ2ó¤$Ÿ€€Ûí% Ѭq¾¹v'.gªq9ÝH¢ÄÌ'ÿÇ?ÌF–e‚¡ÔùS=:÷cìÈ« ¦/K§N,̾ç"zÍ3;vT ¨ ™Î,Öí_i~ú‰Y¯pA÷Ëxá§©DãÚ64 Ëå(N›‹zžúì9q’lw®.¿re‘éÌâ‡u_˜ÆåÕkçpÉ3×2º9™›¾Ž=ÿÈÀÞdÔÏcþ;o Š"»WýSëa.ÿês}^ˆHØÝnNì-Æ‘Ôì^¥O7ŸÿÎ)Åç ™U$àÐÖ-¦¬kwÑ?É>Z7ŒVkZù•–6n‘Ñ4•‡V3øa+nG ±çèÆ:©P% SU…õ{ÿ INk\vŠ"§-L¼²¶xIÊùEIâíÉOòæÍÓ¸ãÝT c ÒgÛÄ}m+ZgÊ·æ½ö1ó^ûت¢ÖÐR{pä¶ekqexPU—ÁlxêeÖ’¥ D8œ\.'ôMŠáZ±|ÍÚDg;FÊ¢…Ó a}–ÿ—s¬—ÛéähÑ ò4BQTò6 rËCwA4B‹Ø{ä07MÕ;@üV´‚LÇ<ÿµ«Xµ}+Y/V«…ËîŸÌŠ­›è>é*~o½ ;òð{³Þ«£¼“Ü®=©Ñ0–®_Ãê[iZ?u»v°píJ=Qv¯_†ÝåAUdãß°'¹PÜpŠ šç°ø‹×9¸c=OÂàȈnÍïЋ÷¦Þª÷˜DTµ†ÅKüær<Ê??ÄOï=…’×¼-ÍmvU9I~Ö› O™ÊŽåËköj:ù‰$ñå“ë–ª*³eoB¢õå“cu8Òi!Íf g*°­õŽÓ¡ÿÐ4Ç^ü̧¨&pH€9Iþefˆâ2ëöN=y¥ŸÅëváqÙ™»l—ŸÛ=Å‘%d g~†zèµ»Õb‹=ŽL6ÿ‰ÍêÄj±ñÆ÷p²ò°i <ÎL3ð–$‹ikAdù–‘$+[÷ï¥Avs²Ü¹8ìn–oWËVmÞ·ììÍ $‘1ûRVîÇå´ñà´¯Iît¥ÁY?dg¹Q…ì,7í špá¯ÉIËÚMY¹Ÿþûqí•X¹º¸Öyk.qï+6¯Æ“äH[5mÁÓo?k~Æ ^·Ã‰Íh!‰N»Ã<ßòMzs…ŸßüŽzÃÛ1åêÛô‰à‚¾ÒÒɆNxUáHùQ¦~ódê ^×™÷R(bëa½¶*&Ǹí£ûRþ;õ®TÍs›¶€Ñb.K·ÝŪâ5(šRËÿH¢…ûf_A\Ž¥ÜOB¾“£¼õótæ­üUSÌ®Wç÷¾2íÐ@sÈ©*½„äÃôÕG6£( -ÚÁ¡“{éS0„¼¬ÆÜÉóÌ[õiÊu*°U™Þ=3TÉ2x}‹ÅÆØ®ÅÛþáó7ïáÏ_fóû7/Szâÿ,úŒï?z6½Ä©IClÖšºÓíHCr¬?€…€˜0j_/ª]O¨(*EÛv±cÿ¡”€¸¼ÊG¶×ÍÚÅ©&wà¸Z…èuÇ *š¦Õ´á*+ÊÉ­ï2 É3S‚ê²S'3öDIDQdü~?£.OFfr<Îï?}ÀÈ Æ1ÿçp»Ý>¸ßPhØ}+Ô ѸÌeO¿Ï'‹‹è]ϳ¿‡õð:íæl§5»²çèIÂÑ8Íò²qج|¿b#¯}ð#Ìÿ‡fõ³ù}ÍöDö¯ËÁÊíûøiåf~_³Ûö±vÏÈÉdC±.Îp;ð…"ÜñÀ«àSqã³sˆÉJÚ$Z]Eæéb2§ÓÅ®â-ج6¾øá}ztÖÛßúð¥ü8ÿ Sr¥'o¢|5ïzæ&¬V›‘|«ù.Q™òÔ,Yþ ßüü?ä¤z9€d¸²øsÇ|Ô@Üyɶ_Épfá¶{Ø|p­i¸6\MÈ|'"3ó!³åð:>úûMŽVÂãð²ýÈ&TM%ÕŞãzÁ®U²òåʘ·îKÚ5ÒéÀLgíX@L‰¦ *KN°ëŸåIAŒR ØÂ>Ç÷ê‹úëéÓL†Ãi´Ä”“2Z§Ë@lÇ©CÌ”,ÿå¸û½WpØ\éiZ9b%‘³„Ag>>$òSÑûä7ì˜ö}c&N-'’ü¬’;Rz±x¥Ï”«X¬öoÞu  a±Y™Økèiߣr|ï!\ÚôèÈ3?àÝ;žâŠÇn3 ÛÏvÄ¢1¶mÝÅ¡ƒGÓ×rÔ•!·ý7Ù×e—\Ȫ•ëyöé×9qüäYÈô‡umv³¼ÜûÆL^ú꺷-dÂÓòìw|ÕÚÇ¿;3;»é¡÷j¨ ½÷" 6®T,àõŠ(‚ ˆÄkÄ^î½êµ ( H¯ÒA   ÷ IDATI6Ûwgæýcv'ÛR@½ï-ûû|6³³gfçœóœç÷´c1›9|2—ÃÇŽ0õ¥E>y‚ú5õR­9§O1û—ølÝj}ŒJS_|êÖcþ[¯óŸ¾§^šÈf3²ÙL’ÕÊöCûÿÔcÜ}Õµ˜¦áó뻣?ôêdÉÌæý{øvÓzC «ŠßH(Wü>nýQwíÇP#r µa †O©Š‚ÅšÈþÍ?œj„wiaŠ”>zƒa\¥cëâGµ^)­È˜×Áýw¤`xYˆW#t ×lܘ7¦L*3D+èî­Û¢Žr6/4<–1ÆÕ“/ýƒg_ý€_×íbáû+°˜%þöÓvƒ8¬Øº?BÈk¤''àWZ6¬ÅÃK‰xÉ ÆâbsºII°²rÛ:ÚQT[–;¦Øqž´ÄjhšÆ–+¨žZ—;‹Ê+¨Uµ!Gò~á­ïæñÎÊ…†‡4ú¶ÅO»> ì7¤²ïø&¾^xÑó[кqwÖÊ$ٚƪíhI 3ì*•Hf•$ÁÚPTì Y+o<0K"×–¯¦g·æøýJTÙÝü3E¼²ø2[Ô pÆŠå‹`˜¿l‹>x5À ‹±ÊVæ¾öT”ÅÞç÷±øÃ×>éF–Î^LNþ‰0Å,˜Wâ¶ëc,ÑšÀš©]½&¯öNÌ0ºŸc„dE–¾M­L_2¥RÞ²þÞ‘³ ³(E“£9¿sï¯+^£fj þ±åscþ .‘±ÈâEöèð¥'v…xÀÌaK¢„?$4ë‰^üÖDÕOÞùð² ¢`¦iL¦?ûL ì[RÓ(ŽUST… ÛV˜Ì†í+Âï5B¹JIJgÏÁM¬Ùüuj6¢V 3ë¶|¢ø5ü.V­ûŽÚ5Ìì=´W_³µ"¿.Ÿ‰ª*„F©*lØöÉIéʃ ·¯¤n­Æeö×/[¾åè}ÌØ¥ë3{¶®Ò=Ö;ô5bàMScŸ,7üe­áùøû²G8sêˆöàv…[¡ƒmýóù1½•×Íx¯Ï_¦į((ŠÊ7ë·’’˜R";ê×l}íH¾Ý°5j¼+ªÊ·¶Eµu®¸“ÉDzrRÌë”k”x!¿UÅêï¼ÍW^EqQ!N¸ÓË FWô0„’@)]¯Çͤñ·Ð¨I3vïÜÆŽ­zî×µ#²a]8™š1y M›âózXüÄ£e†|7¨Q…õûŽðÒœ—yàÕ¿süŒI²iÿ16í?†ÓãeÎ[_ðæwë9{š^ý;½ó%õªWÑC¬€gÞýмóEl=tœOÞIji|³e9ùçé9ê~cMûñÙÏ;Ù““Çãïê•ð6ì;JFíja²Zš4jYf’¹Ãi §M´&qòôqÒS«²fã :¶ë¦ë3.{HY~S˜^%!!‘}‡JøÁçèrG'Ç Á&VË ÄUŒ×paVîþ‚D9‘÷â«÷–†yýþüÆuÈ’…϶¾ÏuÝÆß .Ž&L¤‡ìÌk I¨ ®K©‰éì9¹ƒï÷|E’59Š©‰f3'öí)ý¡Šb†På#hñ -õÚ°m{–N¾/ŒdD¾8±oš¼¦$稊žxX^ây½jM¢„Ô„*Q.ÙDK |ƒ¢]\ØPX8•%%ª]«9‘{^|¢RJ_Ð+aIL@ñ)hª†IZ¸ý¼tÏÜ2Ë•îùi³n!ñúÊ$*~¯%ãg…µQr¾¡¢jW!Ëõ?o½èç2ë‘$$X+qÞ“Æy^oÅñÊ[öïÅb6sßóOñÏu«‘D‘1óX@|¬ýe-FëÖ›/7¬aó~}¬~¶v5‹>~—gÎèž©ý{Øsì¦f5yâ½7± fãõùðú|ܺ`¶aºqîCH¢ˆ×¯°jë¦@¬yøØIHNeß&}>Eñ‡) j€0Æ$Æn¹váSªªplï¾{ç9, I$$¥r0Pª7gì÷y–Ÿ0ËÖròÛà÷xXøÓZ ruÏÁ“'ÅôjXÕëÊ3›DìACصj•ž'Ah‚¡Sû¶áƒ¶¡D„ílØwŒÄa^¹õ€®ûüü´ëWƒ8¬Üv ì;_lØÍC¯ÿ“î­3ðúùcV™¿.Ô½'‡6ï uH%yUen>É –˜Vª·z—Ùo^‹$šyoÕ“äž=À–ƒ+B’[¤éƒq•{é—Õ‹_Ž® ód\6M>ï=ü¼y>ÅKÝjMXþÝcø*áá EzZ"«Vï +Íš n2(Ç›d°ãÐ=Bvür0‘ššÀŸn^‚Ãéå›•á æ¯½ùùgŠðz}|÷énº¶GŲESY»£´,ôgK>bÒ³3Ë%V{dh×þˆ•¬‚ç[‰™ó—-âøé<öƳœ-<Ó°±~׿r7òÒ×UÛsv]ò<‘qoL4’Ƀ÷) "MïÏ2Ž9{Œ­Ç¶óÕÎïŒc«÷­)Cñ3ªY†‡M…_·ì²à¾Áñ¶ðC`ùU?Më´â…Î Û«&ø™Ú ‰pìÄ~’Rظ}…o ÉI‰œ>›Ëë?ã¹×§²øgiP·9MµæËUoóé·K™øÈ$&$óÓÆÏYöÌv¬×ûpHßëY¹æ`À+¬aW¡²Öíq2ëé[Ã6 ÔQ‚:Dhbz¬ÿCßû£BÃÇ©ßç©Ð#YV[•QúC ÅÞ£ÇéÚ¦%‹îŸožœ4;ŒïÕHO+õ˜Gzü•”D+)‰ •ŽfÐI‡î¹p¹œL¿o<ÕÌŒWÁ>/V~ý9Ùºbä.Z8×ðd vª¢ I˜HMKÃír±{çvvïÜŽÕšÀ䩇;EQdéË‹q؆ºË*‰c.E&¼ {¦¾„U)ªE΂„Cn\ð:Ÿ¯×çûÌeŸÑpô $Qàø™óLXò±/Ô–ƒ9x^•ÛöáôxY¿÷¾û€~þ çÚ÷ˆJ2ÍJìÅœ<}¼”T™0¬–}¸Ä0æ…’‰`|,¯J¢5‰ü³'+Õ·BDܽT¯5o­w,Ñ•õU{¾dóÑu<½Q1u×3Üýª?*WÃë÷ðóÁèß»U”ÞéqpÇ«WŬŸ˜ŒÜÔ;ö`Þ¶ÝáóñÞì‡Bê>«Ú´!PýF1Â0¾}õEµmoAyû¡8¶s{éˆ$4‚(òÞ쇌¼’„ä”0ëîÉ}{¨Û¢%Ö¯‹Y©G$~ëOaÖÆXÇMaOÎzŽæï©TI¼Ø‹Kt»^ÅÞãÊÿ®$a¶È\ÿðݼtÏ\ü>µ3êó÷§^+ciMÅ„í$“ ¥§ É¥¡jr‚0‘³û T¢ªRe¼Q¿MY0ïy\.÷ïr^(Vnݨ— “H¡ª†Tñú|¬ÚºÉ8W')^ƒ@¸<î0^Þby`ò{,¶oӤר‹5PÛÛ„‰ªµ²gÃwÆÆw¡ÍùýPxök>}#PË51…ãvPT‡ê÷“˜’ŽÏëEUTömZE&üóeÝwêȾyëiŽíݦø«ŠŸK…¦i¸Ncž'¤¤F1³ÕÊk“&²wÍOe¶“˜–ª+&pÙK8¾[W\ß{$œû•o6ïe÷Ѽ¨çºbk鎳JDÕ‘ ƒˆtu¯ÜvI¸vî|±A_|œn/_øS_ù,Âe¢²jÛR­1< ÁÓ“)(>ÅÖƒ«ÂÁÖÀ&wy'B?KI(» KJ¯DKÅ^Å*éIÕc £Q ¾üv‡!+Ìf‘oWýöù‘cgyzþ€®Ç¿o„Wwƒw»}ÆÿvU¤Rî@’DRSXþþZšw¸²’ŠTés°](¨Ôw:Þ2ˆFuD{]-Vf¿ðXyнݱ ›Çç­¨ÖB¹Dàb_¸B}|õ¾µxCÆRYßõú=<ðÚhÃË+šÖdÒÿ %*A÷ýPBŒ—A9•dMŽ";iIéøü^Öîý®r?:PĨ}îõ¹IM)5ªªBý:῱V¨°¢*iÕQT…”¤t|>/[ù‘~ÝûðÂrœêÝŽ;V`µ$°qÇJ\HLHÑ«^Et¨òdà¾\·‰¤€aȯ(¤<· k×`Á_Æ2jÚýqC¥®çr9yxÊ42š¶ £Y ¾þç?˜òмµ4øø½¡;mâx£ÒUF\u-K_~žgŸÃO«¾%%5”Ô4Ö¯]Ïï Œ¿bµaíê0=dúñ•³_p-]Gü儘F†ï¿ç ¸ÙüŠF$"Û ý^0Ô+òü ¬-Ë(åW|ì;¼+@8‚û£ùYöá’°\ÛȼÌfí¸éšñlûe}¸WÅ}n…$œA½ª ±zï·a6w¾zï7Ñ_•,t½§OàA¸ ¯Çê}ßv%õw™–¨'´Þ´x^Å!¿-S†*#AG¨'#H$‚Ïàù‡6o û;ŠÐ(áùÌi´!ˆ"3ôä«¿.¦èL~Lf+o£¬\Ž ¾ùM“_)#Q_U6ìÿº|-˜H®’JÞáÃe¬|UYTT~XE^™8/ŒÐ¨ŠBuØóÓ–ß%‡ã·–‹%6c!ªè|¿òÛ¼e}_SÜk>]Ê—o<ŽßçÅï÷òæÜ;ÙðÕ»T©™ðZ”z•ö®ßĉCºB—˜’HÖmøüµÇðyô=öløŽ7f¥VÃfhªBá™S|üÜlV¾ÿ¼Ñƽȑݛ}ì„o}ÿ›~£¦(厑`ß³99e*"Ö¤dòÄi³a¶X˜5h@̹nX.cxÁ”@]öX IГkQ žéø&ä{É z^69ƒ²‡Ñ –—Y‰kû¡bÊ€m‡¾/sÿ¤Èó‚%N‡?ÔsU ƒ„Æç÷°ü»ÇÊõªH’À ·ýÕ ¯ŸQcž§y³Ú†¹Ád2•r<^?wO~“£ÇÎ’j0¡Š=—¡ÞãZ÷?ü>— jÉÔ²â=…"¬zU ŠÎcµDߣËãfþ²E•ö<­ÿeK…Î*"’¿«Ü¼È딵þ$X’¨_=ƒW¾Z€Çç‰ÚÌÏT¦·G'6¡J¯ßâOfÒ¸V Öíù®ÜëÆ‚ÏçåLÁ ^|k6¾€Ç¶^í ª´5Ö\Q”˜þø ø|ž27n=‘wž7?Ö£ Öl:È“/ÝGëæ±;ŠÉË?Χ߾Á´Ç¯7®ñ¿‚/×næËu› 9ùÍz}]>i6[÷Æ*—-‹‚y#‘ïËC›Uxåù§q¹œFõ«Ô´ô(â÷ì‚'£¾{öL_|òIÉ)ÌŸ55@.~$95•äÔT6¬YsMX¿fuˆŒ3³{çv~úþ»ÿ¸¾2›å²õiUåô™“ääŽ Ï ®Á²laÙKÂ>[þñ‹|ðéëäž:¶VÇ"/E@ºÞÙ›ùŸ>XZa"¦b­ðC âõ{Ø~l#‚Iàä…ã,ølš®¤ìý&섆mm9²ŽFÕ›’žTµBWÖ % e)Çs<òXèßQåCGñû,&å· zæã¿M5*_ý^ˆõLý^oÏZÄùSg ‹h¿L&½n<ÀßO3ŽU«Ý£{6qt÷&þö¼neÞ·ibÀ]ýÙ+sŒr´KgßÅ·o?l-5‹Øú#¢dÆëv’˜Z¯ÇIbJ´esÿ&päì߯¡íkùîç.‚mDŒŸ‚Ü\NìÝS.A9¸±lÒlIHäÐæMøý~}ÏU­qŽžKš‘ã¡{CJovê+Ÿ!¦ È¢Z®ÕKOR?qÍØ„'˜B²íЪ˜×J´$WjƒÀm‡Vž×ƒ'¶–VµåÀwXåÄPYå¢H¾pI(ŠZ.áÝ}Á# #ö÷¨¼>ªVM6ˆH𚃧Þy±R牂^+VIÊ‹QäÞµù3ÂüKäMÀ;¹Ö¸<}÷ίxùzËGÔH«c(üÁR¾³v)ªß8¾nï Ò’ªrÎv:æ÷S“ÓUà~Þú-‡íŽ*ÃP§+Î~Ì’LbB w¬")1UÏÑ ™Gý¯¯®ï€-,~ýA½ÿ¶êºÏ®ýø_…Çç3H‡¢–Ыh¹Ë k.Ïà VkaQª¢š¦{dSRÓðzù_&*›GqÄñ@’e#´QÅ‹•Šã¿Á\ŽòBª$I(3,ŽÃ>¤?49Žÿfy ÚEåp†nHx1ŸÅñ¯Aœ€ÄGqÄGqÄGÿ2ñGGqÄGqÄGqÄ HqÄGqÄGqÄ' qÄGqÄGqÄGqGqÄGqÄGqÄ HqÄGqÄGqÄGœ€ÄGqÄGqÄGqGqÄGqÄGqÄ HqÄGqÄGqÄGœ€Äñ QŒ?„8âˆ#Ž8âˆ#Ž8þ=ˆ åþÇ6ÁDö}{\R‚(V8âä&Ž8âˆ#Ž8âˆ#N@*$’,qãkP5ìxóíã$ä?Œt¨ŠÂ뻌}ž Ò¾_÷rÛ’d3íû…““ =°7ª¢òʎ+[­Ü6o:ª¢üO=wé"çFð|Iá¿ðyHeþ]Ög’(! bÌãÿJˆq9g@–ä2ú7n`ˆã_‹PÙp)ß1bÜŠ‚u,VA-KæxGÄÇo% ‚(Ð|`û¨5ÀUìˆ:Þb`{Lå(JqÉ¿²ö{öMÛ¶-“ Tä¥Èêß‹Žƒú†UåæYS(8™GëDUQ¸çùùxÝn&ÎZX©öÿ“a1‡+Å—uiUi"! —uie|OQUÞy›Þ®|qÊvYÊòï¥D‹M¬$d³L·¬.aÇ»euÁ"[E‘a}‡„)ÃúA6Ë\Þݳ»ðᢷð+þ°óƒD$TaˆEN~+aÔ¿Ý4 ©¬¢VÞy²dfÎ5³¨›^'j\‹¢À°Þ]âÂ6޲õá÷—ýŠªðâ}ÓÃH@äþýÚý3™zý-(j©!,»Y %¼&uê3´S7“€ÅN.,^¼o:Šª ¨*Ú¯ðú}Q÷ð¿ÑŸÂÿËwÿ3ä­C?°–ùÙÿ<DQ–hЩiL"á*Š& » AŠá1±è±ÍÈ.ŒZ|'í^ÂЙ×EyPâøc!Éf®pÕëÖþB ë%ÙÌ„çÄôL”Eše·¥ãà>Ñ$§oo¶|ó=Ý.l\Ïër3ö±iì:°žÆm3ÉÐë¿’x¼8éÕª¦˜èÖQ0U œ F]3¾×†‹™Óç‹ùûÜqx¼~ÞŸu[Á‰…Ÿ=‹¢¨1•eEQ™ýôhÌ„æbkAèÞ7ó"ÈŠN¼>/ë¶­ûlݶõx¼DAddÿa´oÙ–ÞuïÚ°>ƒ‘Íf.0Ì8vìd­›e"›e†÷Ñ ˆ_ñóò£Ïsõà‘Xd ]³:“hM#&A²ó[ȵh^ÌózbÛ²<²d¦OË^FÿEZ€#º±}náűK¢*Mƒ“NѯU_S´Õxxï®ì¢. Xdsó)î…ùw%ªª0öÆiH¿³¢ž{6Q%3/Lœ†¢*ˆb©‡BQ^»&.¯ÏGÍôª´mÜ”­Ë> ØaGCàà[ÿ £N=TM¥nµ¼ZÔñÛçOÃçò†(¼W=}éõ«pù¼Ñx6¿½W±3.ÅÿÕÐÀQlÃl‘ËþÜf‹I<Úõé†XìQ4ˆÛé¤]×1S@ØûD©tr8Šm4Ín‹×í&{@/²þˈ$ h@NþyFtoƒ(˜°˜%Üy‡O­@ˆ í¬{=œ/<}¯.ÄÌC;·¢Èî¤Ð®Ï›Âš¦•}f‘/ŒåÑ©ïdcÊìk°&„÷½ÓáÑ5É…¡K¯•VŒÁD­ºU?yx‘‰ýEƒ,„¢q½†a$%%)…žº‘š”ÂuÃG‘šœŠÝéÀ*[8tì0ÏÏzš¼³§ÖgI ‰ÛKhÝ,“ûv1¬Ï<^‹~’Ÿ·mÀéÖŸ— ×A²s©žI0ýAc¨s‡¦È•x–‚I AÕú<:ê¶F0‚ž‰X$¤of_´êGÍÔd5h¢*<;úIdÉö>ˆÅcžaÇñÄn~ÕOÕ¤*Dë&$)ZIµÈfæO¼ùo¯º·oÍOÍ,W)YAi—†V-:þ.Ü岇ɱ߃Ø<1c.¯ ³…XñÔ‹(J©‡Â šFîÙ|N|ø•>/Úw ð\!ƒdÉ̬›ïdûáìË9ʺçß oûŽœ,8k‹þÙb>Qè—Õ1&©ÿo„IhÕ>Àð¾C)´×8_t¡Ìûx~ÖÓøÿ%+À¢ôûZ8ƒoã–åx½ß—ª©ÌºjgmgéÝ¢g1 Ï„×ïeëc¡þ™}% í´#»‘>m®Ëqâ©0F±«‡ÇfÑŸâ£I }»¯ßW¹Yƒ‡“b»ׇ( Q^в¼¡ÇEAàÖ+‡0jò\>Y<—¤kôóQU–̘—û1™~ûøU0M&6™+8¿ë…Ã^:vv:4 ‘gç Œ·6§ƒA»p®¸ˆ®»—׋,™;t$GóN!6‡ƒD‹•y‚ÔÄdz¶Ébþ¤é¸¼PKfb,¥˜%3ÝZµeúcÃB¼Ê3æü[)ƒ•$—ÁóL€Çã¦v½zƱ?x€¶:„…æ›ÖYádåÃe˸{êT¤€±R”$ÆMž„ª–%söÈYÚ mgx$$YbÜÒq1#kTEeÜÒq¥k>wì9Tt[×Ý]% m¥¹‰ÅÎ"2j·@DdÉÂË“ÿ΂÷DUdIæž«¢ÄYŒËS±Q^Œa0óú=lù`݉ÅüQ@’%.{äJ(®ô‚´¢ ˆ†’rîh>O•|HÑ©óhü÷A¶˜Ë:¡ÂG ±ß‹¢`X@#?ý;Ö5"…žï÷úøÛ3/qÙ7¡øü!r»™öô’(eÑ^lÃípÒ®oD³D‹NY\3i|ìA!X5M£àd÷%k@/Ü'𦑔šZ¡ËUˆ°  ÒCVIüMm[ÌïϺMZ÷ŒÂíõïcÎ IDAT¡i9ùçÞ­ I¤Zj>¿bœï”—ui…( Ø]†vizpú-Xe3ÕRqyü|òØx<[á}y½~žzäï,xa, _º‹é Úd7ÒÇ–¦a/q!ˆBo0U°8¶j׿/zAMMO¬Ô³’D›ÝƘ+nw€´õ¿ \@@¶Îø0ýºö\(ºÀ…âB$A  MÓ(q”€6îÚ‘ÜcàÙšBÔÊ^pÖoßÈÂÅ"Ë•Ÿ×²ÄûË&qè×Ó¸\^œ|O½†ÄDËoƒ¢(0¨;ÔSY l3“)<Œª}Ãv˜E³q¬}½ÝÞ-toc¿Ì¾œ¸pMÓ¨šT%@Zz²Ýñ•ËÜ™ py<±çm×6a„Þ"KQó%–6tA@6—¾¯ì¬, RU•;&ND’¤@?Ç>7Ô»ár8i•e ¯ÇÀáÃ8›Ÿî-Ö4¤ˆ\šÔôtì!ÑŠßÏÑC‡=nœAJ"õÓúí꣡áu–Fݸíî0]$TqU.êÆ$˜hÙ§%çOœù¹™ŠÇ^ÿ¬aˆ‚ˆËãÄæ(âž«BD44.ØÎéòLõcsãW|¤'U-—È”ýìÓcBþçË”r)o°ê•¤ÔLãí[ž½ôÅVP<~–Žz‚ÍëþaŠåÿñ˜ÿâ]$§$¢”ˆ¢€¢¨hÚ”€âùê?¦£ø-ÌñÏz¸’(Щg&“æÜ + —e‚bàˆŽ ¾¢ÔBôÈswD]?x,ø¡WvAˆÈ;hÛ-<6;//‡›fÜP}aäA·L™ÐT ŸÛƒÇéÂíp¡S¡ŠÀ^M ±>˜ !pï’Ç©^¯6²ÕJ»>ÝIO¯ÎS·Ý‡Ïã-—xdŽèg3É"“9¢ß%‡àya öc­®@«+TH€Ê&Ù…%ºÒØ´»žÐo5K¼?ëvÒ’¬ ïÖ†éÉ1Ï/% ­±˜%ŒG0òŸþ¹†´d+US’(²;éÔ¢!ÖJä ^Q¡ƒ çtÏÖ5£{âõúÑ4HMKMÃw‘ý©xe†gU&~WÏÿJîé“,žùÁ¡vÅ€á`ÓÀ¬/°=;tG6Ë8ðé²Ï±È26G %;h‹…¯–}ŽëÄ9¾xë+œ¿Ïlxß!ȲŒÓåâÐw;9”s8ìæM~„/_ý;œÓÉI¡­¨²ŽC©9w¾Äx^jRbwãt†+ËÅ…ɲDçM*}~‚œÀ´Ëàø¹ãQ¡`›žÜLιœ0o`ä}toÖçO’Y7“ôÄtƒ€ô xQÔ€2o2 ôÏì‹ÍU‚Í¥‡ÇãwœÍeƒrÌGûŽäòÄ”q¤&%òñ³³ñ‡ä–ÚìˆíõpÃá½»JTdîH´Xd3OLÇÏ;ö¢iàó—PBßGk Uc,ê•_oDA,׫­EÈM¤W¡ü«Da‘‹)r÷mâóyi“Ùå’B±$ÉÌø[fñ/^¥[§ÁŒ2€6™]*ôtx<ÎrÃOq¡øÔ±tluhÖ’ä„DƸšGnÏÑÓ§×É=›Ï;3C–̈‚@Çf-éÐ<3º¸‚ bs8h—ÑŒvÍèØ<·×ËØ¡#™|íhì§O’’ˆÓí{Ùy ý»µ1Ä“Œ¦QÝø•WæŒ3ŽìÚA0…ÉæY¾†Ÿ<‰¢ª´h\MÛ‚R†—à×oŸÇ¯¨FÑ’Ð{yã±»PB>‹I2Ý.$³™ÆÍšÑ¸Y³˜ýܶCi†šºmÃZµodŽT¢•¨¬ä=2Ãï÷‡Œm “`Š9.½N/Š_¡A»a¤$ÿ`>W̸9A&£s30ôA*5n4U£Ã8¸æà%ÏÏ~YÃBưSL~±þC¿óU>_ÿ!©Ié1ÛÜñŠ@…µÒg˜Ù4)úÄK((Uí-ÔÃã™_l´Jèù周˜$²£‚U¯IDõ«ÈÉÑ.o+‰ÑÖð~zw.5ZÔåðêÝøœ|NÏogOe¬Š,4•ý<–»9V’•¦iœÉ+doÁû¤UI6<:ùØÅöü¯yôùq,û|&/ûžŒÁåô0óžóºõmÃä‡uÒwhƒÈ ¾¢+5kWAÓv0óžÛ¢®?óžÛuE±Ok&Î¼Ž›ÆEUµˆŽÂQñº=Ü2g*yy9aV€äô4Ì™1³¦0fÖ$ÙŒÙl&!% §­4d«€Â=M¥z½:lüb%% ‘dYßW¤_wìŽ"’ÒR(Kgm}Õ ZŽè‹55‰[?û+~—6£ÂsZ]1€V#Tè5ÑǬÂÍÿX‚ª(<îÛ£·?\¯ÞÕæš!1 H¬cZš„ 0† %Nò/ØÑM·v•§¢—8ÝB®²›ÕûìË{ɨ] A0áöúÙv(—ô”ÄKš'ó§}ÀÜçnÆ,‹¤¦'b–%î~`„AŠì%n5­‰ ˜e‰)³¯¡f]]@VMH‰j3½JR¹¿Í°n "Ãû¡ÐV„ÕRJλö釯¤gNÞÃ.üpI”ðÚŠ°Èžšù4wϼY¶ "ö@˜•×V¤{9C¼÷ !Œì?Œœ“94ÏÊÂb÷n;qŒ^z€\ªPü–0@EQ¨^5ú™<>禋jÇåò²àé`^]J… o¯æ=°{챕]3)Ñ,šéÕ¢'ßí^‰¢*x/¢ b‘,ØÜ¶¨|¿â§_f_TM%5Øï•dkV‹Œ$‰œ½P„¢ª\ íô)J`}ѯwÁVâ1a !oAAaqI”E×j‘ùàëÕôêÐ6LQ³Èf¾|q>wŽº1Jž÷lÙ»L9|Áÿûµ@vãŽI‹$Ü}[õjk@›A˜EsY4ˆf Ñ4ôó‹!,’Ų̀IwS¥V0¹_!‘Ìff6`Ô¤»Ë,®* cf>Pn[Áçäö8éÓãrª¤×¸¤P,MÓ°;m¤$§ãñ¸ðx\aíÇòtȲ…‘—ÝJ~ÁÉJ]cö-ã¨W½ô9+X9ìœ>ŽAºŸ‰ˆ?.¥gˆý»ŠvvèÂÀ]p{½ÙK(r”àp¹%3ý²;é^§ƒ~Ù f‰ZÕÒ(q¸py¼¼½pç.”ðäý£©_»¹§Ïá D' éÙUÕèߥ5ëÕ`Æ]WS»z:UR“xòþÑpü V™‡ïºš”$+²YâõGï"59¦Më1ï¾ëñxý¼¹à/ôÈÖÇÀÒyæÀ±ÓL{9ɉÖäPâî©*·ËEFóæd4o“¨¶íØÁÈ« .|þ€Þ°mÃz šE"ZòE¤@¨ÕgïÀÍwÝEÕêըߨ1&Lx=e뇊_ #%‚Y ZC=ç8£sMº4Á’háæÅ7“0ŧT@ÔšuoÆŽ/v`Iúc“ÂwÛÊÃKÿÌîc[ñú£ã×Í£mF,‰ ê82¤ñðõ Gë¬Ø°ç¢¯¯( ;ÿYºMBŸNݰZ¬,zø±°> ΃ÝzŨv(–Ûþ£÷=HZJ*Ù­ÚÒ¶Ef¹Þ:!Ø5[ÖÑ$î8§»Ç‚d¡NÛFaÖ°Ksn6÷`ÌòIH²„$K4зÍÓ}¦*ª^ë39ÍŠ…ª*LøËㆳX¸÷žÇQU¥Œ* ¥‚îÕ—V‡µJJTUaÑ3Ÿ“™Ù“IˆøLeÏN-*NQÓ4Îsúäx=¾@ÒU¹E¶˜)*´S\hG¶š±:Êû!¾Üò »Î½Ãñ#ùÈ7ŸÛåavÀ«‘s8·_zŒ=…{xoÅ\@ UûƨªŠ¦mç¶{/7”— «ß¦uv=´£MvFLwkp“AMÓpÛpÛa![A$¥§â(¶áu{ðº=hš†ßç§ÃÀ>8KìQù$íûõ`óªïé:|çNÆçõá´• Š"‚(–i…$‘Œ¾©›ix=öè@¬L®úëlö~²’3îBNÒôV#uÓºŽ¿Ž‘ÏMç¶/_A $É"së?_Dõ+¨~…O?ˆÏ©/r ¤6´»î2®xþaÎÎA¸y%‹ÌÈç¦ó±¦‘\³Z%­y"ûsóy/I¬µš%¾zéA6íÏáB‰ÃÃÒˆv?üriQoТ8§YÝêL{õ3Ü^?-ÔdÔ#¯“ ›c&ù†"­JRì´ÐËéÅd2Qbs‘Ù¶AØçö]{µDM iäŸ*¤{½Oþ|Ç ,xa,fYÂ,KÌ}îf^}îërs:v×­g&Ô®^‹‚ çt=68 ¤@ž(áu” †Œ`ކ×Q(²1¼ïh¥M†ûoŸHQî)†õŠÍaÔ·ºÎ¯#‚6»îÙ]èžÝ¥LA+l2ÜëgÎã³ü• a‰ã÷Ï} RM—cnx€÷–NŠ™ˆ®¨ SGL¡SFGdQ޽à$‰1Ū(ŒÈÆ>$Ñ’Èžú¢Õ»e/N]È3B´BÑ»eOš×nÆŸ—M M½ÖØÜ%FÛåq‘úµª# 2›áp¹)*±Ó·s{DQ?f2™àèaZ4®Ï× Ãéö²hß‘\žœ2>Ê;SiV5~Þ±‡›F ;OU5 ‹©Y5ܲ(˜L h£86=¾€'Æ<ƒª*L>…úU`‘,Ô¯Ú€¯œÁ¬?Í¥vZm¼ïk¨šÊ˜>·reçkè“ÙOï'=`¹üøþO¹{Ⱦ˜þ-×õ¸Ÿâã—g0~ÐÝxý^~œ»žŸÝ€×ïᛇW!‰fžó,[#P®·õÌYZuïŒI0„¤zýº »ãf†Þv²5\qòû|<þÍ߸iÆähZ ¨H­F ¸¦lù,ˆtí8ôÔjlßµ»Ã†ùw¨`UÖïõt<5÷c¼^.—Á$ (þ²‡¹\d/Áb–‘%3ãF\Íá“'ð+Šê´Ó?»£1f<~7/œMbÝRYXf%+M'3·‹Ã'sy}Æ<=$(Î4ð:íôÏ ' ^ŸŸüõoPlsòó»²}ß1L&ÃzgÑ©uneóïֽ⽳hP»=³[°pòM¬{÷QNäŸÇît3¨{[.ë7W ìÌÌ?_C½Zº‘êÔÙ ,˜t#…çŠéØ*€]³vÝëtjÁžÃ'hT·:3Æ_Ź-oÅ|„v[‰¾”’šŠIPU•çÞ\F`[G$ž[,n¾ë®À:RÂ÷_}Eízõ£HDjzz) 1bž+( m‡!®È‰ä*É(~¯Ë‹ ¤TOASž”´‚²’ÒQ Ó58¸ö ~oåòcµUì(DŠ ’ áó{÷øôgâò8ùbý‡tnÑ+l]«_½µªÔÃî*¡^õ†¥[Â[fsÕwP­ZmdÙJÓ&mÂί]»!M›´EÓT²³Jé+GÞNNÎñ¨I!J"šª±³à03ŸKB¢…~—ec‹,]¬éÊ€‰ŒuiR­.rH™·¼ÜsL§OhÙbƉ³,‘ݵ9.œtëÛ†å_Ífû™]¸œŠ8Å{+ær†<úÉÂ^âbä ½Ã¨`ÎG»¾z|´Y–ypéòŽä Z‡‡ÙXypéóä9Æñ}‡ÐT“‡2ô¶0 &ì>[@”5ù..¿ëV6ó½áúÜûóf¦.]LÞ‘œ(—hÒ»êW8¶f+iõkë÷•”@Ì 6ÎÆëpéû‹<¾¯CfE¹y´¹f^‡ ·ÍóB±Ad4UãøºíŒxj*ΠŘ†Ý³PÑèuß-ÿy®BÉ5«ÄKÓ4Ü6;˜ðʇÔíк|àt{‘D‘Fµ«†iešn*¥Øáâ¼ÍIÕ<`µ«ÔD+¯OÍmWL-µ†{ü~g.äë‰ çBöÜ †±˜L&œîØ!lAb0÷þw£”gÉ,ÄDÓÀawÓ±GóG‰R©Ç&%-„D™i]ËGoþDÁ™bãÄâ6™LaDß,KÌyv Uª&c·éýåñz¹âîëøî§UºÄæ¡vZe*ŠÂ·kVbNI3ŽÙŠÖwhLÂPX\ˆ$IXÌ2Ãú [`Ñ+„e‹ß†Œ+ç‘>ë+úwíCì®a‚?Ôô\†zˆL&¢¼ç/”D,ˆ—ždZ­jJŒöJÑ)£#OÜü4GÎů(ˆ¢Äñs¹Ü?|2¯Ýù27,¼¯¿tlø&$Aä\ÉyR­)ì>¹USAÓ°¹mônÕ/zœ É–dÒSYxý#;^Iõ”—!·ÜÀƯVè$w横ÊÕ÷ŽGUL‚@Ë®qÙe†J¨ªÂÆ­+ùpé.~Ù·Mý}ÊèׯÓ$äz¿Ez:5h¶Þßyó誂9F•·‚“¹c¿ž:E–Ò’’ù5ï$¯Ï˜G¢Å‚×a=U5¢ò<=t+;÷¡°ÄAÇÖz¸•)PÔBP5¢]gjߣ-[g`w¹),qàr{õ*‹š¦—ŠíN÷hÇÉ3Þ; ·ÇGq‰“!=ÛawºèØZ' —÷ëçŠèßµ5¹ùçÒ³.·×0ÊEA$&êëDRJ2))úªÕ¨Á©Ü\–½ðW¼žè…Yïãî©1¼mšf¬WŠ¢D=WI–¸òá+é9¦'’EŠJOªšFF’ª&áqz¢Dˆª¨Üµü®˜IéA]`ë'[iÚ­i ¬´“I6î%2é=˜$þä´‡pyõ~t{]úÞy^'K§~Ž9¢Ü®¢*l=ô3Wô¸1LGõ)~#?J$L±bÃz´@‚œÈ‚;^F%RÓCd°…F–% ݲ:òíZÝøÞ½Cg..·‹#¹9ÌŸüa\èßµ'%rŒª…ß= ‡ÓI­j5hT·>~¿ŸggÌ [ûŽlÝø3Ž›À3K_ÂíõÞ±²C°Lúĉ$ ›—ÿ@‹ÁYôŸr%Ž %¼sË"F/›¨w )<ôÅcw3oÝrŸÖqOÝ6™i¯/6öù-sS©%Çï÷Ñ´i[<^7uëf0 ÿ5T­Z I”È?SºH¶kÛ=L¸&X“uõ]Xd+ùù¹d·ï…×ëæí77ëĤE6‹Q(µÚ''§1ù¾§ukÒ÷±yë÷a÷åóùéwY¶ž°/‰MÓµJRd«™ÙÏÞÎ5cú‘ˆQJãÿ¿õâv_ø…"m{¶Õ-ÛIIHf¼dui†5ÁB¿Ë:àÆCv׿;uoFÓêõxrÆ;aê¼¼n™3Kb‚¡(;ì6Ã;ÑqH¿0+Y»¾Ý)ÈËCDÜ'Ii)¼rÿ# ¼z‚ aXi$qô—}tÒ-$ìëÝyÏ¡¨JÔâšw$‡i/ŽêÛÌËõëWm\±Ï½ˆLg÷ýÊø/–ã IÐ=wõÌ­t ž(â*Ô Hë+¢ø|’Háñ<Ï€§ØŽÛf§Õ•qaÃUXŒ;@~Q¤Õ•Ã̺‚$áÆA£žÙÔÌluï~Eå£Gî ?æa±1™HKNÀçWÂ”ØÆ½ÚñÜ=Âæt3fPêWOÇ*›Ev'ÍZ5¢øÌ|+—àòú¡*ŽÊšI bpäàiL€5AÆïS˜{ÿ»<ñÒí$&ÊÔ¨•Æ¡½'£~Cjˆ!5=MƒÜœ‚R²! ø|Š‘_d6‹ÔmX-dܘ8sºˆA—g³÷‰p/aa€è9ì=h–ÝAHN׉¾ÏëeôÌûË$D.—ÝPŒ"5»XÉá#†Œáº«þRf⸢úÑ4êÕê°eÇÆ5İҭ¢¡”‚f„lÕ­“ÈCÑ•ª– áô¸)©„¥i¢ ¢ª*ŸoXÕ=û•éUsæàÝó% ¯ÃeèhV·7/œ­‡0–E<Ê€ÇëgÊo“Ù¤E¶€B:~Ñ(*qàñøHIJ«»Ó`(.qâWTÒ¡·Å%lv§ž+b2q<ïï<1Ù,! B WJáüú7hT§:¸<¸<> ‹ËߣÄëñà÷ùøü£¸gú4L˜øôý÷yþ·Ù³C÷.yçmn›0»Ð¤E ¼IÉÉ4lÚ·ÛM‹6møuÿ@y´!95%Ìó*»M‚@‹Ö­Ù¶!šäjš†ÛîÆc÷ *ªA =Ó„fU«y-à 3ÊõrÙt„Σ:£©m·1®1ý†éÜóþ=H²–Ø. ×öKíªõY±r7[ôâHÞÔ€—¯s‹ÞØ…e5ÍQ„ÿó R«J]Úet¢Jо¾~ºî®ìq£¡Ë.ü`:IÖã\'Çò±tê1÷éÛ¹;fÉÌåý‡ðÕ«tݸE+#’Eï²ÙÌÔ;ÿBΩƒ#øü¾°91 wfÝ3…Ï.À«>œs”e Ó³Sºüé2Ö©Kjr²Ö%–‘b ¯2„¹Û‹ßícä‚›ñ* 8ΗDM@“(P/«1[7üˆã| Ͷ3:Κš„ƒøê^I’æFÎjßÁdÂãvѬi;²Ú÷Ä$H’Œ,[éÕc8]:À,Ɇ ìÖu0çΟFÓ aÃ{u8t‹þÌÝw=ÆkK£QÃŒ¹i sYΡC»efç®uQ¥Ñúõìˆ[ÖAAÂB›v}†d#›%|>?/½;•_‹O¼Å…r òqh«ÙgÓöÎ8/ðî—s¸õªyxÜ>~->É[ŸÍ&1É‚( º—ÅéÔ=0ÅX!¶•Êi+ c™¢(R|î<¿¬ÙHFÛLn˜~¯ÑŸíûv§¤°³EfÚò%=Çám¿$élî)>9€1ý{²èûÏ bıÝûIÒõ=F´ÑZÖГa[uÈyNêÉm’„Ëk£„¬i)ÌÈ]3˼ ¿ÇÙ}¿r×} ¸æ•¹†¢UtXú#ãþ´8"…‘ŽReŠ ¹ñ¸}hÚ7œ>©{iN?Ççëæ„}þÔÓw’Xö½ ‚ÀȺ«øÛ5+Ãc‰_L¸ÉdBNM/³Œ®$J¨ªF¡­ÈÅ ’ŽPE5½i]xb¯EQYñý.®»¦BX‚RJ$§Ô=SçmaùUš¦!£®ìƧŸë¤S‡&øB*ÎU&9èeÒA IDAT=H#CìA SFGVîY¥[ÇT…dK2¢ !Š’®šLTIª¢ïÌxž«öüÀÉç‘Sp¼L0–L~`ødœ>HιãÈ’Ì?.#¿è ÇÏårMç«Ê´”׬šNõ€2ëp¹øòÅù<ðÌ+¸½^.7n¯—ûŸz™ëãözyà™WødñÜ(Ï¡ èsÀç÷ãóûéÙŒFuj2cÜM,œ<ŽQ“çâñú¢¼¢(ЫC[>øú‡°ÏA¤eÝLœ6— Y4Ó®aMk7å¡«fâô8qzœôÉì‹Y” ÃSÐRnsÙŒ÷zÈ›â¦W¹›Ë¦Ë­@ÎII ‰?xNð}í,]šê8UQØúÝÜ:gºaV…Q“ôœ–³ñº=`2Q;£a˜§ ­z5þ4å/ü홿аuKª×«cìß”R%K \±Ëî fƒúìùyYzc’RSŒ¹á(²±dÊ2Ã`JìE`‚¤Äã™H’™ªé5ùÓ#R`ÍÕ"öҭݪª²m×î3ƒ7ß[Hó&í¢òJ‚ž‘úuuoI^¾>–;g÷cï-†‚œ‡¡áS6§ƒþYú~~EA’Ä2þ$A lBXV¸˜JZR2’JìWä”Ô°M c_CàÃg'qøøé°õ×íññêœñ$Xeº¶k § øð™ûÈ=}ŽWž¹Ìæ ¸ñçôïÍ3Æ»( éÙŽÜÓáUEõ\7ÙÌá#§˜?ñzþ¾xJ%öàÑ…Üݺ"˜”Ä’ 8rðî‘jÚ”3yy(ŠB§= O‰ËédÖ½÷²ðå/I¯;q<Á$ЩGíÛ§{hCŸ€\$UMBS4®=HçQ SS4v~µ“…{òò˜—ñy|!kIxŸ·iÓ¦ÂPª ÞÖz`kD³HÓnM9°æ¶³6]¯ )I¯¨~>Z½”ÓkNðÝÖO¸¢Ç¬Ý½EÓ¯]#½ 6g –Šs8MaþՌèv!sj¦×ᓵï`2 Âb ë¯~Y—é28½J bŽÃ>»#Ë2’(â „ÆEz&L˜P5M¿Ž$QTbC6˼üè“áët•ª\(*à¾y3)Ùq„%o¿A~ÁYìÝ@5qÞLìN'_ý¸ŠÙèÔ6‹¶-2Ãrêˆê/õ\$“†œhá—O7¢úDY*wr ‚@³~mùå³8 í´ØÑ,Ò¤w+~ùl#*»mIˆY8(äªW«ÓQL*kWÛ8>Ÿ{1ª¦pâÄaRRÒ±Ù.pâä¯té<€7^ûšüü\<^75kÖãÀ{&E%>üx kII©ÔªYŸ›Wbg½Ï=Īïÿ†Çã"11…CªQ«–'š˜˜ÌÎ]?£EL’ྫ™å/Í¢¤ØÉ Ë;ë Š(С[ šÖ¨Ç€ºP,v”æ^üZ|’·ÿ©“ŠŽ3†%VÓ Øá@A!½j2&“ Q)v8 Å0”x”‡Ýk6û„z#žþáW‰>xR«VÁdØùýZª×¯‹ÓVè- œÌ#­z5Jì…1ï!ï\Ó–/A ÙQŒØƒ›š„¶È[äd>û o½ŠF :âÀIBÕ4< ?ÐeܵT­¡/Î’EfüËqS‚¦iœ¤„VW ÀsŒ»¿x‡6£†à*´¡* G÷n¦Jýº\óòýM&\”=z$Ž qhÉ]Iu¤±oõ6†vnbaÒ͵sÞ`õÎCÜ:´Oýùjöú3?|<—Njϯ ?¾<¹gÊ y½`sAåH¼×ãgÿ/¹œÈ9gx!E5Æ9 @‹ a #’t(ŠŠ1'Oø Œ½0$IÀf wçÛ5W…á–’(A |³veűðƦ–n‡+Æ^Nâÿ±wÞáQ”ëßÿLÙ¾Ù$tB/¡KéGD¥ŠˆÇ†Ø+Š¢€  ˆA±bEP¤-ôP¤Cz²»SÞ?fw²›Ý„€žóžó;û½®½’}æ™Ý™§Üß»¶¬–K ˜˜Øm6æ~:Ÿ‚gÊX壹óHøC6«²b2.„¸UçèÈ!ç·èxE˜\ͤ ¼õúÝa}»óϧic# AAad`¢ÇÇò]+¸éÝ[©[©vg¸†ß§ø¿àMT©GóšÍhšÔ„3¹g(ð×ñ¢º×h,Þ¶„:•j›Ä"« ‹m™ÛÙvd»©©zUêñÐç#ð)¾°ç+KCõá©IÓñúýhšÎîƒG"Æ” ¸vU5¿Ë™€õò¹{o5kzDºO©œ÷Þ‰§REú»“¹ïN7´Ìš‚€ÅjåÊ~×r -¯^{“!cFF¸ÉhºÆæ´•8ìnÜîxš4jkz"tl× ‡Ãe~ÿ[þþ0óf&–îáµk6"'/‹ YgÐuìœstëÔsNѦer»eÛjš$· B™3çþàú>÷`µÚÃÈAÆÑL*Å'„¹OåäsUkãÜx—«ÓÍïi[JÍ:Ž¢‘ Q¹ªu;r òkœ‡ó¹9\‘Ü”žßͧϼ¡`PUròŒÚNY9ùØ,2û2Oðí¤G9q滕ß>y‰CÇN“W`¸i倢âvÚóÙy¬Ú¼Ûi~מ›säÄYl9"Á‹ªi¬Ú¼ÛРÿ¶ —£|ÖAïŸ×Ë¡½ûøjútnºû.l6›ùŒã‹Ýª×¨Áæõ)d;Y$¨]þ fÇ* WW1‘‘G4Ucײ]ø‹üYV3çdhƬP/«ÓÊà·G­"J"µ®¨…7ß‹(‰´¿±=Ù'³ÑT‚ì²OfÓ}H÷pò’oX ¢Y5Œ¼Lz¹êO)Š·#Žôƒ©|½l<•èÖâNg@ÕU²ëX¼+‘ #pÿÊ&=øüÙE¼þÍ3øüÞ¨²˜$Iää墨 Éuë›k²ÇUœ‰L’$ ‹ ©_«6‰žxs[±a-ÝóO\'Ó^›Èðá÷RXT¼ïǵi€Ýf¸‡ÇKoÙ¹†uêqu§nÜ>`š®…gÊ ó£Ü÷﹚Éÿ|–/ò×’{*+Ì­¦Lî,K¨ŠÈ~`äbnù÷Nd=‹^  }€5Z×§yÿ¥>”/?Ûˆ¢ø¨UËð]Ï/È%..‘»îxŠÕk~ÆfwârƱ>åWÜn¸—U„ŽéÛû>ˆ'3qØ]lÜ´ «ÅƮݩ<üàXÒÒÖpM¯›ñù½¤¦®3‰ßÁðOvräÈÞR‹åè:œæ_/}•mê±fÙv#-oà!ŸÉÊ£S³z\õЛ!µA\n§éC´zœ ¸a¹ì6†ôîĨ»úb+ÃÅGÓ46­Ýn”ñ)̘²„šu*…m­æ ¸¼ˆ’ˆ¢¨hªÆæõûèÙ'â=Ψ1qn;>¿‚ËiÃj•©V5¿_E ÁtaÓ͹ûÅôG¸¾_{2žÝH½ûù´á(ªF·ÎMXýk µkUböëÂ4÷¡Ïrýþ 8­N’“ø}Ϫ_Y!Dà‘¾-{³`ëÏhšFVA¶)ôFjÛ4m[B¿V}@,އÒt5b}S5…‰Iˆdd… V3ç/5¿G~a!S¿ûŽÃ_ŠjÁò+ª™ã?h½˜0þyvì?\*Á¶Ê2HFv­’Ä&ô.IÉš­;xø¶È’„_ñ1eÑ[Ô­R?L˜EÑ$ A F™BY «†ª©„Îö8Gë2Ö„iåKÛÑrI–-†Žy‘7îyˆŠIÕðÒ#+>?ÒÒiÕ³»©([8ý3*תÁá»9uøWtïLQ~ˆ{ˆ,stábW”ŸÍn竱“ßAÄçõ¢)*•kÕàTæ1Q$®B"ùÙ9šaMÓ(,ÊÇép!ŠM’ á¡ƒŸgá/_âõ"Ën¾á!òò³‹IY@èR?M¶æô™cT®”„ªi8n¾šõ¶)  YðYì=°¶­z ˆ"ÇND%*$T)]ñâ>´b%׬ŌEó ±˶nDÓ4ãÙ‰1`u¹Íkï¸c•$F•sƒ€x?×<2„ß&Ìýo¼BÇ&ÍÉ)ÈG‡R•Ë7ì$Þí¤JS_FÍj±Z䀛•ʪÔÝæDE‘«¯lÎùì<4Mã÷M»¨Q%Ÿ_aéºíÄW¯d!§ÃÊ´Ñ÷Q³ZE3釪i¨ªñZ±a'.§Í»±yçA4Ms õ( Z=Že6Ÿ;õ>BSjÏxg |;‹›ÝQ渾ÙQUÃrtý›ñÎ}1’_,íûœ—ç Y$RL%¾Z<‚$˜¤$b¬øúäÐèÓSh~Ms ² ÌøÞ’Dhì QÉK¸ÒB¢F¥:Ì]óc„U'TÃæÈ–›ô`Áúï(ô°u ©kúfæLý2t[1UEy<ôÎMŒ:•×_y–{V™ë ¢ú£¬_]ÛvdእÌ]ºˆ ñ†ÂîíϦ1òÁÇhѨ)ªªòò#OñÐ+Ïáq¹Iˆó¾w7’(Q£juò 8yö4S¿ùŒuêâ°ÛCœ>|üüûRútÌ"êrºˆó˜\¢GûNÑ ˆd•©Õ¾Ûw§”üÍ—>¸.’±'ùê+¬2 º7ãÆÉøꉥ¶-,Ê''7‹¦Û2íƒeL|ëQ>ûb?v{xæ±×'Íåõ‰?â÷«œ:͉“ß÷Ñ«0æ9ìßþ›VÁÆ““8rôŒéš$.ë6dðòó73øænÜóÀû|1ýÖ¬ßÃsS¸²}rDö-U3â <ö8Òî€R6³g<{Nì1,y¾B4]#« =ðY¨ß²1FU1ê}¸%âìnrŠrK%µéGwàqÄ™¤&”„…ÿ“g/ñpCÒw‹–s{?c“šýËJFþs0/¾4‘ý™ÇQUsY9çåqÛ ûøpÖwåsÕ•$îxîõ0BÍÒ$ ~Åq¿ƒk¥ª©øg¼™á¦ÈWȺŒ5Ä9â̶qŽ8Öf¬Æ¯*$8°[ìa×T5ÃC4…Õn34ÎE^Q ûìY–}ý=}‡Ý…;1W¼‡´«Ã´ÄÁõÍæt˜¶ÿ¾Ö°W¼Qô5ÌJ0ˆŒ lYº‚<ñ©KWðíøÉ|°ñ7ÓmKS5Ãí+ *ULB’dÎ_8°Øå™d#軿hé×Q­INgi;Ö²`ÉT«\Ÿ¯ˆÙ?M5?¦ûü^†~ž¹?Ï0û ’’òf Žc‡FMx™zÕ â³rÛ\áÉ«øùçøQ4 dÂ’îXJI uÕ Æ3UðÄ“—›ÇåFÕ4݆ìB^ôù³bãNdYâþÑ3lVnºîJS ëF|!n'K×§ïv2¨Wæ-Û„OQ9{!—ä¾Ó fUê׬ʃO¿‹Ï¯ ª~E#'·k»\$ŠÄ»8rŠ_ÖmÇi·âõùp9l¬Ø¸ ·ÓÎò ;ÃÈGã-Ì{¦ª†ŒRÒż]—Îl^¿>LÎËÍÅï÷ññäɆrÖn¿dñXæajÕ©KbÅŠ&éE‘wǽŽâ/ž¿Î€{p(õ˜‘,鿤£úU3ŽAåD\·Q·F¬X¹"@î•‹W\å8Ð1‚Ù£(R.F>‚J‹þnæ©iCpGIeï°9±ZlÜ?àiÖ½›IÛäN¤î]k<M-¾×¹áš&Eõ›d¯K‹^|<}‚ †¥‡]Çn''/úµj³qûV6¥§1°WsNõéÞ“ïÏgäÛ¯3iÆT,²Ìžƒû+5%ÁÇ®¼v9œanYª¦±aÛ<îÈß9xÀ ­\f’Ùî¥QituKòÏdslóó7;$ºRÆ ¯3a-Œ€h „‚îÊÆfX³mlnWÜp%+Þþ‰ùÏ}Á]3Ÿˆš¥Àáp±jõÜqñ4kÚžC‡v“PÙ0ÛW¬FjêrÞûàù€0f£~ýfLzûi’’êñýœ6ô%~˜ó!V«MUqºâØžn¬Yß¿G•Ê5Ñ4 »ÝenHYYgéÜÉð¯Û·;:ôb÷ž-“Œ`lA0H€ÇéD¶H€1PkÖ­ÂòE›ÑT Wœƒm÷áG1û•¿lÅC€\¨ZÁðyýlÛ´VîùûêÆ'ELUÕhß„Ûú¼Œ[KJ)¤E-:H›ÓÁöUë{ñæ¦øý¤¯J!.`– ýþº¦qæÈqæ¾û1û6o§j¥šfPd(Nï>ÈÎy¿qþÀQfÞ÷(šª¡)R`SlØ«³&>ËÒoªí[gΧU¯¾¢€.Œk»*'r$e*>Àfu!aåHÊ6œÄ£„#8»?“Ÿƒ#!kœ‹”9ßòÅÐáx 2h\‹¼B/j~g²rA–¸ãšüq.›8§o—¥r&+×R‚€(ŠÊ©¬\*'Äqð³Lzö:5«Ç®Ì“X-²¡­ ±2vnVŸžš‚7 ÉLt;Ùœq¯_‰ÐØ??î |}†›Öî É'аI'_@×áäñólÙø~ Óm‰ +›ÖîÅ[äç•'¾¢fJáó ÑE¶ž_* À‰ñ‰Ö/ߘFó¾Fò‡U©Û7e4¯ü »AÕ" ˆ¢¨tmÓœYK~ß©r­SJkš€€OñáqÄc³ØÙq4´Ì4ŠüEÄ;ã)ô²6c N›—ÍÍš=«È.Èæ¹¯ž¤ÀW@µÄê¼ñÓël>”Jµ„ê¬ ´õ8âñ«~~Kÿ•‰ &0hâjV¬e+šFÊuyþë§±È\6W„tÆ-ü¿·ØÅí«±“pÇ{h}uwïÜHµk|èŠ÷`µÛøzì$¿]Ó9˜¾“ÚMq÷+Ïñù¨pN”$3]¹®édîÎ`ÎäùcŸ‘ääü©Ó(~?›¶DÜ7»Í“³æ¾Oæ‘ Îž?I×+ûâq'„Ì ¯I°¢‘}·+ÞT’”ÔÒêº ÆyÁ˜LS0¼ˆrÝír›évÁ<îE~IM ¸¬¸k­Æ–}lÙ—ªif&¬P×,UÓø=m §«Üd'11*+/§”1©á¬›Ä¬ÅëÈËÎãÎçÞ§þ L":¸;yŽ[ž|W€(Ýc§Îm5 ·ÓNƒFuX²fõkWåÖ§ÞAU5¼>»Íʵ÷ãÓç©ÑóaÔ®Š%ÎeSUÃå´ñûÆ]áëcQ×^=[S6 * ;¶n¥båÊá¤sg6­^ñ\dYF–e¾½ì4äq‘Ž>,Ô’›m$\¤0ë«(‰tÜ™y¯ÍÃ[àåÜ‘s8œFÚÝý®$)°Ú­(^…ŒÕtº½}ý±&bŒãvoÇ´;§á®äæcþÍi‹°rDÚ*I‰*ºË5.*z*‘•{Žü¢\jV®¡|PT#ƒ,Ê ׋AÝE¥JBu¬ÿÎT÷¼²OMBR¥Ú¨šÕú¡(*§Ï¡JÅʦ¢Èn³¡¨*wK×­bö-ËJ¡1Ù (ª¦QTTă¯<Ë´Wß4³Ì=ñúË&¹ÅÎ}{ÂÒtÛm6f/žÏêÔâÍìib81‰B ¤Rr3ï2X­âõóé Ôî°ÜŠf0™iº‡TN®NÁ¹<ê7oЦjœØaíÛN…:•ÑTÅëçë!SøAߎ¦¨¸*º‘m4E%®J|Ô w€ oG–e~œû‹•ƒ‡vqà€‘Óþ‹/ßàÜùâÍéܹ“LzûqdÙ‚ÍfGQü ½¯³éJ•¾cv»Ë|ˆ……ù¼óîÓ$7lI­š LŸÕÊ•k0i²‘ú×f3âENžÌŒ˜„§;Ýa£a|-î¾a ù^œ8H¨‡0‡º=R×îæÜ™|ø…OÕPU? w M3OcVþ’fj°E «:Z½V%–oJ5L©,KµœUXòc CG¼Ì-C{qçÐÑ÷/}åú2Ý‚î0%Í‚ „ùTI‡ÓãfûÊTE!mùì.GT ¬ÕnçËW'™¹  ³¼ÕB£z}ìš¿œ j®S<štú¾‚B¾l¤®U} ›V#ÛmÔÅÄ¿_à ½¥ÐËǽ†P›DT¯ŸžKÚ÷ó™ÖýÌÑ3"¬v‡•̃§ÉºpñŒY¡çŠ’H‡®ÈË-¤BÅ8žxùFÜôrÙ}œk3Ð4‹U&Îc|ÏQo^¼Ÿ³Aežº÷Q¼¾(ÚUˆ ÑLA;,gw¹ylÜ3f!Áà¨VM·[meZ>âZUãê»ûƒ­l7,K`ìÿnHÝÇÍ7v[ÈKâ‘§>áÕ‘·„dÆ’Ìv•+BÉ…,Ã=#óÈú÷n‹¢hÌþq7ߨ%BT3©“ÆÞU® .Á™€$J9w$`É1RX7ÊiË>Âes‘•ŸMÝJµ™¼ä]¼ŠÏT ¨šjaˇ ®DdQ¸‘„¬mV>‚‡?ÇG^Q^ÔÚ!¦0ã÷“²}÷ÅL!Z ›iq ¢þÀúЭM >ÿéÓë誔˶Æù ©ž˜DeOeŽŸ?ÆãŸgüÜ1¸ìnùäNeŸäÝÅ“9|ú i‡6Ó¼m2æÝEʾõ|ðË»4NjBRb¯ýð ‹¶.¤QõÆÜûá]¤ØHó¶Éüsú$Q&§0‡˦sðô–§/¥r\eŸ=ÄÂÍ?ñSê\^™ýÓ—N +T¨øýüðöT^pw~Î$ª_¡(¿€½›¶"[,¨Š‘:÷ú‡îeækoFX)DIâûIï³áç_"~ëžÝHû}iQºCÝkçOý„ø*•سa3{6lŽ8ÿÑ Ë$IF’$dIfÔÓóṉ̃ÖŽ’ :Y¶0tðó|öÍ3.d÷Þ-aq”W]g\˜°WP`¸AºqOÑ\åVw{‘V-c`g#–DÕ4Ü'+·m‰#]v;+·m.5DÓ4~ß¶—ÝqQ·ƒ¬lÆÐÜÖu¨ IDAT—© 7j:9ù…ü–’nŒ м$Ä9MW*gHñY§Ç‰×ç§NR%„úƒødÎr%ÜN;—ƒQïÍ6ëe­OÛgZð‡==…ä:ÕQµ K–-ì·Ü»—ûŸ|‚ý{ö ( çÏžå»O? RkÖ˜Áç¡cÎXïß_¼O„Ä› ÎýúΟ=ƒ$ÉȲÅt÷ ®éšªE(Y4Ucì H‰ê«³`ü s ÍLX¥¡ýíIý1Õ˜ûyE4¸²·M¼ë,×Ѩk#$Yâü±ót½³+w¼t'2N„—G{î›…¼ö>òQd&-5ü‡ÕɰI™õÞ§Œxﶨ…/äžCÓ5ŸÚÏu[”2ÈŠ-*»ÜfZI‚òFnA6/~ò …¾|¬¬-¢i*ÇOä­O?4Ý^ûtïÉ¢•¿‘WϘÞ.»¡²:uw5«Vç÷†Âøôù³dåf—N$‰c^4caŒ!ïgç>ÃM{Ęyñ¡Ç‘DÑtÃ!²Ð ¦çA­[ÝÎMLBR»CCjµoV]²àBŠ×Ï쇦ñøŠ |ùð[TNN2‰Í®%[XñöOTIN2X6çYñÖOtÞÉvqí'2Ù¸ÉH{÷Ð+‘e é;RÂ|ЂHÛ¶QÙ²u5ßÿ0Q”8pp—9áŽÿqv„ ä¢(ñÝì÷Ø”ºMSQ?|8’Ó§„¡B~œ;yó? ® &í:7fHÿ1Xq{Ô¨\™šu¯ç‹Ñ iMr³ó~Û[üqô,Hâ¥'¦³pöZN;ñÔgËú  ›×íÁïõãóúIÛ¸O‚1Qfgãª],]°‰ªq‰ü¶0•_çmàùQSÉË-Àá´’—S@ÚÆÈÔšÇ÷*ÓlX'ÉvÌܵ7 ô9qÅ{pzâØ¾j=zI¡×å$}u V»¯ÇNÆj·G ]UÙŸ–ΡôÝÍ/6‹Ó޳B<‹V¡„¸Phh„»¶8íü:ê ÎeñÁ”‘ØãÝ‘ÇÏO¿‰l·ñáäç‘l6\øú–'÷\bÇ\ÃýkÉ oóËHcLW¨_“ŒÅ«ñåbu:˜?bŒI˜TMc×áÔ­V³»óå¯H®Y…SrMBu± jwæIÜí¹ªéü²i·ù›ëV«È±3Fåès9¨šÆ¶Ǩ[­bXϪ¦ókênS8.ézW¡RÜe fÁŠÓñ‰NRVíŽÈØä ÷AÍeB·),ú} £ŸüŠ[‡ôÀXâ]¥k+UÃU*ê=3cBt¼>/ï¿ò6Mêxlœ‚B„Óa‹z¿KâìùèuŽ\$šõWtNÉÆb14|ËWî Ûµ‘58FÅ}L+µbq%p6÷n52Á­Þ»ÎÔÊ’Ì„…qÙ\¨šÊï{VGŒW#Ž5{ׂ¦£h •)Cc< }…|±æ+¾Xó…¾"A ]½6eZÃ1M¹Ø-°dúÜhÆÊ‰ xý~>›»„ž[3äï½Y»µØ]äÑñ˜þêFûøróoÖ~E퇪róäA¼µà NfŸäãeÓØž™À-obê/ï1sõ|½f&BO¿ªpàä~–nÿ…‰ó'0sõÌX6 €•»VS˜Ãwë¾Aè)p2ë$?mú‘w~žd^óƒ_ßcÆò¸ý›ø~ý,Ã=·0›Q³^ ÐWu ®›·ˆzW43‰É¼÷?æÂ©3+‡Æ¶khЪE)îºFÞÝQÄ•ý®ã@Zz©±‹ ¦~Jõúu8•y4ÌrDæÑ½aûžÃábçžM¥ k%­A‹F»÷n û.ù¹H¢„Û'.Ñì3¿ —ü‚vš®qì„a±±†(MÅa³³`ýêˆt»%]¶â]n4] =Ò“ ¨”ô‘v ô‚xý>³nH¨Ÿv`/„Ï)ȧq­:aßÃãr°=¤rµ€@íê•È8ô?¯ÜjÌ»•¥ëÒ‰sâõ)äæ2âÕ8±€šÎ±“ç) Ô‚Ú¼ëÛÒðÛútªTŠgÖâu¨r¾bãN$Qäõæa·YQU MÓX±q'â#5ø«5ŒG(mVNŸ¼múŽ”°0øyÉã¡Ä¦,áÙç@Ót{ém®hÛ€—Ÿý˜ã™gL ÉGϲè‡uæºzÿZ>ygÕkVd懋„dÏcÜÔñ|2eÍ+7àŠ :`,‹„,Kt©{?G¢zŠ´¨u^˜ÉžôLö¤gòÊ£3hݱ+oaß®£Ñ}%É4iŠö¼í W0ˆñ뱓™ùê$¬vé«Ö‡ß+Ucûª,6+®ø8¥ïfë²Õl[±.,Ó–¦él[±ÖÌbdÈ jÔ KñúXòÂÛüüÔxjTåÔŽ}윻Ô;¿NšÂá5›Í÷™ë¶r`ÅFvÌù•Ãk·R³} ì §($•kP°J‘Þ]åòK µš„ éGyîÁO9¸÷+Ç™µ>z¶y§Ë¸~àÉ~Œ~ò+ü%´?Ÿ~ö$=Ï}öæƒ7`£”l0Ã"¡¨ x(u¾ $zðú¼ÆX 4›ðôkQçžÛ“`ºÝƒÜ5MÃZ+|PŠëU˜›£OaȃðÕ'šÖ E)Ÿ{â¹ó¹&yb"#U*Ç“˜àâô™j&U VÍJL™º‡ÃJ§Ã"ú*(ƒÔrVï^ÖvZŒÍS™8¼!î\fžWª ®éìOK'sWxb‡ @¨iûÓÒ©P­J™BHƦ­ÇŸ»îÆÈp¡¢‚¾÷v©EÃ÷OI”yjÔ 23‡…jFËêÏjµãp¸8r|?¢ Ò¡ÍÕÌž75°Wêäåg“y4ƒÌ£F¬‰®‡ ;š¦±lËF’*V*³^Oñsû¸i—Ü´x튥u°lË&TM£~õt}t˜™šÛãrqüìnó<¾R’g¨ªwRRÁ"I"¯}8‡çÞúÚXß]vt]§°ÈËàgÞãÀÑS4ª[î]ÿI扳$U©À‹S¾#;·€÷¿ùÅ,>»ÿÈ)­ÚÊo)é&ÁOÝy¬Ü6¥Þ sýîMBˆ’ÇçW8q&‹×¿ ›µ|‰:dYæáÛ ÷ªßœˆÕj5=!Ê*¢­æÇÖ XöóÏd_¸`ÎeM×8´?'Ž+• CqˆiÀÑÍûÃ4æAæºwyt†+ˆ{~M£BÊ 5DÓ4sÑÒʈVV»²ÈË¥ÂçõóàMoâtÙxoܬ^𯴉sM6=üÖI,ù1Iyô#ëǘ§>C†Ýðº9°%Yâ¥áa±Êô¸ú!’›¹ W-M3…ÓÂ/Kläısœ8z–½;ðû’­H²Ä7½ÁÒù¥É7ïÒÛvñÊœOÙ²™-ËV50€7GEŸùü]ÓÂaj…Âl;›¶rpÛNì.'’,“¶b-G3ö‡§ÇMÚŠð”Å3vâò”­­ÿâú‡X1þ#.>n.t¿¾4…ë7›ï3¯BõùQ¼>vþ¸”ùŽcróë€sU%cñª¢ñýБ¤Ï^B4Ò­kZÔóÌgìWèÿÂTšÖ1*¶ß=þK,²Ä/›v•kŒü²i7²$2xìgÕÌKú¬›•Ïÿ nEáíý>…G|AíúU"ˆE©®IŠŠªhfõsI–°X$¶¥¢gŸVfQÍÝéGñû£¾J).^Ùz>¢(›kdìûò·‘ßÃÝó0M4¡iƒ&Œ¸÷Aò òq{¢RUUY¼j)®Šè‰ñ‰îU»ƒþ÷Þ`ZK‚Aîö*âH ¨ÄÃ2Ü$ö8Yl͈°ŠDÃ׳&rǰ)¦ ÖÈÑß0âÁ¾$UKd箣‚@ͤ yý8tŠýORXè#uëò/âšJ¼Ãa5 ]³ŒŒNÑ6¹èÁ»BÈù™gÐ¿Ó rÙé"Çþ4>êUÜNKÖn !å:>EáBNxÝŸ7žx§Ý¨c$Ö0kOœËIVn?üºŠ»^[æ¸7ܲp9ìä²E¹öR„OŸy‚ ªhòÐ÷Æž£EôlW2È<Ú^zÝÒÚ•ÄÏÓ?/óó—ÿ~g™‚_´:Gö싪Ù.Ϲ%a³ÙÙ³+¢(šc/(H¹\s>«/àri-ÅUÒç+bá/_Ò°nó@P»nz%8nÅÏá£dçœG øÍ‡®ª¦qüìiÞû>Å_ª% ]rSîšð2…>¯éfïrã¨Z ¡û­T­·€Ûaà l‹6ìÀ¯¨¦;•,‰Fˆ/z=EÕBÒíFëݰ²xC¬Aw­„xUÓùzY*ý;5©%QLDÊ«µ×TK¶1ð–N.L’,„ÃÀx#¸¼´ü~§Ë0&™KQ¡/Âú‘’¶ ¯Ï‹¢*$xhÖ°)ýïˆÏï—‘N·B|¢)ÜmÜž .›a- µFyPUgËÊô¾§?.‡ÓèCÅtó ÍüýÔ„ð¸ãõØóÌûm!e•%ÊÉ-DQT,²dZE†ßß»ÍR*oÙ±±8©B§FjÇw?ÍMw½e.àþÀ&¶bÕŽ ü«Uê3y‰¡À!!†¸‹¨—¡4‰&t?ûùÓ\Q«k÷®3-)e ó%¼”í»Ù¹ÿpñZ¥i|»h9·ôîþüâãPBÖÃà_Y’¸å©1y`õî(ó{ÉÆC· ± ©ü·ã¯" ¦}©"¥®qøH'O1…IAHˆ¯D.×›qº®±eû*êÕij“²´Û¢(ñṉ̃dçœ3µÊŠâgá/_R¹RšfdfªZ¹&³æ¾áí Ü3·Ã‰Û™Äá÷€»‰ª©¸ìt]gùÖM´ªßˆ—?Ÿx3¿R\ Ó"˼;÷;¨d¤÷þuóròóyaÆ$׬fAÁáÄë7â:KsISU Ÿ_áÀ‘Sì?z’¥ëÓQ5Í$ªf|þà«3$1º^r¾„béútdI¤~ïG#¬/o|2Ÿ;ž}Ç'|ÁñSç/k|ŸÝ®m†YªT«ÆüïfE´ Âbµ2ð¶[éÓMtèÚ5¢?EQ˜ÿÝ,ê7J.sl”T’‡*µ£Y%ŸÂGC>B”D4UãPê!Î=gÖ£Kÿ5ÝH± ¤o3¬IŸÜ÷‰‘ KÓ9wô_>ò%ë¿YÏÀ‘Ù:+Þ/ Æ/ Jƒ*æµ£a`çÛY2«X‘]Fb0Ÿâå­ïGñÖ÷£L+«z‘Z!+·ý¶Þ¿:óñˆâ~ã¾~ê/™ëªúׯ¥%ûe›…&áÔ®c¨>…&×µæ×qß—Z®>8µšõkGú¼ 蚆ÍmÇæ¶›‹‘‘ÁH5ǾåÛÑ5=ª‹ÈÌ;'S%9 ‹Ó†d1 à$÷jIÆÒ´Kr)ùO@I"Qž¶e}n.8%Ú†¾W˹áJ²Ìšaw»ˆ—+¢øüœ?qŠ[w JSz¿×±  ÇœÉÓ€Åkkúª”°çº¹KHŽšª"[,œ8˜ÉÍO?l¦}ü¯y¾šfZ<‚d"ø÷¾~]X²q#§Ì†*‰—Üw‘_aðØÏȧbÃjìZ¼™Å£¿ ³/…–ƒ® aƒƒð˜MQMõyYæå._suêÈ1~›ù=,æ½Ö®G²Å&l_™Î8‰p]Rs²}¥Q'Äj·óæ3F'C’Dî…¬?WdæßŒÄ@ŒOÐâ&€[dýŒÔŒ#L_°š©o{éQ¨U9‘ŸSvF¿¦îKÕ[䋾‘‡¦Ø-/öí:Ž,KƹªNQ¡ßYÂÙÓ9adÆb•iÛæ‘¨}Du¹*s³4jrx½^Þÿj:Öæ‰ì;¼Ÿý‰«;ý"¯×¼g6dR¯¦‘cñêâú1¿ÿ¶˜z5뢨 ‰žx6¤mbíÖðÙ@<àñÕÅn …¼óÅLû:N{târÙY³~OØÆýÂèoØœv0ªP é:¿„`›´õƱ1oü@ƒúUq»ìÄý‰b¢A·(£žDGN¹í_îŒ$J‚‘27¸ùiºÊÊÝ«¨Y¡F±»•®²íÈv6LåÑ™Ob‘,Œ_0€'¾~š»V\¾æ>ŠÆL–$<†½¿å©1Q‹.–WÛ[{eøm9yîÿU‹Ê- ‰ÎògÎ~+âX½ÚMÈÍË «^^–Ðj³9°Ù³OÖÅÏß½Yf܉ÝjãŠz <æ¾úm1“f…_Q(ôz™ðíç;{MÓ©U¥*§/œgÙÖMÁÌ ã!¤"HHBÓôÿ®HKeéæáé×t@Ï\±;Зd¶ÿàå·u@ÿlü4ýÇ÷¿ ܇âõ©YÃ&úüi³õÉ#ßлûaÝ"[Ìë¼ôгzR•ê: Ïyïk³ï=ûš}[-Öȱ+ z¿ëÚ\ò=Œvú]×FEÁX3¯nu‘{³¤ìþEQÿ[“: [e‹þî]“õ× /µý;wNÒ­ûa•£ýNQCÆ™$Jÿ'Ö·A½º•{<Æ^ÿ†ýFü÷?‹ëÚwÒtꦿ>lx)sIŠúÿžÏ(ž«Óõ‘ƒ‡êVÙ¢[dY7l¸Þ¨f툾Õ¬­6\·È².‰¢Þ·c}äà¡ú?ûŠ=«×Þl“/Aþù×ÿFI”csÿÒ^¥?QõÆ×¶Ž$–Òorãk[›}=½ùíèA£ QõJ ªé×<ÿÝâ°ÅοŒˆˆad!H î›ð’þì»G?Ö&©(!‰|­±g…ˆüÿ¸î¿Zh³Ymz×¶Í÷.‡SïUoóý߯±ÉBÿ«úD`Ÿ¡çÉK]õô¾¡“ÃþèÙ7Œìü+îEh?ëó­×ï.ÇÆ&]ö±ÿ•—,I±uäü%I’©Ä)ï\] J*€$QÔ{µíXê¹½Úv ¹žñ×i³ÇžEì{] )/k %eµDAoÚ·íe}©Æ×¶ú¯ÑŽÿ_{uèÓ3ìY¶î٭ܤ"öнJåRß—ü,i(oß²$_ôœòôùo%ž6KlŒÄ^±×*©)ƒÈü/þØ+öú³/áRfƒ™þê¶Åy1üyˆ’×Qò} 1ÄC 1ÄC 1ü\2‰!†bˆ!†bˆ!†b¸\ˆ±[C 1ÄC 1ÄC 1ÄH 1ÄC 1ÄC 1Ä# 1ÄC 1ÄC 1ÄC 1C 1ÄC 1ÄC 1ÄH 1ÄC 1ÄC 1ÄCŒ€ÄC 1ÄC 1ÄC 1C 1ÄC 1ÄC 1ÄH 1ÄC 1ÄC 1Ä߆»1ÄC ÿ»Å J@ÓÔØ ‰!†bˆá_¿÷ÄnA 1ÄCÈ¢(IXìVD)|ye ‹Ýý¼Àç%ßÿÊ6KÔσÇQ4ÿ/Ù×å ©mDj×oL݆MiÚ²]ìáÇà $Q.ó} 1ÄH 1ÄÃò""ìOó¯ASUnÿi4UãÞ/_æÙÕÓôúChŠa°Ç¹¸sÚs¼qô'F,|‹)YKhØ­#¾Å ñÅí5Eåî_ Zã:ŒXø–I6®è×€¤æõY¯ëèšÆÝ¿À?¿}Í<÷r‰‡ ˆ4iÑæ¢„DêÔoD½†M©Û°)I5ë"âijˆ!†K…,IYûÒ> /¶–Ý$ÉasÐb±p^¼|IDAT•:gƒmCÏ í't7Ln‡Íæ0·isM™×Ž”b$†bˆá?š¢r÷Œ‘4è|ä¶Ü1ì†Ü5 ›ÛIÎÉsT¨U€Y§÷Òû;pWJ yï+©ß„.CúóҊϩߩÞ¼ü^¦kë°Ø­dnÉ ÕÀîLIç±Å“±:m&¹sÚ³aƒîz[Þ~,ÂbR.J¶P»~#t]£q Ò¤E›0¡ @UUDQÄí‰Çïó2ð¶aèºAZÊcMù³„B”DÚ^ßéO]ã™À”%¼–u¼\ãJüó¢Â¿£EU™=j26‹µ\ý•Õ^QU¦?1—ÝqÜ»dWµêÀìQ“QÔÒªªðÔŸмY7†Ý;1b~5mÒ™5’iÜøJ,+ªªðÌÓ_…µk×¶7uà–›ŸcÆG)x<•iÛæZl6mÚ\vÝYßžAU“ð´k×;¶ØÇ# ÿWú²Ûˆe¾=í³ˆ6²µ½$‰Æ+dã-«¿bˆ!™›÷Ðòú®œæ(݆]O!yÌÕ°[ÛHrëÖ4½¦“Ž,bŽ~ˆ#ê^ É7Ü´d‰“J&ö&ùþlª5®Mãžíh{SOA I¨G›A£('A‘mVêujŽ7¯_a­:tGÁÏ3«¦qÔ·—V×w£0;t"•‹ TŠŸ·?ù oQU“j™¤£q‹¶¥pNW‚ Ò¬eûk[QBÉKy¿‹(‰´î×á’H¥ºU¹íÍaØ\öòŽ×hݯC„ë\yÖj9Úú\†°k³Èam$QŒÚ^ÅðõZüëÖfk@°´ÈVÆ>ûªª ‰ÚF4›7º2ªöýb°É2k5üÓdBÑ4¾½m86Y¾,’!‰"½“[^ô:ò²ÑC'O°ßR¬¹ùxý¾¨ŸÝ?êò‹ #ú°6oI§¦­Ìë]ìyùYÜ~ûKÜ0p’;ðܳߢi*Ãþ‡3Žzu[òÓkð¾ËæÍKxâ±OŠ+íÚõ¡Q²1Ö‡™Àñãg|û(jÕjJ‡ýñz ®éñTb{šŽßïåž»ÇQ½zCî¹{ßϺðï4ÿ dQüß›$IúéïZR½ÕZ¼p_sU¿¨ƒÕãqšÿw¸²q¸¶wÛ°¶‹DÛö ÃÚ–œ²,Ñ'°¹öîÛŽ¶í“èØ©¸ïv’éܵ)½®mmžwmŸv—EB¢ ¤à1Y’ÌÅU*ñ·¬A(•qN ÿîñ.þ[úûo$ÀOM}ŸSG°áäÁ8©dr†ã¢ˆ—Zô팂sœŒR@’ÅÂðÏ'âˆwSµQm¾ÐÒPQmVZôëLá…\DI¤IB;ž^ñw<þl`–( —[ [-[»†¦©ÜyÿÓȲ¥Œ Ô˜[E©° Ÿ&!¤£,â BDAiÜ¢myÑ4•Á÷=Qöw‘%DI¤U r1 …ÅnáôÁ“´ìÛ޼ϲÍÂ-¯å–ׇbuÚÂGÉk´* ’«EfúÃPU EÕ8ðÕäð5»ýˆûR’ Œz3W·iŽE–èФ> ’ª˜ýÑ © }:´B³ÏË%!ÁýÊb±ñÜÃÓ¨V¥’(áW|9¾—6Í{ ËVZ4îDýÚÍ#ž÷¦Ÿ×à÷{/ùº^Eaíë_™dä›Û†£hZ™d!ÚñfUjST„¦ë—D2‚m%A¤w£‹]$’(ѧC÷¨í×ìܱF·šä„¿íìC*GçsŠõéÐI”J}víÚ^Ã… 'INnÇ[¹îº{ùÂÃ蚆$ÉdeÃûïn`ÜØ™tér#7z’Žûc‘­&”ecζkÛ‡sçþ ZÕzŒ~ù%óz·Þ2¿ßÇÁƒ…¸Ý‰4nÜŸ¯Ÿ·ää„¿Œ ”ç™ùuêV‰º0Dó]•Bè^íz†EÑ W»žˆ¢ˆBTDQ¤wÇkŸ_8÷oXËi/Þ”þõ¾«»ÆqºÜïÒ©gÓroe-€Ácí»7ŽÜ,$‘öÝÿ×­ùä„cóÿ€@ƒ^N×|rm,v+EçJF¼GAV®A ( a·Vø(*×5DQ¤ZRmΟ=…®ëQIG´˜Уn¤—âNUÒKEê6lʺߗ„eÌ ¨tÎKhŠŠ®ƒÅfÌõGx)‚<”$&ŠOŠr iÕ·C1q’D s (Ì-@à ‡(K愌?©ÌgeµÈôlÝ, ¨êúüægõ›%óê=ÿ@U5“dhû­¨¿~ÇlûL€€ôlÓ ¿¢òâ'cöŒÔ›Õ«2rð Üӻ߾Gí*‰s8xñŽ¿S³R“Œ\ŠBÌb±Ñ¹mSÂþãÔ!ºu€$[hÕ¬s—LçÚ·!Š…ytjÛ;BÎÉû 9o¡)Üg÷[„¨ IQ4Ù· Îæà›Û†J´ä–H¢€_UÿGŽM›$Ó±Vƒ°c×®a“-äz‹J¶²…–õ“•—c’®2¿SߎáÄf±ÒºaS>ý.ölG q=”D‰6 ›B~ñMk^·!ýý¡÷óü;o1ù¡çCHäïjÝúšÀ=I²2ú•9qb?gÏcß>xååùtìПӧ57nƒÇS‘S§ ñù 9{ö¶G¶X9v,ƒ¾}îÇb)Þ A cÇœ:mrÏx®»ö^ óÈÏϦ]ÛÞ´k{ ùùÙ @´å¿ÉÕÉ—EBê´«…l½ÈÞi•¨Ó®–q^Å×%‘ 5¸yÒ a}„µEš5k&·cÔJ®mÍšµFÓT\+÷ßÿŒ©4¾ï¾'My­Y³bKnP"Š"º®sÓÍCŒöÿ|*L¾+¿po¹ìý§äžÞ­³A:wló—*}>©Sþ2YCÓ4^3î’Î ý\Œ®½Ë|9(oò_$lÚ¬75_{UÄr\¶mÕ‘Ùs¿D–-´êZ#bìÚ‘É„·†±sûaü>…³grÿÖ0*TŒg¸L~÷!zõhÍðDzûP:£ÇÞE¶/›3§³éÐÑÜ:uiB“ºµ©T%žN]›²3=“¦õ›reç¦\Ù¥)>ŒMsòûrýß;Ñﺎ¬X¶ o £k÷æø¼JT—EU9:wš¦1ãù©Æ¦Ðñ$Q2ÈFûpb‘èN ]cc\Ù¬÷¸‡q¼ÊMWâå¡#Èaš&‹šbDnÕËð®<ª¡3Ö7âxÖ³·šG'/!'5®žZyÖKŒÞBOܹ8Ь*ø<߸MÙŽ]v —UÅnÄyÜ•1Âäïót7´`¤Rˆ:\Û ñ%w£»¾…­[üÔ‡#|xþ”tÀeø£Äñy¼Ä5Å‘_A))Ž*-sgL¼^?c£O˜›û†¦ÉDc(Êo$ bÑN °Æ¯®_f~þ#¢(¢i²Ãå‘X´ %[FâñE|>?Ö §‰(º±B2W­ ¥õEc…¸ÖãõÖô› «q°Ž£ah}Äu†É¤3¸½n\¢‹Š® Ÿq‰K{Wv…íX\.PxŸ-vûû/cš^½þb×A‚”–…HhªGדôöžcw¹Õp‚TUÅl·7/¿€Óg‰dëfeÖí}ϱžÙËý›?+ùL!IJ£W\r»™¼9H:F’$¦Æ‡¸p¶—Àö<ëck xAlPqîµù˜7‚…CmíSuj¥¶öõµÔ²÷ÿf%ÄÜÇ$IEND®B`‚vispy-0.14.1/doc/_static/carousel/mandelbrot.png0000644000175000017510000022604614507104216021155 0ustar nileshnilesh‰PNG  IHDR úY=#¼bKGDÿÿÿ ½§“ pHYs  šœtIMEÞ —hR« IDATxÚìw¸$U÷?çTu¸}ÓÜÉ92 9"’YL»Š ¢°‹®Ýw•}uÕÕU×´†GÙu×UYë.ê‚ ¢dIJf€f†ÉáæÛ¡ê¼œ*ºnÝŠÝÕ}ûâÔóôÓÝU§N¥SUßï/|¢ ß$ˆž¦syÖË™_#à¤5pœÚ6Þ6ËvÒ¦Ý÷EÚuD‹ÚΔû>ë¶"ƒ6Aÿ çÔF„|G-ó+ßó/ª¯F¶™ô·lß0Ïðü—ÎþKO[ï·û1€¼ç·˜Î¼‚óržÿ t]ÎïÙºÈ.ci“áŽÌ¨IPª~þpÛ¥ðÒoƒ¨Á>C6,] ? [ìT0T€ªó©9ß–ç¿íü·œÿîûÕv>Êóßû[xþG}pÚãlßøU!ßiæ…µÉò7 ö9íw#Ë’´oôø’.ËâÒy®GŠë˜E_Í,›®å2kòÐ,H-ضȰ?ëùh¡¶ë4ò!hD éO´x~’ýIº~Üz"Á:Fù ®F,^¢ÁuEƒÿó @RÐáõ04rÍÓ›¤Öa/€´c€`€4ˆŸ´¹Ÿ“C¯rÁèÎæ;Ûê&$äm¸ŸzØÖÑÆ{ « •ÙP;º¿£C–3sÚ÷2IùvÁ‚9%Ø3 Ø$ýJñ°C 0á|Wã]™zø•ðq=&q!XÊGdÜeøÚÂd¯KÔH^”ehQ3!KQ¡Rþÿaó“¿ÿ<Óàþ·#,+«uÒÌkÇü$Ë;1ŒËßF¶pµ“Yµ“|´Ê“1Û¶‚ÐdIÚNõB i<(­&xzDǕ֢•ÔÂÖˆeÎÿߨ…Àg kG> #1Ä#ФH ²òNz©Eôåå"O^b¢€G#»€!‡”ìz=Dåi*CPþâbò[rtS0Ð=3ɇtN”ø…¤Ô%±Å{+°QŠÇâÔ¹ù3A^Š0Ð’Æ{æ®/#ƨ !²"`¼$ Y„tá‹Q÷4‚wïˆmÓ%âæÑÄ;¡™wDvW³šß.üÕ õ¬n`jwL[;ÉGšcÚfî©h'A™nÏH»ÈGÒó#2x4J² QÇÕŠZ±?IÉÇ2‘žd™Òîdô,ö)‹åŒV$W'pIr&²ÎIºNÜü$ý4›ç‘6×CQW]’!çRD€&@¼9*œŸ$ù2Áþ@²Xy;DFL.ƒ<&A¡Z~-7Ǧ ˜'àx`ù*(CnLTœdt‹—¼@i‘ P±?{%ß¾QoIfžyÙ”l‹xí5”wBW¿„ûs¬êù1ÝhOÐKœï’óÉ“,q:ˆ¨úUʈãIIˆHH€ÃÖ!÷“ øÔN„“L±?|HA ’\›¸v’4ƃéŽÒš§2Æ¢„“3;®¨V*SuJnG£Ì³Bsê r=¦Ç3’ñÈŠñ·Ê+Ò ´¢ ÷¼èðù­–Þ•ŽI¾ý¤¦‘kš4ç#ny’W"cù ‚$Cx\²á’<íÕp%vûPýR`ñ*Øù tç dè¥%d-¡IHÏ|`T,ÈO€xŠ&lfì”Ï瘷„í[>I娷2üØöYÎù:––vÛ•Àÿ¢sDÆ©ç€Ô¨ç|©eÙ¾yAyÞ\Ž8i^˜š¢ˆÎ äVr¼i×iD.·‘\H.çk…O+s>Òävd™ç‘f=A{$x§#W$‹uÍVJsuRüZ#®ÉÇ’2³ÈG§…cµ’|t2ñHCš9Í0™B+Kæ#*#Ò‘†„4JŒ’Ÿ—@:u#¯TkоÍE+=ýAÁè3Úzg›Úêß3ÔVPPPûܰƾ oì1àqkÆ&Ÿ† R©òŽ3÷Bá­ØƒšiaÔœ\$ ËsP< Œ{4ÉXP€Åݰi¯©aÞ?¯0€ ˜ç^;/ ñeÉdiæ0.é#+þ}P"Pú×ñï—ŠÙ,‹:>/Ùò/#bTÂ};nôCȽöLmä¾H³žhpßÈ ÿ°í¤ßŠsØÊ~ãÖ>£Zæà©À*k’6Õ˜Þd¥™LRé#­æiò$²Þÿ,ûkŹȚˆxx ci€¹ð€”  × Q)€¤wûAí‚UIª’ñÉéaÇ-®ë)‹9 þç°ç6Èm{\Ëñò"Ý ÖÃÈÛ¡g^õFÝß;N±ž¯Ìô©ÒŸƒ?ãjÅœئ`¼¦0ä$Ø ´§c+°À€Á"<ÂÔpæ¨ðEï5wÁ5ž1l'éQ \&ÙAc2h|C´¬°ÿ·÷^[¦îYÿza÷—p_h€„Bf¤ïšÄþ¤À¶’÷¿Ñí¥åªu²$'q뵚dD.å˜I¡YY×|ü¿°HE»ÊLñz4še÷?Ëû¾]•Î9¯ÞßQÒºi½ Aà;-á õÓÖP4æ5I¢êcïýïÊîºä/çXéÖ'Ï¢=!‡Í‚ãðlY‡ (˜Ÿƒ9ó¡z*äþ –_ Û†À¶p6“Ý@¡ )—mÔΓ»N.ßx8Ã+a$/0*ŠÙLÖ,`~¦´ï>êò¼Ô+ž»Å ý²»ÞoA° ¯_j7,ÔÈ_ü0*<)m˜“¤5!Di«±‡}Ûć˜¥ ÇŠÚ‡°kÐìﬖ%m“ehVÒéÔ[¹®Ù*Õ)«‘íÔh¬ÊçÏÇÌn÷B¬ýÑ Ù?ÙÆû÷…jÕ&—Ì'­Pœ¦òw£Ç“Dµ+n™J°^\¥è¸ Õqä#(ÁÙý½Øô ¨– gØïäŠ(Xw*̹XüÆa#Ps\‹¶íZKÍP"R.ÛLœ;öëa㱂G(!1”­ „ ¶ ÃûuXÖÀÍL­Û“eÃ< A ÆŽYEÄ€¤$˃¾U‚1dIÞ=i­Øîx¶Hç¥ôáY’ɆÛ$VüF,ýi–µ*4)Í~5ŠÕ̶ÈpY3ã2v»a™Tp0+¥Ú@>äpd×n&…eµÊ«ÒNRÒ Ä#+‚‘5Éh†Œ¸‰ÏFI kŠ !’€®Y‡É„©¾ÈäÂ?_E¬'cŽÙÿßHI>¼!Wî'G]ZÖ-HèÊì '`¾‚9hïÈ€c”‡?XÁØc³øÅã æA±efôÔU’Œi¸¯^.¾IÑ{0èƒá‚­U8¤nÑ^GО2Úëá&Ÿ»ž¿×#¨ø ˆXá½Ë Ø 7?è›"EB¢úñ®kû}d$d+bY/Iؾ tJAÂfÿ'ç™È2)½“Š6ê1§heÍ:³˜/©'ðdt6ñh'©h%yh7ñn¼ËŒ¶ß bÔì:Í(]%é3J¦Ñ`²äkœ¼dT}¿llA3ç'INEÔ>sÌqµLâdE ¢C²¢TµüýÙLÎah5§œóvÝ/ ¢`LÀ°Ëþ&^ ?\T†+öÃÜ…€ã™Á±X†„ñ1Å@—VûZ{âÉ…ê8ð[ضöW \€ÃF4ùð?ñ³‰Î«ð‚lNˆ@xƒr"‚’¸ƒ’щ‹a‰è‚è%˜\41 PË{D…å[/ˆPÔ… QA„).‰=è™P#:i?îݤ}«ï¢V%Æg™”Þî'I3×#ô‚< /TïGT[‹x7íLô3…PÌTòñB.:÷2l5aꕯfçEý*$˜ô;l^©Þf‰XAÂýˆZ·‘ãOK>üó¼ÅÝß&õ ®Äý¸…÷ºÐµ.zÝOJ58.»þÝP›ÙI …<”+0|/ô¯çÿˆ_Ba5Ônђ÷9 ô0t"zp#õœšç}ë—â Ëóðz0‚”ס®†.~!ßnV]d.°²ʃ’`× 6…å‚ñ²ÂX …Û ;£•ËAèîɱlÞbûÊÇyõÅ ¡saAcJËíŽ9ä£âùvÎmÏ{×ò*Sïˆø–,r&áëñi¤o‰hK̼,&H™BJüçÃ{’T‰:GAó¬ç²Ùßi¯C²¼Iá’´K›”žu•ôé–í}ž€ü±©^µ‚|L7Q8 ­;}䣕‰êí,:(™^¹Ü™F<¢Èˆ™DÇkÿoCp˜QTq¿´y<[±þ¥¹7âBÎüçÈ8 WÂòª_ÁÔš A„¤à\‡ZëˆXÙ êRà#  Žq›Ž^*OôQ(a`Y3|ä€j?¨Y0º ”T”¢`ê—iNÂM¶&{ÒQq€hÕG@¬âý÷‰"I×µSêFr³ä#é»ÅŠ Vaä-Ž„$Íù;G5Òå‚4Bðfª"V ùcPÅ’YÝYÞTí-ö õ3¥ÏNl—ôXÛ‘h>ȇÑF4pli× n?M?¶)ç5J>Â<xqž©{?Xö>èS«ýßq?Z.æÅ…<…-#b™·P`ÃëµQ ȇô½EÌy| ­@»äO`äY¨Üâm >â˜5œyøÉŠCtg&ù0 ¨"Pû¿XF÷cëJÑc˜tyò‡ëbK…&A^ ÿ¹öŽWï5 óHùÇ|\;ÑŸA²š2ÄŒKR´M(#î«°ûÏõÈ噬®ç?Vÿy00J$y®%}¾6û.)ÖkWˆ”ûÖÌqµóÝ?]øÞ;™Íî@;,K˨•áI|¡(GÍ„mwR>Ètx=²¾¢¬ÓéÕH»N+<i^F®Ì¥÷|6«nåÿ‹ïþ}“ úŽ“±…Éy+AO‘ÐbémŸözøãѽ„ƒòþdÀ9”D×Z U+\>W믇ƒ÷+xd _zÍVûW2ò¦ë¹d³Ä”65›5YÎþ¾öe°ðÈ®¼p3¥ï¿õ¦oÃEŒÞ 6mÖÏu®QÙGB¼Iãþ| w™ ±’úóT$ØëáUÎò)×#ãOÔn$gAìZW(Ž»÷ˆë}¨zÚÛ¾ój{ÈK5Á¾†%‡}Lh$‰>®}–¹!iû'á8IzN“¬ß.iÞV/Ý frîG#ßZÇt€(¼ð¥uÛAN:9äj&U,o¡Hk‹"YHëJùˆRkŠû–Û“„[`½àÐ/aa22!I À¼DÈ΃~Ç·Œ pÞeah×ÃåJòö ‹–ŠPš€Q`!:DkNIPW0ƒ•U¾t¼÷³Ì¸©6ýK ì¿ù0>ø~­l§ò‰…”>žã.ªìFÐy!ÔC|Ü1SóŒ%Û7®‚þû EÔ–ƒ “ëÅ·é‹’јzæ„ \b–󑦪ïØÝ¸ BþÿÕˆs É${ƒ–Yd/Ë›e.HV¼ÍHò¦ łΖìM²®9Þ¬Lšöª‰cz¡ŠN?îR>H' 2H’ÇùwZñÀ,LóLâ-ûx<ÜïÁ¡BþðˆV» *ðç1Ò¬]ÂSú@9á'~ á‰Á’x5– ó¥"Ž/(ǃbàâÈGP¡B‚»ôœA`¼ÃιۉΠ)(P º¶až ouÉšP3cRH¾·ÀæÍ?PðwT¯¾Ÿ'Î(²%¿ &«WVézÊöÔè¯õÝðŒÁT9]¯Z•ßcâW· «xî·2€ØH_;/÷æ§àÛ×°yIäg%Ý.WløîW÷˜Æ™œSã=ÿU&{Büç9ç¹Ïƒ°Â@sÐ=ž•g!‹u-”—E›F=#ž£F¼#­’ÞÛO³S€TÖ`&l²: ·Š$Ìò1ù ­Þïvz=² 2ÁËStÀ¾¶šŒ¤]7*,"Œ¤%Ò.üÄÃ-Š–pW“C@o,¹‰Ž#Ÿœ <€.(7ì€é#!~U¡0ËcÐ~IwÆ0áÃþcŒ:Ï2€%É5䘫¹LÄÓÿá&ô.Sp"Ô¾c1~üË»' /¡2Ãb°°Ùø¦¿„ÓÀX×>Ô›Í÷M°?Ÿg´VaóF]ï£æúvȸ““zž†å ‡²CWŠÂC°$“­ðQ‰î^/ž@Vü÷•_¢×/ ÷ÌSD‡X…c¹÷iÞ9ö“ÿ:çÿànßX—žáYó‘<¿Ü°7|+(-Š”„Õi6¬-l½¤ËÚI8% ª d£Ý$$îxŒ<|´“ÉG# C¤$Y%+wùHs,iÛfÑ.ë$ø$ Þ¢ ûÝè>4’Ä %x»9ƒp©]16²J8odqý4Ò6ɺa‰›ICŸ¢€-‡BE‘Ÿ§‡ÊÍ×pt×?SxpcWüŠUØoYŒ+vVf²SPÕñ¨÷¶Jð¬òâ‰qá2dݰ1ê'&qaQÖ ÿ#À`ä½_sh/[X œƒÎÁ™ç $Ñõ@‚®…ŸÅl’†›úók’¼×DÂ}nô™uòzVý¤ÅµYî_»—ÉVœÀ,'Ñ¢‹<ݤb¦„GeMP²ÚnV7äL«íÑ !÷†ô´’ðÒ)Q¥%iÒÍ΃äµiÀIh7#ÈGÁC>\Ø¢óéF‡KÍB‡Nå=Ÿœ`ÄÅ›ÓPpúp§MÀQÀRt¾CÐål7J%+N•HD„ Å.Ãw<9ß¾û½8FA‰SI “á1Ú sQÆnÝâƒÀ¦nŠ{VðÀëµ,oÑì|R©h!]q꛸ê]#ðôR)ÔgáUtñKÀ&!—qªVaÊMÞk4®Ý1ÜíŒS¤{ï÷>1cÁí&IFRòáÏ£ŠR­s ÒNà!ààXÇpp¤ï\ä|Ïÿ}t­ ñEisè¦P·’¤d‘WÙ)¹•8;ÐÒŠ0“é Ï€©U?³×3…tò1gIdÚéiŃ1ë$¯ÜbZð?]£]$#êøâr,Uº "#F@Ë;@Â%]Ôå6¥óÿ4t8Æè 䆧WÆs–C8z€åB[W—mâ!й!þ*Ò„œ» ßÒ·"¾}3|@Òoö{Œà%CȆ!aûˆ½í€žù°sMñÃjÍ?qö{/b&; 6w L!±UgVGïÊAÍ–T®…ŸWc^ÏNNûƒP½þ ´çÃ-2èÏ rtXHNœì4‹ „}L1—ÀkþT-‘„@ÒΈåiɉ_~8¨Hf‰wdz7_Ë5,·¡ë°øójü×$ÎÃa^9ž„„Âïy·H¦zGƒmÒ.k—³Ó ò­P¯md=³'§Ý'Âÿ0©¤¼h7-Ä£]}uB¢z;“½á!3­Ü·N–×MjKšÜMŒq#I¾GTˆ—ëÝÌŽvÛëî:à~g™rÈÂvê*DvÀ3Qy€4@ã†^<.a‰ 'Áæ;à +ÐU¿Ëè0ËY§âAê9IKPø–×£á’+ðþ÷æáØÎñúåyÃò£$z !5q²¼Is"Z•(´~£ý¥YOŸGÄ4œ—¬e~§x@:ÁÑ¥ØjÀÂ>]à~:=Óµ3Má*ÉxîDò‘dÿþXì’ˆOm„|ÄTsíòÁX¬v€”å’§íZ`?:dÊ L&‡m¹á)9ßvÜäs < H¥û¨ì‚B ·tÿ#Î6†§kül£âã0¾eG}ö=Ãc°×ª“Îá UAD1ìÜ…î¨õ‚¼ ®7 Ø5x¥³Û=@=L!É=L̘nÔ#Ö_TABïøwÇñ[…VeÛG=Ôì êÒÈ5¦ææØ€×«’EÞUÄ3Ó®[aÌÎʰÝh^T‡K·C¬©]3«jàJÓ¿èN+€øt™V\¯™.ÁÛ)ž‘NR†sÝòªÉ~¦Ãhu[‘¢MÚË(b×>ª ¸ßòn U¨žrÈËбßU‡4 ‹ ø•¥xÍC0l&<ØìX‚Ǩ‡R ·œe{U žsÚ8–ä2“¥rým; &ËëÅ2<¤ipº€¹ ìWp'uµ"S€©`y/Ô†`™€~OSÏQ’&†0£×½qüîþ•Ìï‚‘' r¢Êì½ß@Š?GñK@"…Ý‘X¦ µ¬Y —¼ÜàýW°Þ¦ëLàûPÙk2ñ4<ÖòœoqNÿXð[ËkÎØs ×*o0µŽ‡\yÓ Îü×Á˜Ðcu¶€ÕJßCAÞ ¯¡Òöm×öŒO›æeU£žEA¤¨Ú?ÞP®Úò}¥½¤ûÐD¤á'v:ϋɞ?àõ+“å¨G’4"»ëí?ȃÕGRE¬fpR+n½f½7Yz/Ú±F֙䙩²»Q$- îdòÑé9!íöŽdÙO§‘´Þ‚ «z³ý4»OÍæq$m›ÄrI ëf3ä#*!Sž€dá J:µÑ úa´•–„c 0Vƒß©zåa¯¬¦×¿ X ,r–zÚ¸ù%ÝÔ“w±ª IDATõCe\ƒ™A§ÿ2“ãþÃ΋"<Î?ì˜ýõ ò€!`HÁq§AÏ&ØlBMÁKѹ*ÃB箜{8ôaµ€î ØÀä°ÿùôzY¼ î¦Ï²lœ¼ëßß°$wØS…ò.ÅxΠïªÿÁxgŽêû ÖÜiqËI_ѦbMùÈç Z¼û ¸`¼ë›ŠñO(ªÂþͰ} [õën€É¤¢ Þü#8Øché%‡q‚~O™›pÝl´ <΂-ëëaXARÌQ`Úï‰øZ2,Kš&ý”ŸäOªwþ,àtàÉÀÀ3ž{6È öì §L£Z%°Y³†h‘Ðð”6¹¼#V£ó,ŒÓíÊGÉ ï`–hE¼¼`ËK `rH”ðj`·€ÙU)kR²X¬ÈAEÁØ6X4[‡à÷ÕÉl¹¿šy.§ æsS“ÕƒŒââÛ’„Ådj2¼0æ~Añµ‡|Ãæ¬ïŸÃñ»Ÿä[v1©˜H›'˼ûÍŠ¯•aëÛ`mìý°ó!Mðƨ{tÂ,ØÞk“º£”ûÆsÀ+€E öHM,½^ÿ=d,IëªÂ¹b ‡”®¨Â–=:OÊë­Éûˆ† yvyÍâž„^E!׫Зó|Gp«ª‡ŸU¨WE·CHFPáGcEOüý$-Æl•ðKZ‚ÑŽP2¿ t»0t³ûŸ´Ï¦ H»¤DÓ²I›ÎÍUètb4Ç‘E_YÕõhw®G£ØUÒýí¤<ŽfÛ6êUIbµKZó#)¡‰ A Ãh·×mOÙ:Tê t²é.àY°™Ê$=àdz=‹ºÊÖIÀ\kœ>&xýÛ ûAØ(a›Òí'< 4À©‚T3Ã/{ S¥‚»€=ÀÄ~}èßCÏv˜; FtMè𬥆ýÿÏÔ4õ‡šxIK JÀRç RÕº¦ §Ì¹5ØoO¾~Ùb-• øüI$Ë©^þ¨m9ò‡>ÁÚÏÿþÕÕüáZÉú=Ó‹uþ¡pÕÍËQ_eÍUŠÁŸÃÐ XƒT®‚’s…ïùUØü’¹´ÚÖaú¾â:}M_ÿQ¼®×®ÇC®]•¸UèÞ£ûA ,¸‰äýÀ*´W¯Æä°í°g7ìÆŠ¤÷AX¨U)1|äÃë½ss¼V9dÃP:ùÜrÚ úˆ ¼AáUÞëæíîIòJ’’éRÄjFô$mYª\µÃ ’t2òðÑVx8ÚÁТ ¶KÎuºÉÂt*Wuš´.´'g¤UÏ›ÿ¹+I³#ލ´#Ì*‰'Ãß..)2iÈ@RÒG>¼@ŠÏ0¹ÑÃß:ϻԥ_ŸEǼïu€Øç\¸äôe@N@){¬:à?Vhð²xØûà]WJÏçÙ©kZyË ½ò{qƒÂTDÉ2<äà öó@ Ðk€u+,¸BƒøÂ‹AB¡•»t‚tMÂ!&/{³]X‘s@AÂ@–|z„û%‹OT ì†gÇå'U|³â¢ÄdIdâ­T”¤.iÊF]W„}ã²·òð؃È9°n9lxr¦ÄnQrˆ!%y –‚—/ƒ£wÁ˯¤ºÁf|7”M¨Ù¾ÊáÂw…£–$<‚NŸç£ëaÂeOU4ë1aõjXºv(˜s½&ϳÂÜ181YÁáHÞº0½hOàrN§=d†U÷Ð-ž=F*pœÐû·_Á…EÈÕt>T™`¥´¨ðK™ÐØç‘1ƈ0A? ^ãÌ_ <æ< ŽB‡Ÿ¡•ëªLV’ R‹“!–þ°p¬(¯QعT‚÷V[ô§¼§õ‚dåiÑz±¤]`*ílÆóÑÉ…óZÕNdÜ®ÇÑ)mZŽÕÌ#—¨7[Q<¬}«Ã¬âÖ!Ä#Ž$!Q " ù$½0˜Z @€‰Îqíö9Ÿ>Çó±\è|¥RƒåagÝǼX@!£5½~sÀtÿ~ØhöþhUe³ >jÖÓ °+ª‚VùpUð²nXZ†\/ä~æRà« òÄ]Ê‚žC ´ú«°ø0úSmù•ý%t)0-ÈÿÌÛj“ ³‚=&Ã{l¾v<¤C¾Ü}:üPX½×`@)–ÒÐçœÐá[9 Wä,UŠ Œ ÀÈië´´AÈš–âýÒƒ¼b¾äâ#·,ƒ÷¬Ÿ°é’Ãq 4[2Ä:Ä,¯[a)xÏ·Oçš[7ñòí’ ÃS×Ãv®³pƤC,jN8ža@— Ë»`WµN¸r 9Š·Â² aâ°Â€Aå #ÐcÀ ‡ÃœOBß>È­‚ùG,Ú­˜»ßd¤Çfsyr>›|»áˆË)@¼º·és»ÐÐ`ÛX¥š,L( ÒENíèmõö¸gŽŠ¹Çƒžq¿—.ˆ|x•íÜ©~t}ž>´TöºÐ:ôl«óqÃ'ƒä¹ÃˆFa €t¤1êHš« ’éHëi$7$K¯JVuñ²ÂóMz{cBÙÚ‘ßÑÑ›ì NJ––ô¬ûÊz»Óq¬íÞæt&™·ú~È5±V‡A6óÀOëÞŽ³ÈÅm?‰¤‘Z’©!IQ¡~9Z¿,¬ 8ŠpîþïA«Ý,E‡],D‡dm¶ç \ÕÏÇtNÇü<¬/këoÑi[DÇÎ+Xï^Ó/cHÅã ñ†æxó¼êSQ©°BsnX˜Ûß ¬•°Pi…+óC¾R€í§ÁÀ/áGŽ[âÀ% –‚õ1Ø,á1[ï¿7ÄÆµ ÷sKpø±Àݨ”á–›àô³°…dk—Í–±ºÅa°êr 1OÀ×l†G•>/sÞ ‹ïÍ£žª0h vÛŠ¼»&è2J‚¨ØD`9ŒJ!5!)›1˜ûY ów’/uö´BÁ€Š †!¨Õ’³‘œBéuõ-SÛ| O/;µk{±ŸÆÎ j6Ô,ŘK8„¤jƒmØXUXüFýQŽ­f•| –ºþ5çTyè0^Ódlµœ£eظÙdc¾FÙÖÒ¸ÒÙ‹¹Àòn} X |Ôy`?w,Øe*ÆjõñæµÐ{C 燡•àÔ\M.°aä9}Íû>öûõÅ"°ž…ûG`»[ >îl£J]®×ö“¨Ú2þß*Ä’÷4чšØN8@{;·¢½•{{vÌ9¶Qçþu%ª½÷¯Ep˜_lÀ[+ÅëE ’õ%ÆËB@?I<.I~gµ,Éÿ¤ó]/jÜ4ÒW™l×:„xušbrYº¸²v—M‡Z©NE /4:E}*¢“&îCú¤ö8òfŒ²Ú1råúnbiÙñl<Üå€YÀãB‘—éUN˜U‚ü,˜Û§Áß½0ûpèêÓÖã½èŠqtXŒ?‘Û¯åM€õV$wI„0zó‚¥üëÎNu¾ÇlSPîõ)Þ9 ©Í¾çƒz…>7Õÿë7ð$:wÅ%y¡Yѯy)œ¾TÀ¾¸­ /uùYpGqFŽeorŠÛÚ«tr ®´W¼õ'˜°J³aaØ×Àćê¥ôÖ}Ëój‚žM’E=½íßó?-ÈYPX´µ#B`ËVöÐõw/E-¨ßHþÏ¥%™ MÁܹFªgÊ¢^X`Â?¼”UF-ˆe§±`p)Àœ¢[‡+í5¡·æ t±ôÔ"]%@P8Ž˜·#kЇ`ëÀÞEŒ¦s‡ú€EÌ>èSðA°¯€•6›³añ¡A³ ³WÀ úîpXñÍ—#þ;±çjŽ@1¯K²Ü!Åáö«•U€§<ˆ9`—A==߃¾nàC çéP½‰‡`ÓŒ‹ÉE½µe¼‡(^\>WT½ï6žW|#™Úš7Äo‡C,LçÛò|oÞG˜!ÄO|T̳ÍïÍ {¦’à9Ûn,Ø O´‚ÇãÖ‘­°ø¶òÀõ~LG¾mÞf+ˆÑtå´£M³fºŠšüV¬ 6B2‚þÛD'ŒÆy;’¾(ÓxBDÀ ¹Òê៼K`XŽ%w޵ 64p›ø‚öŒÌ.hÞ*`ïcHØ]~Ä&{=VR;Àꔌí‚{¯ªW¹ËKRŒ€¾Üßn›pd•°`±N_½T“€î A|ÙiøêK`MÖ˜TÎì6ý¶.Ilú‘£rRÎîcô>èEP²`Á:4©ôŠÃQÖ×eŸUcb)ôí……Ë ®“°«ïï,""t/èy›dáŠ~Äe'1ç¯_Ì7î—ü犞÷@mã‡(ôÁ%óL^Ó-‘GÁwþëÏÙyú |¹ÿºCñÛ¿R\¶ÃæÛ'Aÿ“¿¸¤?ÙsÙDy9|ýEpÔðE¾²n8l Ÿòjø?6_‚[ß§(lx#ß_ÞÃwÏOáç§Q>üæ]Ì»fuçáB¸æWÀI°è¤ý[ ë“t= .ÔÉÏ‚Á“â,ç‘;ÁúþŬÚk/6éŠ5(<㨠ôÃï__öX_ËÞB?&§žnsDôˆº7ÌK@Üë)ÑÞº²…]»`P‚uØ£À¿'hïXXù%í50ÌúxõÊ.G…Bý“xöß'’©ŠUÞcˆ*2èOàw õ1À<7ä2§‰zQê~W8ä̯ä%bÈS’ç“$ZÐC$|gHÒ…\¥}Þ7‹1[‰GÒF¨&÷a:0|ä²°¬N¬íuÁ¬±Êwr_| Yz"fjݸùŠºtd³÷K«’Y¯ï?~ÕäË'«šqŠ7*‚PÈ‹¨ 7~ÉQÃ\¼2¤]L ]ZfjÀ7`jU'ãPû¦Spð40ïÔÐa[çyì¡®á ¹ªúH7ìÊ=®<,ÊÁ=£úXÜP+âêWÅ2<ÇðÎØU0z ²ò'‚}Xÿ Z–Á(ãg2qÈ-0»ÆàP•Ð=O°u‡b\À^U'$sô*˜ØÆŠæ#UÆ®„îœà¢'r|‡#óN'ÿ[ØkØT,Åܳ t|øÜ'sœusŽ›nüüá/©œePÝkÑýÖ÷syå§<¸w)GÝô[¾€®WrpåÅóÎ3G=ÇÝןÆ+_y {-ƒ³#Ãpì*Á¯žÌsЉ‡ðÔ]п*)aφJHJM†â&)(r(sæ0wùJVm¸—ß é±sþsÍÏðæîg¿ˆK‡7p枌ÿvWuÃçH®Ü`ó+óüãÆ âºŸ±æÂ×ð©?=?ûò•ÜÝŒká¼'»ÖIÎ[ŸçCãÔŽØ·(6 'ÄÖ`y¸½‡~‚—}ˆ•?Þ á$ëÀ8®û_˜:)^ÂÐù,¦£ZÖ«ô˜éqîRÞ‘ª€®9ð`)Ø[tžÔs6Ûš°:c|O “Ãø4È_èO,Pøƒ’ïKè· 8CÂy®½6]©Éç£NÁÎ2:/Ľ‡«L.úèüaYÞÐ+–?4Í^⯼ßÕ€yAçQeô{:þ7;Ï;_’MÈUVó]H@ÚÓžj†|Ìôü‡á™Iѧ#ßÃU0j·x+HF#Û‰ÓŒOKDÒ’¤ÊWaÛöç8Yƒj$! ^oAÞGDŠ.s€™+SÚ—Ó Dâ•ÀO4èG“qX©xˆ‡êå÷ ê ~%°.TÔîƒÂa ,¨= ¿§ŠâZûÑ@µ( È_¤0¾6’/~Îæ½ïß¿\HåE†F&tÜýr(| ޽|Nƒû«ï{œr Œ½Ô6^zô]ÜõœuÎB~õ“¸ú)Ä¥PÌ j–γ0MÇa`Mc¨ç’á€~wæ Øò ( ß3…¿øŸs¹ä#÷²mh¼úlÎãÚoÞÆGõðÐsaýFʇÀPÆ…À˜P,Q ˜ÃÄšQr_Ÿ wŽÁ]mqñÇ¡üŒ:oiÑÅp÷4¡t÷Ex¼l§Ø®`ù,xf¿ÞÇÙJÐÓ¯°GA~øðk°@.ƒg•3Ü ¼¤¾>49Äòw¯ê›”U Ë}PfTŽ˜×£²8~5 8Hè<Ÿ—á 7ŽéBœ{Ð$«ì! îñù?Ê·ÌO:‚Hˆí#A„#ˆXxÏS%áùÌ2$kB‘enH’u%Íš¬ÚÇ­¶\fåjie˜¨yv›€tVà·S÷¦#_¥C²šñz´*ÙÜu»'Ù^#aO¢Em“x7¢Â¤¬&ÈGœn3äC$ Q¤‡ëhÔÜŽØŽ¤ÀÔðW2ÕUcú XE°ÅÔòwA–½a/%‡ \€N|íqÑz`Ž€^QkñÇëõóæx³\…åÛaÿŸÃ£†VêzâØ35a ë‚ËÏÅúˆ`ß³GaÿãrÄÝ3 ìËF} Ì3àX `hbf¦»hRzÕç1.€÷î˛÷ !¡SÎM0·ëï…ÂZê 5Íêƒ5¥—ÁõÝñ„¼‹»ÒwìM¿ÜŽ(iò!T,Aι™ C¥iÒ‘“”Z²öù1! ª`« â\(þDA7øÙ®ã‰»¸÷W‚ò7ðÝ3ÖRê)ððƒ#1Ìú˺°€îS—Òß³B{ªÖÁ¶/îáÎo¼…ßíÈÑó°€Û¡pû<̪!¼ë[P·ëaG+ßnrÎç Žò,;X“Ð>`¾‚ž‡ƒ›òÈ. &¨e|õÚs´ðD ÜOì…'ªzLL ±RDKåúǸÜ›ç@‰ˆûØ?ø:ÔòY`´êpcnÓ¡”ã„+Ï…õ”¿B̳2iΜ1°å ö eE“¾K’üW n7+ªRô—ežK–R¾²•D"K mØP ôщ±­Ú¯¬÷­“ÎGVû4]ÕÎ㈂ò¼¼édtEJ‚”`DIÙ¦•Ë Ú†7¬§‘Ü RìoÔ>ƽhÃÖU Û âã§“Ö,ñWäÎ9VÌ^¡……ÜPJW8/¾*[¡2ŒÅЭ » >Ýk /5 ˜ç‹EÖ”êj[n˜×\`›€—åtõ£€• uM‡¢‚’Ôí]Å®°‚}ÞùxI“РqG<LXðp”ƒ"m Þ2?º™Ÿž!‡ÏÚŽü÷C‘·€üŠ…¬ìGž±V@õP²tỒв… ?ƹ'óÓÿ†9ó7S, …Q½C÷^@Þ8<–‡k^ œ» µ¶„–¯„›þß|è¶ GW—@íÒRÏ ò†ö”Ø–ÍxE’rÙv¾;“€X¶VµzÞJ­êc.ojr’ÏUÀ•OJúF/^o9§‹Þ‹þ‹±}`W¿g ë)uCéÆ%ô¿esœXÃ+¿,9ë¬orÊ›&ø§ï+˜}•GŠ?— І&‹nX^—„=ß®±éýóþÙ¤6ƒþ# ä>}ySË”üìnßãsš@ípwþõZ9jìX×75×B„€òI¤>À;á%^eÉêufÀLjøíýäÑÞ!tøØn`û:¸ç›ðŒc°3χ»/þ„󰢑AÞZÑÀs3é3LÙâÖË‚t´Ãèœå¾Š‡vbñFùÔ¬™$»ë¿ê4i׬Ûý±äƒ´k{­öˆ43ö½Š$ÜY·mU8– ^…X5i•jTz7©'$Ê¢W‰+B&™#V„ЛÀ톹žˆ>&ó{ºÀA8Y¹äaü PîƒÊäJÐ;æUÀ¯Àþ×àÐõ– Ø,Ч«°Ý†Š#Ík8`j5šÔìÎéºt5ůÂõC9¨:UÉ'¨‡wxÏ·ô»—Hß=€ëvÈÔ@HÈ-‡Ê{óä7*º¿^ŰxÔ¤>ˆ½²È¿Ÿ/xÃ×ÇÙ·7@ÉRùñÓ‡üô­y6z#<þ¸Þ·BAR.Û(õ2nÌ߯ý]ìß3„¥àÈYÿ@•jÕšT›CdP«c&N~.¥x¾€â0¯G'ƒïèƒG‹ðªÝðÀ¡ð•sáo>[?oÒÛêAíZÎÞ¿}”ýWëqÓ…`g—=a¡”bÁ_ÁšýÀ/ÀÚƯKM*»jÔ†s”¾[¥ë¯ab/\þøüvàu°÷¯a—¶U 8tܺžpä§½ùNÞü;ÆðéµèúÃ+ýëyŸþùþ~üùQF±ZÙ®Û1r“óp¥wÝ0,—›§å†ZzóCjžåþp,•àÉóB*Vþ¬$yÓ´ídI^ÃJÓ_™iå:f+O+ûjù˜É`»“‰G»Ihá9juˆôy=¦“x$!iúôþ+f7“È>òAÄ:DXm¢•ÍüEÏÜ12 8-¿»Ó™ß%à/: ÔfèwÊ ë Ø÷€N–ã:™— /÷À¬’¶$W€¿x¾©Xü¬#û+aÐr”­$Ì/‚:úrd¡qlù+X(`MV ø:$¤â2øŽÉoMv_R5 oè|ŽU¯•ñ°òÇU9é+Š1Ç“0þ (§÷ev}X }wÀ²oÙŸ^7Õ3ë÷hĶ{ìBLó»ärÛIÌØ~õÍœ]•”‡±”Ëÿ¡L­¦¦?Fò,¼ÕÛ‘Q‡`ŒÀ²!0ˆ‡‡'“6Û’L죲óQJiò±z ð1Xõî«Q&„ÅØWu}›yyèV‚×_”ã«*¼d}6(í2é¯1‘ƒÎ òk§, ÈÇ”<»£.;í/ê>%ž%Œ{= gÌ—Ú†%vЧ_xÒúªžÛ!ÃÃzê!XîûÄ+ÝînÏd²PξJÏsÉ"¸È¨2SëÿÀTÿo“©uA‚Þ)*Á;8‹[Qd°/Yoß;ÏŽ!!­ÜßFúš2¼¬ó;Z ”:™|tjbûtŠ™ØFLÃ2¯8—²Ÿv%‹7JFünc;åö%"I‰Fù"Â÷1Ö˸"„6Á5ü…ùüŠWnñÁU¸]z@Áò< W´$ïP6aOMoËpÖ_¼ŒàowiV[öäö‚yÕ\ĆÝðQóÚVW‹âè:--t6ð) OÀ>ÅÞÃaölÜw k@iùÀ–?ÝÊ↪¸á_õÁ¢ay­ÂþK®¹Ùæ‚ß{ &”k’¿¿ÜæÓc’½WÚôæÇÁ‡/=yà»?WT.’¼ëT›ÜÏt¾Ã„¥Cj¶ äÄ;05?å±Ú%œÄrçeÞ?/Çs×-§tÁ&ìý¹ìYj}PùéÁÜ=ú*NÿÐxú+Û)ô[Ì4( [ÜÛkð,g´TŸß‚0`þbØþƒ^øtëçeöYP˜ OŒÀî ¨ §XŸš,¼à&a{­ÿ*º뽯Ýñz”s-6¡IÏSîø:$²*a£=9Üé×_CÇ[ ¾äÜK€ã xHÀ¶šö~nó—?ÝþÔ‡NVßÂ䂌6“누%Ÿ©`¥MJWžsDlfja¬±üó²PÅj—ò•ßðÔV+{àÖQ}Ì´6Y·›Ž|1Ï}+êz$]æÌ|Äyl$ç‚ ç‰ÚI¦VÉŽ:ßq¹¢¶I“ÑãâœEù€©±Ò¼ÎGPÁ¯¨ûÇ F ŽäÅ™®n*~R7ô.ÕqâO ØTs¬¾ÔåuËe`/¨Ë€ƒÁü‰Ø¥ ä^+(g7¼RPE1fÀsÛ Wjî»ÜÕÿ…¹ÞÿÆw+F-˜= ;vi•§¼‡DŽç>þ y[`¾]Q9®yíí¬»ÙæŠò¡Þ‡rM𵿰9ô‚ËàJ›þêx1ã×Ã’OÃå×@áqÅ%¯†wïµ9­`bQ©Ù`Èä#ËÉ=·ãªN>zû$ï}ûN~²†<µÂ¿\|!ò³G‘ÿéÎ_°…Õ—9²•.iÓ5hÒó z¾‰ü¬&¦¬b†QÁt¼e;6ƒq¶Íú¿·AÌ>zß s'à„£`ÄQ²™š!} ?()ÝŸOáJcw;÷ä1Î÷)†&ëèû§¤àÜ×Á2[çjuSÏ—*†ÜAÛ–ðyð"Û,®i21Ædu&âG³áÂw߆3÷Ì/0{g™Ê%EÌOU‘=’ÎÌqÞŸäùÈÇ÷£j¼ù8•„öJ<»å\Ž:âŽ8e€[ÏÅÄ{ž"?wBwÎÄè·ØðÔ¿²nÖßNÊ;ñÞÇ ¸o)»Ä€€šb|~oÀvK{>\BîÍýðz ¼/àó‡Eº÷Wѹ·æ£=”ÚûQº¿} Væ`eîB{)ú©Kæús¥`j(VÎcˆèÆ yT0>ìÛ ·£= ãL®ûQó“—\­pÎýÎ> {Úz%¸£< Êg$Žò‚ýv¿«dãùh—$o+½ I< þúSl7ËöI×½ºŽOKÁU– *M¬ÛLÉY“€?–mŠi\žÅ²BÆ$£jwx_nYôÝlîG#aXQ» Ð¡(/J\Ep5b¿MÁn$pð(p¨ÆT :Û™µº6Ã3Z÷QUvºž†n´·¤ˆ…™åzï¼çÅ,xñ=ì¿%¬*úΪ¢®€qrwCõ×еH æ*ÄC0Þ%)¬´Q±«ˆuðr•‰˜_ãÙëaŸ£N̽âRcjâ­ô¢1t¢ù"C°ÅRXÔ=„EtkûôU&˜õÛ:Ñš~ó8üJƒ³N-ðÀ;Ë,9Cñœ‡B‹01qôwÒT r&•JbApPUq“ ÏÞÇŸ´>Z¢,ž¤ð'#Äí”J&c!nªR^2V±Qê Œòÿ(þ‹D¼Óæ¦!®ÖzÍGBl¦&¢«€ûß/ázJh¯Ç9˜Ï (”`gìÞ Ë•®™SQ°JÀ¨‚;˜ZsÇÿüð‹Q¬NpžöؾA綸⹿l‡˜¸÷UøK‡t|Õ!,CƒK@â’Ðýy]a„$ˆ 8â‘e‘Â4ýµ’¤¤%IŒ–Íô™Ñ[f¶|¥™Ò‚[»  ³Ó@òt‘™|Œ­Í>ò‘†`¤INoà'Ù†? Û"Þ=ß̾NwµsÉäœ$ä#J9È’ö[ú^ìó'ÿ×;VÖƒáá' ïI :Né‡j²áO] ô.–Cõ§ß<Éõ×ÞƒfæÊHÁlP\ú¨‡òÿæûD™ÒwA=ô·ûÊÇè:Ýâcó&øÇmïcìª/Rü¼c©µµ‡¥A“å P÷z ¬ËI¶T% ̶&¨P¶`BèÐ2ØÜc±Xv¦â¢<|}ãR¾:¿Æ÷l§Š®«aYÚ›qìÁ°ë'‚yÏX|ã¯ç ÎØ ¦!«Vu­‹ä£ó¦²*5r9ÉDÙæa òÀɊϵ•‡„ Y»év Ge¼úüS‚·ðSòŸq¾ÍzÏrƒ©Þ8ÂÁ þû3(,Êžv~¯°q,Y svÀ„f )íýJç_©«ú OXÈ×0p¯Ò$~áè7À°ê}ÙhÙä¶–̾M@—‚açû7èð°“ûâ}fy=µþQð>zß%ô‹ í7(Ä%c7Š=Û™¼ÝŠcð¿K¦#!=ªŸ¸mEøh£@ªÙuÒô“„|4ãߊ6ÍîO–Û›ÎmÒæýjvŒ´¢î‡û(0Yf7i]45#â4Û!]ÞDUßš IVG$i›¤ùYŒ#~©Ý¤ëFy@üíýÖTïoom]|l‡”Ž U¸¿¬Aź:tÅšl­ô¤Â(t¿Œ»@̈Šâk6Üû :†Þ-w=+øÀ+òLüÞBýº.1yß?MpÆç·Sxå[ɽïfÞ[Èó™-5öö sÞ¾]¨“ üX,ƒCPìòCo²yQB±(™[¶™U‚®—ÔP÷_týâ^ž ¼ù!ÂCæW»èóuKr`ZÐ/a–SèÄå°dP{9 Ǩñ: l˜hG…;„&5J)é˺¨^£¡¼Ín·Ùõ’ö¥HW]¼ÙîfŽUDí«˜Æ‡ÎtÉw¢lîtä–tªqIæ´hyRâ’°/mñ4•Å“’›$ó\UÕÀú´IòÂh$+IbdùHs "áøKÀ [ÂùŒ9ŸŸ ëë2¬ô¼•ºúMÕó( Gð`+ÈV0¼Š;»0 Aͪƒ¯R¯‚£*Œ<òjÊß¶Ë-¾zí>ºÄ­ˆ—~’¿;†ÆØð•<ÂŽy5ôæ T…¯t›Ð-'×.ð&Ãö™‚îqu÷+`ðX¸õÄëÞAI)Œ…UŠý‚îœN”]ñsXXÖãð¥2bˆî« Nƒªßþ>N>9‡aÀö!ªŸ·šuÔÏÔI¡‹#\ýøÅ FaÇy0j‡ÜcJAA0zÚG`ÛIð¦ +Á8õP¦‚çÓÌ1àµ@QOÜöô&r›!4<Öhדáž¹/‡Òj°¯ ~éZض6í˜,FÒE=Ï«—z¢zÁ³m7Ë}F圇E¿¬'Â&àOªðº a­½ûÏj#Fx m´Øƒ&.Aa¦AD!êZ%1b‚©ji<ä­&å»*krÑnLÝŽmË,;keâ¬Ý$plW¢r;+¬O©èÄcÌâú¶ªº×*‘'ØK5î!Í€0râÆHÛ$«äÞÌXHò? á1/´9iÉMÜ1ø_èQûìÆ¦W2ŽVÀuˆ‡›pê-:æU «G÷k€Ã- Žð±~¸a”WŸu–¥(uç1MIÕ‚‹.½€·¬óÿ9ýCoÉÆ®* ‹R1ÇçÿáÓ†äêŸÍåÏý„¤è°uÉ« 'GŸ4˜åsEy!8ñŒ"ýý*rgݯþ=Ž>‰ ¯NÙu°NÑo”^µÞ7‡†uÀËñ›L*Rç/-¸Vù)—YæÑš8Šú¡,î`ë ¤¡Â!BƒôÕK´ç 8æèë‚ãß:9÷*êã/è¯.^Cç5Ír[òÐ#©ætqÏ ƒ°SÀ¨œ":!pþëap‚¡•®úœý,2YštxÕ~Àp”µVÝ ª—À®«à‘š]æ‚W×b&CÚ4#Õ_ ݨ6ÞäB‹ÆBÆâÚ6Bòò|…u<Ø?Tïø8k_ûcþ°­—ëì2ïÿÔ¿qËÃ&Þ|=9U[‡Î˜^Ø“íܘ5tMš‡}œ² ~ñ6¸p»ÁKžçÈÛ3öì†ÇÁÈ›¨š‰•W˜NžÄ1³aÑ ö6ˆ{@½ †®CÞ°Ää< ¿ ¯Ÿ|˜Šu˜ «ô ÖûLäÃ6¹µŠ±gêžIo=(·VÈR w ¬)Bm«Vj;ÈcRhC‚rǰ³~Ÿ‚Ù†þ”°á~Ø&`§¥IÆ(p¿ó\qŒê ëÞþýêWi§Ó†Ùï’Ló;ɾ7J Íulñ™.YÞоú‚ F@TÆí,ûh×¾¼újçuie’yÒu]òadx?4’TÞÈzµ6ÜÛYLbYK[í<Ž|D¡¨ü‘à¿A°–_žVX_½!Þ"e®U5ï#7ŒcÕq0« cC)=]{¶BÎ]w ÞvAï^³É1& TRW‚ ª²#¼ùËŒ?c6ì°5Hr«TwÜӀۀ2¸x›àÇŸ©±âØ}a¼ì9fÏ›Å=ïç Ïú7™bÿL/è©`Â’ÛwUUÿ Ÿø$•?MnÿmŒÎ:…žcŠ0>ÁSë ʆE— KÊPP]¨KKTÏÚCþ[Àìþ>üí!'ºP {•œñ<Mh¶sâ ‹‡ ÔM UØ)†ÝèþŠhI^7T²W@¯‚¥EÈM€2t˜áãÙX‹Î;]ìP »¡Ô•ç`3õ°ªaÙsǸC:ªÔ=¦nȦWŽØõ¸&‘áõ*]ù+ÈÇ©`©bÕ¯¸Þî"…üotfÚ¥ÅÓÓ¥ˆ•*«S¼B>²ôŒ4»­¬ûšÎ‚‰­fùÍLâh‹”‘`Ü7%2\Ïû²‰òT‰Œ%)‰ #&YU;Ï‚|õ#þ?{ç.IU­ýß®ªN'NNÌ03ÀfÈŒä ˆ¢ €‚€WPÔ«b\Ó§WÄ„W/¨(fQ/æpE%]ADrf†49Ø¡jì*ºNÊ]Ýç Ôzž~º»Â®êꪽ׻×Zïë3+èמp ͧ#—>³“N¤ÊýrwúEÆFBÖÿ žºßþ¿wú6›³·òéKÖò“Ÿ¯FØ'‘ȹ—’rÉB×rò/……sgAq<†„š½-#膆k¯…Æ®pð#ËéÙôr€ûïªsúùÏÀ›¡º}ݾ¤ÊÇ2¹)«›ðÔæ:–&â-üÇS]qâ¢#éÙOçÜÝ<~÷¿³ó}TL(W¡ZÄk¾¿‰¬{£ >]‰}º£î+gy˜¡0 » E½»Xƒ] µÜ0·8Îõ4þp±vTbL}D¥TMA¥P Ì** #¤zŽ7Bã PÛ6Šf}Ȱ€} ЖÁì1éZkŸQýÁ®S›€Èé{œ¾Â«ŠåðN–D¯2¤¿– ÆPˆŽ¤tzò1«ÉØvNô&m£¾}˜iíøY™ Z9¹7_'kJvÔº’Éø;±>j3‹]f<µc–À#ªÓŽë4K×l—$]ÚYÒâù$¿/iíG\@§æƒ˜×:êºÇŒa©[ø Ä2`6ÒovÒDigôÙŽÒs"fDAåîó‰þ·ñi«Êç”+"uýÄh´‚¤¿&àZIÏÊÓöa)=„"Jüpþk æ¿T²ø›GÒ}¤¤q4ܸø.»ú*@0mšàÜÏétÿ†« Åte‘[n¾fÙìo£–D`ðô›uú–_ÍW.)òù'MÛómìtÃMÔ¿wýû¼Ïêt½J¡â‡^ 笇õ'ÁÐaæL8¢w¬Š¸î %xì©CÁt ¬/Í¥˜) 1OUàØ9åü.~ôÞ…èGBu†*ïéëh,ì\ƒ~CEP¦]Ð¥™Ð#aáb˜³¢ÐµpÝð؈zî7<¥"+ÁQ¥2—¦ÎÎlükj%Á´ЗG¤Gý­dT¶£0=‹âóvª‹?÷°Ô°¸S°&*õ*è²üaˆzä)Y;æñ:É~UN8ËÐ.±¾0ç¹áX&‚n{²°_E¥]Eíç·Ì;S¨\ÿöÝ«„î­Ñ¯  {>—Q³¢¯DžÀ\«PÅÙåìT½Z øÒrÄ[ï ·¢10Òº—ÿÎNã²g-dm+¢ë[…šÙíïטö- ã¬ó9öœïñçÓ·Á©:œjÂû4_¶þôÏÜygåÈ-¡Ó󜺽»ÂBé€ì^¦•Ù°|”™WÎä-{oçàsú8aÞæ½ŠçÀöïÁÚ2<>Ú$xp§9)„eàÝEØ^…¾ïjpEí€^Ì«°Cy \« ^U—t— Z…º<>:Lí¿þHñèépÝÙÔ»¾Š|¶î©€Maºº?<#о…>°Ö‚ñvX±zƒék`[ÕÖ±Ó·–öÁ³ÛÕó¿ *¥l5M¡Ášýî|vÄBÝjð_Ê•ßwo_jy&O žJº°ÏM°ã¶[„0É2ÖR¹²\î]+«Ý¢ƒp ;%\—§d=ÀGÒˆH%Âñ³¬û%¡ÏúÝYGAâÐØ¶Ê~V<ž|$™ :ï°söú Æ‹}9‚^Û€Ý*°h†ZÙ ”u(ÔÁX Ú,¸ð+ Þú/€LÀÀ7.ÙÌï_·q2˜¿Qż%iS‰n³h\|úbýÇ—‰Yü ó· Žµ(^«ÀÀ]wåà#·ä֌੺P÷õ¼7ƒ¼sp”™ÿsBläò ÌÚÀ¢k¡Xk3T_›FÇRðú±`iÀ=¬ªº ¹ÞRé†×PÊ/W¢k¯<@rþ¾0Tµõ>´+ÿu=ÃÛ+üñ“Ÿâ-où*…+5ŠûÀôsìgþ ÐýYxß­pÕ*(üŒ‹A ƹÀw`Á0L_fÖ¢ÎaD*%t¶+€t¨=Fu3>LóqðÓÔÞÒW…¥T…Ñ•;µ”Qªß­¦>µ³ `¢"íN¡jÕ4&PÄi×ê8I;Ó;™·y!´5YŠÌšF-f‡Ö®AزFL`‘åuœˆ¨GÀà}O>’Ô€CVÔïˆ;Ãæü®à/ÀQ# @oÌ›Ê0¼+{á‚»àÆ…’[×€™‘¿Ò¹'ßhÁÊ£©Öþ‚Qˆ:TNô—{àLɵïâ™õ5úÕ9üç>ÿdú¥·’. –ªΩtsËÄL©ê‡z/7Xºh&K?önDÿÇ)—À’’½.Z2ƒ+>¾‘#>'Xk?Ye4SÕ\9žK·¸º„‚®!ö³øG/Xóáðn0¾ ן/ý–ýüÛ‘™RQÇ’©}#À{8ï]âÆêUW{]XÈ}à_¿€åw¨ý.u~È;à3ï|âFÉH¶üU±f=!›…åN Ø?l¾ýXHS!}Èuþ£ýŒôqþ½êæAþ›Ó†æé_ý>ûµOĶaËÃÆ,™áv­ø¹²Ç4ñ‚Á5kõ|µÉˆÐ¢rÑ¡s­´ùíØ¦S4¸YžW–ÿY»‹ÌÝôºnðÔFZµò´ºî£A4®HpãЧ¥&ÂÁ"I¢­€¶ï=?-âzkÀ&¬ßôSk¯‡ •ZQ<dä¨Òè…îë 1ì›Ù BÀÕ?”Lû Ôßm:¤¤ëº×±w× ½aˆ-—^Ä­gÁúÊel¹ï/¼ä}C‡¡†¹å–¥5,0DƒW=Ë{ÞþqJEjMP«)·yÓ£Ó¹õsÐ7[Pöx?Ì2a—õpU[RJ”%,ìG©%Nÿ(‡=ÔËϸù$uÜ—~ *º– bF«&ƒ£… ߺìK¼ä̇xçK-¾{<ˆSaù}Ð_RÏ“.Ô¬ñî3áß%t *&X½°J6Ó©†«Öj”ɳ¨º’nTt7àU¨ºñiTa&#|6÷w+F?´$‰0a+þh’IÀ,&i³š´Œ&Úç7²¾hY‰ÈA«¿éùFó›õv;ZÄ£ÕõÂ3£P I;­,ë;œsKÛ!gI³,ˈH\¦¬8Š¿qj@ÂÎ7 ¨ik°ÄÕV‘!ÿ‹a;«¤r6JOØ^É!Pø=\•W(¯¥8œÝœŠZH>³›Fñ;?¦¶]Ph4˜Àz^à¯X¹òµ|ãs_cë3°N7Ÿ;ï\±<·¶‚×=.´RŽ"åƒl?w)£Óá³Àúusþ9“ž3·Ò{ƒÂû`ÑSpÓ ‰e5µ‘t hÁBi'‹€Ã.C‚è¾»D¹ö ¸¯ëupbÕBÀ7oPË_²Ì+Á¦aõ<9»ï|Ðæ|m·±­#Û ¢AÍK,âÐù;€sQ¬\[¿O ´A¼“2¢oq÷CÁbÑÞˆ‰7z ú,úŒiºµ$û‰„çÔÊye âFaÚiz®½ÓEæaË&|ì(:“TL6–•óíe¸jˆHÓ†Ùáç¡Õ™¢¬Ó±âDF¼ßÓ‚ˆÖñÎýãN×ó :ýŸŸˆû…kݵ¼èºGËÀLÔLç ¶— 梪OMà Pi€øKû¹__]ä´jX_í# ¾ òBïQ'e4„ÙÀÒf#ϵÊm"Mçæ9&‹žùAýí’ݬ™ðó ¼ö¶.Îùz•Ù¦IfjQŸ€} À¯Ô`W þßîE.z´†n¨\+3 6´±lonßhÿ%p×à.xdh¶¨hÇÓöcî® ëvGE@Ö¢jB®î6¢Ò±Fh #:ÅçN*——ª7nºdlV”&HPA:Œ-‚÷‚´š Y­‹ó=«}’nTŒžÅñZY®¥u2’¢¬¸m§(4ïDÚWÜm’8Þ“Uó£íd¡„î<¼ŽÂ­á(§ÕæˆóÝ»®ás>"£ßF$éo6’Ô^Ä¥½m|à³{–І”j¥…üŸN~uF¯°ï28fX6/QÓžr pð »7Ã+^!Æ;++Þµ/k`V\ Wœߨ«3,f½AÝ"¹M¨•ËEºu“cÖÂBG^°dwøòÅgòÉ;àœ{pò[§Ó‹ ¬5S°L Úß»^¯ü Ì_¨#R‚PࣨûûFûoü~™ÿ­&zß …ê|fÛ`¬ÚÀb#ððªÞã1”8¡N“ùJa*É$”_VšþÞ¯Jât‚’7‹ÉѬ ì½feàŸgáû·ú|þvRp†m+3¸)&ÛúɸÍD´•Õµlõ~H²¿>J„‹(‰;”¸ß-’‡…Û±¾“$ÅÝq€ƒŒq¬V# ~߈…—j×[[äÑ|@KXÄF;e Ãà&(`á Y–©îic_°ÞÚÜqØA|Ì¿Ðk¬„6„à )9»¤Ô¦‡ÊpÚ‹áòëÔùÔë¹ã›Ûä2 Ea»ç®`6®’tC}%žÝ^e ¢³mĤ`o¯kÐcÁÌïõÐó£!Þ~æA|çÝwdv>ºPõ¸tÛ6n…›·rÜ*` 1Ê'ÂÚÀ](æ«Q¨p^Nô¢†¢ã}Ò*Ã4ÕÐEt'òaù¼¼DA´º~ËtׄYT4ü£õp'r1¢ YF<’FFtÚaä*è“ €´|¼Ð´C&ÄL6p•åz‰ÊŸ—Qßãvê~kõ9O ÞZÕühøˆ!άáúî§õáÝ7€Àx='âáDêŠ(ä>û½‚J½X¶?WÂú˜~$”VÎÔ‘6_„˜ö1ÊeƒÑÑF&ÔSà%Û”³S˃¹í@V*•©VG‘«ÖSÝk¥á¿RG"g”xlcÍ~Þæ¼z¿* nº>Ç.|Ò¥AÒš,Þý¸@¾A"*`Ž€ÞO ª(‰1î_ PQh˜4ëAÜ)UÎ÷ÍHIÝµÌ >ÌÐ!]ëˆH‚R±ðÙüS²„}~q@EVŸ[$Y‚V´® ’%Ñ&ø;V«3Ý­8`“A;$ëv²Žztê˜íþ?ã‹ÝºÀGiUqÛôvòA焬•Ô«$µ\iôJâ®Ë|$¡ÁöåWè>þt“q¢*Dœ‹I“ gXt½ätئÃ…ÕO¦ó`IáÄGNû‡¶˜ÑÑB¤ïa5Bƒ,…÷4`»ÌÁGn;žÉÆ(`pÿ5»pËY¡þ¡#)ÊC8i{•ÝŠ QƒÑKïJĀ䨅O¢ëÙQG7°âx×€X"h¼¶mƒM¬•0hª«xhø/èhx…sÒK’Lÿ Ïd‹»Œ£;ål[ 8}¶ËJ¤“ìR¡Ñ—©pqÒHÖ Èír&Û ˜àcdy-³n«S×1‹mܘ…š9vf“-Ò³4µò]ú8¤íøo¢:~I6i•I:ì4©Yqê8dÌ6₈Ÿ†åÌ’†) {S¯¢R°¼ÑG‘Ù9–qGCvÒàhKåE ú-˜? ôy î. \‡ßÝËmzÒt ì†]œ²+\ñÐij½ä–[+Ιsï¾á}g3oݹh›×6XgG9ŒÌ8ŒïB¥,Íþn_:®¹®† °Ó¦líPŒ¢R¨œ,w”à ±q@Š{½7íÊ:åWˆnyÆ.÷wËgÌ bÜ Zæ~7[û˜¤=ÎöIöÉâ{Úm yôÂ/²V]O³Ü€L4óU»ä&ëúN·3YÇDE Ť“mÚPà‘õÌG»ëj:AÛ†ËíÜË”ûÒ"øÐ=÷”4¸‚ `¢‡ïËI¿rƒGí¸„*Vb;N½Èe•¿Î‡`øA×j‰~ß\¬ÑgÁ%ºÖŠ9º¹å¶Ã‚BhX–…`ÃÄa$ IDATÉÔ­ð(*ͱômW´¢å2Œf¬_ãÐÜ>¸'ìñ04ƒ>¹ž¡étk€x#5ÆFDܵ!¦ Èâij@ˆ¨@0+–ÅøÌ€,R²Òn—°d JÒ­Åv²ZÞ2éI¡›Nˆ,Òs²rÛ}œv¤ZeuîJµŠÛ†ãÌõÒœ¥Ž ­ÌØû} u ÂÕ²£~–iYaû’ðš%½ŽQ!*ò!ö1IRðu¼¼aà#H-ݯ ] ¹OçaU êvNî³Àªõ®>º~+™{7˜O­GÞYä=;¹®] éX9øÈmG7)Á²”{Ü@o€)³`JÙ~ÖnRψÙ'áX@Ù€¥ƒvƒbÞÒƒºÖ|ö ® ïć7Jaù8ö¨ˆ«_áí¼Îfÿ"+?ª- ´Íû¦¥óÏò<ÚQJ¬;v° vã*Ÿ§u8Û \vDúÞÉVW’e;qŽãÜsTäC&p:Ó~÷['S‚Œ0’%0idEApm+‰_·U\ø„E;âF>4Âu@üÒ´¼çêç`4påÛÚ)Ð8x;õEðìù0|ˆÉöCj\z˜â}ý»_‹”’RIÃj6¸`¨âØB;x{sËm²›€«Ž‡wV¥Q”ë¿]ræY¯`¤*Ñ´ì=¦ÑœðE`•"’hXMzø0‡fijh¿4ÆG2Ü„°t§4…ÅaŽsX_\GÛÆ¼`D´±íXÀ·MíšÔDiwAKøh¸“ ív·‘(dá ¶ã˜ílÇ›k/i µ…§•ïq£¢ÅW’vˆyì8à&Îò¸×•×58ñþÏIL½. )Á‡Ftü#~ YaÎî´‰†TQÇ~ëú@?žøtm„îÓ$ÿû+ø±å¥gCµjÑ*-«ÞPűu N}ݾ¼ü„…¹SšÛ Æt —ˆl”pÓWé¥Á¶³áç?S¹WºÞÇNƒ{*ÀýŠ:wPWÏ|E@¯¡¢øû DËÀB„x£~©TqÞ¤>‹×Ï“}hÜãi$ÓùH£ ’%HÍ/s¯³È>?ñ5p×€´«Æ#Íj}NT-H® 2yiyà¡ÑZÝF+…ÒöíüïZí#ZU<qÎ9Iázš\_0îMÁ Óq§Uˆ˜àCw°¸ë@tšiºí˜”Q³¥ÝBwk°ÓÏ¡ñ0þ4Àº¶ì}Æëoaß'uVÿ¥›L*§ñ“©:çþÐõ[nÏshvEoÿ92Ì… yÄ8QPÐ%u3ÃcÙýÊÕ^Ö£R*-`. Pl¨šGÃìgû"ÔDƒÃåU97]ß„k}X.0áGËTâ­ÑH_„îþlÒYeô‰Ðig±øD肸—öð¶ÉÉŸHp±£ îÈm%9–Óá]NZÖrÐ÷¬5WÚTZ9Ç,ghDÂã‰Û¶|øðnï*~)WîåƧ]ÉðáW â7Èh'¢ŽšU-ÚÒO½F£ýêhôüúø)¬Æ±G€gn…s”Š’j-wJs{á˜C³[©Õù´ýH>¸lg`B'ΟKà½'²7ÁCï†}ŽUô¿]?…ÊN0õ?Àülaf*£ð¿¦ròªŒg­òS=oeìðkKzúF¿m…Ô°OãfÐ9Åý·²fó˲½4m™! ¤#8À‰€´Kx,‹ý'´L4pét;yL‘a–ˉ+MÅÚ*qÓ©&¹¯$Á¹±ÞmÒvJq¨wã+ëg«sH{¼,Ž™eGÒ6º\• 8Ÿ¤3ÝQõî\ý¸uIç‚~sœöƒœ}A4›SÜzˆ®uIr½!žò¸Œ¹ÐõÎ|@´à`}®på¢ 88ï† ”8/¿Ú÷Ë8–æjÏ›f9ŽÙ€ Eù-`̯QûÄ«yò={±ìj &"¹å6 t’ 5øÓÙ |dU‡^(êôMoðٯPœş”TÀË¡² æJ•zu» >F«‚D‘wì–ý­$»`ÖY’ÖôªÒ0p‰IzŠIÖ^¬ÿ9*Ò©èG;ÄdÖéT“¹­vl#QÌVq”ljù=jÆäï¼²LËê”aÜIëš!Y€Áµ^=ïvn@á-b ×ÀŽÆØ”Clhjݸˆù({k€^Ó>%á¯ý°ªVü€ƒ×RÜp ý»ˆ<å*·Üì•ÇÁ‹ö„ ¾Ñz[•r™‘ÑQä–ÔΘAñGAÿ-H¡Á6 ~ m/X·ºž…{ígøYTô£j¿œÚ7 {¹#~Q¿aœ :¯â–¼ï ²‚tJˆ0Ë% "ݤ]ƒ­n×N±#+©O èô1Ó÷Lq»‡óÀ¶ìdBf>Úñ,µ£½àp$λøp3X Ÿw/ÕsïöÓíß·™±µ~º·®Ä 8œJQÀ”ŠÌÝ©ˆ~Û ëœûW@I¬y0ń޽ƒ7J¯íbÊ/æñÇG.†=Oá2à÷ößUá“ÜrË-¤_Í:‘4¦k`Zpô¸ù¦>j§ P»CÂ"(®½OŒ¨ç~ñ°²O?»¦©Œ^c¬¡7ÍÊ/ý*ì= K¿²<}—•pø}nMzž†•í1¢–gV„žÖñœL‘É^€>#}<‹&×yÿ:€$À"Ž“¬GÌLtˆ¶lYí¨ IÂv:<‚Ž]À?*âM{ò‹»—9€¡ÌNpP–0¤Ãb¶h°ÑËTã-Lw×6+NYWžÅœnÁ^=0PV)V„n`Á«Á¼¨‹ÑÞaJ÷ü–žó^ ¼‘ö<zJ0X§h€…ÉðHî\æ–[ðlÝB C3!f7-øÐË4¾øõW샵üV¶="Y%P E˜Û rwxäjÂâ`ˆ¦ª¹áÌ„'Ñëð[VìíWxõ;‡P°ÆXet0QG`q"ŠÑ³j?É~îmM{ÌèôoSÚêDJVÖ``²ª¨O&þùb*m~Ç*fèèOšÁ2ƒí²ICÖ ¦Uðá ¿ñ¢hõF=œ¨…C‹;XLæL…¿l÷ÃÛà1rì`ë>¾ÓfAÀr U 6Ypä>°ù>˜ ¬·•É6ØÎR°@‚þWÛsùì,NÿÛzÊfßüÁ}o’” ¨6rç2·Ü’‚‘4QC@C*·zËÇ ²ø5”JðpMÉl³F¯K»ÀTQÊ!TMJÕBÜ‘”ÄM¹ ‹r@ò,÷g-d>ßÀ”·Oìdaz«Û¶s›$Ûj·µ¼£:¶­‚(±¹´ÎSšãÅ]O ûwz›v´Õ®ãUPŽS#¬0ÝqèÊ´&˜V`0‹g.Íq’ˆFýWíV?³. aØ{øp×x= íËá®óp>—ìï'óy=`m£O…-Û`ùk _BE›^å-P/P”°û°›/>ô'`ö¯,ƒ –`–„ŨKE ô‡gð¥›CœâÖ­tõCÿÎUúÞ$ѵ|ä–[“Îÿ|ô­~àD¨~CØýƒ«j}Y·†üñÞ¼ÔÏëAû5”ö³ö쇒PÎùð”„kGàW4k>Œ×ÈH;)ÕŠÔª¯EÂ’Ô'™ˆÉ»NOVŠ Ú7iYÝ8Y‚N´×.Ð’åúNƒ‘ÑÿÚŽs·\ŽZw„“—])¨“.‡®H3O±jäaf6­VŽw¿8€¤Uà¥0žô?Ž8â0ayׇ±]iðá­Ç(x–»éoû€¿åÔÁx'¬¸V”àÿ uº9¶ÀÜM³k¢¡4®»F5ÿÚߺ±¾&a)| ûk˜B”‘ÔöÜȇæ<Ê‚—õÓ£×¹b3|cIÉP© ¹å–[ KÀÅ_†)O*%óï¶O^yŠ N$’‚bœÓ–%ÙÿäñŽ/žÉÝšÎÆ[^Á÷™oDNU„^ƒµË`g­ @†% š pÔ_ãcSmÒ€¿~R†ôq'ÿ’¨q{÷—)|Ô¬ê;:¡!”6—µß´Ü·$I{'4>D‡öiå;±>뛲ÇË:Í)T*Ñ̟ϲÎÃïœÊè¼ÅÞ²L§:Æ4ÅèQŸÖâÿ Ù¤`¹?ë º×ûQæ–[³Q@¥ITìmJöç§-õ}Ñ=°X]ƒ-6:v×:¹ŸçE@Xð2¬p³7 ÃÍ?ƒþ„]³òÄ‹0¬%t]Q£ñîÕ‹~Š5 êPÖ jå‘ÜrkÅ,©ŠÉ?zúþøÊ|þ¿îå5wÏbtÊ È¯ì‹\~0œ};æý—°ûÞfÖï†ihufüó{¬G°}¤ h ˜nÀ·ªô«A»ïp^^ðáýHR!'¾«‘`Ax@G`ašz ͰâÊË;1öÊ ¾_Ýu…Qç–ä|©žýj@ÚA½«M€ñ|¦ìpÒ‰óê"¼ø- Ç—3¨'pŽ[}Ð;±OV³'fÁÒ2úOãÜ+qÁˆî8¼ë Û`‰|”PìVpj¸ ¶Óðv`•½þ.¤ì‚JÃ* -xØ‚gìí:ÍzqŠÙç‹OƒÊŸM³açuœ>>v%ì¼|‡‰ñ!Y¶xà,ØïG¹Ã˜[nms0…àvåï¯^‚|ûµö³+¿”p2ÔËPü¾`ý;`Ö…’á]¡tüK@Cªg}#*ê1j¿œw­‡éê#º]§î£Îøºïç ú :A°haT=ˆ”´BÃëfà [OÈòÉȆ•v›$Ë`â(yµ (S,ßQÀG–b~t ¸me™"æ%qجºS€€oªUW€ƒô½Õ”¨4©S¬3iµ$*?7h½_ÚD‹4&I» „´§ü6¯®‡SïQ²_}@°8x'0èµïó©ÀÀ>¨¢ð©À^X²¼¨ÞÏX0l¸(ui¦–i¦o¹ j–tíoÀz€[×ÁÐt^ÿÆy,ßnüÝm>,ùð‡c>"˜úèbø'|DÈ-·ç§I)ùÇuqò³¿_ˆöÚ.†l?Cc}6” <öÉF)¹çcpïð'¶HÅnõ,cÍMü‹Èýœk·P®{YØø*HFÏ*"Æ…8Ÿ½ç#‰/ÌÛêòÉ(J8‘Ns\¤]"ƒ"ã›%‹u“Md²nÓîc:@À™½•):®¤Šð8k’h5ð¨‡SD<ÔA©~é4qf:‘š•UNmÜ"B‘ oiU÷Ão™wôÒæBpÚ•S‹Q´íQ¨HÆ£À‰ÀJT¤c ªPt0[(F›# *—û‰,{/X·€¶O€QŽŒU5®{ ¸WŽAt@PÑôNÕXþ›^8m+VŸÀ:Bb|áX8åÜýϦvØ2TGî G·?»ôî–µ rË-·`3 FÃ⬃y;ÁÅ_RôÚ[uõPèP5¡ªÛ‚fSÇÃfåÖöp :zn ^GçÃtm'ñ„¸£^M =w¤"(âáF–ÏØ–†Ë/ÒaŒ«íbÀj·a»Ù°d€?œ´rìX: ’xŸì5"y HöÛ¹•˃œðVÓ­¼ÎeÙ3»U„ì¶$3Bq~{Ðñd ç}¢R²Z¡ä úÏ4ÚCÝ›¸ÏÅ鄃¢+A27yÀ*{0Ü ¥öZ”*ZÑk”.` ºÕºÚgPý”FaÐP3ž##Í:¨‚€ŠýÇ/°NÂvÀÔ•¦‡sþ†)a«Åª3˜½¥€¹¡N÷]?‚'ßHuTP0[†êÌ_O_z7šP9ë¹å–[{¬Ñ°~þS{ÁIpñL½4TG!kÁ©R–Ç©NSÄ-=ý— è-‚£ÒgÉo|òîãÝ_úŒÑíšLKRWÒ M¬~o–ç 28¯¸ûkiFR†v…É’ÂÕjªÕdc¼jm¯—E¨ÂxF«$ŒUI÷f±o˜ä`JŸÙ$÷+‹4-¿k' ª“)Yq×Eý^¥ð¨}[¹O¢R´¼ïF‚ë¦ûÜS°É»ƒ€ áð8X¤) 2Ô‹æ‚X…7(ŽÒ¡ÀGÔT¥Ãåïh„ôëj›}Q^® –‡õ+õôаŠÑ o´Xߨ3 °í\¨ïÅŒ¹ÝX–¢ÿØ0lÏ6æà#·ÜÚnîãÖ?ÿ„î™0(ôiôÏc:£¸LE­øZ"D;­ø‹ZÊý‚¶Ó[œ{¾X+¿Ë¤=ih"ÁŸùîD¤" Mh;×e&£HÒc:³n& ‚§3M¢ç”HšT§Ýø+S ŸåÞßb¼Öƒ—*·L‚:ܨZ¿‡µš!iÁOœÐ¯nÂ`|±¹_d*ê{Ð:háóè!í‡Õ†x︸¥N|G VêÐ_€¥%Øi`Œó¡øè7‚6MÀM`~U Ð ‚’¡ž›½NRDü§–äv ž9NÕ™tkÍâw] Šþó4J@õP>üyû ¦%)Ûa-§ÙÍ-· ±õ;‚¡g{9ØõÔi첪B0JM Eé뼉3†DÕ2jcLИãׯKÆsD¥3§QËÛAIã³v"3¦UÀ÷:fíohiÑrG¦@bG­ê-´s›¬·ÃvÜ‚\„]«8zQΡ»ó¬Ð,Î êP‚^Ý*t`¢E8£~/ïƒl¹Þµ`ã'lG‚A¥Ý…ò$Ø× 2Ýš„€2F1-à\‚¢[xÖp}£®¿·ó6QŒWƒÀ­6@}|<\‡ÇG@ÛݯÞ<4N?F%Pk(úÎ^`v]ÒÛôƒqh-Âo-¸êfÐ`êbM†Jë*Ù :’ÝFL¦|\b¼ÊO×¹üá"Çž¦”ϪµÜÌ-·‰´«V8ä°n¼áW°b¬f‚¢†¡Æ°cvSާ÷o ¹ö‘_›—a€þ.®žSŸ0Ê_„ø"YD>:)ÚjxÅ=W‹öËqd » í`,hey;×M´2údH±rLG9DÞÎ1ÎLAÒhˆ÷½b;a:þ9úN XÉå{_aNkViM¢ìN¦cµ"Fè]çáKúÓ¦b…&+?¤Eœ§»ˆ²ÁØ(à&`¾€ÑíÀÕÀÃÖÂuwÃ0h‹ Ô€.¡Ñ7ºäñÌ~“d. mó§À%5ä] _üz.¶ÐP°lf¬bv[Ä…ßú¿|¢Bån@~Ÿ{_\ã&§gøÈ-·É`unÿ”Nü7Ä_áÞ©×у¤¬9Ä‚ÅÀŒ£ÔXjˆ±dºgRG'<+ª¿3ÎÅé_ÃüˆVuæâ8ÜiÅ ³Úg"È}²Ü¯Sm>Ç‚Õ ËUÐrm;²NH–7rڇǙí/â_È”´˜X°ApØ x‰€»%,vÝx¬nЀϸXÀI<¼äïAþNÀ,‰Ü¤¡ib1ŠÇ÷XÁõÇKæ¬.³ÏgêÈGLÄ*0Á³vAz70å‹á„ö $¬ùÍQ,þø}<õØ6¼è#·Ü&ƒ »¯øÛ>ÃþŸºŒ­«7R¥Á”ÿ†ÒaûJ¸k%¬P“þT¼Ãv_[¥ÉˆÕ`¬ˆûå,tö½ ü‹à½ûùi釸µÞñ6J$LÄ}L¿±1 f¬,Ù¯²ÒúÈZ#Ä]ÿÇÏHz,#都&¥ÞÍðL³#‚’´Ž¦ó .ÇËïán'•.Ðc06Õ›Že06ýÇÛúE%¼ëý:áÓf+áEïþAÌ“…Ž×Ý™øÕ|øÝ"dY?¼—ÅEÌÂÕEE>üÒ¯¤ z>œå”˜Ø/ƒ™OBm) ß À”¥ Ÿ°U"~üÌË@?Ä| _Ÿ —¯£ñÁðrÉO^'xÇî’M7Íå• ºy?ÄÁ·)…2 f0h‚€kŸàgÇ«çã™’8í–ÜÛË-·I@föé|ëMÿÉùÀìàï (^|\Ðw¼dù°éé¦CîŒwŠPb_ ÷0¶fÑÍdåWSh|va–ÏDž›öÛMŸ þ ìÞ>9.ÓT˜*z˜jz˜Š{œ‰¾V& ÓL )Êwú”>^¿1Ðgh‡òy;u?vÔ­¬€ÅdБ¨H‚¤)>‹#´½‚Öž±.Ñœ‘îr=hî$7hÒfrÀŸF0íµ Û_‹qýý:ˆ$ÿiZð"=×Úˆpöƒ~Sв Á2è{еÒ|Î9nQøðV"œ»ï§Y ¸6·Ã)Ñ¿9°8JÍ\4ÐÏýK‡qæÃ5nÿÅ¿(Ü´…§VÀÃß–ì|Úž¼sÆÃ|}(7ÓØZgé~÷RX -ƒ!4ªUA—0éZ*á-:~ФPmä:¹å6 ÍÖl7Ñ/{K¾y|òq8|/¨5g¯€¯ÁöÍj,së=§y!a9°FÓj Hâíã¼`Å P´àäà‘“„¸¹Q ¤‡8Îù¤YŒ¤4ÂI—µz.I®Ùs÷ÃEQŸÚi!ÎFR1²¬—wRWd2“¬GPZ]ÿYê´`#îþÞe¥çÑ > v‡½7ŠAèA»svÄœ¬°&º¤8Õñµ2ÛÇÌv|ç 1ÁK«Ïd ’ä5#~àBclôÃ[S¤»±;7» ôg¢ŠÑ„R<ï’ªÀ|É"(-êõJ„¬bAIWEèÅ?@÷é éJ Ä´ÿT]W3žBêuIW—`xX"‚ 7”™¹Ñ`øÂA¶/…¹½÷ý8 ÃЭæÞ^n¹MBÓ4eI<–^¡–}ÿ÷óæ³>ÛXeÔyT@]6û%gm¯é°‹ kGàwU@ —:"…uÆ šc›ŸªºéïüÚñ)Lš‚7:ê3žs…lE Óˆfñ=í6I–ù-O¢}–45+Q VZí$ÎÈŽ”¢5Y€G»£Î,·WH(-ˆ{­ƒ@ˆ‚¼Ž£ô.'Ð)8x‰½~+*gv½§svwš§óvŽ"Ál d—.ÖFÁgf-¬óñKƒŠjS’ž’0édFP}‰ôù.Nÿ ª÷H >â«{¯U ø0¥Ò Ìê¬U ŸU6¡Ôtq8PšÏÙ”|÷¢Í¼â¤®¹z¥’ Zû+‡‡%†b/¨ìu(EüÂae>±¾Î¾û@£ÑȽ¼Ür›¤fY’JIgé&%ªúóù`AýÐv†òÕOö\›·ÂÖP] #;Á‚ÃÁºJ4¬±Q¿Ô,gŒóF}-—ó¯{€‰_ÔDúôÃAƒIfß[Ù¶]©WqÛ锘_»Û2if9d}­šÝ8ŽE’Z4”¼´ØNVŸ°¶D ë&+ ¯[³£ÀXÍŽVhr!>“†ßr‡I) |øÐ]oøƒ½|gT4¤å–\ÛiR÷–\àËOí:î+í~AmùQÁú­sƒ0÷ËÍLåþ½~/oè8ê|’® †Ô§ÃÕ_£#¯ò+h-òញ}‰ôy–¬­ö¬ä6ûÞÛIB©âÅ0`Á°ëZÖV j¯zŠo[ ¾ÿ‹Ö¬TgÒc­ÑP‘‘y›nbþ#?ä ðjÉg7š” zîÝå–Û`#U“J©¥njWþ„ýn€ê(¬û ¬ƒ JHCÌÛKÿ¬H.Fž†Û¯Qb¤¦ÕÏ„ÏæÇðèíßu‚5³¢hÍ£ü€(ÿ*+§»Mޤ“§­N¸eÅÄÕŽú`‹ìuF ¦ú–VS²´¡©U“‰¦·•›¡ÝéUN1wœ$ËHÖ+=Âùvv¶u‘¢òëè²Çn¨Èš¬!&cÙBœÙ§N¤ŠM1f^:‘v/:¸;¢‚IS®ÌˆíDhðöSZ@æQ)Xšë¾+x€sÙ¾gÇ/9 «·Àp£É„ÓkÀÔK%Ìj•ÖÞÅ~sx.E#ÌŽ=MçÆ£sÞß ªÃV3wêrËmÇ3ÁûÏ-³aÝÿ}ôk—À¼7Béç°²ó A÷|‰œr5l­Â”øëJ(ø“Mµ]wouÂS¥ÜQoJ–—Ëd<³–w]#!߃Ҳ :íÊo\bÃêDêUÖiWY²c%õUô6´«µöˆ´±µ%d'€Ø®uq¹,·ñ›…pfûƒœ½VgâDCü¶uÒ­¢ ‚ƒ€‰c~y¯Sl`R¶{‘±QÍõÙI㊵ۑ_a×2é¶aë“ÃOØÒù/£ŽnEøð+fÛ~÷µ{uç5ô4Ô7ëº&Ç IDATÃKÕ‡˜Â.»Ù4›:ú¸ý®·ª¶bTÏ–SùÑܹ¬F;A–[n¹µÁ$OìÎŒ½ObæJ`‰`ÎyPùáäoa‰±Ó4Ìqh«»˜¶¶ÌÐJ8zŒÈ±ä,îHHTC÷8~¯°h<„§¦j1ý„´>b'&ìÚ¥N.ÚtÎ"“;2ûvµv5 $U5o'Pi%Š„¿#ŽZgŠê~‘[ŒÏ™©…`¦ ¸@$.ȈIÂÕ¹y–‚Òp‚ÒuÜÇiØËû€¡Ò¯zP‘ ªX¸×þìÌV;3Ö8qÄ ½³á:ÁiVÞNT:UXÚRÒt¬4éaa×Ó»=1ö«Ä&åži èÂ̵ˆûR Yf…ì§ŸóΚ(p±¸¾ÿgÂzK–5•J1ý¥ÐSË´à#‚o¿âuN"z¾èÖ;1£tÓ¦*ÚßÜrËmÇ´_ýæ†[xÓ§ËtW$Ý߆Ëv{ýN N3]ßüúÛ„êT>8Œu}.®Ù iÍI4wTV‹üҭ¢¹†gû¸`$Ëȸ“ŸZŒIÓ¤5©Y—¨6eŠ6Û®Ú’†åMÁj•fWo#pÈzÿ´Èz2Sôm§“ž&7ͺVR¯ürJƒ ƒ½¦»ã4_Ñì LG?Js6}ØOyœ^ ,ž±Ç*ÍíZ@‡![èP:=³1‘37a<ãISµâ‚cˆ‘‘÷œÕ<÷ž3P\ËK.€ë®3šªÁKÕ'ɈxÂÄzå=h缟ÿ¿é|é¢ÿLøä$·ÜvT+ËÝíOé¼h¿ÝøãÞÆ ¯<Ÿ}÷œ|âAÌ0ä5{}ƒ¹'¯Á8ü“p…`û~q:\ù+Ŷç%ô 6O{‚gÜsR–wÚUÃ3éFko.<‡Ëo, <Œ›’•–åj¢Ù±âî—¤=B@dÚsÊ€¸gEÛ >&Š¢wGÓñ¦!e8²ø¶LÖ<Ì”Ž €è¨hGŸíü‚Šx8mì\ l²Áˆa¯_dw²w¡Re†›K[c<]`°‘Ä=ìd6hÃ>A×%-¨ŽsïF1_¿´+.œwGÀÜ÷Œ'D0< ¤€š©, 0$û^(_âc0úk(Ïñ;èéÁÁÜ)Ë-·’U iÀ3ÇÀ¼›A„IJ4$’gVJf…]CIuþl°ÝVLw×€XŒÕ±ð¯iôs:tä:0‚"ÔpÙkžvM€c2žâ>ª&Ä„B¶±R´À¢*éY‚’‰ F–NN+@¡Ž9’f·NAZ …¬€EÔ6qRôÂ4üÚC‹jÙ§ üƒfíG X ÜFSY}ž½ÏjWj¸TËÞÖÝÉÆQ…… °HGkèlqï·¤TÃYß×Iïã$Ëüî1‹xõ*A÷¨о÷>t×…*Xw),zÈUPþüÐ äàFø©Æ×¿g𞫹E]W­ Kæ©V¹åö<µ‘t•aÞÍ‚®’Ápµnw²’?!™û5K‡Ñ]h{ÀºBm£J¿ôN–Ä?‚ … `š=>ZÀö$Þ ÀîÓœcšž1ÄïÞãÇk’ óÅNÒ•fÕV»é€Û¹o«ÇÒ+ð©V}÷2=¥!ZÜ®•m[YÞ®š‘¸µ#Q´µaŽzÔº4ŸÃÚ «ˆ[ŒìGß*‰¦ôÖiè+WïŠ*N°Åà uªW/$è>ÕˆWŒ—õƒ•äÈsн9½Þ6w°7Í(,].iÁyœ"ö¨HX;šÝdP‹E²Ô??ö5͵¯ßõ÷»5 lÁt¦ÜâNWR«uc¾ã`ô,â—«×pþ&=Ç.àÎÛ¶!%X>pšR“}fcî°å–ÛóÍê 0TÅ’FÁ”4ìýáeuõ6F]gx9ôí ½+à®A°Ä؈ƒð€€°ÌwŸæÔ~8TíC¨Œ€]lð1ˆ¿È DGüR0ùÕiËZ[­•cO¦ß/Àlêâx'+‹h…žbŸ´ËÚµšvZY—v}'ê8Zùw½Ÿ£äø¹¿ë4k7üн½N³ÛÉv:T'ßYßcÏò,@¥i-®B¥d­E…™«4YŒÜÑ¿|Ww'¤œ'/3I\’ëJ NJ}4€¤™ñiW$Œ©…kDÜ5~dºÏý§{€Ÿ“ÚÐeß ì-.Ƙ{f™wRàxm€W¿ïàAö9ø üøº£KÿÂ~Û5*¦ÅH-wÖrËíùjÏÑp5,îè/ÁVxJTYÛÕØlªÉ²ašéQnzyÓóîWƒáLsú¯2Ѝe6p p9ð€ JjŒU^w¿¼T¾Ÿ¢7oÐøê}·"Ögù9ɺ4ßÓî“dLS/ÏJm=1é4øH‚Û•†5™ Ò'`¤iê?À6ÜoÆÝ¶? €¸K°›‰•’5dÏò Úk•±xNg®Ì«§sŒzh“üI®§»àºÀø‚Á´|ìI;Å´<)Ø cf‘ÄÚxë?ôR`|ˆî N: Ã´VÒ bAИ'ÕýWÀ¢þ(¼Þ9OA¿!0ÁÊS±rËíyiB€”pÝqÂë7Áþ‡Ú9G³aƒ`½!4aXÀ¥À‡,ñ&þuÞñÔ™¬+/F¥$?Š*tßê Þ±±Nx!zâ¼&ÄJ<Ú ::@ڱ̹޲)|×’üamGäC´°]R‘…:{Úu"Æo‰«(šut#}®ß¶qÁ‡ >;‘sÓš ZoGåtÄuŸå TVÅçAñ»6º éÓÜ¥GÅ¥áaô‹þø¥ÿ¸^‡~xMJbg ‰JÁò‚½8|ðA€1ŒvÂS±ü>GqÝÇAß Çøœƒp Ϻ1š!vŠT±x²b±Aàmüâ<._Þ Bc0¹åö¼6)¡»¢óòeÿ‚_¼~q.Ûf½œ$…†ª!“V³?ò_8“m%%}lj•ÁX±T/Ý®{¹f‡ÚŸËög+ /;Û.]VömgVÒ èÉ”†•¤=ê¼,NJ'~GšÎ$ûîHEꜟ:mqy;R®’¦YIâkŒB,¢ë^Ü Â;Óï#¦«ó”ö½<`“©(¶Aü¹ÄÝZºë¼tÆëVHÏù„¥XÉ}0çu¾¥Ç!v–.à`#p¯4<¿Ez~·ôY4ƒäw?ÄyN´ˆeIX°¼`R†¬ó;Žø>÷¡üžWѽ1vý˜bQ¡œ ÓžùÔÈ‘³¯­34z5Ý?9‰ÏO¹›ßQE×$¦•;h¹åö|·átÃöù³/Û›+wz„Þ}ax5T7(Ô$ßl>ñp ùb÷å5 º  êv£ãOUëîãJö« ¬¦Ec¿T*Úð}&Üœñ´áéS-OŸT’¶8:¬Mb,j3Ë"yZü­iÚÍâX Æ+£§i×ÈÙ¥-Ji5í«ÐÔÎZ‘4Wf6Z­ï2?V¡8ΣHñ &ù?¼!aÍvÆK¨0v7M•x‡=+l]ó´%:[éIk"â¨z»e‹±3cîó‚ý;7£r{§¡rˆk®öL×oq_3±as‘ CJS\˜öŠ’H£w~5 ~}Ÿ_¤ÅyNñf™fuÅjRTz‰4@H˜s"ìRëO°V¼wÀœSaí`¼xà ‘ƒÜr{˜ÊÒäŽÔ¿÷>Iùz(lý¥« IñVÕwž~Eÿ¦Ae9®·þ`°?Š~ØqšE€òË×õΠEýV™qŸç~ô£«»‡´ˆûT¸fo¼Ä ît`—9 ­UƒýaÀŒ‹áÞÀzj¦  (K˜±¸FÐ8L$Ól~P²v°€(jäRƒ¹åö³Q»Óq›ä_”»aYŸDÊw²pùJVs-u»/j¸Æ¡2p£‡ÞKo‡§4¶@h°ÆR}–é9–;R>8ÀÖ*ÚY³žÕ@Û¢R²ž ™øjÕÇÌÒ‰NÚgfIõÛj=ý}XËâ·èøT+½AëiDIökuYÚmEŒ6¢¨sã¦Ö$ýNˆ³”ˆÄm‹rzƒò꣎U(íE‰Kãëe(òÖ1lð1XS†#‹pƨ*H¯ÑLÅjø8Ù"†Ã´Âê: ¸FÂ{ ¼õÒó[ݳòΠRDE?Šö³ˆ¦c±°gAF8íIgØ5óÒ‡QâB0¯ Y¤ãá=g¿úÃ:œYC8k!Ìß»^ýw@íè6`ÛH“UÄÉÇîÖ`o)èû„Dì…ͧúÐvJ¯*P½±Æ¡ƒŸ=`RÐÀÌÑGn¹½ m¤ ñk$ßx® »ÍºV=Æ“©«(ª^RéV%{Ÿ#û”°iý5 ï„,³àAÏéNÏ­%Ø ª.²¤Áý¬¨‚> ¡ ÒG«~î}ù±AúMHµ³KKãwʘ“¶IŽ™f’y²šÖâ9· @´˜ŽDàƒŒ—¥Õ$‰³½wºÕ«¨ïqk+’ÖxÄ¥6u¯³b‚š8)/a: q Ö¿}‡y¨Š !7€ZÞ2ª°¶ÚNùjÆÖŒÕr„¥gŽ&i´-¼bSºÏïÕ]€Ëýr"6&°  ZC“ ƦYÅäIÀmmóžÖBî_dªè~5Auš Ü@¯€-ëan(ìõÙ°á6Ø8Ú¼¶Ï˜"ô6`Æã` ¿ã{°à¸ÛøÈÍ_ÀøÓÿQ\$8öb‹¦™;a¹åö‚5Kòµ¯–k®`·7ްíˆQžÞÃ# |¨CY]ª‰¦FCQõö<s†®5 Ï‚rlËêWD¥Uì>OeìüIØpƒÒÅÚl‰kQ㤛–Þ 6,ÏøAÐÈ*K 3œE DÏGË m1Íç?û#tüÙZí.HïTZV¯vÖ`#.¨H>â¬÷SÃÖ•4úáDk1¶Ó|>WÏ Rt½ºìÿë öú¡j!ªÀvT¤ASE½ÊX CI8õn\ZÞ¸L˜®GJ.ÐáÔ&tÙƒMÙpn^doÿSû÷;´Ž2dñûMI(zÓv´I‹ÐÃÞãô`Wd¬°£s}K¨ô„à%à(| ØÖ¤u¶\퀽ú¿CáLÊ ’QËzJ]i[¹å–Û ÓžÓäA] }}˜gSfu ^´ž K*ðàˆ=!Ò S ûQàx5Óô0ð*Âït-Ïé~hÐeÁÇÁÌáz ޳àËv¿åLÐUiRÔ{µ@ŒŽ}6iþ=ŠŠ7®Hb–¼­w¿$í9×Ðh±­•‡ ýnÜÔ§´ ¢•ˆIVJê^ˆ |ĉvD¥Î˜„ $Æ¥èÕþOQlXx‰—bÐ9^ª¨n…ÝùJš4½^Ô_d|*—õ­7¬í^—&6NôÃ[íw^^Ðå'ZR@‰.¾ÈŒntM4\×Êï·QG¥8iÄ£a Jà \A*íA’8*éà)÷ÜG%Ñì”»é^ ì¬ÛVÞ7½LcØRT˜E3mðçÜO%CãéJ™Â¹ÇÃZ“w¼Ü²ïF‹®b>rË-7REä·áß>œUÃsYc÷ç ŠØ6öœrØ{_ÁlM0ÿè~¥†µ»ÀfÁ’ù*êí°8Y`Y 5è¾dÌ´à;v_Uõ™pÔˆ¦ëO;ᔆæ5Î)ÛÍ"²ÐjV»~§Ú0Z9¸Nç¢íNÝJ³­:Â:6⑬¢ÞÏ–8$©‡ð[& /hs Ù]bø8æÂî„«4…v¤ç|Ü©NEÆ ;¹Ó²‚fjÜu&Éò_ý@šÆxªa¿Y{'ݬ€ŠPéVímfÚlŠý}»g€q€›À?òÁy¾ÞÔ9oñ~Ò*,+Jù=è„èt/?­çÞé-€¨ÃYÜi©ë:MBé ˜~¯ÁÔOZ¬¹ûƒÌ|ÝÓ%¡$¡G¬Yõ#,Ld¶^FÁbð3O£ÿ\"ïæ¤…ûsÍ¿¸ä“õÜóÊ-·Üì¾¶Æ ¿üùí7°ùðTíñ˦×ÁМZÀ8´ÆàÛ$`Ë,ïÜlñ``Tg†a›aË5ÖºÇHaÁc,†‚P}×M?gr­1î'‰öÇaªêTQöŽV<ÞÉã™+£Ç1-îJ»óŽ>ÒFA¼aÅVÁÆdqòä­ˆYŽVD Ý&‰.ȺÖîû‚k¦§×î¸>PÌWs+°„ƒïvxu6ÜŸÝ·úµßu Š~xÝýj¼Q ç8ó€å6ؘj_ƒSÌÑà¥3`?TÌ]4)%þÊÞA¿ÛO`Ð}-ââ'©èÚˆWWÑ}~Ÿµ˜®©ÿ­¯T7ŒaG6ôû _kÀ«,v~àö|¡Ç‚©»€v¥EiWèzt/^, #5J½e*ò }‡0ïƒK>YGìU‰¹å–[ÛMÚߟÿ(™U‚žuYdcó€éï]—<{bcß=Ü…^y<ÓþÇ¢ñÑSà Є†¹j›UªèѤÚ-Ûc†a…{Zjù€l¯˜¡câ(²-žkæAöî'IÆ:FȶAÛ;À£Ë:zÅÀî¨Üû]€¨T¤g=ÀÁ dÀ_áÚÝAJÆkoxS±ðSvÍB¹« TÄ_< ]`kÐ>öRTúï .a7 ܨ ÏŸ@E?V¡r{a|1»—‰Ë{¾^@äM R@£–剓ʖàxÁǘk!Ôà|Ú{AÞO Õàh ´ ­œÜ¥PDÐ} ”Aƒàµ>¢Qÿ 5˜2UðùÚ]œ}ö <ôôå,Ú ·ßxÎéÈ-·Ür•"ütÎ8vÓ^>“n glø!üý–9Üß97âe#Ä €ÆƒwÿV¬ÂÌ£4‹ì†h¦Ny?,Æ×ËižïÞÔ%°_ØÿíwÇQ*¢"Kìß²›ýû«’ª€pÀ*NA¡S|îVDw¶<¿Ó›‚VlïUXoÄìŒõ°Pë—  …\G? d/ðp®i04Xl©ëù°€®2?Úà—À×ÊpkVK§ÏÇZøôèŒ^hÒ· >Ò€=þÞ|ÿá°©¨Q¨Yô}«`ÕåjßÐ¥Ã,¦¼îù <#À’0MÀ²Id²ø5Mâ·À¡w¼”c¥3Ä-@‡èÂó°¢ò¨õaûEmõ=ËÂô´Û´ºÌ} )Ïa IRQÌ|DíÓɺ·#Òð8Vd|Ìv®K“~'êµo«LCà_ÌfžžÁÅÚV7*ºÑcލ(ÇFšº pˆ™Ê°;SÇ)¯º:Rw-„„D òùéR8mÕ}ú¨ûÌ«má88¬&ÝöïŸiƒ±p/ l Ùï#.ÐQ¥Ébb0¿AB‹@¢ŒVgŒ’ÞóQ©\îˆÔöýÕmƒ¹]P) ÝÖá™…pÅ9ð¡ áF°Ží0^{!Üÿ ¸¾Ì;ªóÍïšè%èªÂÓR§W˜üã8ì6 óÐGn¹åbøj—ÜcÒ¸¼JYW4Ýš‰×h,©F–÷è‚K-ɨ´ÓJwƒ )=,­¥š5á1FLÕîÜŽÚn ðWTÔ|KBâýÜiBÄ$Y0’œt p´ €`…i¨–I³`·àc¢Àg×jó1³*­Ö~d>üFø€è|“x!A.©Ø÷ï4û½A3ÍÊ¡¤íGÑÑ>ì£Áßì¢âU4ixƒfr‚œò0ç¶`;¯£(ZCÓÕ›1ÁŒøÒ=³@zQyÂ{¡ê\îAqº;Ëõ¨Û/Ëõn\¿Ž_$,ât^q,-E¯²~5-÷}£ÁR=¬•`­±>¾üøÓL¨nâ=|ýòñÐR(PuÕ™ë"Ì-·Ü¢û; ü8æÝÐÿu(hP4=£~±6½ŒWiôïb±½}ux¶Øs„0MYk ܧR•w.³ÇgqOX¹'ì¼cZ;H܉¯8cQRjÞ¬¢#)qƒß2½ÄoYlêÚíÄ'iÓ¹ :}iGQyàÃùl2> *é¬rè `„E? ¶g±L3ÂáÌ9ÚÞtš~T1öLûµÖîTÿŠJÅrœs3 #µðç3÷¦õ8Q§¶ãäÅðÈܳ¾éè7" ÏÜ»ë5 Y…Št8³V4#5š|î û³åuŸß ÁJ·n³bÎL¥$iSãhËxkoœš¢Pp‚€»ÂÚ°Xmª€ãt8Ü„÷KjŸXDñ3«˜º«ŒŽ@M‚å*(모ÿéÄ—Á—;W¹å–[¼Žï±°ÛèÑa0&]÷ñ¯‚Aþq¥ú~ÿ!°L‚œâ7‚jIR,(A}xâN5f ¡Ò´jvù4j’Î Þˆ{LôŽiaüÙÛ @²Y’NŽ4Äñµ’žCb^Ù!ð‘5ðc´ª·ñ8“ ˆ$¦ç?søÒÖ¸ ð9ε z¹opg&¦îy WÇ×mo³Á «I 1ìUNDH÷tªº 0™G-„ |HTDbsŽ·TÔeØîÀ üS½üÔ×ÝŽ³æšÐ<@dTtç`–k0Ð]çãM+3<ÀÜy¯3V¡> D¸÷uoïGG,H'Z÷yóijajì k%ܳ\ÚPÝ(1Ù k@`êcŽ —ÃU9 Fc|غZY„­ƒ¹O•[n¹Åë󤄙Cù-ÐpÞaû ¸áª'ëïµè™…wŸÑ\'ÝPPÂ†Ú \”µF¤",°Ç,ï„\V¢³½Ž1ׇ¥FË ŽÕîý'Í5w" q£ãC-“ŒDÙb;“ |ÄM9‰>dÌcEÑ 'MÁr;ÔñhRƒÆ"cY§ Œe£*ÑLƒ* ê#zQ‘iÀÎ6ÙB³(»á4îT$÷L€N3}É .ðò~”ðá^ÀŸ€GQõ&uƦaY„§a¹ òuÏgçÕƒ*BŸŠÒüØ5€ˆ)¨Ü(ÇiTŠ#µÜÊ-·ÜZ·JFF’íó¦C© ¾uª\s þFž‚R †ª0ë)Ø6þ?{ç.IUæÿÏ9UÕéæ{'†rÆ!˜H# ŠdÖ…E\0 ?ã "®îº*˲ˆˆ€¢*²‚€Âª¨¨dAr˜Ä0L¼3sc§ªóûãTÑuëVuW÷í¾së}ž~º»r®>çýž÷}¿ßA æµÃª-p·€­J‹TˆZŠTÒ¯‚)XQiÊ“‰ó^kY3?×úÞŒˆÇd¢ÉuDûɉ>ôÍÔ¾¨Ç™È¹ýŽÓŽ>D‹À‡Ó ø  ø¨8ÉÏCÈÀCï:ÅrˆƒïÕ>¨g|`Æ/fèÑÜzZÞ¶{£ë:²ŒW÷öë~ãžß£ â 9O˜ÎG0=(Œ&׿ŸÇR‘wNhúÞ3.8ÙM¹xJ®Àör×¥}/~ÑôÝŸQãeú®Ñ£n§’‚æ#íƒfÈ=6šŠçŸ•‹\ãD>D¨)¡¹ò ¼¬`Å)P¸¶ È~²*Kþh(]¾ˆÑ"RèW"ó‘Xb‰5jv¹þ}Ö/Ú›ï-:u'¬~›ûRkÜô\6š æéþpE VnÑDJ™‰¸(Äf(¢ïÈ&&°­˜¢×?Ñë2²pA=7c8¼qÁ-ÕÎTŸn¦ÉöµŽSø¾Ž¤^ÑÀFê? ºÛ@¼HP8.ê¬0È¢‹´wu׺Îò|tj–?‚’B;ë~½6tųYƱ;Ü}WfŒðíU œ¡Óñ¡{Ç2Ý{+¡‹ÐMÚ ˜æFÉ¥í:Mk•eݶ8Xèœ_E´aðzýmæÝÿô,Z-®¬ñ,W‹Ž„ÍÎÈÁ‡$\×D†%”«+`ôIؼ m3!õpâœ2[?ü!Ú×JZ³šü°w­‰"©;OWîÕ IDAT,±Äê î¬aʬ|®eû¯ÛȩϬcè(<Ù'`DÂ6[(8Ði€5V“î¤Õî8÷7*L‘aµê/‚Ÿë!WMr‚[áXÇ9N³AE£ E´ð aòÛf:Ù—8~Æ_Ì[ÏC׊bsÑà:ˆQ¨§ÈÜ?ƒWX°¼QúaEêµT¯ý5Ášˆàù¼¨ˆ7ãnR¡TÍ»€c¾ H6PIòÓöÎtAˆ¢2ƒßG…ÒÖb¬»ß™DG/26JÅÆ¤B€˜:‚ÀÄÿ¹ŒŽ¸t3•¾Î5@Q€œ=¯‡Žeú}è‚ClŒŽŒ<Â"3Á40/R”^®IYÁØüá0æ‰(¨Ë˜ËÂÒ­ ¼Ýq †|8 ÷]‘ƒ®›Ld—"w½q㡼ÿŽ'8íÞ—ùòIÅ_ÞÅýù$’Xb‰5îÚŽ$“6(Ç@!=¯mçô#2äï+°VÂ:–ÛzÜBO@ÍWð¬/–tñù2àtÊð&Æ– Í!œt$.™¨Úyµý%-ªg²x2­Yö9¿¬óœæD~ÀfƒVl[¢¶bûDAÒD¯·UÅæµ@Žßʇ§‘‚öjßE|j™(õjŽ k•órO¯EG$žqÁA¯€'”®ÓtC]Gq0ºSÞ‚Ör—{t·^q^† Å®òÜN<çvö[}¹ðuäÒ·¯Ith;˜v4Æö-ïwÀSÀSª’Re+˜·Jk Í€[s½wºûy"Ž¿q÷/ú®Ç!š Cú›G‰l¹×ÑÁXzcÿ}ù¿ûE kRÇóDµE4Ã"kcÀ²ª¤õ </‹mäÜ{Ïí÷sæÙæÌœÍý3áÒ-‰"‘L,±ÄêµL òExñÉüÊ‘}cÚÓ‚»öÂîƒÍÓ ?E=†yµŽ+€ ætKØèT°F©¤+)ÙëqŠ ËêýÞÊkiÄ1# Äu„3YZqjésÚJL^=G£µ*“>Dk*5>ª’‰h€ˆë‰;ÃU8,ÏH˜céÍÐ?€f‰*£ ì–¡S•zMÈ9:©OÀ.TX«vIC·‚9)¶)^ŠÒ¾RçÌnel:” ˜ ´KhOÁ|[G&ßµúu'`|*Pp&ÆÃ·ñÿ½óøg®Ü0{×,‚.ÊN%R4=£#!kñµ2äºÓ>€µ0§ :Jºmwö“À ˜ªáü×z–êMµŠÕ¢Á‚t½÷ÝKÏ“T§SPX>ñ§ 8ë®Âšq;W¿ÆäÎß—PI$±ÄkÀÊ6H!¸ûÉCøæÇÞÁ¾ß×WâÔ‹(†úa¤¬‚-7AÊ)“6* @FÑ ».†¾8óàÉþŠ(¯Wp¬£¬&È[-"`JM¾´"5KLòõNv›ê 6ë9‰E43R½€b¢ë¢´<<4.ª8ÕqE³ÀÉd‘z‚7ó/[xŽZÇŒsq(R£Ñ(Ð%päÕ]ÑiPi·]¼bèQ4cÔÊ:Cƒ»}ÿ‹Á‚N'ÚRÐ3ú–Рð`f t£ wŸ.÷#À¨ ¤ÜãsZý59žCÛAEm¶Lxx[Vq¸ ß=ûïß9l@ZÀËÐñ}P…©ëD@´ÃÎyxdhl4ÉÝñŸ×òˆ€ÝÌ6t:ÛZíJ¹Åîl›ÿñÝ{˜¨c=3mqÓ6ÃÀGµg+ØÖ†{y,0`“­g MÜhÈ6“Žl:ïîbÀé¡“kùð¹#üd‹Ä2 d“Xb‰%Ö9J±ìÙûXûo÷ñó´D,q¸öEëÄwòÁCŽá]ïýÏÍ7øëÙ¬ý,Ð/é>zÚGaå÷5ýü wÜ+ ¸þ9Ý· hŠß¢ª0<çÀsXµÎ¿£€Z _{©I<_-„Ô[d'ZÌ×7O« zÛsø‹>pPö½¼°4èBîXl>:~i268Â7£oÀ"øþ¤õ8Vy3YŒUÚ>Mí;۽浌¯?›÷"~,å»ÔidûÆFëÐa²sÀÚ ÆLȯ‡A¡AÅb€ …€;ïúshêÆƒ€Ž šžvÒ…Œ/”áw¶­1Á!Ï(êyn zº–¿}ƒàÃÓYìôqÈ>Ã÷¦Üf£„Ðm¡`Ú…_E|{5ç^p '>ò8».ØÊTC§¹%–Xb‰MÔö»êÍÌ[6@fù0#7äÉœ¨8²H×ûD/Ø[á©!;™§aë“ÐÛW¼/Žèþ8ï^¡y‰±4öa@"lÂ((.Û ¬IÁrÛù˜bŠÝ{œë‘u?6ÉTqÙNúÐw£@cGµ¢ Õ¢"å:Û¿Yà# X@|†#b‚ ±¯A8ƒTÓ”¿s Îà8„‡óÀîV)˜iÃwfÂAK { tìÙµzܽ˜œ;`“„!Uéð=¯4:3ÝÏRIUòŸW†€ä#È‚¤çõd Øët=Ê% )b6Ç‚u7ä¤NŸ*O26×nq]?óÞ5‡?ýq%N@K,± ›Áèo—ñ×—G8ã“§³î׳þNèÏØOÁKŠ£ðb^w¶[vGat&,ß ¡Ç±R`’­ÌXª]ÿKÕS€4BK«ðwDÀÔÌÚoò4Ö~}!m¦Xœm±s]Ï>Þ,¾C<]ŽV‹V‘z·‰«Dî½—&Øþ¨¡Ç!Q4»ÕÖEå釥ay½ÃøÔ+¿3î¥ u5LÆ¦Ýøg¿ýâzY×!íC3ju.–Á`:2Àg øeý½»†ÒÂu땎" ¸Î¹×[À"SsEY×øUgí@¦…lÏy6ÎtÖu¦s2tJ0 B^¹@÷zX% ¯t¤ÈalAº×.û;›ðRY㺇ò ‡}@$HéHÈL[9Æ`FÏmV,B~oïyè:ztHl Ú ÎkõLbú§÷Àû^ Ÿ?§<óê«‘[†ùÆâ_Z–H¡'–Xb͵Ÿüø">˜¹ûÔ¬dsZ0ZP ¹}ìˆÛW|}î:B_PcS¯üàÃ_p^#6á)XA5óí‘‚Uí*Ë›­”^Ϻz·»¬ÙŠèÁåfÌsÖ ž¶œ"àÃõˆ¦ø¨%XÓTø(RÁ¬YÔ¾IÁŠù€èjE´J¸&T ‚ #„„1¥|¯vt¡x'º^c›€¬Ë*Òä:€½AݧÿXv¶¸àcØÃ}ÿ›ï<]èÔž§Ðу*ˆeÆ Gª@[Ó&½öð_³ás¬³Tt:Ú¤äÜwq‘> óI’°ÅÑ€hÈ ¾AÊÏÂÕ†NÛMé;î÷—uýÈ€o ,06Ää“÷Zåt\Î Ø ½þhQÊýçfà .x`=,mÇi ÃB³A½[ .T0g6Z뾸ò,6:ç1oá\ ÃÀ¶“ÊóÄK¬yf¹}©z ,ÿ?]¸ÊÑ?£Œe±*&¼Tc`”BGX4DE°ì§â ¾ZH£à¢ÕD„ø›aûɸŽy\EÇFÄYâ:¿ŽÏáØÑÁ‡h"ø¨E³ÛJðQsH 63!@Âk6ˆñ©¶MpÆ&Øqڎغx{gtÚÔ(ZQv­0Ø#0zŸöfE›>ðt©~OäÀdÝ—2=0Whv-¿Àb0 “¡’òäéMx)BÞË›¹Ÿ,0µ#íŽ.¡ÏÝ Ì8DNŸFüw'wÞ!à“ºÕ;÷®Üïa]°4§÷Ïø@Œw>\ÐTzû² [$УÁÕîõd¨ÔRÓÄ¢TÞSíƒÂ‡QBQJòQ Ø Ãtïe¿= —†½¨×ƒ¸8¸xƒDü¯‚w¦àÎuüò‰i ¨E¤|Þ著/'‰%–XóL |l8Úaá¯$³a€’•qÌ"‚cš2öUc»RDÓîÆMïÙžš!­Ö¼h–"úd§|‰&îv,s¢jVä£ÚöʇŒÍ£Ôm%ؘh{ÔãÈGo†¸•à£Þ÷zî¿` ¦]ù…«qQ‹fÆ|÷tfüçðA¼Yž.tdÀ«uð¢`ÄC€SÖó\p~ í+ Ó¥‡«ìÚƒC G ÇuÒ%p àcS®LÌÌžs¨¿½mÚ€Ý XVÖ×f3d÷¹ xÒí ö6¸õè,oÿôêî#°Ïÿ3£€¾n `ë,XùúÚ ¾vðƒð¤ÒŠò#e}®¶M°ËN^­Å‡•Ž åÑBW%Æ 1óŒý@ÑŠ˜ £ÿ c¼"üúÛ4d,÷z‡ `µdÜ~¬]³.Ç9íX^úãV–Ÿ7§ ðð­i®¸x‡£8¸³o–¡¶+Icb‰%öj3¥!øÀ܇(Bð±[ÊK óN0m0œJßæŸPóü…°Èà".õîö'Œ+R¤üWÔG_I1ÛĉÑfß÷Dökæˆ5.+¬þ#M¥¨d{‚fZ×¶£ƒhMÍG£×—†7ª€<*K2>í¨š0¡á;FØ+(26«î߯›÷fþ3®c,O¹Ë½ífÌsxJ³ÝHÁ›ßCa¬yNï× t¡5$úÿ¢Ž(¬¤R åýW-4P9Ô‚” OKX^ÛFé°N颸Î]A ™pbu”ë³pF—ä78(õÏ ŠÐ&µj7½‚ ŠÕE7Êá›Yóךxõ)*‘Ž. kP+àïv0*á©2¬£’àÁ¯Ìøz¸ƒ]­h¢ˆx&¼¨RZö†6t‹ê„K`ìFyí£<¹×Ó°pO¬´I©PFJÆš !P*‰%–Xs-mA¡›ns#l¾¶\ )(•ÀT:v€ ý»7‘æïÃm_ìOÁ FMª¥`Ee„­—«€¨/+Î{ØwÛf}®g]œïn3ÑeÁ‰?#ÆuÈZNº§ìÛLð!ˆ—žS¨>j9õ. »¾|Ä+˜¯>¨HT•}ª©¢«€s:ˆ°™°°rÔ¶^çíÑözϯí[/¤¾ŸÒKà¤CaÓþh‘ŒÏhïÜyæ í §áVw÷ SЕÑÑ+r¼"ø^À2a¹Ê•´+ÏyöÄ×)J”ÙľàÌ‚ûE™ïq<¬ ~?¸Á!•tuÿ„µ'òX¥½ïs,zÐçõÛKÃJÀ¾&%½n®Ð¬[³ûtû¥Äøúï^½stûM0E*Šˆ ø=Jé¼VŠîïjZ:¯ºÔˆ!Ø«ˆ:âQ̽þ…y7ìÉ•WíN© ‡Õ ËU>K,±–˜ëÉ÷}œƒuµŽ¬µ+´¥aW4-z)db§_)ŽSº£ÛTO{S°Íâ>K2îÁš­]Q˱-2ñEÔ6õ‚‰¸ÔÀ¢Ià£\ø1Aa£ CÄü“Ä)am¯[0¬b<Ëq;Ò(á;UÙ™CJý{MyȾ^2²xæ< 7¾G²âàHÈ:š« 0¦ƒz8ØJŒæ+Q¿Sî‰ NÄ®p €Œ‚ÞNX°k(xÑœ-èHJÿc0z¨àÐà¬ÅåÍîûzÛAJ(ÛʼÿÓ\‘•p‚@í<‹ÑgtÛ{5+óÑ©T^äô•âmYqì=EyöÒ;Mw`þ~`Ÿ­Ç9r¬Š9òîu7àŒ¬X„kDÕ„•j„"H½\‚»‡a³‚ͦ¯³q.’°ðr>v?¼á?Ÿ…vIÊŠOM˜Xb‰%6³ÝYãÎ…sö‡iŸß¾K,Á,ÆfÇ´¸“ŽÕÆÎj˦¢©:û‰…‰Ô}ˆ¤ý☌sÐf5t\M‹b À êÜ&.¸ ÁïhkÄõ‚‰\S£õ!qþ µŽA4 !Ž¡ãšµ©Vk`˜8 ¸…1|à~_ŒŽhˆ°N'åÐ>ô-Î8o`qÒe?=ƒâ°¤€IùtpŽÔ,Éè'Áyö:dÞ…VSï ’Òd¹Nú‹ÀÐÃ:²±»‚ƒ–™tÀB¡·ïblÔÀ‘:…JþFÀÛ ï ~ÎzÇ(¦ ýC•Ù{! >z“ßQ>´fWRϰ«áÑZâ,šÊ7åÒ Ëü~§‹#­oƒñ”¿ûJ0l}¼´|ôïp°©AŠ)*÷Ô4‘„ Vþ¢ P‘Ï™—6ðôV-ª¸êèÿ¼¢ÉÏy»/»€þO9K¶ˆYt—Xb‰%6+—´ÆÔ©ï†×¡xî[л`€ò;!õ¢$ýÏ‚Õ@JÖN?ïêqD[ >¶·S.¦èuµê:D“Î!k$]'¸hÔ•T¨àê¹É©À|ÕL ÞFÁG™h†°V‘ztLAûÁãË*à¸Z±lÂL‡òµqX›™¹‚yitMüY~êà£ÿʯ¯y™´YÂpÀ?$Õå ?[&ýƒ.2 MÔf‡\ÈŸàä¡CèbnϹÏP‰xŽøZ4P1N‡Ìîev-|^+’÷Qqî-÷ú;-èN™¨³ýë¿qðñ"Ä6Êå±­¥”`_7€ñ¬>VÈÙ0h»l[¶[+aÀœ,z‡5Ì(P%ÈK·­ŽÖ«´õÀ:S§¯ùÁG:²RP°—]Ÿ»3¾F#X¯–~U¯ %!ßýÔ’žZðf T±‡•#w!ÄWy~(?cs‡L,±Äk q;É{€%'‚8ûEÌ¡ð^žP,J*œÖ^2^àV6ÐwÕ"…ÙÑL4i›Éj;\G~GYËio”b·^¶Ø$„Õh$d2ÁGœ˜zÁGÜ߬Õà£V«0ÁA"f_¼ŽÐãW þqÂrE°TkrRý÷â¥yú5+×Áæ!vöáŸJÖ={΂²‚\F"nÔ… Ä6Ä•eîþóAˆ¯ÁooÐ é LŸ©;ÌX%n“±"жû‡À‘ Þ.0N‚öksœ%Ø è”‚ @Œã$Æ*ØoÞ‰|÷;»DÖ(ȔРrØ7Wˆ*ÒR‡ŽÙèìq²%¦9àÜ3{aöRè¾Z‹:íÀß ¼ Äg«`Ng*Ô¼Y¡#H¸œóÜÆö‹DFQìVQ5apµšz›62¯g;ÚQGÍaÉ?ÃÈîW°Ô†¬e$œW‰%–Øä:Îfê)ÁÈ|°~óv`'‚”»Þ$\7+KE DüŸã²gM¶©I“ ŒÄvº§Zç•qŒVP·zPb<“Q' k"`¤žúÉvîƒà£Ñk-¼>ê]qf¤ýÔqpœ?ˆ`lzU\Pæ°zÅÕitŠT§¡£ÝBGÚLˆ#¡íè_8AqÊR}ÜÑBK‚e€•‚lJð†ÅÇ@ðõ+œƒÃÀ0çé: KT¢=Tê!rÀLé¦üÜ‘F¼Y!ÎÈ_)¸uoºK ƒ.GÑ=3 Ý\BÝ`¾ǰaöG#Û,_Püó׎K-¬‚$—rÓºȾFÀ\Aá5 _›eÖñ½´…<ä@® ÔG Į툃Àü|~1˜?˜E骹¯‡MÀꢾ¿ С ïPî„ÍRGžvà%ø êÓ±ÂR«‚¿_p"^?+ ih–²=öÖ)o½®r|¸ô¥n–¢ã–§ÜYÉ~$–Xb­7ËtvJ~qyŽÕ[€»,r‹oá©ûWë¾HïƒLÁþ5jÌ­Æä¥×5{ÉúÍ S©î£ÑsÇùmÍj;g©^Ò ðQh¥‰IئÙJç­½î‰Ë¨v­§ÝÙ' 4U«Ã¨–ó_ 5R€f2VñÛ_nŽ€]mÍÖd+.Cf祙EäOà‡³`Óô ºR:$€ %”^.wýÌyÖ FVX[ EZoÊt -`­fQ«qÊ1,œ\åÙ›þß³<ý(\uâj}Ý'Ù¨ó?ŽñÌ¥d>&àÓ .,ê<ýç~[ß§{]že2‚|^qÚ3¡n)a¼Ë€"¨Áú˜ç)F%dù;‹d·”5…£e!—•p€ô—lºí<³·€üwxê%(,ÙÊÿtJÊ¿€S0·¬Ø€/µìq#0ShP7jÃw§uãý¶ïÝ©`Êg# dúë.ö¤nÛKIdF!gÁÙ=É;ŽƒgN½”m'ñŒK,±–[ÙV Á¿s8åšûX[*38 Ø\Ò)ÁÁº7'0ì×½2|Ë_듃ÖaÔé“é47ã|ªÉ×"ZpÌ©jžZ|EÆqÐâ:òõ7>šåhøˆ‹¤[>š°aÆšh V”òy0Í©\cÛXw•k»¿°BvÇà-èHù:íAΠ”ctöéÙr†º°þNû8lÚâvt½‰R•ðþõpØ&Ù]A•$sÖ5Y7I°S‡¤È ÷*æÈzJìÁï‚ Äûòzpø%lÜúžßç9>zòÎùÔáŒ.‡Â[2œûìf”:¥ ›•÷Æ ©øòeçóºRbà%Ô»lÔSäÿòo>n#ïµioƒÔìN]|ÿ3àù¯±me™ ËÖéã¾¼· Œ`Úû±­¬È§Å˜âú-ËõeC6sÓØùŸÖÎÿ^‹~7,šÑQ9ï\8*[`Ý ›¯# ©tŠLFb¬QÈs?‰9ï8V••¤_%–Xb“b)!˜–|{k6—H½(Fòc'ÏÌÀ˟ΛF× Î B{îï#U‰¼V;ùb;l¿£ÔLÀ˜èv²žaBŠSóш EÛL¦þÇDÀG3é¥âm4«ÚCX-’¡j€"¶ ÿÖSŸÜßò½÷¢å0¼‚iˆ,:‚X¼œ7èÞx«´M߃'àù¿Ç'fM¥…ÜþÓEð!0 ³x²BVP2aÛ4ø¿Ÿ;tŸ³m?:Š6ò`ɵ?›É>‡ÜÌì@aüy;ñ=bÁ%W¿=öÞÿ8a%«®»—kßÿñæ+˜|ëâÏë™´²‰åÞôu×Á=¿ø5ÆŸû¹þGgòáÏÇ~T¢ûDè>PÀ§Ö°Dn&mœ/(äğ߇µê<¸²à–_¬t„¦äVª=óô<¶ž´;þAª ^I'Keë‡4Xq¦•aÐ'`¶[6¨VK™ ~‡±ŒlÁ-¿˜¥%`—’¾Æwÿ¯ Ÿù…×`nn'õ#(_r ß=ç8JŽ"e%X‰%–ØäXYJ†Í—ŸñÎo‡ikÛ)¢'I,tŠk{HiøúÝ ºîîàMTˆO‚5rµ@ÅT›x‰ ,^MFb žã%ô°ªùö8ÑÛ•ñð¶ xÔ .¶U\ðaOÂ=4 |Ä}O‡€Ž`¡°GqZÜÀ+p.ÕÅçü3ÞÊíŒÓÀâ=´ÄÆ`+V©=%´§ {È6àP~—Ä´N] ë3øKÞ—âTÍ<%í[ß/x[Y1z=ðv[ ÿbæ,^\w<—}ò*®¸ÄáQ!˜•AnÎS¾ë³yè…üüÖë¸þÔ÷3tò»ùý5×óÁ²¸æGnP]Júl*Íh±0F­[Ÿ?8ÏåÆ{Üųv†t V-¤`g‡Ÿp&¹ô|ú]W³à’Ó踺2’myÚñÚàÂïÂgn Ü¡È}6ß #£´£o)=øµé6 6®­f 2×·Œ­iäɸBÀóᘠ07ríca§½¿ÅšDõ<±Ä›D³,‹\[;¿ý¯/qç–Ïqæ}ðÂÿBÑ‚rY÷¥ûõÀ=ýð2š51Ȧé±8voð „¿Øše„ŠÐ®×ÏúûÞ0µsÿ²à:¨_ =j]Ô¶P[é\ÔXgYÔ6õlWϺ¸ ¯™ªéq·µc®›P¢ÐE¬²à£ÐbѬm¦"øÀ@𦶗R»çÆõ}Žõ IDAT¦ §³ ÎH;1€ŒÏã£äõ‡‘£ˆçpú÷ϸëÓèÈG/°³{*p -ì—öÆÕº0tÏžŒ™`­ü8"{)˜&Â.SOjH8|_8êx8ö°0‘Èß8ˆ³¡´Jb]lqØÿxòá,…áQ©CèN[†¡Á<íím qü'Ç$¹Ž #ƒ#”Ÿs7HYPt¥x—vÂï{$özpÚ¬Û`Ûé0ü”.ð–B‹3æ€Ü,à(_†«:àÉ­ã…PrA‚ÃxAIj<ãÁgÇ—pŒ€i Ø83'æ¡;ϯËpÂSiÚ³e††l®¼l7ݼ™Û7H‚?K,±É5ÁéÇeùA÷/þ F »ýPFÀ^íðô ,CG—=@¡|ãiVÀtŽ™ ×­g€€øwk°I j„þú€Ltßí@Â"eþç«¡‡F#­­`¢ÁuÛ |Ôˆ5Qð¿ sIÃk9dP1Û j 'E©©û¯%dRŒ-Ø+¹örú-͵8;¡µ)VëK0\€õ@A€µ^bßr)êËðÐç4ø¨§mîzŒoÃë;i$rabðĶöI’ºÒæþ» TÖO‹PZ;£TÔ]{ÑóøaRb!Å|‰œ¥Á‡ijð‘ÉX´weøý¶ïÃZ±ûX›ÁùèÚ ÚôÍ€¾ƒô@(%Pp“ÀœêÁ‚­®â» {ÑióR0«­’Nà¥ÔbÄ ®óöóëºüUš¼(çaù›áÊ Å/¥Á.â8 Ã|äì•ÜöÛ|$–Xb“k©̘o9~ò3è0=}̃N…A˜·Ðàí†$Y·ó^í.is`u§öA²‰»jVrwG`ĪǟiÄ7Ü‘ïw"û{ßÍF8κ0ð¡tþ›‘NÕÈ6ÍŒ*L|øÓš}­q×5# V °T;F䢢q~“¨HJ˜ú«Åø”,ÏïTðLAG–ô@~ž¥"¤g+=«£¤qȼ۠”¶Yq©¾;!AÕCŒ$aóLà¥4ö×ó ôÄÅ﹕ ûK÷O-¡P(¾Ò«—A\Åbq»t`J)Ê¥eÆF>J¥v¹Ä…?þ+gÍãÒGîgÔì {݇¿’˱ ÚUa<™¥ð“ÛÇ`Ö†ý‘<ÊF÷‰Ê©à °éèý&¬Þ 6?_ï6ÑÔ‘Q@6¬@=mÁŠÐ¥ ýÓ’4¿…ei;¦@×lØöråȦ©SK,±Ä&ËŠEذAqÍ{F¸åp(Ü éÀƒ¡çûÐ{ 8—Cúú¼)Oï Š¢ž¬q€l mH/þ Øà‚Ó—ªeª1:©€„z„›Õ;SVŸ›õl¬cé³™±÷œÄB AD«ÀÈTJ»r&áZ'PyÏE*@øë>Â(w£ÀGØ÷àvÃG3RX ˆ_àÎô]·7+îÍI4SȈ»Ì›9Ï éq{Ì> ø2p80’å´÷ùÑÏíØVChñ­}C‹JtßwB<ðJqˆØg’‚6{þ<ÖÞöbß9Üù\Ž#[F±?MŠ,[8›ûÖ\Å¢…ÇòÌ=8mad¬qKXöžå- öëqxxJÝ™?E+¬­¢À‡Ÿßc=K hWÐcBWrs¡ã¨“Ùð$Od®«ùÖEpÁùÄJ,±Ä¶‹ƒN þñ_¡ï3`ã‚}Ï„Ç/nÇþïAŒÛ`h+üÉvITБä>`›„Áiе :ö†óŸ€m†JÝ׿zé[6Õk?ÂjA¼ýüýòDS°j-'b ~êU=뛹®Ö¶q—5º_5_#HøcDœwñòòS :˜A'±ÀÔ*,o&ø¨7Ui¢àc*(¸7º>LýìDQb„NÄþµÓ«EFÒŒO¯ Ö{©[Ô ~%rªÇ†å±†t ˜¡ ÷à<øõîxëþ¼iÉ£±ë02)ÈAÝ-xé[й¿z!ö -›axôÕãà¦Ä.+>óÅ…>‹PêãÌŸmæÊ’®ƒ±øÝ¯oæM‡Ó%[æÙü¢þmç»Ñ†-]\Ùï¥À ÌÂú¯°ô,ïwO»¿oÐm@NAÖýÃfýÏ IÁJ,±Ä¶‹}¢KOŒý`›ŽŽ#uÙ‘ƒ¯_…µ#°¢¢·ßÉpêÞð…¯Â¨„E\]Òèîoçõ«Á¾5ØÏúk@ØAHÜm' @ZH¶(‰šØ «ÓBŽáqïO|'D4ÃAo†“Þ ðc…|D“ïS4és½Ï„?ïÏ y^üŸª›GZ`$M¸âk„· úUb-ŸSê}–è(b!ivN ]q4°Î9×àå`YŠr¹º“jš²ö»'ÂÇn´€Ù¬ÁèhüÊŽbÙ ŒæM L›iò‹ï]ÏñöIìw‘à¹ÐÝeýÆJ§AþT=J†\g,õ4ˆNxtÀ©Sz°ô²ìÀH„D¥ãIÆrå§twÂîgAÛÿ$3Wpû¥6\aqül²Êa´Hb‰%–ؤ›&?“†¼Ë”JC±‡¼îŸƒßÑ)=É“±aÏA®‚»,SZ¼°,a‹£TúÔ”Ï9oø„k×Hß3í†ø)àüàÂ2cÓ°‚T­aÊëþÙò2Я`“­Ñ‚‚®oƒýó(}ëbø7bv >ÿ.u³Dm‚wžxÿñï3ðᵫ¯„\õÕM\ñÁŸ³rÍ^pÒ¶üäú ã 0GÀ¸G¢®ñÁÀ°ŠJ½ójv¬¸ «òö@æ´]`>À¾ ¾Y&°ÅðÑŸ@\.¹Î)c¨W—Šnb‰%¶cOâ„™ Ìì‘‚ÌF“\z:´~Uç XlÂþÀ.À!h¢?Ȉã;1œÞ©®”ÞÊ(ÅŽGÓai6"lS³¼s?K– RpîECüÅÊ–{ÿC§ e‘d(é´øO°¾}>_ÿÜ¿!¥Àq76h©´IÑ¥>ãkç1랯óµû~ò6¼OÁ/¿LyÝ×0ÏíeùûÙ„æ³´—á »¢”6KãÓù<°’ÿ!¤õŸiÄ݇P8àì™]´‰ç“(±Ä›2¢r‹*M¡ƒëB ¤RäËpÿûaù®ðžM0|´íãvŠ}ÀñÐ®Ž€(¡'Òî6¢ë><¬²ÏÁ ¾‡E@ ¼ÝsT¡ù8ïÁÏ­,DoU¤•ÛÔ³,l¹€è‚YUè¨>^À£Õàc2AÇDï3N~–èÈGÐÙ«(êYŒ|x !ã.ËQµ"œOáiÆÏ˜ûY¯ü…èiߺ6 %!•20÷µi›ŸÂº¥¤ JÓqÊ`dƒSÆ0$¶äïDÏè „ÇQ¤ ž+ÙÐmóÆ/X,: ÄýÏÉÖåw2|h–M/ŒÒ ´Í°N1 ÜçKÉr"`Ö+©vf9°ÇEÀ2.›OþÐ5Øosh?ßáº_~†OÿË•lÝ6B±˜€$–XbÛÏŽn:zÿÝïmÀ#{ÂþúxLjëm˜>œM –ÀÈ}ðk4yÉ!#.)‡¸)X*8S €ÄMݪŒ4 Nšñ}JÍÞSú´pѰl@˜ª`¤•àƒ]W3ïy"Q’ZàCR½¨½žT4?X‚&w¶ – ßÃRŽú=Ò>â¿vof|:-gƒ{|Ëý/yu"3܃å I¡ì]d žw° # ”Z&"_(’½ŸLöPÀ`hƒõau»ÍÐ÷i(ÿ·Àœ§Xù2¬v ¯*ƒ§ ùýÃÒî ßïÚt¦$Wvê x¸Ÿ÷µ’ë·%¿Kb‰%¶ýí=gÂÏÖ{K=dλV³öå~¶®‡›•îç ‰S6¢•Ñ †çÀ×êql„JÝG™ñ‘EõˆQµ!S€ÄùÜèøG ãj@Œ*Žqøˆ£ÙÕ?øh”Ū‘¶«7ªÐ*ðV_•åÿNÄw?øð/órþsèèò‘Àbtnë Ó0üt»i÷Õæ¾Ï¦SI­òöɸò´ Ì0ôö9·CŸ™ÒÎjIû𳿽—=žø>ùØ|nûWMùЖË$£e–/iËd²‡bY;ÚMê+_ ÔY Ô­ә˶ƒ›ìÊïëgB3¯ ý²?OÊÒÏU›åhµûCæäƒVrý°…Éï’Xb‰m?“ÒiÉ‹CÜúWXs!l{‹`Ëa³öùÍ8¶¤ûæ%ô*žµÕç^°Pð{W_Z«SX‹ŒO§RU¾«*{SјH›u¼Vî³=Ú§^SqH†ê¬0ÁåÊEÁõ¦ÈÔK·[ÏqDÇ!¦ó]“—E¬}l£ßãÐÓÖs¿qÛ"XóQ |©o£ÔÉ«ÕcÓ£,*‘Xîíó2°Æo“z]'ö·”`ø•¯3À®À~B |¤|Nêº"´;°À„½ºCï*BßE‚ œ ¼î:ò‹,>þÙ3X4s5'\T"R $ŠÙÚpÞÆ²L”SÂF±é¶ipâ÷É\ó)RØ8Ë%È5tÝk°x©@*­â?±@Œ ì¹3² J‚é·¥EÈ¢èÙß„r‰¤t'±ÄÛ®D ×-mã„c!gÀ ©xyà ä6›·?À`V°ÉV(xxÍ~µÌëFÕX¶« ð @ü¾‚™`b…׵Ȧ²¾£_ïD~7;ÌÏ÷§` ´M56¢à»Xª9æÕfªPô6«P» íYÿѬvÉF€|XT¬ÕvT&APâLû€Å‰9Èça®÷ AÅ**i9~Àä›ÐiVÏ26´<&5GÀ. Äë€ï@©Ô.Úáp [í‹¿Ò't»Í·yYØí þpÚlŠg<¥#'¥qÖ0€8Š Jjìo²ý`$#A:‚ŽQXÏAqH}07!æMCHå$µ‰%–ØÔ°ß=u={v:k‹%†òE†Ýþl­j>äŽežºyjeS¡Û-ûÞmÆŠºÚ`Rf|$¬æÃû á)XaјX*VHŠCÅç˜Íø\Ϻ8ß›µO5w[3°\ReЭ…¦œª(El£)Yͦè,ðQoZÕTø¶NÄÂçÈDG>d¨j2!Ç æìéy½g6 üa6:0"àuèH†ÇdåѶz×›qÿ<ƒn§}€¹ÀÆFBL ¶H4iú¹°yX²¡ÜÇð¿D÷Êß#ÄãäÚÒ¯ü{ðÑ|ÛT–¬/tCÿþ Þ ›‡ÏÀùjû·ïtÓIg;t[–2‹±)xaÏ£ Ž`ßýæo€oHê——1{!ˆyÓH[F>K,±íni¦u€ºÀ਽ÞMy`#_ÄpAÆ`› > î¸6Š.0uAEÑ8ü„N'vG‘Ç;ȵˆít_“Ñ>ãŠÐ»ÝHÖpª=Ö«féqÔºñíiøˆ=g³Ú¥™EïYÂõ8¼ºŒ(õòjiŠè4²¨Z‘`á°çPzãÀÁhÑ °;¬X #ƒ7îys@‡Ëþ1H˜íhnõUnÞ+¡ßÖçØygÈnç'`< â+úZß|ÒþüíÎG‘äG†«É°·¿îz4M©Àw¿þoì³ú|Þx íëôCñèÓ0"¡èŒŸó?g8ɰ« m?íæÒ¹[Ù÷98ò#M§-’FO,±Ä¶»™Ò .>+ÅÉ3%Ýk§³í¥Yïº]‘wGžŠ `ðåš—\Ðá½…ýDj5æ«0&¬ÉŽ€ÄYW+µ¬Ö¶Õ¶ŸÈº¸Û4ñ˜HÄÓ‚SaD¡óÕÍ*µ0.OЩm%iÆùqÊk¥VÉÛLÆ÷f·‡¢¢ñV$\/ˆÿ{-Uó0…jˆ.V7|Ï´á Ànç›vî…]LXµAãm Âr´ ð¤€EJwØ9 óàXçvš]À¦”ž2JmÚM0>ž’ဃòÎ}M¬>“5¶Õï™cpØ:±P£Ém4ÅäˆopuBžu?˜M)˜!á¶<œúà°õ½_o/8 MpòP\ ÆJ°þ¶} TÙÔBƒöѰòhX|žç'ƒádZÚCÂð)Pü9¤¼Qî«°ñNØp§V¨/2–»>øŒII ˜¡ §CPT¤7}1í<2“|>IÁJ,±Ä¶s¿—–´µ[lÞ´ü–ýHßy Åÿ*òÇ»m6 MC^ðÿ+Xóá€HØð¶©E½ë„€ˆFH£@¤¤Ö1ÿ„€hÆu²ƒ¢š$'z"ËZY˜^/ø;­nÓzŽ£|Î}šñ)RÞ¶{TŠÎ£þäªJ›Vcù’ލZŠW°¤äû}6¢™±vB×y¤€ŽQذ†EÖ! wp (CaÝ¡÷·çB×I‚ò= » I·I^^}8¾á¯,¼üYöÙmwrÙ#£Éüød[Á5ç^/>(q~è “ðTŽé#E¶‰â+“*EU8ƒ ÷•—Âô<ªà¬,é³xÿÎç!„ €ÄKl ˜r”ž ;¿Ÿü×®g &豃 *¼oŽ”r9T×óhÔy ó=TûM¦53áÙÙÍ:^3Žã?†Œã¬wT´¦xºÙËêÙf2ÀG5:jf?ì;Ô¦à­w›8Û{¿»¿HÛíþ}=pað¾{ªàa 2äs­—x„¬Ë0Á·Ì ) S® hþóAtqžé‚’Í(d@ Køp°3[aÆé!–î„1ï½0_QøX–„õó¿^Ù£ûì¶;™”™€í<8¬µ%×_ë ÿ:§àpÇÒ™ð9zd¤D¥ =ŒèÀÒ¦£p¾{4k”œ¶ï4–¾ ”R˜2iëÄKl* EºìÀk63°—àE£L^‚T•¾ÌƒÝìa,Ÿ#ã]ÇA@ôƒ`%ʉ£'×WmöرÃ? Su\ö×€tûœµ0çÚK¿jƒÔd-kEQzÔzƒèÈG+£ ­Xæ At]†õðÓÞ΢Êõ‡eý¹öªÆ?,µË¦výG¬!®W˜îEkülH½îçn`'™ÿT0[Rü°Cjݙ̙uç}®‡ÍkGùñýì³ÿ¿úçE°ôAÊó-þúý·rôñ¿jÁ|FbÚŒ9Ð3sÏ>ü<X×lç‰È%Øü:Xw³ˆÃ¸ì½t¾Nóg*Ôº…<ùþåì÷Ó¤]K,±©eY £¨Ã,–ÝgS0LV£'Øøž»ožJ*–7QçW=‡J:–bl=ˆ¢z:DׄÐàçzÞëY$É™¨2zµÏÍZµS¢&CùÌ!¯¶]Ô2é©ð¶Ÿç˜¥[¨+еíÀ<ס÷;÷T"a‘ ê‹÷ †)S‡àˈx¦ tdޝS–èΨéÀ\I©÷RùuN6øö·t×d™ŠrÙÑæHöÿ¬bšP¼ïÌ~ # §’Ù6ŒŽ–xá‰!ÎzZ"Oï@|Q fk°i 1æù3BžGaº´Ò§*;Ó@,‡»7&à#±Ä›fZî‡7 .Ùÿ íw(s¾ù^˜;WÓÏç|“li`—Q(¯¨¤aÕQNx5'5®ãÚÊýšmb;ìÛ mÉJ5'D¨Ü‡°š£­j8Ïqk  ñz…8Žt£õ!q¾ ª+r{f0^/L¯BFlC„s]m}˜µ]ðzý5 »ï°û0|û‰À €—€9Àaî{::Ò Ì "ô築*ïµÒ°ÂêTÂ:M8¹]”>* Èa£!(äkNEÿÿ8€IÙE,¶£÷>îOú$–©ÞĦ¢ ®ýò½ðçñîN…±ö ˜ï<Ž¥^Œ»,CÇÇ»p.ÎKï´¹xÄJš3±Ä›& á·—Ã7›|éÑß²téŸyë' sG< ÇÛ [ÀbC×ÀµIx0T¼ˆEXUp,m%X˜ ‡yG¹î©NÅñû–Ó\!ævwÐr²õkX4’Fµ¼Û6[ÐKí‘1¶kF{‰:Ä(Jà°ß<8 çú¿{@&ç‚`/´ß:wÝÀ_Ñi/ÃŒ¯‰êĪE•ü¯ ‘!×nø@ô1ﺽÿEÊ]Ö… ïu’Ò4›Ñë/ 's–)q”ƒrŠÝÍz{²ôoEý"Cñ=6©öýÐÏøô@ïyézÿx öáóØSrÐÁ¯AJ“üø‰%–رŽ6Ö‚¹–Å2¨ßÁæc%…ƒÌUz"7Bñ/ð+ ;š˜Å(ô§_9!/ÿ¤] +˜ŽEà2Õj@¼Ï’øZMµúGÑ1[oÛy+߈3]oêU³S²¢¶ ><'¼–c ͉Au…õ`äŸVKˤÖ+á! Á[VtÆ6tMÈþÀbàq_§e16ªbDˆ(Åój `gËŒgÎ*£¸ÅzÒ U/É!ï¶I¯3é>êþþòë)•v?H°ßNÉ ·#™)¡K™M߀g/.“ZêÀ>.Œ RýÏd(œÝ‘•xÐ;ùÌÎ$à#±Ä›2&¥Rèfiè$ûÁÙÑ7#M·€ž,û8þüØ< žvêg¾ªÆ†UÍaÝ‘,ŽÿÛªs¾ªŸÓ¨Ž3[×É®‡²7 `ˆ*À ^àQ؈ ª¼™sg“!qXšD§gÓŠüð°:‘¸¿‹$š<,µ)*Ì 8ûþÊAÏ*/s¿{‚G’ •ÀyÇŽCÕv/þ"sÛ‡©µ Ö½H v§lÚuJÀ=ÏFeSxÁs\<ùÅ‘ûé3‰„žãU`eLYbڿ¾O:<ÿ w»ôÑb| ˆ÷ÛÊ£w£|ŒÃšõŸá¿V)“=±ÄÛþfà8ðùÏêˆ|o›$_°AH:÷=—»Ï~ÓÌÎCÛÇáÎça=ðÃu ÄXµsU`Dá+©?ª2{Ü„Q ™÷øJ V‡Ïñ‚ñÚT$M»j4•ˆ «YéXøè¸)Võ²bÕRŸu¼ÇI»Š F%¼õžï¥3yPŠz IDAT mîçiÀëÑiW÷P¡ûó‡{«¥_91žKË÷,_0 K #žfIF8K¥H/í~n—Å"ã”0ÿɽ±¯ÿ?DïEdÓ£…¤ðc‡š…qÕé/üñÇøÌŠëqî¢0œ§ÿ¯•ÿ™i7dο’Ô^sºOçå T)iÈÄKlJØG> W,‡;ž…7=¾ Ž» Ÿ†ž‡aÁnSŒ Ç¼?ºÃØî[p_ÕÒ¯<6,ÅxÍ(šÝ°4,ÿ÷ €™*)Xq瘵ÎGëšñ}²–ùS°Ìzü‰Ô0L¤þ£Q@2zÞ8÷១¯â‚ÿúrûÖSD7,L:FÆS{‘H<'£ûjóí_ö,B߃Ö^^íIXZ!à*Ì]Á¢cïšÛÐ{± ¤LHMÌ7t©¸ì"„ øØMgO Ìï~žñþ |W§a glêž!@^⬳`¯2/‹–*’ÀÄKlªØüéÀ¯á¨Ó@Í=îÎ3ºõn†¯…ž·‚ó+… vJå¿!#!Ÿüü‰%–ر ¾ÛÍWþ8„}o™_¯…¢€QU©í(Q©½ôê0=etÿËñù,aÔ»Õ¨ókQï†EV ù5 Q稶mð³¬cÛÉRCŸÈ¶õ,›H½GÔr?ÙPUg=øˆÃ¶ö±Öúj×\oJ1Áƒ÷Ý‹|Df‡ÕJàþáK –ZEã’ÚìV"ƺ(ðÕöµ~{ÿ:¯¸Ükƒ~÷e¢ÓŸâOré|$–XbSä„7ŸâÉ­ <òQ¯ÓéˆT³z£Ûƒ·öÌŸ(kjޝÁT£Ì­E«+k€‘Z5 ­ª ~ö”º½û®FçêýY= #pŸaí!c‚Z4»2&Pð¶+WµR¯ªÛðoÆÅvAÀ)Cë…øŸ—T›àúÓ_x£ÍHŒ¤þ#±ÄÛΖš~÷7ÂùAûÛ@  •1yدŠT&=`âÕ~ØÔ®ÿ¨ÁˆH&<¶7PHכئÓ@e\ç§OɂƋsDF½ÛÄ]æwvk5'ðǬ·=êù\+:ý¾’SpÊp%BÀ—Áx- Š“ ‡n´ ¡ŽX üxÞí ¾Ž°ÈøÐfXº×¡èBrxa Ô¸Nï7µ\㸧ä,6 ÓÎnW<òþ|À.ôþÿöÞ<Ü’ª¼÷ÿ¬ª=œ}Æ>=7CC34³2Œ#‚ƒ*@£Á)š(×{s¿äª$ær5Šâ `"èE¢Ä! b‚¢P&¡›n††¦iz:}†=V­ûÇZ‹½ö:UµkŸîÓÓYßç©gï]»ÆU«ªÞïzßïûÎbË–q@’Ø3}a°˜ßÜ[çøcǨ÷ATÓ÷€> ¯n‘êw¹õ†o3݃B ê­½V\ãÿ¥oÀhq¹€¦„?/©uLèURÈUd}O »²µ!öœ†×¶7ÒRð&iGz »ší¬™¬·³Ã®öÉ,Iòˆw’QÚ- S·uÒ¼ y<ìÀ²ÝˆFšWÄdX éÊ9ä‰-uÉúí!ã2Í›`á(_ ܪ" î 3ìÊÖoÄt×Odé*²ˆÆž@8ÄÙçÞÒ¾I ¨r‡UõªE˜Í©’’öŸÔÆç`± fpL½´UZ{=»Ø!)Ë“óZõÚ2¡SI¦‡õÙ‚´5*3Œå‚§ ü^¶‹ MHÅšÜêæó„Ò“ŒE6lÏG?ªà É¶e<)FwR `‰€#NSÕÛG[!¥x,ýŒýölž=6>õE=êä_|ûö[‚l|X>³!tÿ­ÜÈ“S7û‘<Ýkôé÷ÐÁÇ¾Š‘SOed8B\G+kP^¼Z!œ=5‰0ó*zÝÃKÚs%aé;a™„ÁP‘ ³î†E§ØÝ\_à c¿„Qƒ7EÈŸ„pápì9pàoàœ|…°à% û>b Žà§‡J>÷V½ûΓ8ñìë)‹ ßP»A(˜¿¸Ì‡ÜE°üîƒu[á¿oQƒz#\¡ßu5º‡`%Œ¬°«ÇvfÕøŸ 2Ïâ1Îù»p™™ö‰BšáùGé“F¸í‹æÄi¡5‹É=.»¦G‘N-‡¤­ ][̰á{©E"z¸>² qÉû™Ed\"Ò)ÂR:e —³“´,"`å&tªÒlÿ×oÚØÍš" Ro¯$á°£¡òGÀýj?„ŠPE˜Båx'ˆ/@9„fÔ©ñ õ>ž°HJÂK¥GàŸ¶ó‹'ÎáˆÂ5„¥Zµåß|sÍ–a|ÿûឣbZ@ÁÑÞÉ÷o-IðiÐ<<ò c‡Œ×`]M%PÙ¸äçTtÁãBY­1=ô*­šùŽìÅ0Íʵ§Bà½à;Òb ÈùºS–é.F&å»që9 eºü‡s¦žCö%r,7¢o´4Át™ÞòïȈ@Þº y<"ÝØu·ú-y긙§B¦×çpµ!ny»GH;ômHŨb‚C¨t½´kƒ”FdJóÑÔÛ®KÌ ©u¦ôöÇô1å¶Äíí5­ã3äÇTfÐëOî’…¯|=ñé¯Áû> <ߌàâÈ·‰‡‡ÇîCY¿%*z`#*‚`B¿·Ó®v^ÓŸvñÁ&™¯b¦gÂJªû!S¦¬: y‹îŒ: i¿³>»Í3öÍLÖÛ‘ï½ü7“ß3]¦—yióÍ ?Ò2À\ƒ3©¶=ÉîÈY!AÝëIóÍ6àLƒ–lWÈ)ЄcXÎs:“°„ër¼nõì,¡ùŽ„he‚n•Êg¢YÉ+~·ÉGର®nÚ’ªàI(í‡Ñi ÉÆ€Pó •¢w¿Š"ÃЧ–}îÚYt•Ò‚ ªþ²X:‹ª¾`<`Kôº¡ÓJú ø7!'®DÒ“¹ˆ¸ì¿ñøêÍ\ôF¸äÕ¾]<<XT„¥ 5a¦¤ ›•ÑÛ «"#K¡üvŠêšuýWT,º&1ƒÊŽ‘l¾ê¥¼©Q[è™Ç\E |åctl‹bE /nÙêÛÅÃÃc÷ Ô[00$8ùÒ1Ê嵌.PiëGh{:úÓ4•Í]oFL÷jâ8„$éÿ^HÉޜˋÖw"‘]ŒOcd6,Ý«åMŽñ&ùa‚Ñïv„nl¾O “IÉ| fÛ-iï?ìB"òÃ]2•E²2a…á˜î­Éò6%]¾KÚÕÈmòЭ&ŒAÙ9&ÓÞ†ÜI”ëx#Ê}Ü”:Ìê1(ýÊdú®…ò9À“cÁhxZ‘Ì%@¥…–j™Öå Êg(ü†*PnðM &XT˜Àx½]´ÐΪ€)$å¯À‚?»—` ~ÿ°XÌeD#Ÿ†Ñc‘…–‡Ì[ì €xxxì4K_)19ñàØ;ùй00`ùˆ ÉrÉGÄô”»yIÇLˆD·¢…b ÈÅ–QHŠaÚ v&ÈÖäMC›Ôqó¦M2¸Ëzt ØjÝ”-çÆ„ÎF1m-Cžx¼l?ÏyâòiËvcêYº¤ýã<©ŽHq5ËVèô&b§Âµ‹VPÑKBe¬* b(¼E"ÎÛ' Œ›!ŠTÍY±¤Dëê«NÚȱËsÓ¯=_Âü,kÀÕ!œÑÜÅ4&a‹)Ûר  J9†AxyÀ™÷Iný¶÷‚ÌE„ Š%kOª°âÑ*7+\¸IBTóãáá±ë 6ý®ú“ýÊ|s}7MÑ8J°9fuþ A0‰ ¿²½fŠS&Wž¥ùp Lœð›”å\»iwi@òþgl›¼ëåÙž×€@àšT®kò‘¥áÈÒqtÓz¸áC¶l‹¢dÊq)Û±§"m¯Gp¨6tíŠÚ®w#-íné•Bóxv$•±ÆTpÈ@V(XH~ŠKèL&0C²Ú7m†|˜v8[!…H™}WQY;&¤ê¤M þ0wžõ.®?s!ÿzRq%Ä#1 ü+ˆS¿Wಷ-`ôàÀÇ—qËÿ=’Í ×o]̧†¡x¹ v:„o€~ ‹Bze°ú¸k2ŒøpäÉÇF¬s.ëÃC ^÷¦—qúÙ§ø†ñððØ¥è ¡ à›WÜÅÏσ_C?0fë‘*áÊ3¢­û06”­ÿ€îY®vv¦+¤þ"V€4•¿Ý+ãè¥FE1/© ·Y&²HG¯IB˜Bë˜ÇØ5Ù^<€Êa¸nÊ,vk³u»- ·Ôu¤l׆-NZ¦W¯G·ù6aÀ!iûNJÛÛ§ç™z2¬„ÖþLÀ>=%/½* ïI?xï_Å|æwj?_ú ÁÛ~&9÷~¸ùA8åí°x>,^XæÚkëœ|âqô‚M|û‹Oóüá€ßmùѵðýÛáªåÀå E@¼(¤º­I«©ûŠˆ²¤ø®üÕ-›ùÄjÿИËè/L5cäñ‚«•¼oÌ·‰‡‡Ç®C(`¸_°½&i}`ˆGþ÷8› ‚'Zò¹Œ›PÆ5:3^ÒdºÇöy²´!îÀ+d§íõìíÍu€X ²h‹6ù …td‘‘´ô½I#û‘ÓñÒ€҈‹û;´:·=R_¤^w8 xØlݤ-¦§ “äÏQDbÒC¹ònËò’îõ;²¶Ó-Óè[›’ ð¢ã¹ Ó³jBeÈ·‡‡‡Ç®Å;a!ò+›-.\p$û]rÄ|É×BõN¯ÑÎNê¨æ©ßÝuyá‡ëöP»‡O‡lÄt†0¹F'¤WʆôìMövZVÁô0/è^óÂ6ô#ËÈN#EË€¯ jAlÔ `ªq·ô2‘u>±sÜI±›¨Ú!H‚¶¦& ¥„m§eú2„%Ë;’×#e C–hò8Ð’ÎÙ¨Û!c‹\À ©ÄÿÏЙIÍ\— à ÆêaZÓÇÒ€4 ó?Æ9 5Î_ü…àηHÊE BPoH9 ”׊yW*!/;!æÝï:”‰§>È×~{á‹?ˆüyJM ÕGoýcTýk_ý|NíþÉ[ûÞ¿^=<jÎ&®·£[ª]×¶ÈK*ò<ý“Ò#“ rØé,id"é;d§M ß9 w‘±·&Iš÷ÅJ÷Ómß8øOT¦ˆíøÈªµÝ4-‡H OIF‘$‡FÙmT&]ƒ"S¶Ñ-,.©-íbŒ¡C*(oÅ Àýú7•qÌI^þ6Š* xÔðô“ðô4d›8Ú^!T Þ%h4@ (I×Í?‹ÓÎ8„~ìó©!V½ q,¡8ÌÆûeðÀ‡¨ôW¡šS°îÁ¡'HÂ"¯CŸ³ˆcxýYðö~xåw}›xxxÌ.*¥ÕF“À݇IþE>€>±ÅGÜKíʈÖ„Sê ¢oK?”ãAC)„_þÞ{±oÙETE¹¨Éüíž…ÅÀb _[£Þ™SL¹ŠÈ®\>Óz½²3Ùž¯2·¸¥’4 yµ Yº»ZvZQ¿$O†}\!Ó½YisÝO£×#EÚÂg£‰ ]Ëâ¶‹}<æfœcsSÖšc°?ûP™ýi{j )q× ºœwÒ>Í„Ef’Žçœæ£x¿GU(·kvØ…ûÒÒòÚ…§€ÍnŽCy9EU)ïÓ“Ÿ×­M5ÊÃ2ø‘¨2üé7Ÿ¯<+`°¿°SȇZæ Dô½¿šK ¸Vþ †y2ç!„ÖÇ-ñmááá1‹†™¶%þãÚϰ9šÏ–R¦Ô{ù뀈ó‘$ÏEÖÀªÌA(fò*”3üo¯z?ìaÛÙηঽMò€$}ºó’°Šd§‹Í:©4’ÒÝó’´NÉ2úÍü í=† |Rø•9§4wa·s± ƒ½½’ÞgŸþnB“"g]{„ ι?[¿âV:O"S†DTPz@qþø¹E²ì 0½B{‘é… #©Îqð|Ûe[Ó‡ÒåÔ€þôµ ¬œógcÿ #G¼‘¡ÁãY¹¡¶M*FþæUpÇ;kÕü¦½òÀz˜,óMááá1;¨ÕJÀ»«pÛE—r"*\\Œ«phI[ó‘Wl.ÈNë‚<^‘b‡ì,bâ±ï£0l§I…äÒˆC7PéB:z m2Fl‰ìð¯¤ã´G×kßÇk€'P#îæüGPœO¯XO·U$úÒ1øMæ¨JƒR¥³Úº}“G‘×!Èq½B‡€Ø…cÝnG«QÞ[DoRÖÚû0çaŸ£°Î;ÐÛa FÐÜÞ^w°"}kA*wÃ[^ò—`b²9«7D,á·ÁõÏÀÅû£JÊ ÿ¤ô°:²÷€xxxÌú€j ˜¬ÆL](Xx¬ù[X+`\Ø™"ƒ-¦§Ó%…\ä ¿ê5~߇Kåmxó!…€’SÐvkP2 ß=/©p]–hHd>’=݈ÑíJÞ¡eߥl`†ŒÈöhÿ(°^ÿ×°FÜâ;î9$UkOK-ìz†ôqŽè¶‹í)mf²vÅα@§v#ëá’4ãJ Ú¢Š·ì/ÛËåz<Ü d†àÖ'¶Âþæë»Sêð–…¦¤^R ^2+n8Š›~û }"³+—C¸ä~8÷5þááÜоQ=<<ó†ócN~ÍRʵ \?øR.:þNÂ÷ž§Ú4ÞuJð(R1`¢ð j5O> O¯š„ò¹Úd‘C<$Éaà®ý‘¥ÝðÄÃc ¶ÁC9ÔFr jc–ÑF»°ŒXÛh7óJ™cÚ†Cô¼Mt†ÇÈ2²›Ö9Ï@Ë:Ö"J{2E[×a<&s”1ʇ€çéíÀ}1Œ X#ÛÆzK/k°¤-ùðçpÕX­WêæKX1©}.G¥Û=t¶m…ú‚5e;©€ì‡ð%°~qÀþ_S=­P€V æÍ Ù¶-BÊcâç*Q{xxxxxì-­wì¼®(ÂjPZ ?[¥Ã»…ñ|tV5·«›Û!ÖvH–[ñ\’ªe˜´z!Ý*¡'¥öMŠRqSþfý¶IKÀž[ :¥3©t¾³«ŸïÎJèy×ÍÚfa>í(´aØ ]#ÃÙ%ËxÒËüÔ2b Öò ½Îó5hZ°¥ ÝHº%Mþ´=æÆÜ_©Ûc>í”zÏߎ~ <~r,Ð’ívŒõò5=ÏAºnÕË™°¨´Ãº6ëc2dj©n«,BÕ§ Ó°^ÎèQú‘ œ K¯€ó«uFާôv+úwXUíPÖÓ `h+,\òQ(”¡Øh× ?G_5‹Y´ôµSìƒV<ªXž|xxxxxìE0aÛ×£RÿG1,©ÃãÀÔ*èP•ê]ßÍØ§‹‘íÓâzìm|xxxxxxxxxxxì2‚îC°|–ÁòðððððØùð!X>ˇ`%Ïg‚ì&B·3X™]úv®}¾&5KtEè(zÍ¡Wï…‡LìBúAúÜ[…ªÆW„¾LŸãz¡Oo"ôC¨EÐÔ"ô'­õJšdÜÇtú* Î<\Kº}Œ}¬¦>w¹¡?ªDè[&c®Ó"ô#.“º]v%¼ÝÃÃÃÃcG‘)BŸ„ß?Õ¡®[$Ä& -:ë€D 6BìÌ#ŒÈBâ ˆ' »…€œÒ%iixC’ÓðÂÌÒðÚõ)̼™¦á–QnȌі,Õ†ú:m¬/Di=Õ5æd ÆuÞ;P^‹ÍÎMh8àœÓޤá-êó]¤é ”‡c*ƒ€Ø×i”Î4¼n‘¢MÃk®Åišäl'9 ïÊëqÓÓðŽèý™4¼‹­þ5#ðŽRÈÎ8ùÊ¥ŒlàúÛ_ÊEÿóN9¥ÆáŸa×}­ä¨ÇÎëÂgÂòððØ9èHë=!UÚvÆ‘î ‰ÈGdÙN®dgHN ,3HZEO@æ)LÒ}»Ö-D8Æt¡“ùoƒ64“*EÚ$#¢e:¥]ˆ0ÐFnV!B‘@Œ„ÞŽ]Ï,;<¨—ëÓ†óš"4š u!ÂU!ÈHyvª´C¥ìwg"Œ¬ó ­QŽÉ„ÑŽn…·éõêä+DéeOr!BS7ÅÔö¨ÓYÿd»&-uëZúÚo±H‰¹pðˆu¶éýŽ U!Â/¾0€·H/Ý€ì `âg4~ Ñ7ÁgÎCRðí„¡ Š$²õ&DáÞâññ(+WVxà_ ÄÃÃcÇÆ¸B8÷võ{CØV­…õ± …¤è¤AÛ$›,m Í& ~ŒÍc·ß§‚죳ú¹t ÒØéØI™ Y´æG±—3Æ:LÏò`/oB\âáV÷L÷¶  FåkÖ¶ûõü phAex*àç-Ucð(íˆ8a¤Ánƒ€éÅ’E‹(Ù¢•-êh”×åi=I”0º`·•MÜJdgÆpç›P®²5ß„…™vïG…† £ô"u=™¶0Ûè×ëŽ;ÇXÐdÆlÏœó°D×<<BëÓöŽ]çmÙm9®µç¼‚ö)²%ÌŽÃTd®.9pÃ|mÈ»™¬Žnƒ!2 :Ø’ jWÜnd2z™twu=¯Iz죇éÆ[¦¥Å‹ó·EåF€_ ­ûh:Ç3=_žk9$&íX#çØ"Ý£( MøOÒÓ¶œóЬ6´ÃÕB¡–Y ¬’m¯OUñ6hr2ÞRmH‡J¾wMÈÈoä»ñ‰ƒý…Y¹æ ¨L[__ /ð\º·¢/×éa§}Sxxx̪@P¹š˜3¾ùªóð‡CE^¸.ÔÆEÛ &›¤­áMŒMJš#RˆDZtIÒãpG‰ˆ'2sœ€È5I¬”%FÊ,¹¯Ì0¤“P$Ø2‡±/> B… 5µÁkH@5ßM•tM6ÖhâQ£­×i¢B´F‹>¢Œ½WÊ Û/+qî¯áG?Ž™˜j!vò“«¯Û&CjWÂA×Añhn€U‘"OsœHVè= ³Zõ¢·þ% Â-Ìo4¡_½—ߌª`¿Ø$$$=ºë@òD¬ÌäU(æ ‘{Øvö†ó ìI² ÖÄ9I‡L0Ð%ɸË>Ó¶!,ƒ9ÊØtfm’ú÷BÚáW ˸vÏ¡›—Áö˜mØ#n¨“9Þ†Þÿ$Ê ° •âvŒÎj¨I^¤(¥â ’;„ÁÎ(f{g°þo „ñ_Ei@ YhÐéÅéÖF!ªVÉaGÀêUé}UweRo3¶Ú®¬ŸJµT«ž;Aôé­‡à³ÿßY\ñw!%;…„ÚHM³ñáç¿«BaDY\øzá‡j<hDpÚ)P}Ê·‡‡‡Çì"*²…x°H þFÀ‘‡ªúfyHˆkèçñnÌ„TxoˆÇŒˆGHŠÑßK ·(…@ga¾8cûIaT®‘kŽ9Íâ¦3Âè¶·¡i­[K8ÿn!NI¿Ýynñ úU·ÈF¶V¥Fg¢´j§ÝB±\“ë!i9äÄx7Œ·¨„Š6™ ]±éœO“dÏ”]8QG xæa›„qÙÖ½˜íÔ5I9¤¨öûlC 4I¨` …µpsõÇÜúv¥ïÀÚ¦›ùÉœOÓún€]ߣAgZZÛƒ’–éªÛ€}, k™¬’F _¦3S–½=7n’Ø ’Óðá<§}mÒô *#WƒéáfîÃËî?vÆ/¡Ï¥,U}•që:™m•ôïA`C¤öiê‡Ä@1†²a°/âÉÓË\{Õ&êÍöÙ— Ðhuïô}%¨5`r2â§÷ÀK_ÃÚ‹®€ã?Hô}-ÂF‘V¥ÉŠW|øcdGéL97R©~ûòk%÷xU¡‡‡Ç.‚ýÎÓk¹òÆIŽ;ŽÞ¯É·. ¹àß"®ƒV4½&‡kƒŽæŠËÙÙ®’–ÏCXüãÒ#µ¬Y¤]gÃMÍJ cNúžd$§MÓGÉÓÒéºÛ HÎvZ&b`˜ŒNeTHÑa¨‘ýÍ$‡9%…•‘ãFs'.¤ÇmúÚ¤‰È²F%²Šº°I3ZqÚ;Ïy”¬Q˜àÚ™KMª_÷ºUh»“Í4€*‚8* ¼xܶ ^ñBõнôpøòª…{lÝÞ& B¨¸}ýe¸ø"hUà£Û`á]P¯j{TÑJ@2ZlmúÇè\FY@]‚|\³.]ãÛÄÃÃcWZkd™R±E}Ãý¬_p$›ŽäSú=j´” Ç®IŠ‚°‰Šµ’Uˆ0fz!BI÷âƒIe ’¾ öÜB„AëåÙÞÎ.J8“ß3]¦—yYËMç¢KËH„™ Ï“þOÒ&˜jàv€KL÷ÊœÉáH-:½'I¡RUmhíAäJ¢iZ•8Ç|ªåh¿nS¤3+ +ÊxÈD Ç‘ª†n‹)Ú™À¤õ»Îtñy‹é)yÆÆè:&€Çôo;ݰ¾eB°êÖõ°íNžÕÈÃ_ øËç+òpê+w7¯‰$[îŒ9ù…ðêsà­o-#%œ|âq¼þËxþ°¢U7}ú$\³ö»Š«äÖ"µ¢ê-  Í>‰¼l—ìÉÇ\GXД|\0±É·‡‡‡Ç.~!¨Å-øäÉLSø°ªœB‰vQa7;V˜09Óℾ˜¡Çói ˜ s´Þ6|Öw!qò‘”&6H¸Yz1úÝô¯6Y±«•4ùØN{¤;J1¦»í?rØ~œcýn)ƒe10$¤asž4Èi$¬Û1¢föiDóI)ˆ£Œ6ª:„¤I§ÇÉ=W[êýmýxR·ÇY1ýù¸îK¸ùìo;\ÒX'¸y Äká× áû‹ ¬{JåÐýÇëû¸ñ8øþ¯žÇm_¹îGËxàÿÀU+µ_BtŒ#·4‰[íã …$Š`õkZ|bµXÌõÇ©fÌÿú6X+yÏÛÿˆÓÿøÅ¾a<<‚ÕÒØžÛX‡ôÐ)CbÜÂ}î²yÜZôxžæ{Hr•ónÛÉZ>kEçAù4%v—›³<´FjŠzäÆL3š#`X¨ÿÄPx'ðMà̸}¹¢ È:ˆ%%ZW7XuÒFŽ]¾˜›~ xíùæ—`Y®á܈æ&(– 1 [t¨–¹F (ÝIˆ ¼<àÌû$·~Ûk@æäÈc ˆbÉÚ“*¬x´Ê Å n’Õ|ãxxxìÒÁ)áOö+óÍõ!Ü4Eã<(Àæ˜ÕUø/TÔÁ$툂¤z]Y¬i ‡Ò&˜>pš‚•d[ù¬¹‚elÊŽ‘uH‘/‘®‹Èê,iaTݲlÅ]–µ=(&æÑŒ´Wi§y5©m·éϧP)e·Ð9ÂonÒ$ÏM`Î…ŒãÌòA¶®$OŒ¤í)h’íñ=>D²–±ÃÔ"«$¥JîVÇÅÍ …sƒñùáÀ~ýpÀ(,1õN^ üç$LưImg;W¡O7(ž²òôÅÈïxm U?©·`=ȳc¢§aK ›ªP )ÛŸ@,(Þ ¡åO7Fž|Ìå‘ÇXòÅoÿ+Šl†\p~ƒ‹Îiø†ñððØ¥0šÆÞÔäË#-Äy°fìüÉ“1 Àá¸pŽ ¹P¡ ÍJ¸%a ²ïˆM"H脹‘IË{‡ò!°;DœAB":ÅÅy‡bº šÜp©4FRÚY÷BgÝTB˜¦öDÕúîj$lWe@z¥ó8Ç9f ÙÓB®âò‘”Z8Ê0þ#Ò‹(f]¾ :k©D)˹Û5¨;ÇÑr%Pé£2a…*T8ï`h¯Ä¿¢ µ·Bý{À-&ÉÖ"°LeÖz¨¶ UP-Søp‰úZ´^ ãU¨b¸è“< TcØÃP¹­y1£C©èõKaó5Ï#Žð‹¹Œpì2Øz?¢ÁÛ6zBêáá±{Plļ}¬ÁÀPÈÑ#_àïn>†ÉAx¼OŒÁÚz‚C<’*¦ÃÌëƒôšÇÃÃ`Z£Ç°¿#4bzx“ÏJ—ënçÿ$A”ûéÆ ”áä`¢CÎvl/‚9‰yÇÙw³ïÅÕ4Sæ,ÚÊmÇ€é"²nٯܚ,"áwÒMý@s÷múE˜@®ŒÉ$6ˆ-òT@y *ÀJý¹X)5Éy@Mëi÷óòßÃD]y>&6)¡|˜' XPTT„_†©QضÊ üÊêzOJu,VÛÉô… 53ò€`Á{Æ7. 9q“M·<æÚ(Í¥äŒÉ*ûU[ç©AÝ&P.Àä¸ä®¯ŒP¯ÂÖÍð¤¶wŠÖ»:i0T8¶ƒ¢7ÉK"Iÿ÷"2ßér¯çáô% çkƒ°L÷tº¤|7F}² ]ØvšöÃèO¢œBäXnÄ!ZîþÊôß¶#²WF7í 9¾çÑ}¤éwŒfÃNÙ,­å\±›u)Ñ™~וÔÓJŸ³•Õcx婪[È¥¨:SHÒèF– ˜@5jO Q¡w†ˆ–~e¶×´ŽOèã*k2: ×(ž"(Ü%) ¨y2çðé¯Áû> <ߌàâÈ·‰‡‡ÇîC™öàÙ3¨*é›ôûrj€Îd°4?M;SgZqånixÓŠM§EFt /·IɮҀd-ã~f¸ž×€tÑ€$]Ä´”–*¦WâÌ›ž×Lv*Öšž¦h{(º‰ Èñ¿Ð7§‰÷o9>fz…ô´P³˜|©ˆ³ŽIv¹yҖ˳½™GÚƒ¦gú²‹?&=xlHÒyÙ^“i+Ö}ˆAE,m]Ï80ÔÍ :UF‹P> øoPy•š?(ïÉPjÛKß®Âa»DºÏ=W9^("48OPÜŠß^Ì£¿ú3‚@¨a'¹3B#àø#ï»IкhÎË/y1A¡H¾‘<<r4#"f¨N•‹àìüÑŸÃYÀŸHE@*ñHûì&NÏK:ò^îsTìëmÑái:šéÈ~’Q<OÁŽ6FVÆ)MmÄ&yl5¦·Ôt² !Èòzd¹{¼ÇäÏzÙ‚xw›.!c;nü©«i¡„s Q™< ¼UU«ßLÛ#Ö¶æ™â†«Šœ4lø<-`"j“”°)†Çí²íi³«ÅÊæ 9 ¶"™8/D¼<‚o½ˆû¿w ë^ßûÍ¥D­!üg_G!¼l­¤ñ¡è¹·ä‰§þwÿàbšÍ&­VË7”‡‡ÇnAI¶l¬sÃÚ“‰Ÿ8Œ#jpà(œô,pðUF<Âõ»®ÎH•îÙ®Òë3˜e#~6HA0‹Ç8[$FîÂefÚ'2= Ð{xOÞåvæÔ !IûOh2‘Abv”xä ÁÊC4ÈÙÖIbô^ŽOΠÓ¥Ú" ¶‘§Gf&PËzKÅ´>%ÚaY ÚÌÌd{¡¶I•jyL*í‡IÑÜ@‘‘‡é ±kÒ&#ž‘ðð/U¨×ÖBD#x6|‚‘ãÀ¢µ°xÿw¨óðCû6Ä-X¼xbþa¿v­˜ê8 ÿÜ~{xxxì‘©ßCÝÿCÆî¼“±í!â±2XÕõÍÀÏ Áÿ ­uLª›&B’×ðÎë%ÙcÛÑw¥]ÞÞ‹µ=fzŠ6·#ÙVf0â¤ÿ!;[Ó,Ù=ß4’Îâx² ówµ "£s»Ë$e¹JkϬO—I&¹Pããßì3HØŽÈx@¹ý$`zL©-47ª SxÐ,èϽüJT‚€&°VÛB”ã:kµØû(êm,Žï‡»ê°-R^;å¯Ý>…„c7böRÅXËPQ0Ð (1Á‘ÀÍËùÐw^Îßÿõµþ)²£¯µÈ‡ñ÷Àà*UFÄ‚à0ÉÝ'À©ßQË6}RÝ5V"Ò*ÁxÖ^¥Â¡<¸~ÜH»nšf%LY!ÛÝtžYBt÷?Û>ÜQzš(|&Å]›©×õvTtÞ˲³%8Ï+,ïE€nìì‚]W:ˆK$’:J¹HêH"Gƒí()ÉãÚÄÀ­¯ÑÒÆ¯›Ž6)˜Ì±O™±ß´”w$´qRŠâ4ÍL "îµuSï$‡]ÙäC&,kæ…ÎyÚ󠳯JŸ&ÇêÿÓÅí´k·Tè‰ã's. ’ÐÙ>"‡€ÙD3¶F|¤^'ˆÕ1ÕBKR(DÄË ¿Zá¼3ç5½ß¿õöqÔð÷—A4ý« *€l@lqŸ"ÜYá÷ GIDAT)U_ ÝÃÃc7BJ‰T©GÔjTïÄÕ› ô´zŸ-†ÊvõN iºvB`½3“l2lÁ<éu÷„¼»Ææ*ƒ;ÉØ”)Ä¡[Œ¼RädKy ŠìaûYž,"b“³´ó6SÔ…xdÝ€½Ü´¢GÂâ^øVÆ5K"!"cÛ69³×K#švª[“æöTÈÕï4ñ°+¯KàYÚú¤b±›êô0Ö„RÜÖ†¸äìk2«-bWš7^•Š„‘p\ñ9U>pú}œöüã¦:yìC#Šºœ~„Mbeû¿Æ9rÂg#šóÖS(B«éÛÌÃÃc÷’ELj#äÿGUà™*lxBÍ €òöÎÊèën¿í‘;ÉL·ZhžXÌü}³Wõ·pΦu4@–x8í7=¬Ó‹>"íÄg²l·L«·è åÉÒC„LÏú”çÜéb´Ëœí•w~˜@²Ü§Y™¼òhHÜ‘•2°˜‡ª÷J·±Nq¸Ñ|˜š‘¾ Ú“@à@”ȼÙR$&¦MËžìjì±CÆÌ Qê¿…!,˜ñOñ15~{Ú£œöüã˜?H“O‚´¾Ì0XÌü[Fà hõAéû@@éëëxõÐzª-¨5¡\òmæáá±û`3®½äoàÝo‚zUé([À”€­@ÿˆœsÓñ†Öûϼ« λç}> œnŸ4ðêß7ûÖ~zAÐÍxÏ2l“bÿâ.†­ÌaÌöbÜ’“Ää!iD¤E»æ„kœ»Ù láV’7 ­¦ˆìÒ–Ý„èn*ݤLUnf2CDâ.×— ² ÙÅŠ S¯QÑÓ"!´¾²?ÁåÿÄIǯà³fË–qÞ÷‰ÏòáKâ¸àIÈ>ˆÿùþÅ¿q˜8TŽçú¯_*øë3õóÁ'ÂòððØhÅÐ'àëàó'ÃÐ*Î,Cõ¬JõŽý—1u`ÞÇ:="išÏ$ÝiVÄÉlgºÚ—…؉ÛÚÛ0­!äóvÄ Æ89ÈDÒ2qYè–½*Oí<óóˆ{L6%—H$‘áŒ.D$×é ¡Mód!‹sCº:³\N-KL"EYÛ6^£€¶¨ß„€=‰Ò×lAUq5Ù©ìÔ¸QÂ÷–³LK_ üXO»òkËYß®]b_Ã0P„?áy7Àþ5(}Äf(V·ÀÿÿQV ŽýÃqÎîƒOûó|àÐ.,Þc¯ÅÚûá–u4"õr·ûKcRrá5’ý<¤¿ ‘¡{xxìfÔ$|÷|ø»“`âû "åö;Ù$|)éw²I c‘0…x¸ß“þËcdï­³ä,‘‚¹²ämìn%è{I1Û­P`·ÿéBHz™ORàþnÐÖ¤‘~››ûè%DÌÌÒåºÿ$$@v#‹æ·yÈ™6˜¯§m͇]•L&¬ÍqhZ$OfÅØ"+uTŒkÁŠ›9þAà ©ÿÇŠªÄXu(ÔCúu Á¨dêŸcZ±$øÒ"êó®á¯, §¥ðØQ)B- x×¥’³Ž@ÜÓ¤6Õé9“Àà+‡ÞcWœ ¿y˜µ›CÿôððØc°æ‰^¿²Á–Ûcê $ªÈòPÚK…zGW™ªíùyÓà’ðΞ§ã®ôœÌ$aRÞegz³Ñ¦r]çBý²Òä¦u°Ù($8Óÿ`v´²Ë dôœ'ÛQsÖŒ¹ÒY‘´@>d{3º…M¹ábîï$oHL~}JRŸjéíÎC0ßÒ~Üš›tÐuÚ^&7ÜÊö`±º µ2D¤J§Pݬc{–_Ih6µ†¤¥®ÍÐ`ij¿ؾ&jé ÷1t#üòxõB(– þ­·£  Úìãªãc>ñå3uã$cÏŠçú¢‚5u«äÖ#ûXvοòóUg‘×yxxì1¨ŽCø0…`QŸäÔ›%gË%œR†Và¨À“ô»¹ŸdOHš&D]Ó¬›Qº·„^³|üûÊЕìáÜžë u1Í!:«cBzq¼4GÖHüŽzBfÒ‘óÎïæÉún„\¤´Q·íˆŒcjÍ`]‘ñ»—ïI} ©¡ñèÄtƚϊî[”@|8 .µÕ"‘H"\¬7²ÏÓhMÓ‹$Âôj¯Ð®CÒGÛE]°Äóõ‚¶Ëz¸ý ÅíUn† t+ùÔ¼{3Ÿ>Eò¾U /î£öÙãÒØ/ÓPÀ€„òÏ ÿ¼…xºDq[Ÿ‰×ÃÃcOÁßýoø_×BôV÷?î¨QýâL£¯ñi‡‰ÕðàŃpÅ„ÊD9Eg—ˆé‰[ÜAɤ,¡½"œIz7™ÀÎ*DäX¶—"„»â¿™üÞUóì”ÏA/Œ,ëâçi¬n^˜™d&Ûê…fuÎ&Ó ä%Ýtݼ:I^„ÎJâtÙÌ,›X7½ˆ›ù¢[%T—„ñ:,B¥á¤í±°½­„‡^ä<è’Ž7r¶å®;ËB§÷Ä»Çú§h{MZ(ÏH=hц7¼ä»%rËû‘RR)‡þí·—!ªg·Þó¸l!r ð"Uá|šIB|!ÈEŸƒ¯a™lÐE߈{ Ö=  ?ý:ˆ?½O®¿ƒGçÁºK`Ýcì'àFõ~~!ðïðbý¬³Sôfy=ºe½Êë!˜mOÈÞ4$(÷‘óÈýîíÖ‚|Y™ºÀô`Œ§Í£ êÆ¨w$ä*kÙFÂ@’æ ÉPÏ"öw»zxÈô*à"'ùÈ“ÚW&Œtt#.nEtÛCb–@y(žDeª’t†Y%¶È„ù6qI´3i¹ÛHÊ\†E@lâ!’Ù( öæ|9ý1TÎk¾¼åÅ|îjàÇWrÙ9Õz„ðÉÍ÷%2/Žœûþχ7±í‡56ÿ4E'1ŽHÀä·@|öZ¯ø3~ýð§@6)þ¢{xxì~„!|éSðÃá̇`þ@Àÿø¾à˜±€?\á©ã_Cë 'Bå‹pÇJåù¸ÉiyóFL¤‘ŠÙÈ|埸û@_í‡@;E*äžwë½ Ç{ÙÎÎßši¸XÚ¶È<)l-©ryé„'‰Ô¥¥À“=´EVÕò4"–ô0²I‘E1°Ê(ñÛ¡ÚÈÀ8Ê»PsH†°ÈZÔ…‘r¼6qK:;‰@˜ðP5Çn±J@YBy[‹¡õM‚á"jrÏçNà5Ÿ|‚¬[+Ù0æ*{ùhÅE6]ó–­!+ðyÉØ:hÚÛÁôÊÀ!Ð7Qºkœá¼Ÿ‰¯nçŽm¾==<ÀʃaÓjxÅR¸{bú€_¯ƒÎ"îH³)fB&v•G@ìÇ´[û×NhOc{ý?#Óx¿¶ÀÓIEND®B`‚vispy-0.14.1/doc/_static/carousel/high-frequency.png0000644000175000017510000016070714507104216021745 0ustar nileshnilesh‰PNG  IHDR úÖ_´ëbKGDÿÿÿ ½§“ pHYs  šœtIMEÞ (&ä׬J IDATxÚì½y|]UÕ>¾ï½¹Cr34IÛ¤mÒtèHKç¹´EJ)3EdDEô}EyÕWÔâûQEAEQ™ÁB[J¡#tJ›¶™çéÎSîýýñ|óüV÷IBZ:¤e¯?òInî=çܳ×ÙûÙÏzÖZJ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ3f̘1cÆŒ;íÌf³™›`̘1cÆŒ3v”–——GDe·Û•RsæÌ‘+-- ï1f ŽÑþv:‡Ãëõ*¥rss•RÇív»ÝnÇc³Ù\.—¾÷p@cŸÍœÍf³ÛíiiiN§Óåregg{<§Ó™žžž‘‘át:N§ÝnïáãæÞöÆæ3v”C|òóóÓÓÓÃá0ç Áƒ?óÌ3?þ¸Ëåêèèp»Ý‰D¢²²òÞ{ïu:ÉdÒn·=øàƒÏ>û,>âp8œH$‰k·Ûív{*•2÷üôFTrÝòz½ñxÜëõfffæççO›6­¼¼üî»ïþðÿøÅ/nÚ´éÞ{ï}íµ×–-[–™™9räÈ¡C‡ƒÁÕ«WWTTL›6­²²ròäÉõõõN§³££C[YS©”Y#Oc³Nééé^¯7--mèСYYY999#GŽ9rdVVÖÀÏ=÷ÜÜÜ\»Ý>eʔɓ'Ûl¶Ñ£Gx<Ç““““——×ÑÑ‘––ÖÑÑ!Œ£±nÂÜcÆŽÎ0×È¥ñæ›o¾ÿþûív»ÃáÀ+“'Ož?>g"ÌMñxœ«]*•3f̤I“¸Ðz½Þ¯ýë£Fâ{’ÉdGG‡™ËNË…^‘J¥ð»Ëåš>}ºRê¾ûîSJÍš5kÅŠ X²d‰RjÙ²eÁ`0==])•H$\.WNNNGG‡Çãñûýééé ())7nœÍf»óÎ;óóóçÎ;lذiÓ¦Ùl¶¦R©d2iîüiìNJ)ìÜìvû˜1cF=bĈ .¸`æÌ™cÆŒYµjÕ¸qãJJJ†ZRR’žžîp8R©¶…±XÌáp1bäÈ‘ãÇ>|øe—]væ™g._¾|Ô¨Q ,2dˆÛív¹\Éds aI»´4s Œ; ÊÁëõ~ãßø×¿þµsçN¾^RRÒ¿ü,ÕÐÐPZZŠ?m6[47nÜSO=e·Ûãñ¸ÝnO&“#FŒØ¶m?b³ÙJJJ Àã\}õÕüßÿý_Ix˜eòTw!b†B¡éÓ§ggg¯[·îºë®Û³gÏ´iÓ”R‡#666ÂÍ^~ùe¥Ô‡~h³ÙvìØÑ¯_¿ÚÚÚ¦¦¦ìììÆÆÆ`0øÁøýþmÛ¶egg¿ùæ› 0`@ ˜:uj}}ý—¾ô¥Ÿýìg3gÎ\·n]^^^ss3÷†=¥}‰¸Êãñ€ˆjnnÎÏÏ_¼xñ|‡ °oß>¿ß_VVÖÚÚ‹Å|>_VVV ‡Ã¥¥¥‰D¢ªª*‰ÔÕÕù|¾ŒŒ »ÝžH$ü~ÿ®]»|>ßÈ‘#³³³çÌ™“———H$ª««³³³KKK›››±i„W›1 –1cG<IËÈÈX´h‘%ljjÚ±c ¼ … °”Rùùù(..æ§ÚÚÚvíÚÅ?ƒÁ`mmí!CøJvv¶i¥¥¥AjcìT'.¾øb¥Ô…^xþùçÛl¶âââH$²~ýú`0øÜsÏ)¥š››«««;::8 ”ª¯¯WJ›ÍÖÒÒFC¡P4íèèˆÅb^¯7¤¥¥µ··gffVTTø|¾†††ªªªÚÚZ›Í‡‹‹‹çÍ›§”úÑ~¤”0`€YOwÊÈȘ:uê¢E‹Æ·dɧÓéñxJKK+++kjjvîÜé÷ûƒÁ`KKK0ŒF£Á`°½½½££# T*‰D’Éd,K$¡P(‡Ãa¿ß‡}>ßž={¶lÙÒÐÐ`³ÙÒÒÒæÎ»xñâY³fÍœ9sÔ¨Qdîž ô¬Y³233ÄÙ¸qc4[pÒ¯-''ÇëõÖÔÔœôë1ÖË™+™L.]ºtݺuô.›ÍæñxFŽéóù䛃Á`$‘¯Äb1,\Æ¢Ñhkk«úææf¨M¹ ¬¬¬¬««ã@4埗^zé…^ø…/|"-c§–;Œ;öí·ßþÕ¯~õÏþiëÖ­ÉdÒf³ÕÔÔ(¥€ËÃá0"8ð4xtTˆé]%‰ôôô`0èr¹‰D<÷ù|X>#‘H*•jooß¾}{kkëûï¿‹;vìØ±c8½aÆÜÜÜÖÖVÃCœ*kœRÊãñtttÌœ93æçç:tÈf³}ðÁ>Ÿ/F"‘x<Çý~?f!ŸÏ°Ùlp§T*•H$¢Ñ¨ÃáˆÅbáp8¦R)|$V8ŽÅb~¿?‰øý~ü_r¹\^¯×år;vöìÙ¯¾új*• ÚLhÖi2yõïßÿW¿úÕš5køú3Ï>)¥\.W$yýõ×§L™²cÇøX*•ŠF£ðx<\¥Tuu5@L&·oß®:[XC¡ÄFñx<•J¥§§Çãq›Íæt:•R`áw‰À ÀA¼^¯ÇãA¸ÇXt'xTZZZ<?ûì³[[[£ÑèÌ™3ß~ûíµk×*¥vìØ%  0ÝÔÔ€e1†Ë$ä5‘HÄf³utt€š‘žž …b±XGGG4ÅÂÙÑчãñxcc#K¥ÔK/½„Œ=:++«¼¼üšk®yøá‡%ŒS&tØ÷| ú*›Í6bĈüü|¥ÔôéÓßzë­`0xèСC‡ÅãqLYYYmmmp-8CGG&"Î?Éd>ãv»áiÉdºÏT*%}ÉçóAÛÜŸH$"‘Hcc£Íf+...))1bDyyy$©¨¨ dÿlúÒé"åž››»iÓ¦5kÖüæ7¿Y´hÑ’%KxàK.¹díÚµ¹¹¹'q€áa_ùÊWÎ9çœñãÇ?üðÃÉdÒš†f줛æ$øÓçóM:U‰pa$©®®ÎÉÉ‘oÞ²e @ßæp8víÚUTTrB)…YIc¹b±˜ôd¨µþß(--##,^ñù|»wï¦R~þüù>ø „Ï8cZšÑVö!h•L&‡ rÕUWA3vìØ@ PYY©”úÏþš*Æb1 "†+S2™ ‡ÃXù”RÁ` ³–C`,¬‹XU§p>™LbYÅbÉdÔE CÖÞÞÞÐÐPYYyðàA§Ó¾ð…/ 8pÔ¨QƒÆÌÕ§6~ÉdrÀ€&L7n8ìH$RZZÚÒÒR__ï÷ûãñx$øÆÄ‚Ñ…Bø8!T,ÃÖ.™L¯ˆ¨ÜñY^~ÅbøbÓØ+âgmmíÁƒU ƒ³gÏ>÷Üs¹d‡a°NIçK&“¹¹¹Û¶m+**?~üÞ½{±4¾ùæ›k×®}óÍ7ï»ï¾Ûn»íd%ѤR©¬¬¬o¼ñî»ïÞ·oß?þñ³;ìƒæñx ««« _0FµµµUUUÚùý~ ŒGKK‹×ë•XFFFMMMVV–|WAÎ\XùxFÌ€|C8Öþ„.æççOŸ>Ýét‚™Àë†í#`}úôéiiiuuuÓ¦MûÇ?þ±sçÎÖÖVIJ)`ŒÝn‡;[ÃC˜©ŠamkkêÂRB|ƒt§d§Á—Tgâ*–U¼ŸÅbÁ`°££#ìÝ»7--Íf³-\¸°¥¥å­·ÞÊÊʪ¨¨0ãxÒW7øRII‰RêœsΩ®®nkkûøãëêêl6[zz:Á4  bĘŒ‰„ÍfCýÖÁ"ŠB™T*åt:ñAì Œà™‰DhŒIͱX,åÃc±XUUU صkWAAÁ°aÊ‹‹Ng(ª©©ù¬ÍKöÓf"ëׯߖ-[ŠŠŠF½wï^‚e»ÝþÖ[o]~ùå_ýêWçÎ ~õ¤\dIIIccãÛo¿ýꫯÆãñU«Vaëif¾c#GŽ|à˜Á‡]`nnîúõë5Ú)™L¶··Kœäõz³³³5]Taaa<ׂÑhTræ²ä^ÄÆWÖáć}>=ÇçómܸèJ)õå/ùž{î1èê¤ÛäÉ“].×СCçÍ›W[[»qãÆX,ÖÖÖæóù§kooG¼+"`†>‰¤¥¥a-d†°~b5…—‚ÓJ¥RrJ„J$ˆNâ¿n·;"ìˆE7b]lmm­¯¯w¹\åååÕÕÕóçÏ/((¸à‚ TW%ߌH€åt:GuÉ%—äååµµµA£Y[[  ”aA 1Ðñ:â†ð%Ä‘1 E"¼)±ÛíðLÄq.²§ø,œGhmm­­­ErkVVÖŠ+Î;ï<ǃ2¹`böãÿxذaãÇ?xð ]AuVùûßÿþÒK/=õÔSª“?ñ6eʛͶ{÷n¥ÔÏþóÅ‹+S·Ù Aƒ¦Nª ÊСC°øIžIƒAXI}I(f=‘ü vœòm2ó 2‹ÉWÆŒ£í±:::6oÞ,}@êàÁƒÚÎ{GíÎt’W f1yðp8Ì`6”o…Bííí¼øh4ÚÖÖÆ666fddðÂ)f­àH(½˜––öÇ?þØ.ÔT~¿Ÿ8†‹"ó°ÚIü¤:ëqSMEXFî è ¿ÈUPYdÅø°šŒáuð[LC£‡[ZZR©Ô»ï¾«”Z±bEQQÑܹsÓÒÒ°Êš²·ÇÕ—€¼GŒ±téÒñãÇ£ªÂîÝ»Z@PqZ@„ÎÀ8 þ„c…³Ð1ƒ€t„ #†ãa©B'‚6üÀd©½Œçjjjª««ã *•_¡¡¡ÿµÛíßûÞ÷V¯^-¿‹úcþtc¼n¾ùæ!C†¢ªÙîÝ»¡^…Bôœx<Î:CJ©H$Bšè¯„Ãa@.RPªSJ¿Åú'/ƒB+,«È· hc|‰$#G 2†B!PJ©òòr¥TEEEvvöìÙ³G=eÊ¼ÙøÒ±_ív@«ìì쫯¾º°°Ðív———×ÔÔƒA#`”á …± …B +†ãëp8ðqhØ‘ Hº CîŠéÌx?ÈKªæËƉx:4þÂgiæXÄb±@ ÐÔÔ´gÏžºººqãÆ­^½º¨¨ˆ›NK˜uj?!N§3‹=÷Üs‡ºé¦›zx'rP¯¿þú1cÆÌ;t"§àÌÌÌ9sæ°$R8^»víèѣ͜r²@ùž={®ºê*-·%‹UVVJé:Y(ôÖ¥ 4ûH+š—RR*JC¾B„„%›TùVðÃφ†I‰I}XCCCYY~GIR(©y.ãÇð¹Æò0yòd¥ÔĉÑtû(¬vX±0Y!?”Ø—Œ”DÒü3-- ƒ%?‡322ØŸ„à œÿÂ"\…EË'‹qUK$èø‹l|Ò¸6ä—ÕÕÕµ¶¶nÛ¶ ó˜ÇãY¶l™ñ¥cŽ®’ÉdQQÑÙgŸ=qâÄÌÌÌÚÚÚýû÷ƒŸŽÇãÌõ“y£rÃÆx!;<¾mŒTelqfÀÚD‰X˜è0³Â$÷‰ÒYà½T§ Œ,S¡óƒÎé@`÷îÝ>ŸoÒ¤IÓ¦M›0a‚E€ÕWÜ1/X°àüóÏÿéO ¹h=þøã÷Þ{¯:±±90XÉdrÿþý¸H@þ¢¢"3­œ”uQ)UXXˆGz´™ƒE!‚|¯hR*-ö§:òE›Í¶oß>€<œTåÂá0&Sy.Äápdee¡²Uuu5 0ÇÓÚÚJ7+--EJ›Í–••µfÍ™ÒhìÓÏB¹¹¹ÿó?ÿ£”Ú¶mz†à†`Ùl6\TÂÆãE¦ABp–;ÂZÈ•’ˆ Î@1 H*lø²p6ÔÄBøŸÅA°xpF‘À´!ï5‰ÔÖÖ644 2dåÊ•………Æ—Ž-_€~YYY---[·nEmO¸ §D"T„ ¡0ˆIzŽ:NLTP±všê,¨Fƒ ãÔ`§à{p†™X»AžMÊe*k8®©©©¨¨1µ)¥{ì± 6üú׿v»Ý=蘒 ýõ¯=yòäþýûŸ°ízüøñ¨¨:kÛ¼ÿþû‹-2ÓÊÉ2+fRJYáSŽ 5˜‡­ÕŠ1§H¯ÓNäõzý~¿Ü ¤§§kÞ˜H$4É—ÌC—_¾…•1¢h–ßïç5´¶¶²Ü|^^Þ<0xð`~/#ü4<èyççõzŒ˜  96îJHщlT§8{z‰4U²êŒ­PÑÂE‹CFípé4`,Äjô"œ.„÷£þ…ðøE5öTÁïóùÖ­[wë­·2åÖØQoö ùÖ·¾åõzÛÛÛ÷ïßà2@-«±Ë ¤'™_àHä¹P¦˜:*øwnÀL Ÿ0ît?€*`8þàä‘Hq8ØC‚ò`xšÐ —‡™»Št #miiIKK[½zõgœqšÅOÕ/ƒæÿú¯ÿ5jÔ\àt:µŠÕ0eìÙ³' ~îsŸ;a— ç;÷Üsÿò—¿(B &ùùĸŠ#°‘zâ‰' «Òœ¤¬¬LÃRäáµÑÃK¾Ò%ƒ¥W>åääØív¹õ×T𘌲²²˜îJ-³uj9š‘‘Á$/Yô9bXïñæ––ýÏlõ¿O¹©ËÊÊr»ÝiiiW\qØGôêfÙd'ÜGð—Ê*& eÂ Ó ¥BXŠ ñJ©HzáL/ÌÏ—ûL-L& Q1ëÇ׆¥±½½=ÖÔÔÔÕÕ566Þu×] ñLÌ£p'š?þ¢E‹‰ÄÁƒkjjdá0CRh%±;¹O ÃÊàœ.„'P±Ç¸0P¸qUgŠ=þÅ3‚¥b•hN8t?¼ŽTÀ/™“(+·¯äÕ××·¶¶úýþåË—ŸuÖYýúõ³Æ À:¡–H$ ¿ýíoßsÏ=2G½gëè胿ýío¯»îº|ÁÉdRë[·ÿþÌÌL­¸±ãá*RÌŽ‰=³† ¦q¥¥¥šË*Kç‘333‰’±niP‰9«¢6 <Ž:¼f43bˆPž‡ˆˆ¡ÉGÚÑÐ#…'Âæ~ñ‹_\¼x1KÌûDÃ]½öÚk/¸à‚P(TZZŠvox´A€Ÿ–> 9œ ILßã²'ƒt¬¹RáBæ 2¡OÖhÐF“ÀK¢jÉ+à\8åÉô ,jŸÁ@@´gÏ´:t襗^ fîm\å‰+€¡1cƬ\¹rðàÁÍÍÍ}ôiBRA R„NJȉ5«ä@ËúUø…)~Ìi ~Q/ˆ¬“ËÄIqL–•‡ÖÜþ„ÃÇ; sQ;£[ˆâyÙ·oß¾}ûÒÓÓ—,Yræ™g" éTßþÂWÿï|Çf³ýò—¿›Õû¾óÎ;ééé………'ŒÁRJ]vÙe/¼ð‚\ùêëëc±˜XÇuËÊÊúéOzæ™gjÜårA†¢}Äét¶··ÈðY*•ª©©±úX2™”Ôç +Ï$©J­‘ ,hS ×Q€¤P(¤)å5@&ÿ”Q†‰D}}=gáùóç/X°@£gŒug^¯wìØ±J©ºº:<¶Dø+„6²Ö–º#’R¼ç$ $ÕDÃáp"’‹Ü††Ÿè$\ ùA”¤‹2f 3 °Èš`)D"`ã8€4‹qãÆ]pÁFùÞ›y Ã}ýõ×4(™LüÎ;ïÌËËÛ¹s'<'05 –ÍI¯!üâxáOY“ËUÆô¬CX®¤kÑñðN­Œ»DcüÝn·£C9ÎtNi3RLã§0™ë4q¾>ê”îÚµËápÌš5kâĉÆ[zæAÇwæ™gNœ8±¬¬¬¾¾>‰ ˜>ØA†k1Ü Qa¤À©Sl.=ƒëp8ÐòY*ð°㟚ÐS»NM™€AG2„Ýë¹!±‘€¾Dh…ÏRõE=EcápÑúúúúúz¥Ôòå˧M›vJCöSoJ…OÜsÏ=›7o't¤ÖÔÔôÜsÏÍŸ?_(/ü3²tâx<ž#FÃÛbÌÊHeff:T{s– åÈYFË]¡áxN+2øb·Ûµ€µÍfCaÉ€5KlÓaü~?XØì¦¥¥‘*“Š›Wñ&|ÈŒ&*¥¡Ç‘ñ‹_(Óåp€Ž{5a„¢¢¢`0XWWòwIªdø.¼“À(À[㿨È/³D­èY£µHBHH£!£Š…®‚#Ó‘dh˜×Fö‚Ù¯TåËVwüúc…B¡òòò;vL˜0aôèÑn·;//ϸP—döÒ¥KçÍ›—››ûÞ{ïÙl¶`0H}C½|¨ñ)Œ&†ƒ„õœ=8¦R™Nà呵¦ÖíŸDWDNòé`¿B ÷ùNˆÇGX=K¶‹¦_!Zšêêêæææƒž{î¹K–,9u»×Ÿz«²Ãá=zôe—]vÿý÷kq“ÞۻᄏhÑ"¯×{¼q xÚ/}éKO<ñ„ô`:zvvö§|håK¸íÅÅÅK–,™0aBKKKyyy{{;K„Î2ySf…ctÃ%F†‰1}A›…©Ã:cÈ™‡õ8ä„év»IPIgÀåA;o·ÛÝn·I¯Òf!¬€d¬õÀÉ)‡¸ x% &“Éæææ;wNš4iôèѤEO­eîXàÿïÿþoãÆÿþ÷¿º5÷Ë/¿r8 #‘L§DLPÊV¨’áúD996úXóäî_ŠÙA<±ZÒâΚǪÃ{J~K"'®öf-˜¨I¸Pu k‡Ã±fÍšSÈ—ì§ÐÄǯ¿þú#FÜxãGZšÁ:U½õÖ[W\q…:Îxp~~~—«¬63ÅJpÕUWýùÏþðÃgΜùYV•nذá›ßü¦²Dè¶ËÏÏ—u§”RÅÅÅ¡PÈétjµFµ¤wé0h(i­† -uZÐкÏÓ'%Ò²xdé,™£1g2,¥ ™].—U¨:KÝÊÈ G/j«¬ôÙGóFâ0MìÅŸRÆ'×Q+-*[äÏcâÕ2” §¢°O‰*_J)ļÜn7$üýë_/»ì²… ~vúêðÙìß¿ÿòåËGŒÑÜÜ V0´3I;„QN©< •R(—ÅEHŒZõL‡%©äë8ë‚2Ìv:JÔe,¦>™~A·‘•Ûpm,ò‡Tô¥PFYîA2¾Ñh´½½õ,.ºè¢S…P8en÷øÃ_ÿú×555G­¾Â¨cw5pà@¯×«õâ=— I¬õ‚÷ïߟ••u8\~~>2›êëë÷íÛ÷™í 6tèPÇ3{ölk MFµö5J)¯×k­P¥qãÀ²Ûí™™™òíííV?”³ ËJ–žÂÕüS›5¦M¬ÇMH'.³Ã¼^¯–o(Kì¤R©üüüX,æt:ÿò—¿@ï˜J¥&MšôY¨'’––6fÌ ñ† °†!²E1-- UøUgLͪ>‘`ˆb4–¢f {w&Øk*%«î˜Í+µ±pŠÔ‹LR•¬)â­Û4¢T‰îu¤»ÈÏQ1ƒ+ÇñƒÁ Uظ]xs[[[ssóÖ­[ÿô§?wžöî”J¥ÐþÖ[omll,++;t誱Èò.Dä2òKÖ Û$¼ACç( })‡U`I/••«dIø$~Ju“%^ç‹Ô¿[]W®ä^‘A@0èÜæ!ËUV¸Å›].²(l6ÛþýûW®\9cÆ í«€õ©6·ÜrË!Cî»ï>СŸòPJ½ð kÖ¬Ñú¾ÛËŽÇãsæÌyþùçUWºæŠŠŠiÓ¦¡dÜQÜóÎ;ïÏþ³RjÏž= ø ’ð|, ˜º+Vo‘e¬¥- P‰JërMbžŽõbø¢¦¿‘ÊP9âZíuî%„b¥"%tÓвn­Õ%‡‰D(ÿGŽÁðáÃ÷ìÙƒ*J©?ýéOPuœ®þ\ž••õõ¯}È!‡ã•W^ÁýÁ,É0#2TË6êðÊRª³®#uǸHN¿sIÓþ¥¯Œ$J „SàOþÎõë1ëƒÀðPÈ·IÀ­Å%G‚oÇ…™POécÐ`ùýþªªªššP/ßþö·Oït@áåË—çääÔ×××ÔÔTUU¦BÌK#qçYS” ¾' ¸ówÉ\j©Á˜1ÈrÉ_Rª,§Î¢ðpèHša ÂkY©Anÿï–í èÃtWz;Uüôþ¸«±.<”Ç¥¥¥UUU³fÍ;vì Aƒú2Ñ~j¬T*UPPðÝï~÷þûïoll<ê°š¶)\¿~ý¼yóŽÆaÇŒ1u—ÐþÓ”Š@gPð4---’_ùLYFFÆë¯¿Ž—HIÀ¡-rêÅúA=Š6ÒääyyyZÍ»º„hJ4'‘K—¼<&ZËö>2´×Cö5û‹iœœ "½×ÖÖ& ²k@à_ÅÅÅžfÜWˆcl6[vv6ôv ÍÀ…¤ê\Ë"Äh2O,;1ËÕ‘Ã-ÑŒV”HS Õ)œêR¹Ìn'|—¸ŸG±fÕUDXËCLP6`aïF<Ñ¢˜qF¥TCCä¦M›l6Ûyç§”:a4N<`·ÛW®\™]TT´{÷nû•í­èNØó_LUJqÒRzÙËï§[jAÙm ¡@˜Í8!ÈšŸ‘H¬ŽÌ“Ÿ âG8fê £Ûô À&Lb’?“S.ûð"ß&TÒÑZ„áÂ*++·mÛ6tèÐ¥K—Ž1¢Ïb,û©âµkÖ¬)((øÝï~§ºŠµÀr8=öØÕW_­Ž³Î½ç«ÕÊÑ=¹á†þùÏââ[[[!'ú fë”””üë_ÿŠÅbÖlpkÊž|]6¥¥¥ 0 ½½]c¼¸××ö©Ý œt'|Jö.$•`dÞ8Z9=QÐC郜LeUeMßj—Ò3%V£®ˆ"Yüùꫯ²|óiVÏkÆw¿ûÝÅ‹û|>ÔbˆÅbH$%£õ¢Q– ?ÝI’C²¿„¿]V¨’xHVJÓÀ·µôƒJ$®$9¡DRYv”#X”M ‰Îã©ùP TÉpQ ƒa`kpÓ'‰êêêT*õ—¿üå²Ë.;÷ÜsO¿‰E\ðƒ´µµÕ××#-^9+œág8ÆÝFz]8& Š»è §5:!«¿vYÚ 0G¡ý.ø-TÖêÂ$Ãú´Ñhy Ìã“m –à-ÑhzV¤1â’pe97‰ïe¶±\…§D¯hÌc@‡ª³l!÷mmm@ ¹¹¹ªªÊápœsÎ9£Gî›´¨ý”˜ ððÃï{ß«¨¨8V Çét644Äãq¯×k%-Ž¡MŸ>ýwÞ±pˆW_}uâĉGZ-WÛ¿ÿ]»va2}öÙgÏ:ë,$õœ®@ª»¯†Î6ÔÇhØqŠ.?™ó…?Q®¦»¬rký!×p÷U’ÁŽÑ0f1IbQ‹@n‰`‘Q²ô’´ÇÚ¦‰èy=ZäÑãñ`º”ÝÈŽÆb0xðàM›6 6 —wÏ=÷Œ?þ´ñ(|ÇI“&¥#¯À»d°4ÆKâc™f/IJ+]¡:¾P)×ÃŒ$YI޾,cƤ- tˆ$*‚bŒk*¸à9\ÂABh+_w;9C’DáÌ/‚×Cd¨'*­¬¬ôù|Û·oôÑGålvLM3fÌX°`ËåÚ¾}{[[ÄþÐ2²á1[Ö¨Îøj>×ÌÊdó|„É4Ëð±tQ„Û þ“±9ÐK&:!jhÑO 'ÄÇë@?˜X“dmdø¡µE˜u¯+3Eˆ™6áñxx‘¨:+¤a =pà@"‘X¾|ùرcûàÚ××c Þý×555!uüØÎ­/½ôÒç>÷¹ãʽwÞy7ndʆfï¿ÿþ²eËŽ”ÀÅ#¹‰O×§Ô¥õ}³»¢¬ÐÊ´¶¶ZQ”ÇãÙ·oÖK+T²ê|ñŠÖ»Ëx"Y¢îè±î !fF¹Bc·ÇW¨–Ôš:\ÎL錵v—õKI‡²¶• ?>ÈS`ºT¨½^/®ùª«®‚zWúá‚‚‚+¯¼R)U___ZZЦ%¨ÊC]³ÄC\ e%TíÆS¥õ'‘+ ) Ù…—Åô9¬2TÇŽ„X8e"û°8¤ÔÁðJè]¸–°ÒŽ @XÛ(Q‰ê`D&í£l=·`òš››ËÊÊöîÝ;dÈ¥Ô%—\rH²’ÉäÒ¥KKJJrssßxã —Ë…Zä|Np(wÓ@@RÎç xdxŽ£Ï#Ë”R>ïL…ÆT DäD²CÏ\æ`£T¢ã!aê¼c”ñ ô^¸0ü$ùDé•ìP.ÛÒy.êëéÛh-7™œ«£Ñè¾}ûÊËË‹‹‹/ºè"°ŽxgŸŸÿµ¯}íQU䮬¬ µßRñ‰5´ ½¼ø³Î:ëƒ>ɬ”:tèÐàÁƒO׊ɩTjÈ!<òˆÖ1†4U<G]> “•””$ ŸÏ§ãÀ×]%ý¬¬,mâS‡«X”(ùh½áVQ‹ô[ÙCŒ+'Œ×ÊɳÄãq¿ßÏ+—ØÝf³±ðŒêª?†üÊܪâwúBé&~ëP(½mFFFUU;d÷|ìãS ˆŸâââo}ë[Ø#ž‰D€ðÑFuÊ~•ð—#'sä ï‹kí¬œ„V[ˆÕ$NRš™1ªOGe»$™C1MŒ 3W»î44Œ-jåB´¶ÖÈÿb¿ü‰D¢Ñ¨ÇãyòÉ'm6ÛÝwß'îT¤ÞñÝÝnwIIÉ¥—^ «ªªˆÎy Çù¥ê@Æ÷ Ž)Ôá…c2ºÜïIüAìÂÑ'„ÌDãŽ]K „©êªÃ)üH‘Ɇ¤{±ÅÃë¸Ë<¦)B_P‚0úÉ?î=ÀÑ‚`FѬC‡UVV:ôšk®‘e– ÀêÉœNg,{â‰'jjj~ùË_vGaÔ{ì1t}>N°@)ÕÒÒÒ]³,®GÇ<åää ®izß¾}S¦L‘N36lØÍ7ßÜ%`…Ê¡ËÔ¤¢äååi(–û³.5[Ú‹¸ÏÚ›ñ{—Ý0e!(Ép@=ù-‚Á  VRx!y5í,R„.ýÇn·{<9ecF¬g£:uEªSmÃãl%‰ššœÎív/[¶ ÍæN­‘Íe“ÉäÆÉ€"¤‚*¸áΰªoÈ L*•eEɰ¤µˆ]¬1¸.•¹ZR˜2,‰{˜ÞEnƒ$–FGYEñ²°×&Á]A—}e«,ÄåŸ ~ÌrìØØÑÑáñxÐÑüÑG]µjÕªU«ŽáÜ~"7~^¯wܸqóçÏß¾}{mm-‚lkÖŠ˜“,ôª ŠêÌÙ± Ï‘5ЭSl<ÏT>&· ÉÈ#¦f‚&G-xùAîK îñ"+èBvFÌÍ($~!JFœ8â-~J¦Hã³Tƒ±!–EúŸX0d¦á¶mÛŠŠŠn¸á†¾#•é» •EW¬X±råJTýôɃÖÙ6 åççk…!oøŸ»œI_zé%ÔL;ŠJ gH<ªNG‘;¾Ñ9眣”:÷Üs­DfkkkKK $´]R§B\c°Ô'¥ à¿Hµ³–êrù‘¯ÙHþ¬©©IKÂ×Dî]®¾<„bšt %ˆ¸I•o†|‘HŽýeUg{W)LŒŽ 8pKJhoo×ø°.µYÊRPŠ×™™)×(Í&kEí£ºRΈÑGÁ ¦SPº¤õ]Æ rpeÖ'P‹,R_%–bº1LD섃¡ü 9¾| Ù@¬ Ùˆ¸Çƒ²AØàɰ©u« çjr½@¨L Ñ Šƒ¾ - úýþ?ü0++kÑ¢EùùùHZ2«k»í¶ÛFŽyóÍ7cüŽÇ)0a9ò8}…îBWÖ­Þ‘ÚØ±c™‡ã<ýôÓçœsŽ&éÙ¬¥Ïûìºè÷ûù JÀétöïß­é­³37:ƪ­­UJÕ××ké`V(–Ýåý„çH.ÚJ‰qYÂlB6‹ýé”RYYY²¥+¹[NU]ÖpG Ju Òe nM¿e]®º„ûRR ¾ë+ëca pÒúúz·ÛÍoÑ—]#xûí·ã‘›zs@r¨êpE¦ÜøI–v9ôx’J¤òaÏD2€¨DB¢ÌTЄ8ü“ð‘ §Th©ÃsBy‘²¬ƒÔË_¨¿–{÷â{IÀGäŠXRVVÖÇŒIläÈ‘ .ì›L<¾Å!CÖ¬Y£”zýõדÉ$BÌXõé,ÏÁÚ›äd¥²2²  $‚`ô'$ÒJKúPfâuI‘’Äbûg¦¦r¤¨sâPrŠ—¥eAzÑgØÐ´¥üâ2&''¶ãDгóÆÒß222ˆápvÜÄ££ÑèÂ… çÍ›‡gí¤,sö¾éÄ‡ãæ›onmmýÅ/~¡ºÏÏ?&ÖÐÐ0lذãtðÅ‹ïß¿¿»|+|¯×^{màÀZí¢Odtà;È#ÚÓwtt|ûÛßž7o^ßïéÛ¿ÿìììgžyfúôéÖp*Éóî„nÚ"ǸžÝnGkÂà'R>]jÛ1!ÊHCl¬­ÇUŸÕ‰º“ò¨®ú–Hü”žžn€>™LŠÒ‚ò %S";¥È³S1C^‹^AÊÚÖÖ†þù èƒÁ.uJ©¦¦&%ª…Ãa¤²Þ/Bö±ÁÜ‚!†ò H Ì„e¦âŒLÓcÖ.’¡sö®A„}“ØÁ†'ŠÅbh-E¤(ãªòÊ9IR¡¥”ÑÚs¿Äè3õXøîˆõ£—v4mhh¨©©Y´hѼyó0ñ€õÿ¦›¢¢¢{î¹çÎ;אַ®>Þ§ûè£b±* sKOOïAm‡!ï½÷P׸÷‹Saaa"‘@gçΈÔ|âq r¼ï¾ûz衇zH‹âøÇÕðDÅb±¼¼<ë=d€¬;€ÕËÚGz廼³ ¶Õf °ägåŸééé’Üî®èt?Öz§(0©ñ|ZÁ-éûöíÓŠèÀ¯ü~?–R©TUJíÛ·¯®®®ôy²lìØ±?þñQ»ë¯ý+¾hKòRÌx dÁ>…ª RéiÔ¦HKP !¬²é¸4²$‡t?ÂÙô”†,±ÍeI&|QIC@&k–ÊŠkšD8O6óaýRÖ7âGØ;ÅÊÐs!t8¡PÈáp¸\. *2X4ÄãñÖÖÖÌÌÌŠŠŠýû÷¿öÚkÏ<óŒê”:õËËËëׯߥ—^šH$^~ùe6Q@Œ UÎå`ñÛiñw>Ë j›³ð~h°dA m‡¦O?”â9Ù¸NÎͶT5¡’>Î}ŸúT*Eª uÛqàÎ1'»Ýnè ^LKKs»ÝéééÈ”l­»ñÈâÖ‰ZfäÈú¢ ̤–ŸÔ ج`0‰D***ªªª/^ Œe–B(÷wÞY»víŸÿüç0Sc E5ɱµž5X|fàÖ½ÿ²YYYÙÙÙ²_ŠÓéüðÃW®\Ù›mž¥o|ã?ýéOKJJŽ_¡Š£ˆ{v° qz—Ȇ“”4¶é%‚älÕ3,ÓVJbÙ]•k­DcØ\ZË֯ßû2žÖ°¦Æ„É¢2HÆáÍ‘ÿ¢êËn·“š‚–ˆ0±¥¥…E¡d^XSSË-Y²ä¤khxë\.Wÿþý±Blß¾Ýãñ„B!™FGxD jŠÂ^¹’±d@ !R²â[ͨÃ눪ÎÜ(&ŽÉRÏÕ4Uþ¦Ä‡ñq` ¹\1@Ã,HYôA¢=Š…å÷•ÀN+. ãP„z²>4lÒ¥êH‰ ííí@jVf% IDATe¥ÔwÞÙwè«9sæ|þóŸßµkWGGGuu5Ø< })åj¢õŽd ØJGV Å —Ž'{ËÈ"¢êðÂ)& 3•Ä!)*üV[Œ)+\?¹+”©±CÑ8$,SÈïv»á"‘j0hH©¢œøàPȯÉaå ȃË<àxrZH~  QYY9wîÜ©S§žxÍ{ßXn·;ÞsÏ=ÅÅÅßþö·OÌI<˜——wœ$555=¿GKµí%££•¸„ã–””|"Xÿ 0 33ó®»îZ·nÝœ9súB(gðàÁP[ÿër¹Àev§W“yOò­ªª:"~ÎÊ„‘„ommµ‚Ic`úƒZÒŸ©F·VE’<“UtÕÝ·èvttø|>Ê}(ŸÇ›››ÐL¥Rmmm8ˆÇã±Ùlýv»½¹¹™Š~(OUgñ'"׺º:¼§¤¤dݺux–O"ºJ¥Rgžy&¶Ouuu±XlðàÁ---X9XøïGþvänæIÉèª ©#áªF” Uýrí²LƒvdéHr·À7P*§éo´Ë Ü!]$sYOK®ßTãq•’ak­*/EE<šìÉ£•§Ø ¿0å£@¾P‰Ï7ÞxÃn·ßtÓMª³ÝøÉò%Üá+¯¼rîܹÕÕÕ555áp˜M XAj-¾…T,;Ôˆux3fíNÕÎ;Æ#ãc8ÀICEOe.½—~Â!LJIo†¼áN(Ù1dX ç›QUA|gGpÀ ÙpšæF–›W‡Ë )Õ²ÛíR×Eý x#hR~Ž@ª©x{»Ó_²h±dÅ5©â0á€[É-º7± Ò] ®¸H²’Z &ʱˆ²Z¦êTti•WÔtB¤u©ü“ÔsÇ‚Á ê3ÿío»òÊ+W¯^}RâÎÄŽ+V¬5jTYYÙ¾}ûð î'ÄFl3@– å!ÉG‘µ˜2m“Î`-åOOx‹ùð=–SßϬX2‘DØHtÅm'(´’ù,ˆž çâLåt:Q7‹ÁÅó¶P)Ï} „h²¥=(7>ApoYpŽt;6xƒÜÈâ‚ôp¬ Á`°¦¦¦¥¥eРA7Üp·OŒ;õ!€…Ûú‹_üâÀÿýßÿ õœT)µeË–åË—w—/vÔ†!=¬Àûo¾ù&4X½?ûˆ#¢Ñ(›–pjÛ²e‹ê]EÖéÓ§ƒÝÙ¼yó AƒNnInˆV¬Xáv»»dqÓÓÓÁˆ¬_¿¾_¿~’B•x<^WW' >–á•ææf ¾hyïD ---“rTYÞ’ƒ¸ œþp§¥ñLÚ¾–’Õ©?“ºc«Þ¥7[pž T?÷µ¬5o·ÛQúA‚B¯×KÖP–€‚øÿzï½÷Èš››©›Ô:XŸãÂ?xð`¥TFFF¿~ý°yãÒ%eݪ³’>o—\iøeÉ (Ñù·Q‰öº¼¥`zÎ]B,O6š”Âg•B¬ÖÐ6ñ e]l¶éņž 3#PX_IæIô&ƒ¡ì#ïÅê©€hZL¬¯Ò‹ˆ`¢ªOjíAõá¶£qS*•úðÃöíÛwíµ×j¹œ'f^?~ü¤I“&NœXYYYZZ âÖívËÞ Ê›&5Ýiii&Qqø¤vJËH ΃Ojî— £÷’_”"PšAÖÜ'8†'ƒ"\£·ÈêHXÆé¢Ñ(DÆÀF8©ÛíÆ Ã&߸.—‹¥wñ'ªüãÈðF™º¨åÞ²¢¬Æ2TŠ].Ôlð7ì ü~ÿ®]»Š‹‹o½õÖ†±úVˆðî»ïž9sæ\ÀHĉ±-[¶Hmò±åäz†;x‡~DGÎÊÊÒÃÉ^zé¥åË—3´ß]tÉ%—üîw¿SJÕÖÖVWWŸt1iqqñ®]»6oÞ|ÓM7Q( ¿¡B?`!‚àóù ¬³êìŠ-Æ H޼.7Žª³Â§"[¿‹|C3nëG¬_V¦”BÕÝ€J—ÀT w& äfeeÕg=LÂ+EøC†bñ»ÏçÃF9++«±±=—––‚:ºë®»~ùË_2±îÄP'gff¾óÎ;ª³¯"gpI¦¥¥ƒÁÌÌLy£¬a?+MoNn€(J¦d_ö†€—D ôv,ÀXöHŠ(KÚ)‰(’m’|%PÊȶ¾Ry­Æ,Ì!v0YJ€”§ÕŸ|$ø#ÜTB‚†ÿdÓ¼× ƒd]~ùå¹¹¹GÊôz;vì9眳}ûöòòrD1™`€pÍdTgMZ2€DÄôDND·|šèr¼WV"_Û®SGI»6[r«†ËCíS‚iÈÕéÕ¼ƒ‘BÞ êK¡wN§ÝiPjÍp!ºÂ›äE œ^Ê™ÑC©jgÑQȶ¨ù“·‹xOðð¶ÊôLnyY0%‰àÛ­]»vРA_ÿú×srrN@jWXË–-»÷Þ{o¹å–;wö^•|L¬©©iܸqÖ†*ŸÒ ûøD –:ò:Ýq>Ÿoüøñ=³¼±¨c^SS3lØ0$EŸDóz½N§ó©§žKd­lÎ. Új-I‚ÂÂBuxÓ\Ò3ƒ ’²jHåÑú÷ï/S‚­,—ÖqYò%6›h†0ÒËîZVçÞÐD®­Á´ŠÝ1X²È‚×ëÕVÜ@ ÓŽØ~º\. u²2ÖïËýŒü/¾—V­mš233Q½“µÞzë­+®¸ââ‹/VG[™ùˆ| §xúé§£Ñhmm­Ëåjoo‡Ã^¯^² ãB¼¨Dq VB—¹rÄ»øa»@v™”S‡, CÌäŸ(º’QlFu)¨‡. þÃ.æƒ\„ÃsÔ˜„  …¼^/þ‹,BÄþ€®ààÉdÏ.X4•)>¸“¤¦0‡P€(«PqEÖŠ=xÏñA†ÛÛÛ©liiyûí·Ác¼Þ'–Ëå¹~ýzÐ*']Ùíö;wŽ3FÒòÇÄŠŠŠ¬œG—Æ>»½4ÖH´nq´Zç]‚¹‘#Gîß¿\0lnnÖø°o“&MŠF£}ô‘µƒ„JÖ&h\/»œy¹nu\"É÷ÈØ«¦Ø¥sbÈÈu!¾fU+|°».±n·»Ë•ƒ'•}0z@Ï,£ºÒìËJâZíAÞI©š×ªj(üv“p<ÄP† â°¢¢BÞ¥ã³püôôô~ýú566*¥*++ ÑÉDj€$mI‘2ÅIX{˜0%î(ob°†C¯Å¦»„Ôri$V£~K– •¥;µz¡ÚѨ…#¢„éi\¸ô2h¨¥Ç³ï!V/ļdÓqæÉ’‘ß]¾S‰NvTóðÍH¤— ¶r/„“¶¶¶æææ¢Í\ @ 0//oõêÕØG¿åà`éÒ¥§µµmp@PÃÔÑÑ …ˆd%}Y–OʆdÛG´d%eËKYÍ÷Jj’dç"Õ™ÂLƈpœ—!‹D`>$¤Ã^ ñ>\*H8~)Fx“ÉdFFy»ôôtö’ƒ… …ç²KŒ,.›u@¤±±"—L"áÅõ ø½d–µ»$ ¥§A‰DÞx㢢¢[o½†ã7/|€•••‹Åžþùœœœ+¯¼òÄ‹6¨Ã(**:¶eÍQŸð¹(¥Ôo¼‘••Õ›¨(&ÓüüüÍ›7[ÿ[]]Ý›j#GŽÜ½{7sâ;'±|ø›µk×^xá…V¨ ¶æÿ[5IZü¥7Ü¡VéÀçóI¾]v±»1eiŒc]S‰w5€ÅÃZï¹6‚YYY²d/¡†¼°.›ùh sNÍ|.ÕU¾´¹sŽ“ëtyy9à¬Ïçƒ+##ã²Ë.:tèqÚ8åååMœ8ߥ±±Ã‰D°½Æö—4ü.(ˆ/‹ˆ²¨wÕ i•ce…eY2)©UR *`#­±•¼Û²‹ÔÄhhÙŒAFOÈAʶw||0)1¿Oæ2Ü©—Ü“á_ †RÀ.OjEZ\‰¥dKc­*=2 `n‚Á`yy¹Ýn_¸páðáÃóòò´Ú¼Çj-H&“3f̘7oÞäÉ“?øà@+ì@ 3M‚*:†U>…c•&Û¤ž%Ô%úa OÙ:F^™–T¼Û3ú0g¤¥qLꌆÀRj'$?ªDßnô•‡pÊápF3Ãá0g­…ƒ°|—¤ç™±ÁÉm-X(U I¤1ÌJöçâDD2ÊÚú`ñуxÈ!wÜqGvvöqªón³ÙN2Àr»Ý~¿ÿw¿ûÝäɓ׬YSQQqâ×x8÷ã?~å•W[%ÑÞ¼³±±qîܹ½„8gôèÑZ~æÐgŸ}vÞ¼yªÂg9 <÷Üs3gÎ<æÒ#<- ?äõ;¯×‹þqZ>—Æ XቫîÊDÉá–mÔ\S]¥Bæ‰{%sýdDƾ԰E·ZëëÇ]àÝL©šâÇûõë'ÓÓ”ìàmpWkYšî܉t®+éééUUUxYY¸À´´´Ûn»íì³Ï>NÎ3qâÄßÿþ÷˜…kjj¨ï‘}veâ7~'a SâÖA­ VÊq°–mð¢?È’Xr˜0¥OÂW‡7oæ%É>¾X/­Û?F6YêE†ù¤Ë.¹Œ¶HèÆ…J+ J8( z×]%*)Ñì…7_.Šì|BHÊ%’|Ô·ÄÒH ‰DccãÆ§Nzûí·óöXìû÷ï¿|ùò¹sçîܹ  3¢6œuÌÉ ÊÈk}qFbÁq¸–,.Ê£¤åÖN›d¤^JÖÈe-. q@‚ ÖbLdÌEA1WØíväL0Ýgr>•™™‰rð‰D"33T.Ø/|åN.”4§Ëåb‘R¼Aj"q?!cÄYNÑÜüpÿÃŒ ç“N×UåP£+„B¡¶¶¶7Þx#//ïÚk¯•|ÿ±¥BOr1Àh4ú³Ÿýì†nxüñÇŸ}öÙ“UÏ^)USSs ¡fÞ¼¼¼M›6õ’o8¢:³VÚ~ QÔªg_Y¾|ù‡~(¬“ën·{РAï¾û®²¤@b÷3tèP<6ííí]ÖJèÄHÀÊ{Y޲H#•] B Ž/k KYІ$zI¼}J?Äü”Ö O‹“┽‡©£’·N²Y`´@›LFæ&¾ÚîÝ»srrØ=íø…¡}>߸qã°¥Ù³g?ÆØ*‡ôƒV¿Š,£Ä5Ž™ÂJÉ{ÒÝ Jv‡•ôe±Dâ<)“½P´è„)2Ö†ÕGÓòŠ1) ƒ hEbIVb“ÈÒÚ4‰Ô—1Ô¶º£L ÓÑ\Ñçó±F SÞ°`#É·¬¬ käÅ_Ÿ° Ë "Î.— c=î$ˆdy"¢nõÃÛ Á%'x•]ɵÀ:U‰Àî¸9TXÊB!PQú‰Dþõ¯õë×ï _øBaaá1äpm .<™+•JwÞy·ß~ûSO=uã7fddœÈÌAm­Ý¶mÛ‘ÖJèùþ¦§§7¼K/¯¡÷ÖCÙÉÞÄC‹‹‹¡Sa•Èžéhw‡Ûí...FpÆ gÅ+}ôQ~~¾6«ZkVIV¯ËEUv¬(G›»›¾ÉH‹_×CRPêoz—#ÄžýçÝd³ŠØ’ŒŸµ2X6›ÍçóI°Åð™VÆ_³¶¶–•ÍKKK‰ãµdÏ£Þ *¥/^ ¸¼¼«­­Íëõ²ö u0Œfr‹ A0„E.f’›!-ñÿoR;Á–ø`üøñ]tÑÔ©S?ý"ÈÇósŸûÜ—¿üå“°0Æ'N|þùç÷îÝ{Ýuס…ÅÉÂyn·{ÇŽ“&MRÇ4E…½-?Z•••ÉK=fò²².ßЛ"X2ŸÜn·üñÇS§NQÜó©?Íͱ²8ò_DT/¿üòÙgŸ-³±$  …ÀUÈ)^–ÛÖ.Xæ^É×ý~?N*#­½Ç¬ÒiõZTW]·åŒ 9ÎÞŸZÊYä(HÅL¬ïÑÒ呹¡L$ííí˜àdˆ„žÌÌLDð Äb±ÚÚZ¼çé§ŸVݤô©C¢‹TA‡Ã€…( 2Kdû[#ª3§RFÖ°^JJk¡(KÉ™~­7ܪU’Ò¢.rQ+¡^Â(3‡|GÙ¬†Jj„ù/ÖÅ7• ¥ŸìuˆûL&:‰DœNç£>ª,µÎbEœ0a¹çž{ÑE­[·¢«d2Y__@3&g\*»]1Ãd9)@Ѝ8Y[R2;OëIÚÑc+a½Y†A$ÛŠs‘‚•Y\apÂx<Ž<uø"€L¤` çÂÓ ¾8K ¡GªVC7S>̤N‹aJ¹I` ™;j¼´½4¶O…ÈV‘éÞr*€w¡4WYYY~~þ…^¸páÂÞ÷(ëa¢¾üò˯¾úê]»v€¸=vìØ>úèÀ ,ÐÊXŸ,+ Õ ¡[ÊœYYMž”èaJ-<*ë6QÅE€(¿ymR’¥Ìù¥XC®¦23@u–ƒb®ßÐ6›mÿþý~¿¿¬¬ìž{î9êü <¿ùùù×^{mQQQYYÙÞ½{Qæ»/ÊÂ8ŒÄdã»HHöˆ7ŠÉ¶Ú3NÐÀi¤»}©là([ˆâI$ÕÄÝ¥Wä½dæ&.  ºr·ÛM+T1ú†¤†àÁuÊmlѽñ¼³}'ºËj‚ þ.¿;pDŠŒoRÔ{Â+)DT!ÆÂ-ŠÇã=·¶¶^{íµK–,9jÍ;ñ¦›nºêª«¶lÙ²wïÞ °0sæÌÙ³gÏfΜ‰XÕÉ5<¿ùÍon¸á­ZàQ[zzznn.J«"fJ$#GŽìý(¢þPw±ÎÙ³g÷°¥+))q8¼íøTCCC¿~ýzØ?Ùíö~õ«_¥úûˆÜ.;;ûïÿû믿níùˆ¢—………P¢I™uM•Aî’LJëT˜J¥ÚÛۭד‘‘A€åv»Ña^Û_J¡:¢­‘LÆ‘’‚îŠÅã*µS @È»!³ÌTB´ÎÙ)ïªÜmkÁDâQ¨»òòòP“Œ"wmÔ8—áÕÕÕ ¨§M›V__?jÔ(k?Í^ÚСCÑs³¤¤dË–-ééé999 $c±X]]×ud.i „™£„t*y3ÙóDà€"tÍ=º”J÷µ”„)ÑV…@–k9•7’ ’d¤l*,CFD9d¶¤LXjȰãýØvÊîäóHiX·WZI\Vrgw9œ!'B.Y‚Qž@ €#ûýþ–––­[·^rÉ%^¯õ‡têÆÊ½iÓ¦;w655577766Ja%å_²•DW<©§k; )‡‚ Mþ"•Úì¯ sByjÂMÐx¨@Ð’šQgçÇ9wjàz 1§›áøà°Ù`o0XˆîaÓ ö‹ôgFFëݫξ:ìoÝþ1f­åÛ²GµÖp–mYK"0êôùuäÂ-TQq#ïÝ»÷À_ûÚ×–-[vóîù·¾õ­Ë/¿üí·ß>tèP<?Ñ+ÿð‡?ܰaáC‡¦M›ÖÒÒrKhФ©©éX4‡ =þzóÌéMèS±Ùlýû÷ïýéð©?üᨑÑe¤ãÆõ«_=öØcGD<À³g̘1pàÀ¶¶6°Z'c§Ó9hÐ ü‰Ð€v„ööö–––.Kåççón ëG&Ï×××÷>CI-þ%ï0–îø$-R©5ûû4õrÉç÷@FZG“L{oµÖòé Ö pŠd‚·„é¤C|>_^^êíܹ;„ïÿû¹¹¹Gº(®^½úé§ŸFÆxMM ÒÐãê:YN“îéÉú°n{zD£Q€o¢ V4ø(«tIYI)•:­ø1œlb¨M’ea‰&Y´‚G“J5–eb_+1Lý5w;ò‘ÁíÍÈÈà‘±xC¬ zKKKSSSee%¶²G:þóŸÏÏÏw8›6mjllïæ•1V.ÏJäñQmÝ«SZ$K‘AÏÒ2êJ £D3xkµdRDDÃ4áòÇLÅÖ“¸c /e X‰.ÝŒ'‚ÊbÜO8R8F4B~€ž´´4ÇÚï,(Q%üAt<£öŽO ±¥W¬Ã^ 0¸ òØ90Ցク‰ØŸŒàu|ŠQ`&„â[·nmjjºãŽ;Žc‘ |ðÁW¯^ýŸÿü§¼¼q¹°ØŽãç?ÿù÷¿ÿý={öL:Õï÷w¹Õ;YkëÖ­çwž::wÜñîjK~z¾-w›ºk.Ks»Ý(¶&_ …Být¹É‹Åb ,())ùÑ~t饗B¬Ö{ J¥n½õÖûï¿ÿæ›o^³fM—=yÍï¿ÿ¾VkÔívçååAÑå·æô  ¸ÀvI)i­é5,¨½¡ËúŸÜsûý~ç”OþQpËÝ= ÖtQë+½Dr”UIÁ>è÷ûµp!=e‘$ \2 Avúc¿,0˜=QÒ ×pã7"ßþˆjüƒÁ~ýú¥§§‡B¡ææf|‹C‡9ÎbiDém)ãà ˆ†hø:˜ëe*œV¤@Ö°q=dYÏIkƧ “æTrƒ®/"ß&CNTÀ°Ô'WkÙL—Ê( òeÁtV¯Éð²¬?3ùYˆˆeÐiN~;IïÉŽü:¦ÿU“xŠ4MY5^2vh-GµÐºuërssËÊʦL™2uêÔ^ÎÏ Öî¼óÎùóç?ÿüó@ gddà°pW›ÍŽï lAîG:,.%ÿܹ±:ƒllб€”ªËùPŽŸ&b:Õ>±ÓãQ­È²òlŒ(u · ¢¶Š~2X؇°GM,)ÅÑÄA@/1Ë’ÝòØp¿Òòyér¿aåÖh w[ë×N u"  ƒËn·¿öÚk@àî»ïFÒ[/„T*õóŸÿ|Ù²e/¾øbyy9ÒeB¡ý@+Œëܹsׯ_Ûm·=òÈ#3fÌ@Øå˜×29jÈâñx^}õÕ%K–¨c‘÷ ŸÐ"V=¼9îß¿ÿoÜqРAëׯ·zþlmmíYŠ>fÌŸÙ£` IDATÖíä§ÚÛÛ»SÙãhßüæ7Ÿ|òÉï~÷»7n¼âŠ+z¥ÆÇçÍ›÷è£îܹ³  €O¯¼Wh‡ìv»wïÞ½páBuxöôÀöÉWdŽ!ëHοBK–ià‹^¯·»Ä(+G¥Eë¬8²;E—¼Ÿ]~A-CM*¸¯‘º¢²,e±%’¬ËGÎL)Lâ3Œ09Úíöææf®‘¼Hªªãñ8z™©Îr‚ªwY±YYYìßÜÜÜŒØeŽ(,Ä­­­XøYÇ5>ð§¹u\™¤[ÃÜ,W¨¹ŠµX¢6¦Z)vÒ²¸}—Ùyü¬ìð£ù#‰šË!“2`5®ÓçÊ(7R&kä²H’F£˜jÅÇe -À5,²¤8\‘§ˆD"ØO’r•ÂÎÊüqIIɆ .¿üòK/½4??ßãñô0n:·z¨¡¡!--­©© V:::_̸ž$ê\.Eß²£‹œä~OV(`õªýdVê*1B¶ä\ʳPÿĺP¤ÇÔá5fêeÀ—Õ"8v¼ó2¢gA!€¦…X,Æ0X7©Êâ6 wD³VqÍ ‰œÉFŠÀšZ(­“’:¼w'»-±Áµ\¤AIrûŠŽIcABËårµµµwÞy'üà?8ÿüó?‘pÁwyâ‰'V®\ùâ‹/655µµµù|>œâ8,ÜPŒôw¾ó_|qÆŒ·ÜrËW¿úU4Dë#èJx£` ¬‹èˆ#Ö®]«z— ‡?Q†…‘ž?>úéZa¢Íf{óÍ7'L˜ÐÃArss%–‚<óÌ3«V­RÝw›YµjÕí·ß®”z衇®½öZh!{CÌD"‘)S¦dgg×ÕÕíÝ»rT¤H&“£G~íµ×ø)ÁÒ'ÀÖî$GÚDS´··ËFôÚr.‘VU¡;2IrrÈÚE=À¦.Ý^^vcÙÎYcV¸ë•[Òd2Éâ4]ʹ¸dZq¯ G–t‚ŒVt¹^F£Q*áÚÚÚøtË‚–¸Îp8\ZZ‰Dòóókjjúõë7bĈžkGýö·¿½úê«•Rýû÷ÿý÷5I{*•BŸ8–céÔÆÆFܦ=B Æ™ZÖ¬§°,'v²ÙŽL'ìò1Ôb²Ú áOF(¤¼]Ò„ô|\‰¬éE–ŸÐ’¤¨v¢´…FmØÊ—ç€-¤Ëtu¸â^’sX; Ù»ÌÅÁ/ð4©»Ò3ˆá\PÈeggcèm6[sssVVVeeåîݻǎ{Ë-·Pzß%ºJ$—_~ù5×\‡ßÿ}ÇÓÖÖöÿ±÷åáUÖwöß$÷ææÞÜÜ›Üì! ° ² Š¢ë¾U«v´S§ítjíêc;ÓöçÓé<ÚÖi­3Z­ŽOÅÎ3S\X- l‚ „MÖ Ù—›ý÷Ç™œçäû¾¹„€-Ú¼ô¡ñ.ï}ßïûýžïùœÏ9â”%)U©ÕTªhÝz<µ³’h’ô’ŽþpçÌIJÌ*åƒqÑËËWjŒ&é0I¤<Áá.ƒv¢þoQu) -0ŠßïGJ2¡Q‰c¿›ˆÁœ±«Wš-Ó×;M7‡:k‘vUy/‚- ?Ô+ñ¼Ñr@TKdœ6hé¼­­íèÑ£6l¨««{ä‘Gn½õÖþì~8W/X°àÒK/]¼xqUU¬ÿ~ÿ A+œP[[Ûõ×_¿ÿþ_ýêW•••“'O~úé§c³­7þµ×^»îºëú{8O«:wîÜ’’’3^^¯×²ðîï@û«ë‡ÄÇÇ×ÔÔÌœ936g €zÀ;ç.¼­­íŠ+®hkk«ªªJLL\´hQBBBQQÑ@b…ð‚ÏþóÏ<ó þrâĉ)S¦XRƒ‹/¾øÈ‘#|—Su¤…øS89ÓW¥k½—b[Ó·«N)×û®/V\È•^šzUu áÂÉ™e€Ž²ý™*99*­ôéÅÐA*Æ4ÑÇ–šKÓ„²/Ü_ÖÖÖ²«®§§'33S?I\sçææ¾ùæ›ÉÉÉ………/½ôRQQ‘1æ¶Ûn[¸p!NÃyûpªS§N-,,Œ¯¬¬zVÅÅÅ>ú(ÿï† ¾ýío$V/øÂ¾ÀnÊgžyææ›o¶fŦ7æOor õõõ¹¹¹–ö”633Óµ¬é¬nÄ.%sæâ,©Æ?–“»%¸Š¥\o(OŒIyœt´Ù[w±ÎÿÚß/²›uµNj9ýÀõÑ8ò !FÆÂÉëõb¤uvvÒÑtĈeee………‡è*‹‹+((ˆ­Ylll¼å–[º»»ëëëSSS³²²:D‡/¬‚õõõ{ñp8Œ?¦¥¥i3&í‚,xDìÂM9æb±â/lsÓqHƒP×Ò0. &³Eβ¶²êÚÚÛÁ‚ Wpüd*ÌØcÅV5P|£²’ÄTb¥YŽM®P€jB(¬—qÓ“Ñ^<^X\uBÂçÀ:œ1À¦ g¢ÑhSSS[[ÛÁƒKJJ®¹æšáÇ£{NïÎÿ…^ظqcQQQmm-J---¸¸ì~ ì‰u^êx¬‚¯fMòÁÁåÕÍ Šy»U<ç:oXD)›é€YÔôç0æ0õRk°ªêêêBµ±¹¹ˆQÛa†Çƒ;kÕÚÚŠò‚?Æ)ñ‡óV2SR{®u«Ó#ÛP0zé}êºØ©S ç‚Bb\ÚièaÈKöWµ··××××××oÚ´©¾¾þ‰'ž¸ûî»Eh4ZRR2iҤŋŸ8q"¶µµ555¡-C4þtA€tˆá …Î;ï¼o}ë[{öìY³fM~~þ /¼PPPðÐCù|>Ë­õXåååIž9T_æ|ûÀÀ°x «Aw?räH+À£  ôËѺ÷Þ{á¡€¡ùðÃßu×]fÀ¦¬_|ñsÏ=‡å~ÍÎk¥„K9*mòx<ˆ@w¥ ú[ýýÝj/wÞ2«b1XºLªp$M úDÍQÑí{d’`ÀzLžÙ*‚ù.ËA` O!£¥x°l¸y>rÍ®nooÏÎÎîè訨¨à<˜˜˜˜‘‘ÑÞÞÞÖÖ†«áóùRSSÑIj1‚¿øÅ/®¼òÊ„„„Õ«WÓï4L~~~yy9×u&ÃUÅb ­Ozz:CÖ”4„R³V1æ›þæ,í±#]MGc<Œ–E§ú©Ž³J׉ÆÔßHÕ?J’i1¢L*  4ÜÞ#ò3•S¨¤•q¶ãYÂmÕÈ33G‘~2à2 P)€˜{ðXMMMÇ‡ÐØôÚ¶á|®ºêªú§ÂÚYQQa{sss\\\(¾\&9NK¤ÕseûhCï ;"ËBž•͆º1ý„4PŸ„wÑžVO¨œB¯­¶À ¸þ@TÀ@Ø0PØp°Â‡ã"Ó)ÓÀ™ßïGØ3ˆ–ÝÙíˆÛ‡&GÜܺp/½7®8m-I»ÖXàÃw’ØÓ¬<ªìFÔ=€ ˜ì@ FOž<ÙØØxâĉ>ú¨©© NÊU#tuÔ¨Qo¿ývyyysssKK 3•žŒO<£§ïŸ¦L™rË-·üë¿þë‚ víÚõûßÿ¾¹¹ù‡?üáܹsï¾û*ÇsJËÍ¿z‰Ðãñ,X°à¦›n:ó ©1&//ï½÷Þ¸k÷îݱ 6¹ÀCÑéÚ-ˆùV@®~H^¯·  ÀjBÄð]¹r%dXN‡ñ‘#G¾ôÒK˜Ý¼^ïÆSSS'L˜ Â&0g*`Ç6}út€3+V¬¸îºëTìÙòîÝ»y&›7o…BÜÏÁüÆIw¹"§þôsúwKH¤?¶®®î”øCÿ­­Úœ+usf½‘NAklûfÒ$¬AP©ma»””§6v€ƒ–PsXœ0Ô¬èh4ŠY•x×Z™ FŽD"ìÙfI½ÓÒÒH¿ù曨q£úð“Ÿüäæ›o3fÌ¡C‡`׮ޘǎÓÔôµÅæ| B¡y(Z,žuGøFçÀ#é\´jÉ©ö%P£ë4E»ü"Þ_®sijÊ`øåSD*¬¿X¬‰:ÑS.­Å;l ´ØÇ•IIŽF0rÑz8˜qåqe ÜцDR,,Ûá¬hƒñ†Ö h®SSSáhuìØ±§žz ã===]tÑ 7ÜðóŸÿ¼¼¼“dUUUee%’ “’’T •0…¹ìeÓþ'Ånúϳ𤤣FŒë]îo«ÃÎDm¤ ðˆb;\Bgu8ãÕÃ3Ëq¨Vï˜Rh«K)¼PŸ€ j©~¿wµEZŠÐ|bS‹Ž1^ €fJ]µ¦o¯7}øxy2­ÆdJþÕëÊ}6¥’ D—LMMÍÞ½{W¯^ÝÞÞþûßÿþ»ßý.®CzzúáÇóòò–-[¶yóf€õ®®®ºº:ºŽýŸŠ`à, ¦¼k®¹CÍÍÍ?úÑÆŽ;bÄLs¥¥¥>úè¢E‹:„ê@l/Ÿsí­}†‚[xõÕW×ÔÔЪ ÙA§„nX~0’œeSSÓ¾}û²³³aåJظ.çP8:ù›É“'ïÞ½ÛZ]ž}öÙ_üâ·Ür NOÞÎ ÿ{Í5×,[¶ cÀçó•––ÂåA»“.½ôÒŸüä'ÜßG£ÑñãÇoذŸöîÝëŠr°ã„ÒÀ+­V€` üd] ¦9ñùQe±Ö ­2Ÿõu˜éúc­,ÿqÌ#ÜÏñ£xµÙfÄâ2F ‚k©”ó¸1ru`M”Øð¬Õº¦©k3. "‘t G’’’öîÝËÓ®¬¬ìêêºþúë/¹ä’/|á 6lhhh@§4Ò âââÐúŠ4:¢‡œœœŠŠŠ””åŠð]iii'OžÄFœîAŠ!:::Й¯2K¹ŒËÎUßòø ÜuàqsÍ ÖDИ„Õ\¹ë¹pÁÀI¶··£©H©\­‰àÇSFžƒåNÝ3àõIIIx´¡ïÁÿ‚$ÐÖ =•vRÕ !OI™o²€ü E¥¬$b!ÇlÃB0à ×þ””ìÄŽ;ìïþîïŽ?ÞÖÖvòäÉçŸ~Ë–-uuu»ví  |Á€çŠ«±xT1l’““ù£¬š‘F‘r×Â=Šhˆ_¤àZPfi’? ”¸ãøøp]WAi*@¤¤$¶dò‰hjjB%õgôFoÌMøü¢AQB¸„6>Ÿ/Â% ÅGà6*9fX‡ˆ P ?p™¦´_ëÑjy£ùÜΡ­A»Ô4ð&$$ ôY^^ÞÐÐ>Üxã¿ùÍoòòò^xá…M›6ù|¾õë×ïÛ·¯¡¡¡®®®lÈjljjbeöc »Û¤¤¤ÄÄĤ¤$m·1Æ|ôÑGøÃ>øàƒ%K–Àu -ñçf•°¥¥¥´´´¨¨HI—Á1XP¬¼ÔèõzOé›åº·vâ€äää¼¼Ÿ¯ººÚï÷oÞ¼yÆŒ‡žƒê4A¬£&–Dêt»EmN1®V“Õ‡SÝÈX†££)Õ÷øð¿šFµª6Ã: <“HtÖÍàBss³– áÏŽ- £¸s¨`755ùý~\@L mmmÄ[ y†¸ž ²ŒXá¸6Zò¿âÙTg”1}á ÉÇã*±³G‘Þåñx°…khh…BÉÉÉ0ÊÊÊÊúáøío*ç„„„ÌÌ̦¦¦öööÊÊʪª*8/awtt È’»³³3--í¡‡â< øöþûï[a ~6ç2À‚L2;;ûLÖà%÷4dȧÁ ƒ£F*//·’/}>ß‚ æÍ›÷½ï}ÏzûܹsYVà‹·mÛÖÓÓóƒüà·¿ýmWW×ÕW_½xñâ²²²‚‚‚?üpøðáØegg­X±¥zŒûW^yeîܹ+V¬pæÒ¸^=DõW,,, ~U”©¨ˆß¥]œ®Y¸[¢Ü«©ÓNš?0 Ѭ¿fÒx:ËèMÄdÁYûLRÒ]¸Ù<Ï|…E<EiËR("Pðù|˜LÙ™™©Eiii)))Û·oŸ5kÖ‰'ZZZ†>cÆŒeË–…Ãá+¯¼ò«_ýjKK˃>˜––6vìØwÞy'‰ ²S“õü~JJ K¨ès“´^ÔÅ H«µé"©`+")¢4®©ff9\5U¯¦)oê5¥ü®%UÄ•ÇâÓ CêN`ÃM°ÅË2ú$QÒÇÝ?ÕV#›øÚÚÚ€6TÄ`Ík¥¹r–2ÏJH ÊI½ÄÇǃAQSÖÄÄD0£L©ONNnnnNKK‹F£7ÞxãÉ“'ÛÛÛÓÓÓkjj|>t~`»‹‹‹KKKSSSÉ… üK !$§ >Œˆ999¹©©Éê²´ÚØaÀÐ¥Î(ˆ‹#_pÑØ÷ÊL°4)@îUPÔBá~á]¼¶ œçZ×Ãaƒ­!Àwkk+[aQ–ššFÁÅ@Òr±±±‘m†Ä¦]¸¶ÀUÉÉÉêGJã•þ®kDܲ*ÅN.Ъ«˜Þ°B°zÀX­­­`Ê™vˆö8â[ZZ ,ÃõILL (î±XŸŸ3€cذa@àW¿úÕO<ñÄO<ùä“¿ýíoßÿý¤¤$ÐZ¸O à>½‹›¡´´´3¡¯ÚÛÛÓÒÒÞ}÷]s:Nªmmm§\)ñi¸íï:³e½?œAºÈ‰É°Ø[õ¬1cÆ,]ºTuè˜ø|ðÁÿ÷;vìµ×^ûÖ[o-^¼xøðáS§NÍÊÊzà0è/¿üò 6èV«É“'+¸ÑŸë¯ÖŸ£íëÖªo}N“¡žJ}õ’j©T­˜Ü§Œ©l²”¸9ͺTž{IæI²~ÄEWC—±,Y‹±ÅQ¹¢(Ó[«Z]N‚øK0ÄG²èè¯çTûÑ,«F¢1•-'$$L:577wïÞ½III×]wÝ“O>ùoÿöo&Lxùå—›ššüq¯×{íµ×ãǃAµ"c•SªékØèºÚáI$¿¢±¾Z$U.S#MœW[51Î~1ª£ŒÃ.ŸŸFÔ¥N¡Šfè ¥"}òp4jçkH>iŒ.ý)(èAeŸÜ d¤v(`Wƒ¬„ž¬¾YƒGåí¨EÒõQI,õkà' pÃ’(z_ÐÙP__öìÙâ!==ý[ßúÖ£> %xbbbZZí$p2áp[ '¶¡á7C8Ëî¤ßŒØÓk\£’šª°¦ìŒF²ÚÌkÉÔøvµògVdp˜oÕø'Àd ÂHÇ’ªDØ3#wèö lÇÜÄîîn6Ú?qÛ@ƒ€¸áãc‘¤t•;B«z*¨P¥a/uñ´l§Å¨S®¼3jÝH’:ýÜ¡EkooohhHLLÌÉɉD"°wéèè*xæ™gf̘ññÇ#"º®®½¨~¿½ítiþ¿Q=@y;åoªuØ9è:tÕÚÚºk×.è"-Àêîî¾í¶ÛV­ZuZäÚ­L-Óë ªå+5œWÞH'E–d”}>_fff^^^vvöùçŸõÕW‡Ãáxà[ßúÖÁƒ¿ò•¯ìß¿ÿ‚ .(((@O$ñûý©©©l[*oØ©ŸDåzTWWŸy† ”q§õèéô£28}útHv\žÕ††PD®„ê¡C‡\eIƘ%K–|þóŸWÅîèÑ£«ªªœST?÷¹ÏmÙ²åw¿ûÝwÞ‰§ÑëõþêW¿Úµk×K/½ôï|göìÙÿó?ÿc‘篼òÊwÞÉ5ãòË//++SqÛ¶mcÆŒQÀ¡"Ü *Øy}zzzòóóùP}NÆE«oª7RtbÙ´Ä m¸èâ1q¹Ÿg+^¡Œ†ÝûTÈckÓë¤jÕq¬Þ7| ÕZ­­­œ×t1S}4_€kÞ_¡õ+tÉž²øøøÔÔÔ²²²ÎÎκºº¬¬¬îîî””¿ß=1šU¹õlll„}l;°kƒ~¿¿¦¦¦¨¨ˆÅt/bµ¶¼Ô¼kS¯¤×ëe‰ µFºöÐ¯Ž ‚%ÜRjŸR iQ˜D«jndéÙK¨w+"­1ØGiY€ò÷’uãÇZ–V/¡FéQ´®Õ=®l#ıŒ$ŒøÐêÒè|pX•ÓæyzqãA€™솆ø2pá÷z½Á`ð’K.‰‹‹+**ª©©yðÁáwâĉË/¿'ÁL7{ölôô¹.ùuuuVL ûu]¿hýúõ×^{í~ðÆ×`.¾óÎ;Ï?ÿüßýîw¯¾úª¦ú N´oß>5A­?°³³sܸqÆÍüwĈý]4ÔÅ»ººPË·öú˜ˆ! À¬!¾T­¶œlë€Tª8L-Ý]Ó¦]©DmÄ{¡ ãÄ¡›c~vØN6.F² ®Õn^¿ IDATÐúÌÇ(Q¡æ=ÆÑcoÍøVœŽéµ…+..Æ÷†Ã᜜œ„„„²²²Q£FA—öõ¯ýÉ'Ÿ¤tZacccmmíÉ“'¡œÍÌÌ,++Ã̘˜˜X]] tUSSCrËÒ?)w¢…ô=aAUL¬ñ#LkQÂÏéÑ@ks¢rŸ–A¥^@.<ÜëãÌÉ6iÙ -I##/Nïlåh÷@¡åÏ;h­÷\à5¡Åªi*FàeQ¿×D¥ŸL]#?Ø—ÎOIIIÕÕÕðžmhhÈÏϯªªzã7Þ{ï=ÞÍÖÖÖ®®®ÿú¯ÿŠF££GîîîNKKCk½¬­5ÍxòFŒRÕ€×”Fl̴̇ÚQá—¶M˜¾I2N7ÆCiO¥Ë:í>æ=i„_D—þ‚Á¼  ¼x0À ‚l‹\)ŸˆºèŒÄR2ÏŠî?¤ô cø:«7´ÒÎÍÄÄDòg8j¼ðc•\gÊsË » ¯×›šš SRRfΜ9lذ»îºëùçŸ×ÍóÎ;¿þõ¯¯\¹rÖ¬YÉÉɬÆÂ’Æçóùýþ!€e»wïnii tº¿úNVVÖš5kÌi¦Y[ÁýñXñ¼p ·2ÆL˜0™qÎaKK‹°&OžL?îþª–VÕ)..®¤¤dΜ9÷Ýwß½÷Þëz¶Öv Ýy®ù€øûáǸï¨Û¾UË㇤§§»Ây}¸?³–-£Q®8g5¶Ñê¡•@óÓÂÙ„,®ÎjzÄYÞZ·`¯´“uÝ,g KD¢EM³N@˃ŸÉ¥-{qqq©©©Çûí·ï¼óÎ… cÞ|óMôÍX£wéÒ¥·ÜrKWW×»ï¾{Ï=÷”””ÄÅÅþù`/~—››Kq®sÔ9Ÿ-ý€[¢Å%7$Z=ÑØ8b57ŠqC-bOË=úàgSÉNkæÚêjçš ¹7êDù—6À[Õd ¦DZéL+H]¾ÔÇ‹Ê*Ìz}ø½Ì7´P>³V?A™Àˆœ'n}RRR0„ž:555‰”——?¾   ¢¢Â9«<ûì³K–,9yòä¬Y³0Z`R ?\pú˜ëJŒ‚ØMU²¦ ^… (zR-dúú¼c M•u+¯0>ÒHm,ÈÜàOqä¨CY¶¤€¡iii#ˆ;ø‚Go äVì0€Ö ¿8Õê¤&]D[ ÝðÐ6VÃ*0ö1Ù¤¢la™¢+Â5 ÒépŽg mŒªßâ§¡ÙÙï÷‡B¡”””3fŒ;öŽ;îxñÅAFüÎw¾óÞ{ï]qÅ£Gƒqqqéééh‹Æ0X. aä“…c¾øÅ/nÙ²êßØØØXSSsÊ ±×ëuõ_°ôòýÙºö—oƒÉqÉ’%wß}·.)))Gí¨W©×¬YóôÓO×ÔÔ¸Æ oÞ¼9??$М9s-Zƒ}9ï¼ó¬Öt¥ë‚VûsWOJJéeG1„5jaÇ>|‚*lûôehuŽñó×ߥ¦xÈÙÌe]aí~Ç §€W’]Ê®—×ÙRö ç'LÔ¨û?LypK¤ÕÝÝ]\\Œ¦,™`@ûÛ`ÔÖÖ®\¹1£Fª¨¨HHHÈËËã ž####++KÅì|z/Y&2šY¦›xjß“Ù"”!]aܬnUõÌ•U»üHÜêDzœGž eÒ?üvÖ¶XF$î„ä…ú3j{ ˜è§eù’[cUÓ¶DÕs‰Òú*GøêïeM°ä±\ŸkÀBb#ÖÄ(ú€Á-((HHH(..ÎÎÎÞ´iÚ–]•š§ººúâ‹/.**JMMõz½ÅÅÅ E±»|a‰Vi!\IAqOå|éæ@r‹æa|’šÜÉXø –´8Nh‚Ou,6Èða,ÁžŠe8Z¹¢ ±Ží¸z]]](Œ‘ n0¥´¶¶¢h 56±ö$8+Rª¼•‘X”Ú.Ê09W6½r(òÁŽçLpIËS%¡yz|¦8ÀðŸÀ™Èfff†ÃáéÓ§Oœ8ñî»ï~íµ×è0g=Ôeee÷ßÿÛo¿={öìÑ£GG"‘@ €nŸÏçõz‡–‹Ø< >üL,×N½ØGmm­×ëEZHŒO¾ä’Kþüç?«ÁŒó8|ø0dzÎÿ”˜˜èJGa VVVrÝų=eÊ”}ûöîE°œâœkãÆ—\r þrÅWÔÔÔÐ_M1.~rNNŽu#ö§u³Zé0nöúÊýi°ˆWx2.g]LWë¬,±-9}sED'‹¶,±ËÒOù'‹š"´Bh ):+5…Ë¡ÒuÚØÐÐ@`­ Œº§¯­­Å¨©©ÁÒŽd@fhŒ1ŽVƘU«VÄ÷§ìÔu«‚ÇãIIIA5Á·9‹üW›4 Ãaoll´î3 pËPõ`Óa™Êõô»œû .Ãèÿ·XI"«“ÜÚÆX2 •àŠñø¥êÅÅìEåœð9\qu]tn9ÈT©u>)•[f]ºeÁšw™šÓ„GÝ‚Á ^Œ_zzzffæèÑ£8°|ùòåË—³te@$UUU/¿üòÑ£G‹ŠŠ®¸â 8ÙRYO›MA¥ñ´=S±‹˜ô‡)a´R†”µjSö§åH²Üºèu¤ƒM˜@èKÇš V ›¶¶6ôÐQBŠƒþÆØàúp˜””ÔØØÈìm|”ztÑ(Ae”!²¤)GÛM_¯f6©hÓ ¥²z*RÄ}Q£2|.{$¡¾òù|@ ==}úôéS¦L¹ûî»/^ìjÁÍ饢¢â§?ýéòåË/¼ðÂÂÂBôÍüŸÂýL¨šÏê­yFFÆ™|ÈàœëÓÓÓcDMãfÁ=6ÑÕÐЀSçÛ/ºè¢uëÖY{J ”PÖ ä±uëÖAüVÙ]ÿ+–FV.œW†à®+¢…°4ºR}~µô 1x)ë‡8µ–¼MºíVL†· ÅÆÚìZÕÆå]«ËŒHN«K¼ŒÚ o9ÈsÁVCÎ×Ú@`•±´É‚k´ÚSQ ?­±±–W à ðXaƇì:cÌM7Ý´ÿþØxZ·Ç{ ]‘HE¶¶¶ŒŒ -‡Ñ‡F ,ZS$ãl­db,$ó(µÆzŒEe } zeøÐ±þè¼#u„c…PK^­EbÝ¥:J]³U¯î¸ ä‚ÎÄ7g¹PUÕÄs„–’O/‚‡±ºS¼O8¨ÁS´ƒJHH@g+.—ßïG"Þ´iÓà ùÇ?þ±²²2öΟ¿iÓ&@±iÓ¦%$$„Ãa07>X€ì¼#)¢® ü"Í)2½ÞH8gœ'÷?$2-«Rp0JZ2#<¿ì½åÕf÷"Qç^”ÃHk‘×Ðážÿ†?–ÊP¤¼0—9p Å ‚n‰$7½lbUµ»Ö ùR7©œ·Í5¶‹ïÕ. Îd§Œ1¸ø¤rYöU· ÕN:uÒ¤I÷ÜsÏòåËIÄö75cªªªzè¡åË—OŸ>½¸¸& ˜ë†–ûè´8>ÃÓ{º,ãf±=èCµºüOš4Éô“£‚$¾?üpÆŒÁ)))³g϶Œ1ÏðÀ5Y¿~ýí·ßŽ¿€½Ð夵µõøñã))) ¬uþ:Þ&,·NŸh«”†P<. ýõ`Æf¸Æ8ó-M·M¤"PˆtÊMôQÄj]1®q7}Ã#ëß«¦ç´7}½) ¶fùžžžP(WeËà{ÉÏÈÈÀ[ŠŠŠÀ9r$77^RR‚ôecŒMžrÓò£ýèØ±cG0aB~~~WWWvv6:‰@QèºEa9^Ò3P‹J)qc­œ &ýY{ 'ÞÒQc*øc\;­1C¬£áß,Ò§”ϽÅ9V™C® ²\«xÇ?º†ý0ˆ–ãgEžOC ]=XáRé’šôÀÔ××ãe^¯7755]xá…k×®ýáhzMA2¢6nܸzõêH$’œœØÝÝ}ÕUW­^½zk èj §4àþð8úÈðvÈPöïßÇ ¦õ>øF wÜqÇ‚ ,å "rss­ŒµÖöôô[p„ŸcAÌööv‹Òsâ×+ÖØØ¨ßÎli×ÙÜ:+®Üœqœ‘ÌŽ‡}—eÓe¹‚YÓ7”ø£°\·b¬Ghì†q,^FÏÜ*GÈŸéõzi c¼fúôé¸w­­­ï¿ÿ> ÃNkˆâ$C¡ÐÖ­[ÓÓÓ± pƒK~T“[ °63h[sØÐhå­‘Z*Õ^$Bâ ×ñ£"6ëVj°’¶Ñq¤±Ve¡7.™TºXë.Ïì #3dÄ:U㹆i¸¸Z}‚ã1½ºl 6À»È(( ç´€w²­4 ”Ñ D?ŠÃK GŽ ‘Žqà²W<—_~ùøñãAl +öoxšPW² O1ûôª„Ÿ ÄF|:èÚ¯•\â×⸂~ AÂ]€tL·UÑhWŒ±€ÔlÐL] è c#¾D± ÏRbp˜—`DÇì)|ˆ•ª„m 0M/°c^ ˦Ø*ã±Õ½G#ŸDº¿TYî$z%á%¤›%>Mèœ` šý~ÿ”)S.¹ä’ï}ï{+W®`ƒ0ïTMMÍÓO?ýÖ[oMœ8qêÔ©ð@X.Œ¦*0n²³³‡HvíÚã¦b䡘{)//¯­­µœNé èÚÚÚ+®¸£söìÙýõžŽ ‹";ƒúƒ›N›SZ¥ÓpÁúXÓ¿ÖØÙ±®‰–3 _à*ÞÒÒ‚¯g\Þ©S§n۶ͳ|ùòßüæ7ƒ6üiëÖ­{÷Ýw=ÏðáÃág6éîîîôôtrªÎÆk£2Xl'¬˜?:SXJä8ù?EHN«L.*FR ¹ÏVµ8µ/ÚªFt®Ý…\õ)Ca¨°FëÚL®Å²¥ ‚g˜éµ'5}û4ï\8þðYtm '§ ÖTPhÔA äåå]zé¥Ó¦M„0g»qãÆ;w.[¶lîܹcÇŽ…‹wjj*ëeTó˜^·œ8”áT<§Nct[UKµÕ`µ‘ÉÓ®B*Ú±âK™aü©^É€åBtÜÀëaZÚ lÞ‚:>ÝõZZZào¦õ¹¹™m€ ±0ÑiqøJÞ54èñ™¢Y?N’aˆ¤Ùp ÝFh¡báT–t!ÑcyTŸk^ ùãââFý¹Ï}îX¹r%ã³>/AœóÜsϽóÎ;£F;vìP‰°_º¯°°p;ŸÄ÷îÞ½{Ò¤Iš”§SRggçäÉ“]ûØeÊìììaÆétßÞÞ~ÕUW½úê«ý½'¿uëÖ±cÇâ/—]v^vãzzzjkk÷ïߟ››;uêTÄ; .BZZD`|A{{;DˆÇòê¯çja§—ì Ì]?ÖYo"{Ïí¾õÛ5¯Í ¸u¦â›ÑbòÈÅŠº–0iQ»·ÚÛÛ£Ñ(wÉßcÎÒ*f.Ôãü~?2FL¯ý ˆ%Ò]ápxÊ”)ÕÕÕ>Ÿoß¾}üñà âÀF_üâ1MAÇ #„çPòB}†b‹a‚ÌY—R-ߨ » Õe›Ìë- ¼‰K«ft2[Z5šR90ὸä¨V’,Qr‹•DL wT3I-=[5d¼hÙZ´.#n´0Ê\<‚•ææf04üE¡P(773gÎ\°`ŒýQ1à…ݼyóÂ… ÛÚÚ¦OŸîõzC¡Ï烥8~;zÍ€?(6WÁŸ2mº´kd$/˯à{\W 6»(§8¥G¬üb`PFOQM³ÿÇßE0 GyDŸÏ‡Ò?-ü”ùÈÉQÖlá¤z¥9q±TÊVVš‘’%½Jñ™rº ž)c@µQkÓü:ÎK€q )Á`õôô¤¦¦æää|îsŸ{ðÁW­Z…–ÌA¬k€¤óæÍ[´hÑ”)Sòóó‡–˼}ûöñãÇ£Ùj r?ñÀwttdddÄøÒ‘#G‚ˆx¨Âvª¦M›vJFª´´ÔçóœÝ|óͰÒ>»fm$‚]yå•/½ô’¡ÓýÌ™3ág£ë«ßïgk¡†ÅcèaÑŸÐj º+#My®¶þZ/`ýˆX‡*ŠØçàŒƒD*ª²F|(Ô þ”Ÿ¦À ³ƒ–µMIÛÑy1•Ò³÷;ÓdrŸÇãAÚ’¹pOSRR€í°çV01hàN)Ê øü´´4”DUÍFQˆ´(ðXbãºUëQ°°qeâŠåü!Š·T ÈËn Â¥1>EÉ'Ëœy#ܾ«8¥=Í!SB=–åìe ñÉji]k~³J~T¿©¤¦§§=˜j— dl%õª7:[ón233ãããóóówíÚõÎ;ïìܹóL¤«¸Î¿þõ¯³³³sss333³p}或Ôb€«Îai™ŠÑ$åQ þ”CÕ²ì­ÁÏ ¾˜O®¶=²¾fÄîÜôÚŒéÿ%@G=Ný8U-.38/€v ¯!Ó‰ëàóù°ZQˆö:Ž[zŠr&ç€$²¦w#ý¶ ¢xÝÔÚ”î|´Óc0„¥”|&Løío»zõj”æ=œð½/¿üòË/¿|Á ,÷…Ê5þ}€ï½å–[Ö¬Yƒ‰l®3iÅj ľ´´tìØ±ÙÙÙÓ¦Móûý[·nµÂ2Ï ÀjjjÚ¼yó·¿ýí¼¼¼úúzËw;:t2ê””ç9cçañ@8ɺº:í—È5­ÁrMJæ+]SwP¨²ö@NÆ"´ÝÏ"ú+¹ò©£\K C¨añv„ŒXÒø]Vkä¡âÇ4u¢s /HKKCµzûöí¿þõ¯Ul{&£ˆÊú¥K—’À¢-E}}}rrr]]šYLbéŒF£bXVÑ$«0_qWW…6.9@u˜#^!JS"Ó©g¢¦DM>•‡`‰?«¦ª‚ÎÀFpc ÅˆˆÑ5Ю®³VÂ.×?Ô’ÔBêÖj©£gøê±mý~8NNN¾è¢‹¾ÿýï»zÁ nËgŒyðÁ7nÜxóÍ7ã‹ jG8Žrp:i{„‘Æò(''E Z"Ó›Á¥Í¡êîÆ²)iK"f\=–˜Y#¢e‰Fûì@ЂøÁY¡ÙÓôJ3Ñ<ÈñƒŽN"K ?èÈjz}ð 0ÍçÏ!RÄÛ9hù“ÃÚƒõ>’¼t”ÊqT3¶Üô¯à ƒ …B'##£³³óüãÚµkǃ:wìqqqo¼ñÆüùó‡–û±sçÎ9sæ`Ìî{ q455ž»\¤!ç"§ª´–Õsç °JKKçÌ™óðÃC~~vÑM›6Ýwß}%%% y*+ÄE[{DåÉ[»–Çã¹kkú³tEºÒk¿¤NÜÖn$.Sx…’ÚUQ1€~V^¦s¢«º‹¸¾Ìx—Æ![ŠoÝã/µµµRqqq999ØÂ¹‘EÞp8Œÿ—ùʃÎÖvè‘Gyì±ÇpÂh'ììì¤ ÆrЄ™^K}¬vXê«}m 54ÐM½è¸CÁrŒ]™:ÍreÓ8r¸-‘ã‡Z™R½”Ò,K‡öŽ7TýÕS³¾i¤CÞHvžéŸQPŒ“$©ê𷀩) W1f2 ˆÛ^*‘½^ïÅ_üöÛo#¶òìFâ¾ûî»UUU|ðÁ¸qãòóóQåGáŒ1Ézƒ`q®;4–´q œÀ7-Š4j¾¡ù¾eb§­4€Ð¤B#ÝÔ³ƒ£bG!{áSŠ!ÎfWj“‹(n2ÇÈÞ%''Ãðú3Í\ÂÛÑQÈŒL/Á`n´F:<𣀡ép‹·ÌJ‚Ä J/Ö©Ái±‘–»5ƒÀ¾9sæ%K–lß¾ý,örádÞyç!€å¾3~ùå—çÌ™c%uáÿûؾ}{NNŽërHËÍ>ø`  çƒ>˜1c%ÕøÀÛo¿}áÂ…1¨5¼xÅŠ<òÈ5×\óÌ3Ïœõ)ŒÇæÍ›7nܸbÅ ×Ë A{ô§H‹qðz¿ßìØ1ÕW©âØUƒÅDÅØ…< t¡Rm miTÆwJBtÐwÄ›5ªûój‚©USS“f¥ñÊkˆ}kk+- ð¥Ú\¹«/>onnFÝ–ܳˆÒÈ'--­¾¾Þ“n¬mpˆÈÊʪ««CECc×>”}QT­Rw¨Ä¸Ëø‡ÆF9û¬LeÕͨèJS~­…Öôz‚ë­ÄË@¥°•OóæØ j¹2ZUH¨¹Xb™ç9hl³"B+äѸe¨ðÕÍ\VY±÷éñxš››322àxt(1bD|||eeåž={öìÙsÖ·ØÝwß}ùùù™™™8IºXAÄ¢f÷ºgPm¾qx5ñÌTgZ^Ií¯´æ(¶>¨ 1F&:éÈJÒ‘c’J"9<é,‚Í¥—(uB}x-¸¿´ÈÂsOž'6¢Ø9“úÕ¶erºj5Bo}k3Ìl'Ë0'C1†ÆÍ; ¶0œÒÓÓ§NÚÝݽ`Á‚#GŽXNfgHÄÅÅ ,÷åŠÿé>ŸÆ˜’’+¡o †ÂĉV<æÎ»}ûöØ6î8 粺rF›Ã9?õÔSK—.]¸páÚµk-‰÷YL\ÝãÈ—ðD ºi!±dDC¬`{”![ÊwË´‰ú_úo‘â¥P'÷„„„êêj†ºÀ; //ïòË/¯®®~ì±ÇN+Üótk…çñÇF£™™™ ÖШ0•3<¦bMÎæíô¤¢º¡­’)uͰœç,#_~ˆ2Íà5YÃåèå­Äù 0ƒ^ùêcGtNÈ‹a£šz|ø!GÙ‰U cMæ"@>A ¬eÆ €ª£âðãþ îN¼„r'===‰L™2¥¶¶ö™gž©¨¨œ"h@<Ö¢r¯¿þú¿øÅVåô˜9sfyy¹–Oëè]Ñb€"(™b³ý-Ø­­­ßøÆ7n¿ýv`‘OÈ8p ¿—ÓÇÌ™3W­Zem/8û„B!ndµGÉÃÞ¢ˆâããëêê(cä´È&»‘«¼ÆBsVs©Øå]¥¸¬@À.!®”›öð[cFÍŽqÚÚ‘dÜòL ž®‹l¼—n pà½ÇŽ+..Ɖ=÷ÜsóçÏÿä¨P‚Ôùóç/]ºÔ‡Q&F£ €P(„µH$BØÁ ¢FÏÜÍsR¯Q¬",«1Á+œÚ&a%°º´c@&Ië+¹‡i8U,XW¨Mf¶]*Õ,J¡ Cº‘` ž“÷¥vÛj™äŸÉ1°\–+ŸÏ×ØØˆâÂéî“Ñ{xºh ÇÆ ¨þkkkKIIˆÅNàäÉ“ª`ýä€l¦çÎkú*¸‘·€iâüóÏÇŽÓôU›Å¾Aô½4}í|bŸž²úV‰Í‚PNyò@*ƒýͤƒ»°L*t~csdX²b úSn…9‹¬®¿Çã¡EíÉ“'/¾øb@ü²²2ô~¢Ã Õ|caa!äw>Ÿ âwxÐ766rBס…•€WFã8xe(æ'pñ ÁºeŒäzƒ¸iæÜjZ4}ÍÜu!Ñh ¾ª5\¥º¹¹ÙrhC%Ÿ¦w ªjÎpª3ŽDÑiÐ IDAT N'ƒ~ë4X æûê–’`¬0Òî|>_ff&*üqŒìù³{”””455íÙ³ç¼óÎ+**b3 cË5®X¹F*í{àÛèÖáç á¦g¨\ÏHŒ ïÓ$•ûá0ÃÅä‡ñ€ïQ…8þÂõn@N€e„}(àâxÞa4jzµíP°0ÇFQN’ô—×þËù]ÕlT|"ðö­Ç‡ñSÚÀÂáðäÉ“çÍ›çê4°þB$S“±D“^¿iÓ¦H$â ‹å2ÀjckkëäÉ“™eŒ¹é¦›V­Zõ‰ gñ¨ªªÀµ:0M9r„*A±‹E/%%%ià£Þ)畱d­¬£YÕXÓ·‹P¡ŒFû·4$µžwõ=-LjÁ„5: êGd¡´«Ÿ(Öˆ­€Ɉ“KKKÃk°¹„QûY©ï—••¡!(cm†ê—EéUÜ£ÞÑÑFHNÙ¬)ó¹Vd£Æ›–Þ¼?Í»%†c¡„õ2uÇ@)ŠÖzl Ô¿˜¾ž[ÉÁZqøÅ;W2#‘Æ”];·"`8°èj¸u³%­´ÆGk+«Ûõ&¬¼¼<\öY³fUTT<ú裱ÛÎ:-êñxî¿ÿ~\œaÆAh¿_ø ÀêIiô±S‹§P|[1`õIÌdùz¨Aš’è|lZ4ÒåþF[p€é™Á@—Nì‘ìÉM0wss3É*üRXŸ@áŽÄ„=k94[oÐý¡édˆUÐÉÆÕ½A,ë³ Ég[­ôa ±™gÀ]Í›7OºCë/z`¤Î›7ïÞ{ïUsççŸþÖ­[]"ܰaÃu×]§³$Ÿ¥`0øá‡¢¿w ź§`WK§só@ŠéíÁ±žŸÏWXXhÜRtœU<üüææfW}LKK ”Ñ&ƒ.ÛŠ:ví*eŠ3©,–ÕœÎ{§ÃÖÁ ådȬ_G„gÄèÜ@º¬rKgDÄcúÚgcêëëãââšššzzzjjjTÍFÖ$<†h(ÒÔÔôÎ;ï˜Aå5áÖ(!!áþûï‡_<¤Ê`°ˆ8©ÃUàÅÒƒö÷)ÖDéäÖ,fê±iiä¹û·VY«ü§ñª¿Ñx;Í&Ò$D̹3½Zx­˜°á`‹=\¨D˪£"ӯ׳a#Ö’-*MÈÕë(/VVðÌD"™™™·ÞzëüÇTWW[!Q«øüùóKJJ®»îº¤¤$ˆ C¡®V^a>G lV`rë³øù„› 1yÍI™««>_¦¤)UMd”) Ä=ÂÉІÊ8BÄ[ZZ@\i6…IIItðcÔÑÑáÇ'¾;\(ô˜·›¢xü›Jy 5×'…Ü+qq0Ñx‚wi¶.¦¥¥ùýþÌÌÌöööÿþïÿ>qâ„«}ÝÀúË1Xååå§+À2ÆLŸ>I;ƒc°::: q®L'N„£Á?ùèÑ£ÙÙÙ\ ÊÊÊþ•š³rttt´¶¶Îœ9ë´…K`NãŠ9t§¨£¦¦F©E®Z8³ÈB ®!Œ¥¿Z§î;IHXEŸÓ–pÇ5½ÇÂyÝÝÝÎìdgÖ Z]Syžuuu˜¿˜çŠN"•Ýðß´c¨¯¯§ÕçÁƒï¿ÿþ¿üŠHâ—.##çj Ût\¨ŒŒ °;¥rì“Ò”º*à‡+à¦f˺‰Ú†éŒ'reR­úš.–Ú³iEA«‹©ê®Ø>Fô¬‹‹•\ÛÀ,g…”_ª.©ÜÿP:myÚÑa:žî+p;H¸bi …Bˆêª¯¯oooß±cD ù‰¨«««¾¾þ£>úÅ/~1cÆŒôôt¿ß‰Dh\„A`¤³œ4ïû“èZN¤Î’–J³õÎjŒ•éùådOy›4Δ›FÉê¶l‰0½B^Œ:TxQ†fï8N:«sÓ¤JƒÃiƒÂ‡KUö*ºâ%Ò’«:ɱ¸I+8Ô.‰½È4C· GŒQPPPRRòâ‹/?~Üê/X…ãàÁƒ—]vÙéîÂÕ›qÕ7^V‚Õ^xá…;þ+_þò—_}õUWÏôsð8pà@BBÂ7¾ñ PÖ5áj–K7ÍTqj1Ž&¦oø >«¯um‘j}/vo®ðÅš”™Ìe!Œ––íˆ1’¬lû8]ªûƒkõ:kÓ¿-ªvÔk Ñô•TÓW ==ƒ9sÿÍäWÊe]€ëÍ_þ`•äñLJEVNN,òù «Ð ˆ! bPáwY‚f.i䊂YÞHÇS;'?ªmS$3Яz,šH‘ŸÐ*’~:QI'´Åü;“ž-úÊÒlio#KmËàZˆb1K±ð±´H>zø¯)))l—]vYUUÕÃ?üWŸŽêêêöíÛ·víÚ¬¬¬Q£FÂa"!Ì6MoBa.7uÆgì ^¬Rw`Õ¶³å.¬ÚÍ£‚$JÝ™€D6”0x&Ü h°”Y$M«úq4ýÑ T"šlá{UtumÌtk¤_Ä÷º>#4 ÓŸ†¹Ï ÕxnØD±ý3¤¦¦^xá…»wïÞºu+Èø¿˜Nf`¹ïZ|>ßüùóo¸ásšrãæææAÄ1U…õ‚Óê䯯¯‡ŽP(ÔÔÔt½Ô>Ñú îBvvv®åN.à‘#G¸‰±Þ‰DŒ[wWëj‡Ãa+`Ž…6½ŒÎKJ?Õg`¡a(~œEC×&m#‚†§ÄÑbÕüž0ú½ù®H$Ba;\?Œ1•••ùƒ‘mXyn]©îþáÇ·oߎZá¸qãP+dL!½§AÛàápšvü_.{ô¾bÚ z©èÍ]xl¯u³â©­ÑŒ^Ó·‹¸J»Öu8 0µ°×þ>íITE˜¥g& @€±4£êNʄɉmà½H…Ò 82Ïš5«¨¨èþáL_ûß¿â¿nݺcÇŽ544\rÉ%ØÝ!ÆN!&…Dü™$ièmm®‰r“tÀg´=Wl8àŽ‘cñÒÄßLVÅí€ü #œ©5~¿ †z|hyìPéÆÇ)øNlEðJ¨;;ÈyO{]Õ‡(M«çº…`÷%Vdí|´Šø@ÔZá"f¥¦¦Îž={Ù²e6lˆQX…5þtáHZZZKKËáÇ·$x½Þ­[·^}õծů¬¬¬;w¤¹/¨¯¯ÏÊÊâ‰ÎŽOE}Ç‘#Gn¸áte[«5yè`É»ÖÜÜœŸŸïz];a«kj²S7£s\}‹ Ôg™ FãGT;5!Çi ¦9ÅIàÉ] ¦hÞaB™Sä‡O®¯¯G|„BOëRD£ÑñãÇ“ä(**:tèP\\ÜÇüàƒ:MËÎ…ùÈ‘#PÚ%''SÛ …njrH´ÑÑÑ Ê´É1¸ ¬11P¦3¢€˜Éô °Æ?›ùu9‹ãü{ŒôzKwïÄLZOuí½Už+(›çñP0(Kr(bQ \rRRÒØ±c÷íÛÿI$ÍúÀeóÍ7_{í5lü ÄG¹ R$®ô´ö =IºÐãPA³B(‹náÖÛ­um¨ïy, 6mÊ!Kµ¤Á`Ð`$Ö‚ û1 ¢.vt–Qˆ†NÒÓ„ÎÅk´@’iÆÛµ¬L ŸªÐ@ ‰D.ºè¢×^{mÙ²eƒ«, ¬Oêq2Æ õ™+Ó)ŒŒŒP(CðÁm’Œ1ãÇw]¶çÌ™säÈKvcs¼xñâ»îº ëýe—]¶nÝ:ôªœû?ð7Þ¨¬¬\¼x1£ý´ÔÔÔ”’’²ÿ~k öz½€•È”µ |¦/+‹ÄÖ3±H,|&"µ &Åx »ôu™´¦EÊ\,îÁµ“ѵ2ˆhiäIâ‹€”Wg1Hâ8æµsÐbû˜mŒ™>}:>ÿèÑ£sçέªªòz½Ñh´¬¬ìLrR?!6Úó³ŸýlÉ’%§¨¨(==M€N%hô¬·›¦ŒK³ŒJÜ” âê2@ ètµµ0‰ +"Z U9ÎZ0+>Ä…ÔÞ±¿ÏµHM¦Ê*KÑËQñ½úŠiŒ˜ •Â¬ßøøøp8ÜØØ8nܸ@ pà 7üò—¿ËÊÊ,ሕro!K‘­:§¿¢ ɹXÇÅÅ544¸~…ªè°L²¾fú×™õGq‘à pJü«D¨)°–ÂZçnYÂ|å³²²¸·9r$—ÒsvŒUTTÀb£  •b4‚á ªTÿd$Q€×\¸MÒ|YÖ¸5VYk‚Ên*‹@µœÞk«ÿ\ïÄ­{ós°†ißhhžÌ±ø½nÈ!DSJª»ÔxÆŠüEø¿ßßÚÚzÏ=÷ìٳ痿ü¥q$ œ;x½««ëã?~÷Ýw›ššŠ‹‹333ñÔÐ tŠ­IéÑV[h+Ê:#m¨ætJ¯,+Z R–G"ÍJ¡Q÷MmÒžÈËÈ}bßëWõââéc¢y̸ËÌS ¦CÑzÜÔãJ·‚¬'²¤ˆ÷‚"¥•¨ÏçCYÐçó1bÑ¢Eï¾û.8¶¿NeyKÅÞVÆpZréééƒ `¨íرÃô QŸ•ÓBiëׯ¿ùæ›17ÞxãÁƒnñp.ÍÍÍ%%%®%ÑÖÖÖ‚‚‚ŒŒ §ñ­ç”v¶–+KVÅX «ÊRëò£¯T#lë½N€b¡õd²n7=ôuš†2L'SJ²Ô+Ùôur§ÎT×E¨Á,KqŸÏGczÓך‹ÙjƘììlˆ—1óæÍ£âœ-@³ŸàÅ_,))1ÆäååaEŒD"DmmmÚ(ª†ã¸8ìãCÁÂH3t¸¦·û ë "êCm!9å9tyëo?à¤E5¸Æˆí»fªXÐÍ©S䪯ßÎõR=âé ®=hÜŠ >…ë¢.{FŒ¸rærÒ¤IÁ`SßúõëÏq©hOOω'¶oß^RRrÛm·uvvÒ š<ÞPÞ/µ´Ð‰}”ø;±¸Z{á¿:ùlU×™¾º|,²r1^Iµ'ÊwLGä ’ГH®N…V¥¶FG‡¥Ä·$VŒ;çlÂà`óù|©©©çŸ¾Ïç{ë­·V¯^mef9°>ñ D÷ÜsÏÀ·æcÆŒ‰t‰ÃqñâÅxh­¶üÊÊJôÏü£–,Y2bÄcLAAÁÖ­[ͧJƒ»–š’’â÷û¦ 8HÝ;WY §rá±î2gæ¦&ÍÕéîî‡ÃúJ\êÔÔT¯pÞÓR£ë8À`p@Ѧ‹ Pª_¤Ä~0¤¡÷¾,AòÑ$ÏxMÔ)®×ÙÍê¦ÔÅ€v ƘÌÌLöo~ï{ßcÆ9^t6Æ,\¸Z׉'B¤ Õ¢•8v R¯–ü@&7àþ&&&ª<6þÅY™åÚcÝh˽šHKY ¾‘Œ‘s¦”yMË©Áôu 7}5ÔʨYÑΪÍWöT¿­þü) S‹ËŽ¿ èj69“¥cÖ»ßï‡QÙ˜1cº»»wïÞ½k×®¿þR5„¥ú›‘ãããOœ8¡±*)̹j¥Okhmm…‹¦>B°(//?­,--ýÁ~`Œ™4iÒ9ÒŒsV8×W£Q«-e —Ý5Ï„¹(Öß]åó@T¼k©©©VIˆIXŠö F±&JË—¡?ì‚/ÕUx ÿ„þ;Nš8ƒÈt]ÃT:£YàoøsÆWTTdú7+?÷ÜÜÜÇ'%%AŒezS`±mú¶ê8±ä2P~°û æUgµÖ´örø:2C®^n¼Ì=4}cÚ¸YOŠÓ\{Ç0ÎÕßi4Ãæ,çíV’FÓ„,8€5’olnnNJJJNNþæ7¿ùÀ”––žk¾Ø°Ñüã„„„ŒŒŒÜÜ\Ør²¼êF˜_Ä[ÆKN/´È'¯ J,BœÄ¿Ð¼T“4M £RO,ĆròÁ @“ÞÖÖÆlÆp[ɽuuuAn1[œHéÂ¥#ç}90R§Vv'{0ƒÁTBY^ÌH]À¿ÔãñäääL™2娱c .¬¬¬< C+Öº %²Sdcáwöáâ{‹VJJJZZÚÀ3$úÓŸòóó¿öµ¯VŽ>øÀ3lذ1cÆÄÅÅUUUáj³Ç›¥ Þ ð €Åˆ¥ÓÊ«ÕêÐF¥‹š°›¾æ®Ömå¾_Á–U²ò€Uz¥.V—†Æê…ÚÏ Ö5¢Ö×þV+…¬-ˬêN¤OÐ?º\ãâ⊋‹1]tQccãþð‡O] ëeË–mÛ¶- Áç=0nX!¡Ó!“Ä6£Óé5}e+UAe¤ùNÔùts¤ÑÚƒ~l9ä¿i+3f  ßh+Ï&e5§Å³@å>†¢5 ÙWÄ–îKéݪiÍÆ´ÎÆB{D03fLBBºuëV¬Xv–sD 3°Nuv51Vvv6º=õû|¾5kÖÜ~ûí—™™Y__wÇ3Øqqq_þò—W®\ùë_ÿú3sGÈ7m\[[Û‰'"‘ˆÓƒª£¬¬,gß LM­uŽÄC Öʈ&Æùš––Ë®Fso0 p ×>]£ëøzLÊd°4z‚倨ïð½D–•¶)KøŸ¼^/8ž¸¸¸ÒÒÒOûÞ ¿±ººúí·ßnoooll<ï¼óp=qq˜Jˆ\lúY×POþ_òl/ ,Fë­Vr޹›Ó6VÅ|Zf¢ðœEðĶÓ·ïŒ^A\ÝÁ.X(ßJPå5X '(êÒHJßeÁÏÏÍÍõx<ÉÉÉ—^zé˜1c®ºê*#ñŸÞ©±±qß¾}o¼ñF(ÊÌÌ6lX(ÞV­,+꥗&E{”O± OÙÜAÍ–qms¶Zó´“ôÕâ µ6Í©|7` ¡P÷¤ôžùT:,ɼZѪ-¶š"q•(·§ã.»•„ü+..n̘1iiiëׯ߱c‡U…Xçô,ÜÜܼgÏžë®»n€¯'Ip†èÁY–=ztBB‚Zxä|zzzvìØqíµ×ž r¿Of9!òÑ œÒ¥£Gj+œÅXí¢Ü~9_oÕz\›™^;% ¢õÌYˆÁ4§%B˦u"ná>¥Å])­flÇû.åóÚÚÚZZZtG\\\JJ O¯´´®Ÿ™§ÛsìØ±ÿýßÿ ½é¦›ŒÄB·µµaƒ®âwèÜAšâv(e„Ö0t8SDiçãêêÉñFh®cOË‚n3â<Âsc<3F‚¬“ÙÒÆn]OŒMÖàD¼ øXžáɉg3+++‰ÜrË-óçÏohh`¡ù30–:;;£Ñèúõë[ZZ¦OŸîõzÑ?©(­Ì)®Ò"m¬G=ÎôFNaâ¢øA÷`®V·NÝCi”´Æ~@-î(- 3–éÛXƒyã´Tç´õW§é³’Ñ´<•ÍBƒ®³Ø92a+àóù|•””4cÆŒ`08þüúúús¦¬S<6Çï/»Æš@AAÁ‡~xæ_ ®ÅÚàö×17@û3v_ž~úéË.»Œ­vzø|¾œœœƒj9–ÌââbZÞ‘7B‰DsúP¬àBgù¦¿2¦ËE{Gkf´ü”‘¡¡á)¡‡TeUíííü †Òó¦‡Ãa•Y¸ ÛÑèóùÔö‰K8­"Œ1¯¼ò bg>¨º Á¿§„gÄRJ ¹ÚëkÏ)Süà³ÐÚÚÊzù6M}Æ ã”ÐÂ|kÍ´Æì„Ýó@ø{]—N *»ùóá#b+ÝYaüQTTÿâ‹/–••›Uæ!€uŠ£®®Ž)l§X………h»ô×aº|ûí·/¼ðB…ˆ…:“ÏüŒeee«W¯v2Xx&/¸àÐJ•{½Þp8l†´··ƒÂq®g–·²q(–p4448aVS «!‚¡ƒùÅï÷wuu544¨V”Ê'šÊèĤ¾júP[[«„bI”™àY…B!ÕaŽÃBÿ~ï½÷8ðéÕÊÄÀXÀ³gÏ6Æäçççååc"‘HJJ Ö<\1ðÊ\©V0²Jê)E) ·àºz©ç¤Õ£îª'bÖ8R | ?\ ˆ”˨»‡²P®hϲ]0½½аsõeŽ >Ðçó577ã¢y<žÌÌÌ®®®«¯¾ºªªê?ÿó?Í9lK{†ÌzCCÃÂ… ×­[7mÚ4¿ß‡C¡ì¨c#r2âCã vØaZà…*v*Ç-²Êõªjäë¶,Àq¢`u[óŽ·L׉®®®h4ʦ¿Ñf–¶UÜ?ô·+Ó. r@((ÔD”µP(”““SPPPSSóÒK/?~üÜ] ¬Ó;vïÞMß„SEN;ÁÁÑ3GŽ=z´.‡ÅÅÅk×®ºÖqý»Ïç6l˜+ïèZõ‹D"ìQfË9a9)nÓ« qöÞ[_Dëa‹º×új ”*c S|Ì©ÇHŽ®‚:sYviiiºdª”žm•Ê`y½ÞââbNô–Úã³Ç‰–••¡6Ÿ?|øpT»0=¹:šÞø¥vôþªÙF,UP¦oJ’¶õ9—F>~éÒ¥»wï®­­:u*æ NÚUÄË¡ã×Gs8 u§µ,o=E@R詞ûlH×öCö»hÌ}¹L¯ BŒÀ%ƒW IDAT#V\c[9TŒÔ£éPˆóäØpqTÏŽŠ$ö<¬LNNÎÌÌLMM}ï½÷>úè#g³ÈÀú”‡JLL;vll€eŒ¹è¢‹þüç?ŸáÎ Üì[o½u÷Ýw[ûà¿Jø§njkii©ªªºú꫎¯(x)–ÒEÑŠ iooOMMe#:i­––ÝAb*‰D"®±nÝe¥dp³¨oAR5?ÙrW…–®¬F.”²²šØq8åääI`Ñ’¾¹¹ùÙgŸ=xðàßÎà1Æ?~Z·¬¬,\–ŒŒ Ó[­0}ÕKúw­ ÉÖå>žÀ²äV/Pãhí/ü›AIü·vZÂD%´L_—v'Åwq7B o…PžÞƒÖ^¯|'t@ »»{âĉíííiii¯¿þzEEÅg•¸r%Ë1{öìùðÃëêêfÍš•””F“““q•ð¿Ìĵ¥¶I[†+S­ÅDÒ‹í;ÚéBÈ¢­ FìÓ@jrԱòUÈåïèè ³¼¶ ²òèÄ^¦·˜N¨•`uÏh4ª ‘º1&‡Ãᢢ¢úúúU«VmÛ¶ÍÉÆ ¬OåQ__?ùY‘`´íß¿øðáüØP(4qâDD| §<вäêûåª aý=++Ëj Õ°0ÓWóÞ_e‡7Ñé„D1ÏÍjÈÇüh£¸­ôCµ çt¬¯NNN¦×W}}}~~>*o;Hë™gžyÿý÷SRRÆgŒill#HóU Ñ ëšc$$%%Ñq‘׊‰u[ÙWå/s%c=ï©" ¦ñ½ÖÊÇ¢³2„óbúv„©íÁ€ˆ\®[o½õøñã?þ¸ùŒŠAOyTWW¿þúë»wïž6mZBBBjj*üÀO°P ¥ñâbÉ“T¼±-!#ôïqqqeeeO<ñÄÀ3Å?Ûë¢1¦ªªê_ÿõ_¡~»üòË1Á`°¶¶ÖãñÐYT¹CR•´‘4’ÉtìÔ­|:êÊ-³?âÇ™<í| °¬„iåz¨Ý6Wq†œèèÂJ>`+ƒÁáÇG"‘uëÖuww/^¼Ø*©ÿ âuÜ…U«VUVVîÝ»7 fff2›“1«ÂYe˜h¥¦28²J ¤û9ëѤ¯Xζz iY»Jîðùý¿*¼ÓÍ›UP&=Ï?²ŠOµâ ÿ‹|>½€NX DéP“4•ÖÒ21<ð\Ùqõ°ÕŸ¥A´èXÅ^ÜQ Á“€;4ƒ ÉŠKÑv @ކ|ðASSÓ§±=bˆÁ:Å‘°gÏž”””þ,Hó¾ô¥/½öÚkælè àüfŒ™;w.þ’ššzâĉ¡{qº»Fë6õôô\zé¥N—|X–9rÄ‚;ȺijjrbµÊÊJ#:œõ딢 ±¡¯ÑÔ^ç$èä·\!”þWU7#š?‡¦;˜UC¡MD?®§1t¸û÷ïÿñlŒ¹ýöÛ'Pµ·lÀT`×2%š¬’6%'\Coœ‹¤Ç ¹ÓØ]»d4¬(PÛ1’™¬eMÆ¿SRRº»»SRRü~ZZ²;W¬X¿eש¿ñ£µµµ¡¡aÍš5¨åææF£Q:c1*›7˲à ¢Ëƒ-wY¾NT£«¹s÷¨{<i–BF–5{¨æX—hîO·6zÒr iii#FŒˆF£k×®ºú”ò Ck@GSSÓ„ \Å8 7mÚtVf®µk×ÂðÐóµ¯}íÝwߺ gràÆ3mzVßèÑ£ô$ µ3:99YóDaEÓÝÝ}J'XM_& R€Å.kgOè_%Éñññ†£­‚>ŸB+ÇS[[;qâăb:{öÙgþóŸ;ï†=êëë×­[gŒ(Äëõb ¤²„d’µÑÞ“ýb$À7°Å°Ûj€p• RÝ¢ƒ–ZÎ "t5u#ÓÆbþ_mmm¬gá¿666z½ÞP(4gΜM›6-]ºÔ|æÒ#Îú\T]]½qãÆ²²²ÂÂÂ@  ý~?-mY.loo§âJ]ǸSÂ@‚û(UçÌÞÑ'šh –§:ùX6뮤©kÊ ·|Z ¤ó-\ݱǠi¶j¡Í‡»¼ ŸÏÉÉ¹à‚ ***¶oßþÑG}ªç¥!€5 JÁ³Ï>{óÍ7»¦`ÄŒ=ŽÞg먮®žñ §Å=cŽ9o§aÆ…B¡„„tbr‹¼ÎîcŠK|©¬j[[X+¯×ÛÒÒ‚Ú }Œ(£a=ˆñ|¯en¤ _ lñGêÈÉ4Þ‡ãPÿ/MLLPC=)..7n|h׬YSUUåÄîCÇuëÖíܹ³²²røðá^¯7‰´µµÁ£÷S梠FßNµÔ¢Å¨‘ú2Ð<§*öxh)-m™vÊ #ÖùzÐfªú¢ú?„£­£FÊÈÈHKKÛ¹sç¾}ûJKK?3i`ú tôèÑðÑ·½½ýÊ+¯\¿~ýY'™^~ùåÛo¿===}áÂ…ºƒf°,X°cÇëéõz½ééé` ôævtt8½´÷Ja^©“©›^çO+c'33“’SaC?!--„v(LãÒÒÒÔ®0 ‘»à‚ 0ma[¼eË–!\u&ÜCww7¨Ä§Ÿ~zûöí^¯wôèÑð) 6ÒýQÀà—§¡´P7â ÉJ ï8lHµÄõ•*@’zÑf©½½Aõh`ci4PÃæ* 544Ì™3çøñãÉÉÉ=ôy‹¡át&Ç¡C‡Ž;¶råJìÍPíEQŽYNWO€*`(·¤ÚÚÚhºÆÞ,¼˜ý}8p×ÀY‚U­¤-–&¶ñsL¯[Ð^ ÕFœ9… 4ù.D]bbâðáÃ333«ªªV¯^½e˧„c`}Æ ©'žx⦛n2n!Æ [³fÍY'™þô§?Ý{ï½]t¦Â¡ñ ~¿„ JMa©¨¨¨°²ÿ,”+ÏÄCÁÈyj6Æ„Ãa `fE€9yò¤U¿KOO‡(,,,TÃú¢¢"BüöööqãÆÕÔÔКj†¡…ð,[·n­®®†¯ î)’}#‘ÆÖ-Tô(`ÂDÁ›¥¦SºISmkÓ¬à˜^=2ÛE¹.…SÍCµ 4âb!FM¹ð.¶›%''777‡ÃáH$R\\ÜÒÒ’˜˜øþûï¿ùæ›1:0†ŽÓî===»víÚ¾}{EEÅøñã322RSS)òÃ(Â,YàX•Á¼ï´w7Ò¾‡ j‚¨âa$´µµYÄ1–µ¨Ñüã‡9Íì$± \N?Uê´°#1bD^^^jjêo¼qàÀ#GŽ|öêËCÇ@÷¬»wï¾ì²Ë\Öõ×_«â³»tõôôÌ›7 9C‹âÿoï^b£ ¾Žß¡SæÑé<Ú–i§´¥¶¥ˆ‹0¼\™hŒÆ…nL4Á+Xº1ì a¥nHŒ1(ñQÀX¤AEEm–BÈPj§-}M;Óyõñ[œôä:èÿ¯¿ ?i¿Ÿ…‘¡¯t.sÏœ{î9wðU¬àÁ\.·qãÆo¾ùFG¨Í›7kKeU0¾Ænƒd?8;;›J¥4F·k­4Aeÿñö®}étº`±IR]^SS“–AHKkw{ì1ý«?üð•W^1\ïºÓäÙŸœœ|ã7r¹\iié=÷Üc–&z<me¬ñÛv/+ûš˜ý fª4%Ïö–4¿½ú'¡›Þt¶Ëêõ<ÈX‡>ò1ÉdRëÜåc´ íììlii©´#Z\\lll¬¨¨ï(Ç£+¼ƒèßô533‹Å:::êëë+++¥*KîÙc|>Ÿ(Ô¿k»YJ–ë¡°1K7{~¼=ãȾdc_ÄÑðNÈw×A‰Ÿ47f·¿’èJ»žj–4‡ÃáH$²zõê¡¡¡?þ¸··÷êë °–?)O~ûí·üñßý€;w¾÷Þ{v óú¾ÿÇ|Ü© ²§§Ç388hW$Èv‰Dì|U:Îd2Ú[RÄßþ4IÚÀ¾³¸¸(oFí¼—cékûëØ³ŸµØY©¬¬´7ìH$bŸLIÑ•úh´‡;²/,,ŒcNž<ÙÕÕåv»¤ò]V‚$Ÿ$­¥ùNÙ2íÊ<ÍrÙsxtÕ'· 2Ï“ÉddóÓQ‰:œNHÇm$Ù 5=’µJ&“òóù|EEÅÂÂÂÃ?Ÿ×d•<¿~¿¿¤¤Äž#ýˆ5!/F©T* d°ÜnwEE….òù|Á`° ‘©ýYÉdRºÂØ O!bzzZf’èO¸fÍûQ$‘Êz ô_ýuÃåÓ6X¿|ùò•+W2™ŒT¹=ðÀ>ŸO³G`é9‹<ײ™é슊 ;ÅU0RP#{mÿ­[ä*´Y{1È~iç!$ia¬%½lïñxäÄGÞ ´¶¶Ê4FcÌ¡C‡®^½zg›ÑàvcÌÄÄ„D´7oÞôûý•••ÕÕÕeeeÒ8J£+íä®'ÂK¾S²<$8Óü–, Y3kí¼±®hØ×4"·/MKKª äøO–®´e÷z½n·»ªªª¢¢"Äb± .twwkyÃ2Þã°þl cŒ9qâÄ#X]]m_”¿’tÅoþ]-)UòÊUð1v"NÛU¥ò Ëå’bꢢ¢D"!5Ôvh×oܸ±¼¼Ü,îÙ³G2<Ëÿä¾hŒI$2Ûjnn.ƒÁ{ï½Wò‘‡cffÆçó-..J©»^“gV‚0ÙØìƒž¼H¯,Y9rr¤ÕñÉdR/2KvÁãñäóyŸÏ'cXä»H ޼Ud†rɵSiȾmÛ¶p8<==]VVöôÓOcnݺE°þÏ‹Çã·nÝúàƒfggçææššš¢Ñ¨ÔfI¿™=/szËÁ,UHt¥íøuÞƒ^¬‘Tö„A¹=£¥]QÉ:”ե׫5¤Ó{‚òeµk¨¬ðh4‰DÖ¬Y322réÒ¥cÇŽÙ-—7¬?E¶Þ3gÎ<ù䓿·wÍžxâ mõÁ/ê®Þ ÿm,IöÓ …¤&ÔþH¹Mm?"ÅR‚Ú$I‘œ¿Ú°aƒÔJk „×ëÓ‡§?Òúõëíï;55ÕÑÑ¡o+‰øÿ‡$®Ý¿ÿÈȈ×ë•'®µµÕ, 0¿-·*˜Ä%©Êl6«'€ö©;™&0´TK^‹ôìF‰$æÖ+Ih¹\®¹¹¹²²2iêõz].W$Ùºuëèè¨Ëå:xðààààÝÞìqìèèøöÛo/]º$±ËÆ%DÖNz¨g¾z²¬5Ró¼e¥IÎRéäµKÇ{Ë’÷‡öõC³tñÐ,5W“…-Ù,Çã÷û}>_EEEeeåðððéÓ§»ºº&''WÔFI€õg,§ÓyòäɪªªÆÆF{ëÚ´iÓáǵç–ÇÓíp8Ž=*…5vLÓÒÒ’N§ .¥ËÎdI>  (h“f/ÔyvÂiaaA*¦5Àjii±3XÑhTÚ‡ÊgmÛ¶MÞ`ýK¢öééi‡ÃÑ××·oß>cÌæÍ›%Gµk×.cŒ¤B¥Ñbqq±,!ûÔX‡CKFAŠÍí ‰f©7›–û¬§§Gúl±þç$J§ÓçÎûé§Ÿ:::ÀÚµk+++×®]4«¤7ìÃe ÖµÝn]«¯K:Ï[ÿ«Y1Éä [vCÉZÉ;]½Á`ÐårÕÕÕ•——·¶¶®Zµjhhè£>êîî—©`9Y»~ÓÍårW®\Ù±cGoo¯<(ÓlìmËfwüî»ïöîÝ[©TTTÈÕ*»Q0Ô5 $ÉÜ”÷‚ö&'g4úŠS[[{ùòe;ÅÕØØh¬&4RÄ ?a¿6 5ÆŒŽŽ.Ë›8wûZÊf³±X̳{÷n©’„–ÜBz½" 1GŒlrZ®§!»<˺«Éäm¬%‰ ­†‘4§Ï瓬•ìN§Óï÷‡B¡ÆÆÆááá@ pöìÙP(ÔÕÕ%Ç=Ú¡”'ñ_òŠd–j Òéô‘#Gä$ ÍÌÌ477Çb±éééÒÒÒD" åÚ ž>ËÓ‹„r®§Ãš¤8AZ6ÈÇhñ»^Æš­½ÌRMaiii.—‹F£óóóëׯïééùî»ïb±X*•*:¾¢`ý…+™L?~üùçŸ?räˆll÷ß<×x ËìEíö%•JÕÖÖ4ãÞºuë—_~inQQ‘×ëµsÒÙ¾“X\\ìóùì]SkŒ5Á¾ªªÊ°¤è^Þ îÞ½[Æj³¶Ãç¾(ÏÎùóç1ÝÝÝ{öì©®®–"ßíÛ·éü%  ¥ØNâ!½Òe—Æk©–Ä^‹‹‹Á`0—Ëùý~IbɧK¦J¾x$¹zõªÔÜÈ•‹;v\¼xÑét ´µµ ܼyÓX½ŸOJNõ´Žô–SfIŒÉÏàv»s¹œ„ø¡P¨¾¾þÇ\XX?zôèõë×í6o+v!qDø×|ñÅ÷ÝwŸ”¬ºÝî½{÷ž;wŽ. +a”ÿyçwÞzë-{‰Ä=’x0KUóCCCÍÍÍö×q»Ýù|Þ^*§ ~Íš5ö#áp8ÚX(êííÕ‹Ó7oÞ,èÁóõï_HÙlvttthhèàÁƒÆ˜_~ùerr²ººZnŒJ³=iiVRRRYYYRR €öÝ–þ’ý’t—d­¤V]îjŽÜ-•´Y$‰F£'‘Htww߸qãðáÃù|~``€%t×­¥|>Ç'&&ÞÿýóçÏߺu+™LòòòH$ÒÒÒât:ËËËe³VË’p¹\0Ɉ-½ë*·%²Ÿ››óx<ÒY^R¡òaÅÅÅ ååårÕ:—Ëõ÷÷÷õõµµµõööö÷÷Û÷!Vò¢"ƒõ×tuuµ··¿úê«>úè³Ï>ÛÜÜüÔSOñkY9:;;eØŽýÚñÃ?èíz)Y˜žžÞ¾}{&“Ѭ©©‘;‰vÈ555ejeeeÕÕÕúÇžž °2™ŒŽÂ  _ä Îóæ›oct|CõõõEEEÑhtxxxnnN²¡ápXš)hK®MÈÕ}Ç“Íf¥Nkaa! z<žšššk×®µ¶¶¶··?øàƒ/½ôR.—»víZ,›˜˜8|ø°Ö¿ã®Ž·æççûúúŒ1'Nœˆ\úN•––nÙ²eddä矖yÒétº¤¤$d2™l6 …äMIII(J$ÒËTN—•••””ÌÌÌd2™úúúááa¹r±jÕª±±±ñññU«VÅãq»ã1°þ{/¿üò×_}üøñ‡zèСC×®]ãhfåÐIòòGy£öî»ïÔoz½^yUÒOœœœôù|’„×ÏF£öKRqqqMMüQQÑôôô‹/¾h–ŽldL½¢aä!äÌEºÝcž{î¹¢¢¢öööD"!Y(¹+ÚÙÙ)ÑvCCƒ1f×®]lnnîïïoiiùþûïëêê~ýõ×uëÖÍÏÏ'“Éh4Ú××FGFF®^½jŒ9pà€1Fzʘ¥›_t^X6kI[ g29}ŽÅbóóóíííëÖ­Ëf³n·;ONNúýþ††éûPSS#ªªªššš¦¦¦B¡P>Ÿƒn·{`` ¹¹y|||dddvv6N¦R©|>ùòe™M®k‰y”Xw`=sæÌ¾}ûžyæ™¶¶¶^xÁW_a/döÓ-÷ ?ýôScMY\\œœœÿüscÌéÓ§5ý©ƒäøý/³xÝN#ÉËËÅ‹;;;eiiÛcÌÐÐÐüü|*•Ò‹'ÍÌÌÈÈç|>?66váÂ…ááá©©)™gZФF×-› k×JÙ £lP0’ùwy<ž 6Ø–Ï竬¬ÔÏýÝ…ÄêZké×Ñ%Æé(ë÷ûN§ ûçÛ$IDAT*¹äõ—¾2VàŠÒÉ€.—ËëõJõžßï—ÞërÍP»3ð;+ÅO“nð³IEND®B`‚vispy-0.14.1/doc/_static/carousel/galaxy.png0000644000175000017510000115444014507104216020312 0ustar nileshnilesh‰PNG  IHDR úÖ_´ëbKGDÿÿÿ ½§“ pHYs  šœtIMEÞ +w„† IDATxÚT¼O“dG’䧪æþ^Dþ© @÷ÌÎLs§§ÉÝ^({¡ì7♟voØ;2ÓPUñž»™òà‘èÞ)dA2#òÅûã¦öS5gëW$Å `R0C´K†AI¶•t$f/DP I¤lTJeì’Y.À@ˆ´AÐ,[»ì2L6P3]°X¨( Š0-Є`!a"C´’N¸lp”-”‹Áªay2’É"`Ú˜‚P´‰ v H³& š4LaÁFT1É”¤LEÁN5H“ DS ö`iC$¤ÞMº:¬hduÍáV® ÊPG̪š® rØÈ95LyšÎ™V2Û`£jز„Ó¥RÙqÊEBD‡ž­Du²,‘SÑm‚ÒçÈbX0íRS)ÓÓ•2{‘¨Ì„‘°J®Š! ä²Qž51117˜°®[TÌsŽu¹eÉ}JûÔäFb²Bt«²‚Ml ÈhŠÞbccáÝYÑ‚ÕCÝZÓ•l3÷¦^ DoÁ¸¶ë®hO[æö|í×ëå¹tnÅË—×Kk—Ë×ÛínÎçë·7…€OÌÚ§fCNTyÞ}öBKÏqÜf}I,£_·cÜ/Ÿó¬;öÛe‹ËÏãóüòc߬òÖùå¼uÇ}Ôl7V”áŠÓ@6ê Ð³oO{q|¹ý(O·ÒœuëmÏ9Ç8`¢±Š-kDõæâî;]q½¾Ìó˜|C˜ph×íw^ÿtó®§?}ù÷y1ÕÇEኒE!3Bn=èbÛ㊭}ýå¦T)Ag%k–‘4r7rP4¢·ÖºÝÏÝDÈi¢¨™˜8æ)œ¦øòúáÇ?ýÄè@P:LNF»¼¾üæ—Ÿ~œ×çgµ¯”ú–ÇíÍ.[, ù‡o?ý|ÿšB#ÙÚek©i¶¸¶§×ûñsÝ“ôÜ\w=_><]Ÿ~þúorFÏééQ À<‡Ìóé²ÿî‡ßü¿üó8ª¿¾Ävýî§Ÿ?ÏóK`œ­<ËÆ‘å3jÎ÷rÎ30m Çô¼4Rü÷ŸÕ"ÂÎ×K}øøô¯¿ü¥æWE G´ Ä–îçq&ÈÜó¸„:‹—†ç_NÏ[™˜È&²à¢·é¬8œ(ÕVŒRÀÕRÏíò»zýþíç³Ê˜œItÑÎI‚ˆbÄÖN<·4#Ë%#GR³è˜•@³{8%ŠI ÙËw÷è–› œƒM°¤a—6æV)Õ^Þ½»€f’FL ­sË­mýò–ÕóòÔOq2®›Æ$Â}`x¢œãŽ:g1m°ñŒœ4ªpÆö̓GÍLÍQz3YœFV‰iÎašue„eG›ÆdUf4bF@Á 8íŒÉ"Hœ£œbÙÞc?ë!Ó,™Kü4 Ä»¤0M‚ Ö{²ÙÅÂã¬9m­¤ÖÙC`ºÚE`£ ¤ £ˆ°ËëCI–2ü«Ö{-€2ÜIVd‘ŠÐ ìX'"@¶.²möúY ¨ ´% ¤Ð"ÀT Å’CÈ$\k™¡%X))‚0I°´®›BÅÆ *ÐICÒF R7#zª*‚šCFÁLMA*á$Z™³¨"à)€§Á©Ê,ÉH¨¹ÎU.Ò²œ”¡uK2`8Ãá.#Š"š‘VXPQP$h• –Ûº4Ubye’(¦“ )š"¢ »m(`Â6`—jiWÌ@~÷úýù~¿Ù•!³Z$ŠÈbۚεþ Ô¾.¸¹~¾ç=3r` ÌÖÚ‡çý~~EHâõÒ_¶­Ž`ÛˆéÀê|cª€h§(UT‡4«[Š«¤i¥aÙI¨¥G"ÁZ%¬Êè%¼DDQ aÛt<Š~QDÁxÔØÇ øø‹"L-*@­RN: B¦—þ2M{ …%7ÅÜYòq) Ö –„&Ökh, ƒ¿¾ï_ÖÕÀÒz¿~'} ¼Ë"¯÷¡cé¤õò¥€¥îjÁJ D@˜ ‚9·„@^2 ©’(Boˆ€YVYéÒJXõ´ €É¥ÇM1 I ’F™UUŠK”Ö›Š`‘(>>;—’2e)WgQV}’Ý+(”‘PÒ@@µ4-íªÕO¼ŸlIK»9 ÓeÔj2$š;Hµ¥ÍV{ 7jI¶@ÕÄQŒ¤ìK¹‰ë‰ãã#º\hŠXª@:‚ÎLNÈÈj¨ªä$*ÉìŽ"Š™Ä4‰²Í E™ C)7%)ÀÕÅl8'ÝRDQ2%®^‹˜Ž°h…Õð]ë)ÌÊŠH–J% ¤"‚ždÙ€Â,Ûá—{´mæ‰$š\ñTU(z=†b¨O„èèhtkÑ J̓óZ ©…:b“$°7÷g¢¹:_"¤¸^ö¶í½÷|ŠËµ·®ÒËóåùã‡øðºa†Ðè‘È&×V1£G¸àîäÛe¿¤<ÉpùcÇGÓ{ÿíëë_~üò%¿äýËé‚GU$ç¼··ÛíLB9ç ­Ûç·ó~¢î£g€ÑÛ¿|Ú~º»ŠH°¶×§1ÞèÓ1º§ …²'³Ö£.ï€Äœfí}ëû8 f±5ŶS«£ÊÒ_Ÿö1Ïš.ç*>Dàî0®ÛþíëóyÎÖû9î¿|ýòùË8œDàIÐRöuwSRXNc´Ø.ýš™ Ù{ujš‰r6†Ö§¬ÚSõÞz|úð›~Ý󞇫_úþÃÿþùóç¶µÞºkJzúæÛù|¹(8óÜúÞ¶¢è^TÛRîè2{k=òùÆã˜Ø“ Pß}÷tÏó<†ç,dé¹m…ò¼ŒÙÜŸŸ^?<ý`ðí>fÍ™–ÉÎØ#¶"­‘}–X²•ÇDœ åË•û% þÇÿøÏ?ÿôù§3]™ågaæÌ*ôµn¨Q2fÀ!š¡ö±ãíÌ?ýrœ9ï§'·ßýæòöuœ<§D‚Ž SØ8¿iíë‹I¾týão?~½ «Á*°-ˆérÒ•5oç­ä>ÑzÛ®áò9†@ð¼ÁÌ{JU ¶ëËúµtÚƒ¡~ΡŠH+ì^Èn„ T g"T"D÷ÆBe¢ Μ›¸W âªçu–!±öˆm›Çœ1N¥ag%…bi1¦`XQA¢E¹TŸœVÆ›zëUL:(ÍUP8«|¢-Ñuy}~~ý8ÇQ%Ò(@`ÊVÛCFæ4ŒF^2qÕi`õƒS&´ŒbÒƒŒÁ²¥1âQÖ )6.rUV‘„]$‹ ¤Sl â‰rhñ ‘ ƒX¨àp¨–öH[ËŽàƒðL È\XãrL¸Í ª[¤i†©Ck±áêÇIÆê8 D˜Ô¢᪢ÁZ‚di SZÀ„h2 !âQ¹!!ˆZ裀ZI 6,…|WcxÔuÐ`[g†ÐzOBïâjýï;Ûñ*þüå…ˆhKóx(-.ÍåE…Þuغ¨H=F@Yhɰ ÂpÍ¢‚hÔ䇳ˆŒ* “ës-µiš¬µTȺ¹h‚"M.˜F– cù‚‹1¡àaºhQ €Q0˜¤DÒ²ÜôР€‹Ò:é«‘pd!(b©!{}C€ cFцI’f‹/z¹d©hlðÐ~ëS AZŸÅª@g$i*zêìÛCºÂ°P… +äBQmy&…zàcAl••Ú€ž#fMÏJL ÓªeÚÂË@9Q’¡õn°IõZ¶k–½Ö7Ø©|P¼ryQLbóneeÂ(!ÛºñŠ `60ÚjäˆX«LºŒžµ8*‹„Mùùõ#‰ã$”G"—Ýk¢ZÑTQ „B–¶_^_>eÕ<3!¹#¶l­ Ml­t½¼´ë6]ÆæxUkÿô»¿¿ê¨hׯèýÊ¢Û¥}øæÛ¿ûþ¯x1\uæ¨Z7c55GëªÖŒÐöÊV1i’jЉûyÚyËÉâOo·1OÖÉŠBF•çhûöÛßüð—ŸÞ„·OWC…äú÷Û—„320[ìßýðáüôsþþÓövæ0ó8Œ* ÏÙÈîO zTUT £m­&{T…ªÃŒhª—ç¿ùÍ?|ùòyΪշ·tÞ«fNöˆ…C [±äž®‚Î#Æ9‰ûôÌ3ô ÓMŠmš°;[A@£#ÍÑw²…« +6¾ô§ f:'’Úä‡êz~Ýç¤p€£Š³œó>2.[ß#o÷á$ž# ©M>iƇŸ¸mîûõuë—¼( 'r¦åšÇÌ·säÃáŸM*’6¦™¶ˆò£¼‚.³©õ§ož¿y;ÎÌ\¶açt«ÃR|}ù˜yJ.ºí}Wk®‚Ñ\êA¦l'G’LLJš(ÜD¶ÇXtƒˆØZ‹B#–ÊgÒ§J-EŸž÷Q’f žQ†«N“Å$°WKÑRŠD‚Íäæ`(É­4Œ‰,Y×MÑxw!Oª U‚™0„& v‘IöÖ`/æg-n=â»ÏUç‘ÅÍœ3 ~-„¢PŽ‚ TØÑ–Bt’Öo‘ ”~"K)pq+J\õž)Zîõàl4Õ¬òã‡W£U<¥{´ÄÏr:Yv’¨B@>‚CDÙU+ƒ²–ÓL `5+P&µV÷X¶Ñ,ë—Q\ŒàA¯šf}’uŒ @L`!3N:ü42؈‡ØËEPë̊釱h‹*ÁXu‹Ò²µ°(\0BĪ­ä_5Õ‚ã˜ùWŒµ<À¿QRˆˆÆwVü~åôYüÔõ0ñ«4\d±™Y^œˆˆ'¢@ŠzÔz/¸§X‹JðÂxa³H§˜µ8¢ý¸×»(bõbd5‚dšp,b´‡^#L´„4ÌIm.:P$j˧}üB©@qQXÐÖ_9昵D°eV¡Ìw»0¯í,a$—•¶ !!Кt„Ø'Ë B (XÑ@³-ʉòápÙë.Îe¢Òp&I¶,U&*@pϼ»Ês™ÎcyÓ@A§[Í*d­§²(•l‰U`W»{óëqÖ°àÊyº÷KPÛ¶ýt+½Çµ‰éûôwŸ~¨¬¹¡Z­m/oC•°‹îŸ>þýǾ¾½}ýúó<Π/û ‡«9G¥«ÑÏ}ËŒýéi¿ÜÏÄ©`2´oŠÜÙBÜžƒ‰s ßÕú“xÞßþ{³kä±÷œ`›÷ó²ÈPP´*à*3M{lJrï)rÓukýÇZOì o?þæuÔ¡<ŽÒòòªN›‹¸¯pšÊrA‚ÃNŸéÙ\j‹éÏKÞ!¤3Ç9G!ŽiMè•w™€JÏ·ª'¹µQ•9™’޽‰œEÖ‚e ¹—¼1º@A S;„ðêztÆu»l5šÛò»U[`K„ÁÕº³Ù¨štwÛž÷ ÙPa8’= :;© 8 ¨r–Xà‘óG$)!*fgw«•†¸ªwÔ1ÇÆËPÅ{*9Î3=‡«ˆJŇ“e>|@šU¦[ùå*³e•‹&¬QòƒÙL†¹Þ„±xŒQ†a'Å€Õ7EëÐEHH<šY” $¾¯À¢$ ±²Î~OIªº` ‘•¬ñÊ”Á í »«²‹«fPA´é ¸tŒUUi£±–G É_l2 ¶È•‹"P2èXz hÂzD»ßx˜<æ{j›öbvµ„ÆŠ;°HkY¿Š¨u¥Úy?¸‡-òr ß­;|'XLõÀsð!Ñþúú_ÝÂÌ -—ú!)?¸~ßC7¦¡ÅÏVdT`)i±BKõQ±DzÙ@aª–S\,T{è"ò¤éý( Ö’–Dˆ a3íÕ×-²Úhò½Õ@"×ǧ4$[~œ„Åc0jqC] „|¸Ý yÕ‚04"Öá¬.€ Æ#˜¥&J4 b[T.’cõ,JÕXÔ¦‚J ͤW¤_ªJ°&Ë®€«`1#W’EeYeVÂ8³pÂ3 ­r¤˜éBÑ6²L2"V^îÑž^>÷Û\©@PPz‘Cýêd¯{r…×qši.*Ï÷gˆlî\)5i%ã8˜D2 à¤P‚é„iM¹ÒÓY°ÓW\ø ÅÜ–ð[¸˜½fF2Y—¥ã÷ýº]Bl½ÇÄ–•9ÇÌ2”lb瓸õíÒºö~}úîz}Ú/ír±;ãòt}ñÞ~G¥çý>ÙÀþzí{ÀÁÙæóó¾ ç@;Ãnµ˜„îãv?Úõúó/Ÿûüðô!9Æýó` s“®Â—¯÷óíÈHŒ9ðæyGÎiÛ&NÃ'ë¬ôDºæÝ5«ÖЉsPr:“†SšP´CTU™­ÎHº•¶§«?}þzŒ;——ØVkXi¹’½¨4‚³]w}ûA™ó²k”ã²wôŸ¾Ž¬ZOLVÄÁ/w?=}Ü÷6\jMlýÒ{Ѫ4œU@²5÷Â1S(¥ê-:Z½¶‘c|þeVNhpá ÆÞŸ<ϦÅo Šs‹ØŸ®;ƒ_/ø—ÿôËŸÿ̨„{•'ÓD“Z—×o{ï·ÌÛAW¡ºŠö¸çñåííüy»È½}x¾fÎ:î(´^†c£ÐÚ?ÿçÿúÇ¿üä8{G EïM²¦Š¾Fóo†ÏñåëíÇYGA³êœ¹E~üøº=?ÿ·ÿó¿ýñÿã8†Å°£ãù©w5óœFT™*•"bˆ¯;>~x9î7L¬Üoò9_Ÿã˜IŽœÇ KͦLVÒF4eƒœô&”-+Z¿ìtÙÀ ¡ ±¦L¡Ì-H" yÒP%(#g°Ä²Ô39¥T5¶]9 ÃREnÔÿñOŸþõçÛ9Í(+’ÚÚµ]= ̈(GÕ,–ØšöµæT&ÐPl(«1E® ‹L¸ÃdRž2Ñú®ýÒ¢Ì(ÑfvîhèjhþkˆÆ0ÄŠ©&C¤Ed ApªϭŦb5GÑ((²à˜3+Q¦(ä¢Ì|Tî*¹ìDTA+:¡Ækß²’–Êð 'zVs9oz×#~·¾X@ÄŠ S!„>56 ²–Ñbd!–¡d„]µ¸2Í$)Åb?.¼æ¢ŠL¯Ð Ã G`®”òŠ-ñ¨J<„µŠ³ÞõL[8HW¶l.c Tf­@ôêèé•åZ"K«<“åe¢„ +)ü®¤_×£±Ü!by+ÇG¬êÝßã#iÅ¿1ÿú‡ÿÓ?W%Œh b¥w‡qÅå³ò: ^Òó¡Lù€tµôÝ;ÃWœL+ŸÕ;ÚjÊK€3°Î´ê¥9¬GÎï=Àoa¹M &¡Çµ'Œ€øÎk‘Ed¬VaÙÌ¿¦Í¹š ™(³ ůCXá9A«WYîè é›’ö#ƒ·T•´r}Vê–# ™B4-Nr¬¸Ä%ÚÓ‡(WR5jMÄŠæg`÷0Cï­‹Pi DÀR zŠd.L*3¨@š5l ;Cd¢0¤röDõšå4k,ÙÃ5ÎÛ=gUZÀxÅW’?DP`¸Œ`„Ð gÉ­Þ±sWÁá…­kI°Yƒpˆ*UЉ’—ˆSµÇìFy¥"@`R‘4€he‡Æê‘Y^3*AQ–Ë슎&5=]£Ç…p­…Q,F\¶vAç¥í¼´ÛSß®Ûeßãï·Ém~ù¦*žž¯Ã¹éôÜH+¸_ž~ÿ?üåó‘h˜udÔÙ±ïaTµ4ø„vÎãóíó—sÖA7öºåŒY'hʉ62¿Þrœ3y2šTÈYÅÐ2 ² ¯/ý˜#Ó‰TÍâlD ½:ü´÷4O&W9Œ (jôÑÌ ¯Ÿmª|gå;#­UYæ ˆ­¥Ðs“eµt§oé3sF=õkî÷7z¤Êfp»n}”2µmMtbÄæË®o®¯ÁmÎYœUªZl¾ä=Q,…ùÜ­ÕYDŽÊ}¿àéÂ1"ØãuŽêzÙÉD»´ * Ð¦Öp½lÛçx»‡¢ù?|ûéòáE·/ßgâºo—¶}è¯3kœµ©;!I1SsŽÌ2ŸúðÛù¶io»¢Íº #­Ÿ~üÙ¥MjûÓóÓµÍ1ë”B¶Ç˜cŽ[ö»çôŒÈ •'åýãÿwû’¨nC{ô§ëËí~Úžk¬ÑÙÿþ‡ï?¿}aã~óxUÇÛ½æ\Ì%2“yfÚžžr²C4œ°f™¼ÈÑÚ¬Bävlî—ëÿú¿ý—_>Þú+KYò†MXp¾ P›šß'̓C®m»™Uèvš¨ðF5+í‰RH ÈÊÉ·ùHTÄZ×Ù˜Á¬ 3Á†¨V–ÁV½$llÝ0F®Ú ,·NM¨Ä|ä1U­7¶‡ë 6)kžYU3¢/4ZGÑtÔ£@X†°¥,4h9,IÑJÚ V¬/+ºŒéYžÈ(sM³ç";³Dg£CE¤cåFM@e»ìÅ´¸dìía" c…,³ó‘Ò, òrS2ëôczÑåG{«U®í’A…©E¾"{*låÊ&Kp¯l{®4ÙˆcD‚Yáå÷8̰‚åX ËÿH–² Únï»py>Xæ¯×¢nÂï©ëù©®ØÐc Ò‚Rˆ‹vji‰eRLÔ¯°‰KZ™\/é[³\Ñ—•”’­Â{`~©D`yVƲ¹+®@ú»¶â¯Á¬_éTDt>ŒÍ¿®¿×úaù11ð0+éÅbý.Õ×[ËS%‹¤¸Š<•`H!ìQA  ŒÕÇüjo®40uÝESXz±#X+µ³’T„-¯G}…Á}„–EŠ^“­‹G­H™c¹ã…ô|Ø $Y+åˆá*°–FF îZIKYë%—ÏIÄbiŒâ1;â5[!M¡Ö3 뺲 IDAT«¿†± À1áÈ©_Ÿ ‘t_M¸,òa¥…Ôd‰å²á3k,Ëü„á0P­Nñä<‹5WnËHe£ÈÀQPCU>ŒW““ÌZ%»ŠKS$&Ø,Ti=J%=`¬[³Y¹Ï¨€í’ ©)x]EfÀå%œUÈÒR€$].yM|8c69Ðe@°fÆþq)h õPï ¶VVÈÁ ­v·Q±kþýïÿpÿåÜ·ýO—½ÅuûáÓ‡¶_>ßrõøäör™cœ¡@U\°Ek¾³P¨¬6§²q£òv¯ÊcÂŨ·û1‚ˆ9¾ŒÚ¯û8îYCžDÁºÍüz²&E˜}û—øy`zœÈš†éMãämN 8e(B!L(øýu{;FmëÊr‰l½?í;xÝÔž.­f±µÄs„–Ó½«?Úª–…òÔˆR„ƒÑÕVåYG ·íùÓ§¿Ë<¦=ÇIrÓS-6â8&gr ‰ôì-[kn˜j5rTèúüòÝÇïoç[UÓ x"G±©µm¿DíÿîÓÇ·[`k¡gù¾©í¬·)Ó=óóíLøÌyûüvLfleDëÊ.QÔjàN)UÑ‚1ÝŠË•V¸´NW±zÓΪÔ÷ Xb¬Ñd™òËóõ­ò„¶98ªÊœÇq¿S}ß/ûÖj¢r¾ÍÛy»!a²šž¢uµY¬éûé|ËÛÌsQiWs9PhT•Û¾_Ä}ŒUiµl^0Q’Ü.Û¾íã–YkšGR7ðöÓ×ãta¤­ÛÞ.mïCh.¶hìfg«j2T—;ûo¿ÿûQ÷12 ÚcW×cg¶&d‚YЮŒé±ùæ²1Ýï»t[­ö\ggë"u‰¨B'SÍÈS kü‰VçÓõºïð`›`*-£¯¬„öÖâò„ébº:kª7˜^;ÓpCÌFW˜«ý΢­Ö<9‘d¤çèN#Óà2?YUÈE]«”RÎ÷0‘Ë6È)+zƒCÃvÖRµ†i1é´ƒ…Vyt±eØX›)Ñpã’IñÙ ‚!¦VßlŽ 3YZÔcþ4ä„q"S\+ —7¨ ÄÂTkgB½Ð@¦–( Dç_“â*Ú~O¿ã×ó‚?ÅZùóµ1”ŠˆJ’d[úí¡?÷yŒšNAfÚ^ú+«ÿN|¸ö2õ0>PpçòÁXzßžŠ‹èA±ø¾;•¿XüKðoþ,°¶(x,þºÇÁ»¤ó{Þji¬÷xµßw xH·GL[k[ƒ~çc?-¿Ðk휠†PˆD«¬ztŠ$fÍÂF.ºå’äX®AJU"Ê‘ôš¨\”°¨æ‚”—qµ6ñÒOÁZ‰E›UË\¢ìɲZƒŠõ@‰X™z³jÑÚF‡ïù¶÷‘ʵUµ¶¢Œx%™jkQ ‰¼çiÒŒµF£Õôqë¯×~Ì(Ë͉¦ hM¯  ¨AÀbÑEª+,Úe0†+mBqÚU° ©ˆI cVUzT6­½V*ÎÕŠØ=tÚë‘*Ç„YTµ‹DÅR\-$%RŽ| ·5UnŒeÕ×"ÒBᬆõظÂxk¥9×ð!œÆšßÇc B¢T(-÷‚ bÂØ÷­G›F B{Cc—BÁµ‹Dïd¹“ízüt¨Ëåù)š»zõKÄv}"Ú0~óíÿ÷ÿõ‡ÿþßq³‰v¹tkßX±mÉ ¬€²“Õz6¹š©`Û6Mmž8Ï™ª½j_?¾¿‰5ˆ#Ã3GNLl¿ýôO·qÞÛ¨ãëçŸ=Ç8kÞÀVpÇ}ŒáqlĨÎ~´RLŒ1óÿçê]šäÊ’$=U3;÷ºÇ HdÖ#«º»f¦GZ(2MrÁÿ7üÜðGsÁápfº«ò Âýžc¦\Ø T5wáþ8vÔT?=f¢dTY‚–Åœ•®mlÛ>@bÛ'–ç`y(Qñu~™ÇH)Àùéñaãí6©2ÕM‰"èŽeVOOãÈå(ÊåKíK-SLå`AÞë%?™,ʹ !ÄÁd«dJ`ù^"cZºQî‘rÁé¢Ì,†ƒîÜ}»ìZÈµŠ‚[Wh Kœ²ÈDy‹ °ÃÓß.>³Íçe™«x«­i”(’\pY-s™±\Q SžHI³6Fîš±Xíz÷ñ<Üe"Nó‰ Âç>«PŸ20‹ž‰ £ê¤´•¤íEZ†9)'±ú¨î•\›]¤B‰,T×9QFhŸ˜=0·ñfÀèeá¹ò²<'/U +Öú :.fln$ÐOå™õÇ7ÂÃéC¡ËÌÔIB{§Y¡x>p|ËhNZ‘¬“ÆÔOo½Çû[•ìÌð}s¢w(±ÿeŸIÿÿ¡ê›µU¬zÀ:ç¨w›ûùTÚ·ÿÊØOH¿h€ê(à»k¾1¨s¼0™9Ô¿*ž‰Iµ â2Œß¦Œ²î‰*S: µpÎJzúïQlÎÖûZ½ù#à8!4ªåôŸÙi¼oF?ìªV˜hDCŠ,˜N±lB°Ñ{èw·›C‰jì‚õÀl[h43fç&Ϥ€ÞÃgvô4ßõ#ªŒ0xPH7 ˜ŒÃ †áòyàÈ¢™#À2iþ>Õ›£¬Ãá ¥WÈ­0° ¬yŽVuwéeHy¦²P*Ô YµqP…¶1˜²¬h‹Ið`»þm¾»ˆÊvG–“…Oß}:îS+ù>¯Kd¹uDÃÁU2@iòr6: iRíc+WÈ Kî%-”Jb¡Jâ¢*§Bd¶sÑiQF§Šæôö{hôáôpÛ¶ ÷Ü ´(n0\;mSÐýÂa4—áãÊÇãós}Åý5ÿëÿûüŸÿÛÿó¼­ë>¤ ‹þÝãƒdÌä~ÙÊVÙ„a¦9Í7ûí§ï™óí-UUGͬuÏù¥æL·ZÜXº3ý0|•é~|ýùó™8˜÷œosÝs¥{ÊYÙœ ‰bht°Ù¹Èdy¸PûLõœ;Ë8X©7Î#Œ‰-š¹¨ªR´¶QA-¤HCÊ‚ážPѼUr¥D/RÜ/OÆ(‚.cëÝCÀ6¶O?þq7f–&á²ëy³×ÔÃþ0†­œOþ¸çcÞÇ̯¾pyÞþôÇÿx,MÜ}»ëPg±—KXÆ®_>ÿl°çç¸}ýlY°µÇóQ¯__û¶@±Ý 6"><}w¯û¼çÛ—Û± ¦@®ˆZqÚMÄZÂ1íëÛ=3‰¹Ê¼Æx0à óAT͹nBöm@eVÐÊ­RX¹E¶ì/`8A+)„µà>F)DU1!k)½¨…,(&|³ë6–0ó~¯T"“¹±Jf¦!7‡Ò’UP9˜æz¦€—YX^è´}ì£Y|Óe´eìX§*UL„ùÐfÎÌûÊÉ’{.læÅÁÍáÎ0•6:<¥™kÕÛZäá Ø×,ðÍô2S`€Ì@ia¶Ñ}‹ØöãUe‰;7]%‘”D&TÅvÊn´’¨‘°w éx wšT—™Šp/O¦ Öª¶¶·°Ô ªÖhŒåM¸B§ºt.ÄмîNæYð I¸8•'¥î°²æ*PD±u/¸) ¢wmq6@eçÍË6µç¾-9.tFeÞÅbÉa,Q~áéÖ¿6‘Œ¶ÑôúKÊ3Nf‹ ¸DXf[“D•ÑÊÛÿó¾6ešz'’ªÎÏéÄi´¬síVçš çlçïtu–ïqJÑÕ>¡wÔÕ9Ku@¡`ï¶tý  ßìXnÍQà_¿x _§ÝÊšÕd»˜Ï§–xw7·k½?:ÒØs‘Fk~fCN¯R³²Ì:GO5‡‚ÌšºÐRæ M`GûÞMa4XFXÙ[TyC!Šèñ•ýl}ÍMÔFÅ;eÂ`xç$¤‚7öA‚TbÐÃÃóï~÷Çãþ–k¹Kkò)5Šñþõ6[Mï× Œt XŽÎVà 'Úè½G}s† ·Àáf6'/N´qʰ ²S—[1œåÓ"³‰B³qSK`R¥UŽZ¬^d¡€„Vª£3½4ux@¶Ý­x'ÖiFoË¥‰X†èÀ¨ITpDÕªûÌÊ"¨è{F§¼iðû%9˜è{LI2„£P}‹—¶‡øôéù¸:JÎÚ½Š†f¾̤lP—·XLYf¢9.®•›GìæftŒáÃ<¶¨ØV b‡…Ùv¹Ä—m»ŒˆmDÃ÷B¬ûr³m{øõþÛïµ )^Ì®Ã/—ÇÃë¨Ú=Ée4áw]¿{<¦¾~ùå>1亮~ÿÇœ÷Ê, 1ùþð»Ûëë\¹2…òik&³ ;ªä7œSW¤”–…E§üøÝ}K77Ù“Òßz/Õ'˜ÀÛBÒÌZÛfŒ‡¹#=c» ü¶:”åÅ£7<A-/#”nBéåb¡Û’Œx¸n¬•GGF>X-‘VŠö«”hJY•PäF2®ëe`°RB9Û4J4KQ£ 0m°Å ULäéYâ¤ÁZýѸû§ÿ§?ÿôËí˜9<çRˆf(š§Ã\¨Úm‹4d)÷ÝýÌ\±®—ÇËåéuÝäͺS²² RùÎZå^•Py¤»P΀õÄ1laylXærE™»AÒcCF0óPnÌ$°ÐYRHQ>ŒéÕœÃQ¨bº@b‹ÇmÖf ¶Í3Úž®F>(Dõami0ø¹"`8‘ëè4ÓóÃE6gVß kp;˳JZ*;¥Ža©9ÈÀ¥÷T!¸ó<>µÙf&'€Õœ¥Bù³3ƒ:`fÖB-Ëþ8®wëÿ" ÍÐ/ªwti/H!z6©`[ÙVÛ¢Áæ%5OÂa½ÿìøbœÒ‡%éÆ&ù7óHÓâQ,M ƒïñè$-¤¯¾¡Û7ªP`{~Î-Ì)Pô?¯ž§ª÷Mý”½»ÑÛâ¢ÓþM®ÒéI±o_ø«•ê›3KßxíNo Þ93S}K(Úcôo¸,á‚´á–í:5%Zƒ[F;± ½ã4ÚÞ{‘VÑžÿ>xÛÂ$Ø{ õý±9 EØÙES„è„2z¨6gõ¤ÚãD’Q½]µ¶NÙéå/XâlBlÊH»±Ü<Ìçñ¶êÜh‚E婊 *€½äꤤVíÂ;ûuÈÖá®¶Ü; fae¤W»ü®Í- î9ƶ½î …a}ýôíåñÓõ!¸q÷ËÊ †Ü6òññŠYËVÊYå .¥öi¶û¦ •–µßXOëãõãåòò›ïžŸ>üæ7Ÿ^^>Úðºñ6mì »Íº”j nW³1qÞµÊÍ®¿ÿíïüÝw^žÆ>ÖÌ™ê`ÏeŒaÛïþð‡a~[sŒ¥!>ï£Êx~|ž¹â¼Ð·"EâDs†YkI±o[ÒòŒ/íãá¹;pSg$Ú”íuÉÈ £Øv­`$fv¦Ç”XF—ï‚ íÚ_•¯ìf-2”‰ ÷R&ò—/?¿®»p¢.ÑAð:ÛDè«TÌÍL¾]­çò0šÓ7ˆpƒV÷©Üæ15 Hf¬^º›‰e‘B‚×p0š•årwƒàªÂb‡ÜdVÉêOû\éWkÛ¶À(+Ûèž^“ .•C´Ê²Ù>Þlˆ¢VÔ2Y÷Õt̽L„™‡ËAÎv›ˆ“mc¦ ÏД€•¹šEÖF^Nw_÷ŽØ.Pu“Vßfgì`JÝHÖþ”¦x¡OÌ>p­ÀÓ¨+++ƒ•9ÌìœÎXã²z#YeÅ94Õál˜àuºƒ\'ÎôtÕ’ÙÃãi\¡ã¤RœT¦SSÑ€³Efk‚4Xu£Ü9fTÿ´–&;•¹Óm8š=]$‘ßLäd½ïËü^ʆa iÿy?=Ó'y¨¼-k:É Ù6ûúH¨¾AÜßÁ :s‰íÓâ_KtèfÑ{¾ŽkžDÖSûÖ·siö·ëÈÓX†óÉ9•A W¯møª?Ÿ~™5Å¢ ð½_å©“TŸÉئw´vîÛБ pT[ÑÎB¨:ËwΜû¹ Á0í}âr© «LÖ{4²ëQ,jÎÛÛו“”1I“0Á‘¢5nVTõ¼ErláëcÇ}ü÷®ì´¹ËjSgX j¹ÑØjÞæ2˜›½eÞõV0˜IAVç”é?'Q.̪ÌÀ†Üµw©/ŒEb U“X¶Tu€K‹Ör.=Ñ&'﹚±ª\¶8¯JX²Sž5iïX Àf±1tòöëX‘c믤dµí2e(”Üú ìŽ!8R0e¨µæœY™‹…\óP¥à;k\fd0hÀ]aô Ühðpº€ù> Ü8|ãVªt "C±ûªËQð] ãña×úûÇÇŸ„Œ²Wx-”°íÛóõÑÆÅ‡!ƒäâ}ÑU¡\¸—ê ÔÊÌBmK€U¢–Š´[ÛØ˜ÔímâPå]K•¶ NÈ(VBž`Á¼“Ò—K‹×¯GÎ¥.ÛSš¦ª @¥Ø"/'}—VNNò󯿮š X¯_Z·éA`–ÃN)% ¥r )Îäfìb9²çÇ—‡Ëööõžš·×m³ZÃ6ýûë¥p¬ò‡KüÃ~?'gaN–Á¬¤»«j»Œ•–;Ì9ì2>}øx¿i)LØ·ëï~ópŸ7w7­%¬ml>¾ýº —t©n·m¶ÙîÒMn¡B8QtC–…Åv‘ÙE±].ÛÓ‡ç—c…ÒD›©ò°ˆË“?×f×LJßýø›ÿíÿO?þæ‡ÿþçÃ䟿ÔãÓï?ýöïn?ÿ>n×íw¿ýþR+Ê&ŒÆ”}÷øé‡ß¿üŸÿÇÿøv|ÿ—ŸÿrÌ|»½.i‰®äL½Þß\þtýxÌ_au¾fM:hT˜TW|V3ŒÎôë” ¶4Y¥|wߟ¿[÷%¥Çuóí^YYôቤ›;F£^Ìt8äe¶»|J&`Ûo?n‰mÖTmþ(wlmWÝbA–™œ³è¹h1!˜ùR»_N[ìÿ‡ÿøöú«FVx™3ËK¹SEVÞ‘Xåó°¬ Š)ìLWR¥Bæå¸À†E‹Vã¶ì)[N`Tû¸r@^Ì!VMI« U¯í“àa˜ª)CyZ–Oó49“–«DÖ¬ èwpÚvm—ÉMØePe7±iÒ&Ò¥%T–­L±l»8Ž#öëåé˜w9}žì‚³ã…g¾Ù éì:_%˜ç6L½!!8a”ÍÕ€L6dÝ7Âéì ËfËÀU]tuªdÆæ]©)Ñ2…wcLÓwúüD*Ä}xùÃß}Å_þe­;~þ×ÏKëóëÏ‹øãÇn‹´ýv¯/÷W£æZFûøáÃåqÿòåå—ýåó×Ïùó/Ƶya®¯u¼}žëí˜æúR>Íbuøàâ>.ßؾÜ´U‚Ãh²¨8™ZN³ÈÄUUîæÀ×Ûk­18˜LUX¥uO[µA"ÓŠÁ‘j-”¹TG—=œsþ[f\žö±Õ<œ½(Ú…£<G)Á ŠE.t_÷Ù½t"H£Å;¼µ®»Ñè§^M˜Q"ÒÔN{¶YoŸwÕéÙÁ{dö„¸žëL¼F©+-Ný©/®ÓVÞàµãL8[ÞXÝ vŠvïÖéþM„P5¸÷F±Ï{ÚÆo£UGÅzUª÷oÛ{_Û.:4Ÿ´Î>$SðI#ÝšMÕ¨ ÓŒ'-ƒ4†ƒ0x™ÑųÔùÔ@ή%qªæ‘4À 1÷¨^–e»À:÷ˆ“"jçþÎÕæt*ÞÑlmwL{/€V -va¤“¤-’²î>pž}Y²W§@w¸±™6î‰õîÏ7£ªûN{ü,Ön5”!¶Q1$ e}Û0ó.êúøèÛÓÊ7© áéˆâ©ß¶ážbf²ûJ¡Zp“R Œ±:ÙiJ­l–P›…pbOÍŒ2mÂ’2cQgHÔpÿp·ÕAÁªÒ鮳aJBªI¨ªT¢‰ÞàÐVO«Qe×Ëad2mŽ¥ãXﮪf4gTˆq ¾Ö IDAT¶‹¸s÷æô7¼Ã͘NCð²_ž®×OŸ~¸Í2ØhAÜÜÂÝ}\.c?rp¼h»l¼ŽÛ¶ßsaâÃ}=><>î˜Y˜Gã¾ï1–í|Øöd ²&T¥Û¹<‰ Þ¬¸_¸¨£Ät £¨Ðò_lÉ=<¶§<cçšÞÛóÃËóë±$zx~܆Ý[M©à.÷mß®_o÷¹‘?|Ø ¿/sÃížµæ‘HŠR!ªÒÈ’2'ÒKÓ<çzË‚q»M¥Ë|ŒñòéÉê­œ—±Å\dï^CH?2ÅIj¸UNòª´9WÁYä’Ù¨£¾Î{•µEUò}øËs\vK¥Xnv6N^>üîãÛ×/tÀk”—CV¢›]®ãßÿ¸Vúî*‚Îy‡ÇÜc™o”Û¨áVà½|!®ÛówŸ~üù¿ÿeÖ¶öÇO;¼ìˆ1­[%œ&­‹?üñßÿ‡ùöËv½îÛ}Ý<}»îXþ¯ÿÿ÷þ5ßò/¿þü–¯³XGí—ë?üþÃëëÛÓÕ.O/>Ôºßî³2†]‡oÿÛ¿ü¥ø6çëÏ¿ü´qÐcßýïÿôÇZ¸çZXNµ3Û…€Õ¦Ô-·rà ˥~ŽÕy”tÉí9>||úîííà²=6aÞm9ùããeVN˜Œ6àî+¢=HØX©‚1Íœ±w~‘µœ+™IÝUî•b-”iµªß”^Ó´6A¨è#¹7WžI¼óÿÊPÊòˆ2ùþü0 L,õ‡y/7THаHO–¡h·Ó0TnVÍg³"sšòŒXs%éÝc[„#oGYÇô•U¢Ù”“y‡læ–.LdΤ˜µ]ø÷Ç—[ÎE­b¢T.YéXd¢Ÿ@Ú¸–ܶñ¸os­j/7¼ÍÊ:yó½É*'–•âÒ¬ò*–òaÐôÞƒ 5U§Ïá³z§§‹6aõFñ ·•uÞKMneÖ†sHuH8ÝoE“ ݇ƳCÊ“‹ ‡ƒp—Ë?Ú¹8RGóo a`47v4šèÄ6¥8Üûäo»Ž—ªcŠý÷KHÔ™·kù£m+çïäQeõì3¢sŠÝ#Å"(74C°®…Ÿô¯h{iÆž¤ìo`'t ‡8½Ÿ>úÛRgáßP¾ù±àÆq:ÈpF!N–;Ѹø3nØö·î¾â7~ÃIF}tìT—Dñ½r¨çÖ2˜tVk¿ÿW´.BruÿÏ §múêYÀ¤þ›lÆæ™€<__ïõÏÞ›*œàxÈÝ{õ´*¼3[Ù^H®VQ­5ZIB9Þå\dÊ[ØUfspþ`hålê8ÿ? s²ŒE´§Ð`r"[—6¢„8*HÀeÂòr™…™w©; ·­Ê ¡`&KdGwi¥:Ó½UK–;g“Qí|·tSxÿ:Ûùm÷3÷Š–ýºÁ(ÞgüØEyÖå:œì·@U*Ó¦4šK(ÞJ‡uçl·öðü¤ëç¢Ãͤ{ÐSS5Ñ?¤Z|gÝvùX5Ç®ûIiíæ2§kø`9aÆ0÷n§ü|»‰ŽafŒà´mÛž¿ÿíý( ûãË£OÏsº™?í—ñ´©îåµ#RÛÛˆ=kÜ6qŒ°e®A_V~çš‘³Ö¼Hñò8.ÁûüZ¦Ú铸 PñaxHÇqSm¢¹_6`ÍU8¶M¿ýÍ×üu_ªJ¼_)¨î°ñãïûûŸ~ýúz䢥)T¥LðvŸt=|xyüüZG­áûQoߺ[Æ|\¶íø»×ûMKûæÊhcÌûZY¨¾Íµ˜‰2£êŸ~|úå­¶Çï¾þZ°Piß/±ÇP0æ[leX,ƒ-”Ê,< …MàÃóÕÌ ÚÃøøÈâœ:æMi ùñéûëë¯_Ráûcžô}Œ°…¹ƒnð™0üáù‡…ciZê6ÝËŒÜÂm\Ÿ>>î[ ¶’ygÇ/×Ç5¿Ü^?ƒÛž·£nÃýñááv˜_>ýðÛZ¶nw»è>?£ÀL+5J㸧Ìc~]¯þ—ÛOþ©¾­Z¿|¹__ÇåúáÃÃ?ÿûøåëglã~_Ûx®Í¿~ýiæÛ/_þüëG”ïF(Öíõ®ôûÛ+æªêôK9%$£öð§—OOá¿Þëm.KÂÓ%³¸ö™¦´†;À”qø0J‘s•æÇO/OÛýxK¥á8L‹SÙa>ùîdÚÂÑ¡*Q«äîÈ:ÎDvÒ*_HªÙÌ–}A-“/x1NG .aã2+åµÈ=ö«{ÂÅ3¨æ´ A$äÎZÇ=çªnˆU!rÆ2AgU%6ØÆ²Ó³ý’ñ,§µwÚ’næj¹½³¢N‡>OÏ~3ÊÈìƒ è’Á«Í4|¯²á;Ák!û®Ý– º©'’é|¨z_%žÇ^ªzmÓq.°­­%ñ\ŠöäÚÇuQ§§êtÄèÎXæéu¯}þÆq èfã¯C؉\ Î@d 9­¶ôwnÔûÑ]~z·@ºŸ|Îô¥[Ë?ÙÑž~It}Lò¯y Ó©nµ´h`Âaò¶Êy OõAgæÐÐ€ZÍ q*48ÑÞ»‚·‰)[j“Îg2!ÈË4:CÙÄvÊKï”QªG+vtñ´÷yW+JwÙ£dN6ÓÍØûû¬[ƒv˜›¶nê«Y Ê+o‘ëD†fk£—eJš)–”./•¥÷ϬṊ&EžíÝŽ&7Ù¤µÞ^w¨µý¨!Tåg§¡ÛÊúÅZ=!iÙB%¥÷Ü>£s² ªN×b+ÅP#º &+ †h0I¯SLu¿•3K( ›Z'Ï’é³³ ]™îff¼ì{Äe徉ì0œ4wYŒá$¶ˆÍcøv3qð//a´£ïn¶ÅåºÿðéŸowº<>­™ÜYÜÍvLJív½nÿø»Ë/“oÓuð>˲¶4=_ïón5i}õ¾§­•¡´¥J¹ÖQ+Ó‰9ç:î Ìâ‘•ëó—ˆ<ìÈ#¥™x.âv?êþ5b+­ ^YDå1‹Ü>}üá_~ýõ篯ûð¿ûÓüåó—ãu\#.ó9ßpŸ3oÇ:<1¥¼lQ+丷©u( t©5oo~›,—D,˜ «}NUË€qÉÍy½ìZÞ®›C¨åÃwÄ£í:œî2òN—ÓŽ5 –Œ¡$F™ÊÆ\‰tGÔXÅ#Üíñz-qî^«Ð ÍÖÆõSA¥'†[è0£ÁÓi º›‹† ¬W¯&ôn½•†\DÃj–LŠSNjªù€µ[ža!.CE™E7Xë½R LO#m¢1ÙŒý÷¶?§ªä§…ÐNš¬z…Hï%8´åN¤«‹™ìïŠt©ËÉ D-”èVÓ¬¤U¶5pF‰¥Ï^™Y%QI—•›l7ƒY,«ûZfÛÛQÑ/³As÷áÎ1d{øfÃBãåú˜àšD× r[N3ñb—۶߿̷¬¬Òº+©¬ª´Àß¿\Þ¬†mÿôûŸ3‘ùVã¸çý¨**kÛÂÈZ?U•ù èãVã¾XÓÈyŸ¹xÌL[\F¬•눚sÔcÈz«¯Ç½2îL–– eª¬•¸+YŽ„[aÈ,ªìõþåX÷Årâõ×_¶a´—¥cÍÊû¼l—_Þ¾¼ÝïddµŽL\·qñ8æœëÞ¶ç1^!‹4 Cv‰aÔªEšžž/»Ù1ç6¶"ª*äˆEÚvI>ÌGĶ{\÷•µŽÅ½¢JÚÉë‘^%søæÎ .Ïï7¹+×.#.›½)‡ ³Ë¶éãu;ªjÒ/¢¥;#㾎y£Y˜ÉYÇ¡CF¡öK¼ì/Ç|º=nÛF„¶k> r-ií—' 1>~øq_¬–Á=Uk˜Õó“Áü»/ÿü¿<ÿó?âíí»ÿþÓ«³ÞîoEÀ.3ìr1¹©äie¸\®Ìãn4»ìë¸2!6ÿ»ß—k½ÍE)ˆÇëóeÛLç’Eé¨Ì¯GƒÐºñ€ó^+³0¢{™…ùõó¯÷SÈbœH0˜Æî!¢p6¹Õ¡í²0V›•¸{¥¬jc B)iÐÃ-sSRU’Ñ÷ò×5çZ…˜Úî·Y¦–V 0–§à©B´>R¬å™(t\Þbe»6!¹.Ãþ‡üŸþò—y·Ä´Qq0s_=úåÙƒ¶fJ€«î¶Œ5°*q/[…U™ÒášS‹Ra –ÒrO 7a–©ä“™0Öˆ=ë(,¤Ñ‹jï/C %œÜ𬠤Q†,$ UôMK¨Œ3îæMî¸jå” iË;jUvT­¥’«K5t–àh$sµ^ÐL+¨#©­ œ¡8ÒUÉo²Jqö­œ~¢"ƒ'Ë@–`%Tߤ¨úþ(´¢ÐrUc·ˆÐ-q}RÖi©$á8ÿp:Úyç¡k¢"©T¤h¨lkv­bö^h/f;·‘ DZY¢qlN:,›b@{¯ŽOá«‹Z:¹§iTɪ·‹ªûÙU·mÃÒ·U_ñ:ä™é²orZáìDþ›h}û3Ü!q¦¿¡³ê½â™ßz’NwR¿þOøN·y“  ïÝÞÝé¼™5éÑå Áiî†@7ñ[Ôï ËqVýYœ‹ÒæžyBm«9ÈPé„ÄtPÀþ¦‘›Ž@ú ’êíT'#ÏË :x|:Eõ–tœ°Ò~Ýɺéü\Ÿ¼ŽV#O/Ú‰p¯Jt/'«ÙÔ:•@ðì¿9=HfÖ¨X(œv’ÏÆ0º! @Ñìø3*cõÕ%[ z±ÍÊr)%Ö{æ@ÍËåeá–̲%OY4—%@•]N°{§¼cÕ].U&hÊÓôÞOÙH—&Ušç:ƒf9’˜¦o[梱I˜‡¼HÚj×ÀD ´º:#1 4í„¿Yk¡—m»îk­*£e7º (@K¸ÊRæá¡ÍÝlÀéîÃuñçx¾~'àn&ìæÃýyóÝAlcìi»›cKrúKìN¥®&mE®Ï…‘„ðç7Èq?¿½Õ1K^9“äºA_1¥šÜ‰T ã•“kä‘Ë uÙÑ^3ÓR¢»¼ _6äÛ:JòÅâ½àXƒ€ÓcsÊ2[ Ñ6”rZ‰TªÂ½=J¸9¢4óÓÃÕ÷§áöÓ¯Ÿ·bjVÑ/þñåa©nugÖFçõrÝæ¶Áañ0?<½€qܾưÁ{dV±~óñC­¼¿Õ‘,-ˆx¸><±4¶mån±Õ¼%×Ãvýîùº`ZrG7˜DdpÛ·]Ÿ>>mpCŽUô¸¼<=_žÜðù>·ØoÇ›ûÛfk¿~º\Þ Cû&aŒmпÿð`ØßjVÙn5xÚ®—+ÊÊšðQ‹÷Êa#ÌÝQæ&«0ûø´ýñxýjö°ŠÃÖØ¶±_÷Í·‡Õñ§ßþðëí@îËüáñyÛ7þÃó×üÁž_ðýüôç¯_ÞÖçŸßàûYxyxÚ*ŽãÀfk_fæ S¹PµæÅå°ƒÅù:«ª¼{–'ã²6½Ý'¡Tùœî°lÍd§ÇÂ\fnÆëó3æ›Y%_p• ¬"eûx0Ó< е ›×ëc[iJ7ÕC²rB–01@zÓ½«UŒ«J÷»´ÚË+D"‘Þ%i0 ‚›Ùê`ÓH.Ê*ã!ÔÒrd»DÌ–R?ýåÏÇ|+Ø)×hÓ­aéP«°2O‹Ì²êÇ*]˜GòÖY$ֳʪÊXbµ|£°ÒV´´îQ¥U:–Ñ×: ¾Pff.¢(OvÈ®Œ«}Ê{v‘ ½Ëƒ5jt¹¿“òÿ£êÍ–äÈ’$Kf‘{UmñX2r©\ª©fº›¨_æÿ¿gˆª:»"pw3Õ{ExD Q“oI±s3QæsbjZDнD€ p¥n“‰P¯¥„R Õÿ^C" om@ãê;¬újG‚G¥›f%XH)³†¦ìóðEO²‡“¯¸?9!Ò’¬zb<» ü8t …J‡©âAžu©\Ùef2<¡Q|*ÓYÚŸZÊ1 ¦£BÕ¡¬ô¡'öCFô;ßóaêÔk cÈø†YˆRÁ&£j’ %óðÔð°?¦³ƒÐPƒƒÑTX†Ã£ø ·ð_,þNukGaì ”¯ïÈw¨ÆÑ}Ôí`“>lÆ|©úG7'ëf®ãïýØ÷(öq%® RPl†V©86ƒæ#ô˜]ˢ,GäÄ®#àö ”=€¶€²~ð2 ¦8Ríèz 좃Üf¹2©cõ »Ñ[f8HÔÉcÄ­W$ËeÆN6ÐÄæ0Ú£ÞâߪœVž X]E¥~ÜÂ[Aý MI¦²B0q¶:÷q¢Š9ÅfªÅ^¹ØÒaêìðÈúƒÉ„%'í¨69RàlK?ÿÛßÿúÛ¯_2õÓ–³ZáÀž–ž‡ P¡ #ÓL”3 -œàÀ‘…Ø#çXÏ:‚ûd8‰Â©dµ8GRÁ,æ<êVXȱªóHHÍÉDåO„ Œ‹±‘ÖÀ¤{‡s]W—±²-n«¹¶Õyf£`Íš±Y;5ëm™Kó¾®ËBM¶?þù_¶ûþÝówÛÜoýÜÖn{±ñ(É™­=}J»waѰý®±gdútSk‘¡) sìéŒ F6`†fc¤ƒYéÑ=41Æ\ÿÀÀ=z2Á&JIC[xþþel_\D¹-çn5V´ÞþôóÏ·}n(FWŽÙ–y¹œ·qËÐv{ŸÜ"Fo2¶fž\v6›s<øuˆcŸ“²æí¤œ‹y÷q»ïa³ÓúšÔ®}Bî™9¶¼<Ÿx9göÛÀR‡zÆ ¼{kOýù´Ü·­¹.—'÷JkÝìbÞ '¯CêkkffçóKc¿ßßrŸä6·Y?wàrÛöó©=½|jýÖf+ÛÈÖËóËç~Ò˜œzz9¿¾|?¦žŸÏ-óÔÝíÚÿôzöNÉÌgF6¸{Èr nsF`Íìù´Ë&=_.Í;dÿç}›èì\ûáÇ¿ÌC^Ök̸Çòÿ/ÿýŸï¿ýöööþÖ\}ýüËý>‘%«iÛ=æ€F0 `ËŸ¿»n3¥9™hͯ×׋÷ˆ¸å~·EÄÜ‘I·ÆjÒfš›ÓµÐ£ËN§ÖÚ$ï)üáeÝNká^@p•ymËóÓëí¾Gb4bº¼ÁÀTº)CûŽžÖS ¢5x;ðu-˜ 5:Ê+1[á§õ¢HhIdú*¤EQÇ]®zøohIXpÚ`aäL9É‘Eq“†ÃspŸýÜ=ÍTjyÚ48Ú4ENËdjŠß$° ž0Ebæ¬4ÅÈ7cj9ars¤'{ÌV¬±˜f´b÷¥T]rËcaJ–v@ÈÌlƒžÐ‘©ª<Ë2iMft4‡”žÅäJ³±e-¢83R!0K$ÃêØ y4šL”0‚aõA;:– `>.q@J4K˜"!sAÇcU…*Z]Tò¨¾ÖA‚ãQ,«óȼ$Šüzä˜ëJY'1bàñ%𨪠õåE ð{K§Ð¦Õ%+öVÕåR`b*òÀPU2íÆDváíaÒ¬•˜iÇv%³Úñ€¾óô7‹àÁÍ£ËùH\ÕÖãø`ºÓÍý`U=À©††nÔøPñ<\¿××°[¥s”[ŒÃÙn#<¸T¤³Ñê>Tÿf‘^ý ëzàIõUkp«[”+ÉB`‚H&¬f‰x´#K{yL±µæ£ 3f5gQ‹¬à³ŠE›×7¢©1aÂ!†”`(á0ŠòP&Â#"H”sÇ£½ÞbI´ y–×¢Àûé;!w²yT’ÈŽV Ótb1hfǽҡ¦YšZ}Ï›,È´äÔ”™<ÙÊÍ`Ɖ´¨Ÿy"3ë3™b‰Üã×··È¹°åÐ!Ì’c ALKkÂ,]™rÁL±Fª´Ì•6MÇϪ9ÍO>Ú§¦£EÐÝ®P¡"Ñ¥fyØëÉ£âkP rLX]ÍšÜ î4cï§ÌKæ*úimçó5åj‹zoŽF7cD†·Æv²SkÞ½Ÿ×ÖˆÖW^œà:¥y{˼ý#ÇG?™”}}Y‘›Ä°625î¡Ø8»(Ͱˆd°§s÷ž9•™S9=,Hfú<Ñûø˜“ŠÁà´>1&"”ÁžÏ½›rŸ{È2F½€f»2Æv‹Üjuº,ízZ÷Ü•#üòþ>÷ÑÜÔtyzg£í{€óïý×þöC1—<ÜÞwÄ=,Næa‚­æm‹9³›bßîk[ÙÚo17s®«-ëyßn3˜t_èKBƼž/?ýáó~}Ë-C'§àí´,Ëš0‹}lãÝÜZ;_—×á㼜œŒ–ÝZ¿~˘¶âõzUã婟lÅ%N_šÏÜæä6cc6§““6fty»¾XÊyû|âäõt:ºœO§ËÇÇÚ “š8su¾ßsó/o_ÿôéù_þúß~ÙṞ‡ŸžŸ§ ±énRrÒ©ö±eJ——OŸ?ý#&\{öÐO¯ý=\3¼™—Mºñ´žº[ÛÒÎk…(úFìq×”¬N׬ÇËæ~9¯3lûø¸“Çâç—:éà¦7º^­@¹pIæMlˆQ­¤úl®TóÓëù»±##aš³v-M6e¢i¯À8ŒkYÈ•-33=€=sb愱ÀœåÚ“& 1•˜£…%bšRCû®Ld‘tK¦gdšP¥1¦91‚LCKP›Bb¦Ë,€D:áÒ"3#I%ÓC)”:cF“iGSK–IM&ÍÂ\DÓ4Î ‘P")3Ñ$ºg–CY@D&Å`ÒËÞ%hÎ<¾møåáÀ±‡²E2ŠÊºêUŸ­Â82àYœ"e8èfz¬Utd‘€)“1Osm@P3*(ÉiµÜ:ªðV7-QF3³B®7CÕY | ^"m+¡äAí!LÁô·Ö4 Dµîe%xžª"&ËâÓœzV\.ø’™ÌÇ&+Õ÷÷4å£âžòÊ^•¿æ =ÄúºÌ§SG-õþ«ÍðǼ¹?š<þÂÄáÏ9<õ²Õh”™=hî…èÒqK, JIœ?3ÏÕëÑŸðÃú}¸•A–õàDõ7K\¤¢Õ[v …͵Z¶¥Ã…#§^ûаƒ¿ZwíqCF8Ûq <¶nYGÃZˆ>äÒ¿o@ÑPá¬ãZå2Âwlhë4kò—U!Ðkf“÷ƒhL²x ­aVÒ ,ùÂxhé5Q˜\ˆŽ¼•ÚÑ(cVõ‚B€–ÖàÑ íá·<þ ’Ó,Í„ØE/kÎöÀÜ™“hfhr8­¯'ZC ¤›£uï³k¾ts[}9ygs;[‰}—ô¤›7½y̵tŠz=·ÿû¯OæOïÛÛÜÙ:®Þ戸MŒÔô$ÍܰaÙèS☡Žß׸kFËm®ë‰ý¼ï÷t9†™EƒÇ–š!Kbå~>-Þ÷÷PÎ]ä 6@"G´F­ôÓu¹ç¶ ]½N¡¨,dìñîæ ÝÇ>öùïÿçß÷»ÅœlcSÊÎ á¢-–ƒG­~‡rXôÓåûf=¤Ì¹w§2°í÷X½-­;ð²æ¹A1ùÛ×»&š7zûôòݲ¬{Œ 8OÖôfí‡_?>Þ/}ýù§ŸÇ¼µf\Zw[Ûåi¹Žm_Ö—nmßoqZV'2ØÞÝýä~!—emv(ͼ±µ‹YïÞ„AÞ÷mßOûñûŸßïûo_>®Ë ¹nc»t˜õ¯·Üo@·ëéôßÿñooÛ}×ìèçµgÒ^_¾ß§F޵[Á=Öõ|“ýù/÷-öŒiÖúi=ËÆ[×ÇǸ|ÝÚÀ܉½­_¿ò>îûéç·¯·-å–vyú,m3°ZϧE#Æ( í‚§§×?~þþ·_þó—ß~9ž7…rR’æìLH‘¶—Õ)ûü´ŒHg÷¥ƒØcΙ•KŽdgñ›’rvë—õeÛîûT2å|y úž±ä]<ä¹(Ö×Õ5™¹(8Ù±`E(H몘é$ÕlšBoãž1U¨²6Å0‡=|йvãpq-ÞŒ‰‘Ä$mΠ`nËs·icF~LˆÀ„+ödk“ÄD„;FÔT¦LR̆`®§×¡Ü¡±kX,9ä³ 612¡Œf!ø4„AINzCNä k˜aì¤Ã,ÃE1¦lRcψ‚ƒ; Ù€VÒ›ëèÓØÚi«{wϦÅÖµYëèN—wë§Å|õ&w[I Ë “cvÚ2®¶À£5]®=Ó¿yòŸ_¾ÜîŒÙ[úO—¯·}((3ý=3Róš; Ö hÌ€…GË©ŒŒéR÷6bŒ9nÍpy~ …ÇÞý$hì;2%ÓŽý>#÷™°ˆÖ,ÆÏDl°¶´ SlãÖ´Óðíòw‡wšÆ4d:±Mº7÷¹™™Ÿ^ž??}:ŸÛú¶½!ÐmYÁÌa—«sbL\ŸN/'Ÿd_>/ëš;öÄ}ØM]`b#®×­ÙëKëk£ºÈ”ŸOí|zr‡»·nîÍ:Ü@Sïy9ŸrÈ9Rù1lÿˆÔ}ö®1Ç ×åõùó6ÞsíÆœ3¸7WžO~:_.ç‹­œƒ#w¹ôIä¿ýéûßnûÚçÇ6g˜dþùêÞæxoçŽ{‹mº¸»`÷ûסmì#cßçHέÛp`‡Æù|r#mŽToëwŸ>öS³óù¬©ñö>¦OÀÛûm,}mŽu=EîÑ&”û®‰ÜÇÁÉyîþúzMíI„éSÀåzùþ»?Ü·2bØ}î9÷ð¥žW§3dÏÅ ”{ÖÔÖÕ"–H7ë–.«(ÌÌî ÂÜv3tÖÓŒIh¹ ˜ aL3?ˆ—YwúHX={&ž|ž»m1£ðŽÚ-¶\®Ÿ÷=F “Éèëj´œF$‡r(8‰;6´™Ó=iØËl[(çMûƒ>¤=1ã I‰9š# ¡eº¥Ã VIi¤ÝZÄDÈê^Æ%&挡Ìb“ &%O—vtÈ&9x\ÉR–È)#Y%¤Šì2-#ð\#•¯Ý !éY*ÉG‡Ž€ý -2à©â#ªR+DAd–‡2#ÄFº’ßl.‰s?-YšɨfÄ1ôPGÿ Ƥ2GU»Ü®á`È«AH–N;—ÕZ“ d=g1 ŽjêãÜ™µ$:ÞU®š›ð1•Xùý¨—e;|ܱ «[³_MW‡Ÿ¸|4"ëÄó WUæÅé­ÔäZ#‹p …'²ˆS†oKžÒĺØ·Þ_eŒ1Xä&¨Éå"º•³ Gñ L@Vq:Ò ƒ©#Üd 󀈷ʎ)ñØ]P×)-A¹ÉŽÂÁñjë¬ÞkŽà¬`§Ž±§´<–µn9¨Ð‡“hi2º™çÁ¯ÖUϼ±¶F›È§z»\.Ï‘‘s—‰è©Å¸Õ —f--ŒH¹ÃEgº¡«Ñм]Ž“È¢ã²œ™¢W+EY ´Q°±~è85‘!Lr¿Ó£ï[ÓÎb¢©«8š3@ëaQ¿¸ªû™¼H&”W Ž4r¤X,9§"ÓIªU`0)ÔòIHÖçn•i(*‰­>¹ÝØNËjn.¡™/ ­-ŽnKÃU멵¾Xó†l—Õ½-‹u®}岂ììvt~ÍSÈ–}¦Y'´¸ßnsXlóËÛÛýŸoä–ºM„_,Òb€8µSh#âܱ˜íÇ p†¥E¦¯»u8>¦6 ‘{Ž»rNÔŸÿ}„Àˆœ#‘c¨35Rȱ·¥ÿõOùúþ+bÊ€VÈYbæ˜3!ÅÌ™sŠð\Å•üË~0[¾¾¥iô®×çOj>F.l6aÀÉÇ}„èIÛ÷m9Úy}ÚÄ{L†!§Z»öËŒdÙÌ>_¯·ÈyGDN`Lo§õùÜèÑhX"n÷¹¿G ÷“õÌì¤5´ËrEs!ÙðË×±{ûîùóûoÃ,H±Ûb#5z_–³èÍ¿{}iÈD»í.óó‰­d ËÖ©™¾8ŒH*¦sXs ÝBs‹¤µ‘}Ylu3žÎgoÖ³h¡÷qcGþ¯¿=ÿô§ÿ±æÇ}óÛ}©“mm«NËùéóÇÛöq¿µ6ª˜¾}ì{?ÛŸþø7šœ§æv9·]9¶9¹ØfæÌgSË}dÊmfKÍýúr½.ëŒh§þ·OÏc‹¯ïsŸ÷·}ÎaOýÅÖ6ƾôuCîûM6OpRÍ ¥‚±H²7%,昽03†K;1Ö²î0[[¸Dºœ›fKkÑÝS+ZN0Ñœ“Ü3D·—ïØÇ¶S­î­D²Ý`Upeí4LPú"‹8r;iÀ¤}Ȇ,úôúºå–™šCÕ˜ƒaÊéû~Ëm”z¦x¾\ Û÷E’(Ðü˜@F(# (Á ŽÂh9EÌØ©ÙÌ]1ê+V#ÌŒ92 ¬qí 2h˜a»0„8ÅSÇWÞ eO0&#“° Cqj?$¹Òg&w‡'EÌúä²LÂÉœœô’53ˆB° D¯‘Æ(ÐcÅ£ZX›ãZS^ G‰õêëÛ×á<`¢n M©ƒjàùˆHÛ±¦Á‘'ÇáÝ9v‘õœž’eCƒÈ ZéдÒzÕ ©2Ûf…\¨-㱜âÁ­:ìÓVKŒ¬sR°¾„Ëuй/á<@áÕc$t‘ô­­ö èYÅþƒ?Žñ]ÔL„W0‘V±åß Ç]±:sîµÊ…‹ú&¼Áï.B¸y¯âÀ#Æ~PÌ=–=N‡æز²íÊ"F­­ÙÑw#ë—UZÐÐJ6Y¸–´¦9 tÉ‚$[©sÐü8ôzm§,U}É8¾wI™UõÍ]–%Yªã²`ÅgQɶ“GD®Ê—éÌf®PJJN'•v€£”G-ô¸x÷Qkéú…[ |Šp«¨WѸŠ"[ËB4˜yk5Ͱõjw¸y;dÏæ5È¡ò19-SnÆ}ÎŽ$Ãe¦j¨9µø4 È‹Í5Á6ÍYK.ƒÓZ †Ìt¥"Ë÷ÖÒÓ1ÒªY0™JU?åà×É8!·‡G •XeËõö´ŒƒõZ¿ÿ„ÌLæžyü<¶@ò”šµv4ÌÝ=¸¤(LWu.0“²Œ’-DÔ‚lp{k?½œ¶ÉÅÍ[[ÌOͼYo¾ Ó›KsÉ×ÕÓÈ´“µl4oì0¶ÕÚb&ZNî;ý‡×³Enî&)rYûéçû¿½}Õ}·¹Ú%5s^û·üíýí—=î9mĘ!†ZKŒô oœ$#§3’M–“ˆÜízÖ¸o{“"(…[˜ótz÷ɉ&àíýëÈLz9¿ >*í»å° ï—æ§œt×çÓ§§g³ñõ6çÎ765ÊÄûGPüë/_¶“cˆsisÌí¶Ñvm3o]ZW;­¾ßCx´œóçóÊæoï{óöò|¡\1×îœ*iÃiœÑºÿýó˗ݤéÝûj½õóe]úzº^Þ¿Üoƒì§ŽÓíýîœ×“½^'Í—Ö9çr¹àtæ6º”#æ×Ñ‘îë\uÜ¡~ŠT7üøé´ŸVLá·Ã<ÕÛ,ðHï½ýùûk3,žÑÐÎç×ça´‘ù·ÏÏ·ÄžþÏwüû/ÿ{Œ1€m0ûêŸÎ—Æùérý²}ýôt¹ÇŒ1D4±¡‰Xˆù§>u–Ûm¿ÿõO~¿½™Ò $Òú²|wöår]GdFÚ:m ³@Ȭ¹¿ÌmÿØ÷-ð^Oç1pÓ@,‚[ó~»¤Þšñéù‰F ^_..e2›+—TÏ-3àK5JËå`÷}Ü“çõ¬°Æ>Æ Î/Ss¦ZtyM™Üxê‹z z}§#-î·Yˆ.z;w;tI.²%dø4HÊ®k’¹H³–ð4s0™±àDÈ g7a9xXpf-W„¹¯çÖ÷ÛÛMd†$MDÎhiVß‹91!y¥vai><-|"""x=(¬†š8Ò ¨‰t‘<¾WB%èIÏ„—¶ˆ¿,©;j-<¸»]Q²d5+~ü²"+I“g•ìu¨k@J“%ÝàõÆ0Á|y~ú´´eŒ}äüûŸÿòÇ×—~¹v»5=è¼o1³o»~ùºÙ]à8-ëåšû6'½©»§ÑS\{wO¦²Ñƒi®µ//O'¦Z–þãŸiLËHC&ÑÍZ7—ë믹GóÅW÷ëùút^V÷û¶ýúõ}*œgkÏ×—{³ù|iíâ)øº´u=M·ØS÷kÄt?÷ãϹ§ch¥ÏçÏ}yÑ~ëkÏlËé”|þúönÒÒºS~ZÍÙè;Ø<­÷³¯/çóm¿ÝÞÛvŸ_ïc].ëå1·1ȧSo3ïOëz}zýÇ?þúåíë;èRŒ9,rZƒuÐZ6Òºaém 3÷1F¦x³Þ5¶y:ûòÝÚliíÓóÕmm™·–­û‰z—´o±åfôï_Ÿo·¸Í ¸ô5Ôd\O]ãéÔÚÏøyÜÆ6gšzïûè™VÔÞÈiÖ=©@ðñyŠ™!ºÏ è-Òm–§×ëÓÔÖɶ‚ oþýw?l±gî”g6¦Ï²N<§œh½¿¾þaßGê¶9›hwZá™:\V4§TÀÌ­° ´´‰0Û),½}>/÷HMk9{_š3w«þlíø—?ÿóí—©‘1C3+.«K˜1‘3ʳ!$¦I©–ÄÞ„°˜È©Ø•è*al0ÈDL)%³LÙû×/¬jÁ˜ÄðÝŽ@5Æ$ YB‘[†ÐÖÌGzZ÷5’ž²)™Ò©„£lg‰Y’VUS‘*;H2R…€I<ÐRµVpeh‚Vˆ†R2 $ÜŒ~pÙ‡¼îá ñÚUŠ™Ào·Az…‰ÍÌ,I¢Ü¾xœ •,™YŠ’Vë•oõ,]†‘–õ}|([²tu^4L›xØUÙjó±À›G6ÛÀ !¨ãtÍ4xÿñéåëÛØvjh·œ ð´Ë6(Å9g„Ô–Ë6wöé&Oß[[40 swB»Õk2s™‘ij™íi3áaéÈé -œméŸ_/_¶°}Âl2ÿþ‡Ÿ¶ûûËóËý¾K{Ã9í´í×÷/̘1Þ·m¦¿üðyÜ~m=¹‹×§õéz l>nyŸÁäõùäíSºOÍ;ÂsŠˆXÖþúüùårýúþörY¥ù1c×G7w[ÂÊëÑÏýœîÎËzzôvn8?·“#§&ùeÆÞÙèÞÒoÛÆ²›s›ÑÜ%¬ëiÙ0/‹…±Ùr Íšëä}Ö2†’çók[O÷û¸û¶Ȱöo˜mB½cA³ "šÜR¾.M1o÷ é9Ô—TdºÆ¸žOwL_lùùÏçËé×_~Qȳ Ÿ‰Æè–‹'Иº&Ì[6çWùrYoï–’ï™8_¾óì] x߯ÞÆî}Y×ó>o(=3!X,–~Ç}#CíÇëk´æMîüñõóv{n¶Qϧ¾wªÍÁ}?í6åþºž??]’míOûîHkÊI4kÖm1r+Oû8¹¨ÞÞ¼³ÙŒa`ç0ÛÔØ2å]òüH5A1yQ45#rF¨7ÆÇüðižw5µ¹Ò@óxúmÌô²0´éçËËÒ8ÇþcF]@Z=–n÷¨ëÅ9¥_¿þ6î#T#‰Lʤ%‘;m$fš2X@ò‰™éʤÆnˆ¹¶àÊ^ÀutK j ZBsŽƒ1TèRÚh"9 ³#QçÚz´a9A›`ÔŽä”ãL–d¯êq,C‹×L3o檣}ûp–0!¤1%Ã¨âµÆ¢‡ËËWs$s •-¥Nj’Z½¬í@Ö_IÕ|öp煮QY&:癎(· r¨úôÕÄ'2+ R쪅•]îà*àeǹPµ¦)ÆCÝæä)ÈÌ¢â=v 9qø—©º¹åÃõ›v¬wZ2){¨uò!–Xó=bW…ÕôGÒ· ãu̪e>Žƒ´–sßx­ãw ¡=”8‡Dn¾<4‚(ëµÑ`tVƉ‡û€˜\ß´„G*ˈþ¸ »ÈNÒÍ 2ôê¨#WV ú7šJWWs‰Ìí0¥d¸$ËãG¬* å-FTÁñ-ë|›dm 㸔âHdAAÈë:Š¢EýÊÐy‘ñ¢–„ÿ[28 NãCçG2CžYK£yŽpèê2d¹,½R‚f4<6º0¹Ahtw‰4w7[<$£ZAÕ›4“Ùª•D0eA‡,àÉfG‚\äTzE‘È0øHFÀ$[“µÆ9`¢~L‹†iDöÔùtÉR‚†ƒ%ÂGvd‹˜ïÕb3Ìë¼Z#¬Ó|äSÆL¶U5y÷ãOÑijJ˜¡NÔéÝeéG»D®ž'HkF:mé§ÞN°Îf„›³Yom¹œœlÐ)“FœWãÅó;¸fh¾œ;—§§Ï—Óó=ßI¾x;+ýÜNß®sè¶½½¼G†Û¢—§•Þ)O|òuÎ9²býÇO?ß>¾`Œ Öå¹uË}#<—{†!ÓFsî³ål€4g”ÞrÀ–É ì»,Kæ²}¼ßæn*k$Ø·ûv4ás&Ûòé6wÌ6â½àAuüÈÔíí-æ 6ÙºiuÓ³Wªk¥]Ö¾ßî·=ê="5r¹Ý6D6¯C7ƒ!ȶúÂÖ~ûõ—¹Ý‡"2Fàpµ\ξ,ç-g³ÆLEìëÉ×…Û}ì3çû°9âù麮ír½|þü—ûöæ­e2å9éŽn¬1¶Ûír}>õedš[÷åûu¡”Z?]N •û×{°_ßn1ß^—Öc3žtmíåûÛ쾎?>›·ÅèOkh_ÞïÉØFs _NÀõzyù¸ÏëùúÇ?ÿÚröÓ¹­ÝçÜ,néŽDŒÒŒ586sO--]ŒXZ îocæÜrn÷M3`†çÇötÒæ£L0ÙFÏ(ËÞw/-ͬÍû>s·P#[ë ‹ÛmÛc~¼ýçÛ6gì©÷‘é#1ÔÉìëz==_ŸŸ?>>æîm=͸§6ë":MÆ ­H§ys5'›!¯íTêÜÞAe6Z‚Òi=Å»&³žÉ¥bcÉ”;g&B»h ›ÁÁÝŽŒl Y¡A ?­çóE3Ãä°ór¹GRN)dìæ®qßvR ¹, sÎŒ4EA>€”Y6zæ6-lÎj:_N=˜ûG6iDD6É2kžbB²Ð s•Ä+ÑÃ[æ°Jyç  ›žŒ’ÆXº©É™œH£Œ––if³U¬‚Çщ®Øê1^$% ž¦a;r;”ÌT!µný§çÓÛ>#&$¥µ51y¬½¦Z§ÓTxÈÇ ŠÁ×7ÚXäáu•Á•T%;e(69UC— KKcÅ`óƒ™UÀ¹ˆ‚Ç2Çñp²´<µ ‹£b¨BU`ÛT©&Ä LdS+¿Êô´hY˜¥¿ƒ®šÊì'-ê59äàj’sTnÒÒÍÊq(ˆ& pò­T¯ƒÒN;\†È¢ÔPðû‹RWÅÃÇ}LTù·ùmÜ:v\¥ {[Lãƒã^¾_¯×®äq¿åAÕ¶ZmµB9ÔÜÙ2é„7QòC«˜¿&fnŽæV/P½m ÄÂÁÌP–X½`k•Éþ–Hgà¡W&³¨p…Ö-Üx½¢«¾dŠ;PÎz2½⨖¦4óaVyõD=œñ‘9×_ ¦ÉiJ`¶ú«Rx¬%¦³â÷™unfFë)OÊÕ“¡p0èN.™4§¥-LëÍ%34 E“—·6“FÊö†&‡Œæb´ƒŒÒ,ÈYS,F"LAÔfI$bN)40£-‹lk8ô€? ¶6Ѭµ³¹õnî§?¼|úü²¾ß£™úéDD}*Úª§õ%ÓÙìÔû©µËS{½^¾¼ Ó×üøé6F„èX—•†Ó9®—ËõúiÛ¸ 4Í›–¶¬ß£¯§¿ÿë÷öST<å„¥y&é_Ç)DX(­@çå¥)ÇE+XµÑÙ7n]¶8‹›T]&sðˆZÕêKµòȨsàL%uðIU޵T˜Šùb Èx9dÑ –^SR¥è¿åµu¬·RÝ ])¸µàÛé°óEÐóÿcê]š$ÉŽ,½sTõš™?⑯z ÓÍ!gØn($×䎞K®(¤° •™áîf÷ª*jž@­S²22<ÒÔTÏù>†ÒqL@ ¹ÇâÓYwÕýWÒ•±cœìnš©[“$é²?—+9¶ C(wÎŽŠdÁ\÷W‹"^ÔJ§0Nõbe­xÇ‹Ãw«3¾%²Šüôw{žšÎâ2ì› Ëå í. dÅÂ…eè31Iݧ ­C{‰F¡rf—O a‰&Zëý=¡EÈ4]XÄÿ¨KHxÒÅ C?’^TPÉÂ{ª÷Â_’„‡„d"]*eV£-%vȹ$"ëe!@zjhîÀú¨‹aFR‚U7`Ý¿ V§RÁØ9lBa«¯å‹Ø¹§}I šHÎ š)–t¢!*§iQÏ€Z¦&Mk]ÛTP¦™¢ŠVçe­¿¼â’¢EÌ•h¡©ŒúàŠ2Q@·eš›I„K’LRµè"5µÔ±®.«™JQh‚Ö’Á$º#Ó3„"A Q¥ æ>·îÛªM`_J  BöþñÆ5·úAÍ`Rò­0%QPHOmu¼Om•-cé“J%©JÝúˆLЉLa“6/çç§ï2 Åœ“³È6‹56`B™õt>YSOŸÂ±´e>¢Ç[÷yR=œœ!n©êdbÜbëƒÜâºÞ:3<%c §w×M5œRÆa΀úš>ZèÓwÏ2®ëÃQU…ܞ·m[°ãór`ƒÎä6¸¹o—Þ×ô4WH£«Îµ–­¯<\¡¢ô4Ó IuuOÓëíÒÇJBF _Ñ‘XÚЏm7éq Ä9 c½ýù××­ß„±®Ît™f¶)š¾šZëFX¦MÓršÛq9ÒL†È·u€³ê, ©`³.¦Ë2+*s‹ÇÇóÿüßþðË×u뙎ðÛ6ú2/1묤ho<Žò›Ÿ~¼Ýº=Ô­™MM$*Ü~þú¹#>-Æp±fè/×ÑÚižæã#C=V¸:ÖèzP»\z?OF¶óÃq½ŒáˆíöDÿú²­[ÔÇ|¶)ô œƒWÍÕN'i\z¬IÍfL@ݰ%ä0·ÿá?}øÿþ¶®½§#±‘nú°L Jꘌ¦)Йb†åxüþÇ¿]¯¾^¶‘Ñé3އO‡ù´ÐÍÛ÷á9¹ äp<=?|\o›çD(G6¸ùÈõr»!Î%Ì•MYB (ª˜£Mó4?FzƒYeÀ=Èô)$ÑîÚ%à„÷p±H4ˆr®{”zˆÑBC'2(jÉpx]YÛ‘TG¤‡B&:>˜O£ËÕ=C¥¼ˆ4!©Sfæ‹ÌÎm¸ ;Ò¹“¦z’C¾ué8¸®oc[‘ˆÈLÄC2Tc»Bó¤Jl¡YW¼ºÌŠ;M廟~÷úö‹ø€”j*(á{Úë´UïéT=.ËóÃñ²]3ÙˆQ_u™„cr#C¡ÈlÂH*àdl„!ð¤FɈ=öÅGFVj€#å[CM#]“¥Öe’'n:•"άÙh™èICr§Qe÷ë-CöÔxRöÐÑžäÆ®&¾G—ŠAX–Þ;j€Ø!Z)Á¿í*¾Ÿ)* »óÜC(%*Ê&H©t9©TW±±ú„ uÝJ^[Ù„Zw!4vtUQ(EeOá®²Ž¬ŽaÞ™^õB¸#S‘ÁŒ`8”ã:ý^”Ä?ÀÛ jZÏ?RµöqøÎÒ¢ïÿÔ©~@eG»W90U¸çÅ”,°*¬j˜¤hµçX:(*Ьd¨" †Ô5Uêh*VOÂ).Ì”º ïÕìžÝëõQ$˯D2}¿ÖíÈê<бԱï3ƒ‚T·ÜIî%)üïT☽À_Š$ÐSá*Ô¢Mìѳ°Äàõ7LjIʪ̪•=¯ÁJjÝ ÓaªËHNLj„U„ªEXB¡z¹¿ŠTR¹§S)5 ¹hXŠJSÅS"1f2z U†'[š0Rè)ÙèÊÉ0QÜ—Çé‘1$Ɉì½úÙpö¥PRDEuhMûQÓæØÝEÌ9‚0¥… ’‘ÐØU4¯¶¦á^(—}ËH‘„éLiÕå‘&AR+.ÆÒÛµeVA€°Ê:LªÂ֦Ȧ3™‚©M˜øôøîÇ~3OðÜ‚†éýÓéë:lót||ê©@˜'mib­:ŸÏ“µëõÖ ‰áÄÈ€÷1I%e[GŠ>"¶ Ã1¨Â1â|˜olÛ¶®=n[ʺ^ýêéc^¬»N)× ‹HFC&•³eWEU ”ªúº˜<-¶LÚŽNÊÃñѽw_›.Ï?ÜÖ‹‡©».ÓÃó»—×ÏÛÛkªyŠÓ¦ù|>oëÛÚÇlÒL"ât<l†ó¡½{~R³×ËeÝné“ΦM+r‘!‘a´ÃÃÙµ-K$FÒˆÓ²<>>ZüÅ‘"ËÖù·Û:/·õ2"…}ž(ÖÐ9·y #¸L"‚·Ë6k[ÓÇv ¶Öì·?~/§C|ŒËá¾.‹ÛÔ>¿9€-˜aÏO§ðÄØÎËÓrnÚ¶ÓºáºÝ«MžpN¦Œ¯/›Ê|>¦ã=»A»šèð˜ˆ*æ6›o iËÌÿãß>ýŸÿÏgÑ “dŠ9HKðÏ]ûÉè ¶e é‘2ññ|êݘ§œ–‡í6þúõ—×Û¥oêN]æ16ß.yœ÷èžœV¬I2róËXûp[RUxdP¾??Í9å|ð1Ä-)!0´²Œ2á¸`~Zf›Xy÷)Áˆ#£÷BV[ÐMC†!› ÅŒS9Ø#“ÜFRÒrd'ºHæ€xB5]vÌ`£L‹{ô¼rò}‡”€¸jRRºˆÂ>üøÝõí’¥4Hfl£²Ýc«PäHOO¸§Çš-‡;‚H`ÈáO ¦Š¤Î6‰°Ã!áš.*h›í >¿½~Ex²Û·ü32à5±ˆ€tØ^:BævÛ6TeTª|†Á®ÊNÄRãBÈ0• ×ýîC âÌH-à™*J©KMA#“RN&‰ª¿‡hV—åxz|':‰PØÄf³¹M&ÖþðÛþŸþÇm󇱽¬ÛfúxŽI=o½[¢å‘©Èezÿ°¼^·lSôíº9¢'F Û–áÙ»ˆ§7b(;e¼8|¹]GÝ/ºË”¾½!»{¿u÷Þ£¯c\¼¶Ñ[®ÌŒ¸eˆøyBÃ4ÝBœƒ%‰Ìï?}×»÷ÑgëÖBέÿËûåóæëŠ<™úð>b¸Ïbüü6’.ÓøÛÛ×íFOÂë>Bºaޱ2!Ëlië<ÎïÛ´¬ëšÓ|x|úx½õ±ú|´óéìÓA›ÛÔM4•"9‡ˆB,W±68M=Æm»Eäñôt8œç)3]Zdj_×å¨ÿöýóÏ·Qa`Umm¶üÃÿë¿ÿå—uܼg¦ÑÕ´½7ŸæÃåJ4 EÛ¢v[.:ELmø¬rÝ6n[˜7;.-:›bâ2=½­—íµØØòøô¼¾¼ÍÍSf¤ŠXkËÃñôñƒüõok69œ¬Çúýß_·ô‰úi9_º›µó2χóñt8ž_^]¢S£-6kûøáýÚ/‘W=Èqi1.CºF’â—×ëæcžÕ0¡„ç6Ìsë[ß"„[f{¸„Y°q£gkG1Ùr@fEóÐÅžžÞç¾ÎÇóm[#º*B“t`RªŠ Í¥?¶&ÂkĤқǚð”‘{â4 ÌPº)9íïL*€‹©ºzs­"6bË–&áaÂBŒ).‘Ò®·Þû&©s )±€0J’Ê5ÉŽÕÇåíkß6ÉÈðM¢{¥q{V0Dº@Ìâ•üjá 10R-ŽOÒÀ ’ª6„`†pÒI¡5#S†1qX¡­H j¦H8;6Jîw¥®Lfê0a—¤–)M¦d *"¢,ož(z:h¥$R‰\¤ŠdìœÍ;qŸd9)’"9D” '3á;ƒrÏcË^Õ -`NÖ ±+w$³Ø@’je騉 åwQ+†;Uª¦ä.0*B¨T± uÒ+ÐSÞqšÐ t[ÝN­”¹›þ¤.Áû½÷ãÜþ{î×Íš{*F¾“ªª–̰`à¾03 ‹kJV@Nï¨{¯¬ N¡™õWâÚ÷|¾—jN(ˆ&wÑŽ<Ý•0ºïUv»Íý"i¶ðï÷Ê¢ ÕŠº);ô´Z…Y î}ÑÞ])Y”F ]¦Ö‡ª Å UIæ’E!ËÝÛ]¼·¢J2QÔ%¹kËe„ýÙ™¯‘ûv*p ˜±÷ ØkáWÀÓÜ‘s]“Òl'ƒõ v`%ŸÙ+†’*ÜKÜuˆ ÓZ@q (rTFÛîx ©›á>>"ÍëÆÊVä·„¢’J*ePÍ% µÝu‘ªuÔúÜ ›0$—¿iJº%)¶/áYçÓPÒC\Ѩ*„3¡‘–"éõ>%I¯@Wž›À3‘!ÈF$$do_–53ë•D¥ |Ååt*wä½j*¥Išª‘-¦5^g0½I²éta`(A  ™duO5BÍ>‚A‘)¬!T' &šM¢ÇÔÑNËytt²Í¢Ì‡§>ý>¢C´5SÑY”ç6=?ýðôü|[ãõu›åá?ÿë¿üú—ÏAÈÜ79$a[w}ó—-†ô~}㸤¯kŒ‘Ö©ª+oH—¼ÀÔõëzéð¨TZ‚›ú4àÑ=zÊP„˜Ghƒ\šêÞ±ED:AÄ,ŠMãl?<´——/[ß a2ˆ|~w¢ø_^®[/M‡¢{lK–8F¿]ãæòþ8Ï ×ëIš)*,èÊáq5% Ú”9"’ÓÔ<1ú ÌðôAqôôæEýép8žNÇyõ-3¨â•ŸKMä¢`#Ä&ƒ³K³©MfB&1 ­åtü ó¡ÒkÔéãÇ®—ÛŸ?ÿ5–. “%©y8ŸçóÖ_雩d¦l>yœØ9ƃ¡C=Âéu¢¿K`ª (Y©Å$‡&ÕÜw¨¸äpj“Ú4åèâ¹'en©ÑD@Dk LCUNW3a£H„´y¸@é2*,cÂh©îû©Ø™5/$&)РxÙh¤#BH“¾JzÜs@Le†SP}§ ¡«YÆ7LCìx¦,ÅGa˜¤"Û±ç±ÛfêJ[’”{‹«F«D•wK²hR ˆÐvuµ0¡å†-Êëé®ßª&ŽŒÜ Š (¹teø`õë[–¿'îkWÚ6dz‘G÷¿¸;u2¬ó‹ Ù1•úš• Œ¦€Sj‚Ô ¯1eOOEú®* o¢ØI)Šƒø]mX2—ýª(Ø÷4uÝ÷Zõa6[¾!_©u *Xù7 b="S "™jßFÈ>m¥”fßé€;‘ ¶dR³š1%aU€„ÞÂ]ð·Óä#=²ß¿²ðà{tJ%Ó+s7àYËÁŒÌj V6-2Š€YÐk©NàþÅÉ®-º ¤‘´oû©J}Q2Ôî£{?SZ «¶¨[iJ¡(jí‹YÜÖSDR%´ÚB;¡’÷5fõ2ªã:ïBs¡áM¦yR%Àh®ÆÂ²R 03Œ]2Eá¾G(”6ŠŠ¤„/[Yx"‡"!Œ0‹ŒÃÓÈðìíEÙ¥Mõ!‘@(h;¶ý®ŸÀ>ˆÓ–6™Ö˜41&áõÊ kÀÀDh2¼`ÐB‰DmëS“åû#SØPhfš9ËA ÿîÿúù—÷nAÜ21c|<Ë×uã$£#‡Í³ôÎ\•#©‘ݙ۬Öé:(S4YÁ4IÈf²I:4N3¿¼]=s»Ef§êÓñü›Ÿ~̾½^·.ˆ!ãºr ÍÃD$/oÙ÷p!000tâb­)‚’ÞmPUÚršÒp£¨ÈÈ·[3! Óu’}¸¦d&&á´<ÎóÃo~üðëÛ‹ÈôáãGxL”œlùpœŽ‡Ç§÷Ÿ~øîûÇOÓqB`ë—ÖÖãÁÖí<+žÎÍ==fOßoþêÝ)Ö·5Õ¾{ÿÃz» oJYÚt><þ7¿ÿéËKlëÛv»8Å}VXdº»‰ÚÔ¶®çÃ4ëévAÊåùÜŽs»-»¿]b Œ>ˆqÒþúm9¦*å«Ú†ô˜›µ?¾?JÓŸ_Æíº%úá0}½l¯×¯1üp<™h6³±Iú,€hýå:<É5rx”5æIއӦè×klõ =ŽmÛ¢ßVш#·×õºÝ$WêÖ” SuiÓ".Ÿžßî±YWâùñü6x‰Uç´<4QÒÚvÏÚ¶ÈIO`BužÚóù¸Þ"Æäâ»{×rÄÈ1jCl ZzïŽäÈPˆ„¥ˆHÆ ¡ ÝÅÓ}h² †_ûëí¶]/o±…ˆ<«GˆÎzøôÓ?½­W_E4hj:U¦YD1»è™Ðª 3ÌÕT3½÷ð[‚Ø<,"#%™H£‘™’ê#Ó#+Ÿ„‘HOEF°öúdV÷$*tn…4§H РM’Ñ¡"C"4 ĵգ+‰¤¨Yoa9EZx €bá¡¡tÙ͸M©ªÚdÐ$„.BD¤Tùsx©ï’†$àšÂÁˆ¹Íï?ü´¾} !StßhXÔ*©Šv¢úþñ!0zïAßãÄŒ ‰òñj±;g䎴Ü{"TB¬šåBÃ=ß -̇•õ­àÒ;©&“ýÙ¯"ZŽDîLÍêÖŃ æº”p†£Ê¡ÅšÎo OB"R©áܳæä=µ\º/E¬¸I…¬–o-sfªÔ”%VænØÉlí¾·â(ˆ²&„ŠÿWj¥rg…2ø–âßL÷ö<R͸[kPÝÆW“@x÷)êNÔ,¦ÃN¸£Y!@m_äÞ +ÔNÏ/ä¹Ü•†ûo¾ƒµ2²fņ`bU%^"–bšeÉœ„‚Žf}m5¶WÞO:ï zÞCiô²d1 IIOf HA¯³mbª:m0ÒêtYgPwõ¹´{îO°{´ ÒŸ¤@¤ôI»°Ð-´ðü!`H¨úAD#%(Œ@NRDVµÝ½CK M‘})­!%§°* îÙ0΢ÊF± ‡v?Se—šVmˆ(a'<2ý?©JDTc#™â…FΊNzA¼ÈÚ³Öb±:¾ 4H³ý< ! Iƒ‰0Å÷éTTdL#Ê•ÃÀ,›<ªEUd ë_Ö!Ò¬R§FÓ©>b4å¯×Í!¡‘ÒÚ^%mÊô6š´f"ólÖÚl€Ë-ú—ë5¶8Ÿ¬ßúñîÜ^×X%Äfx>= ÛÀU‡£˜np‹Ñ![f÷î}ëûü' ·ó²\®áhª_Þ®Ýaëè mzœŸ¦ãð¯Á5r¨¨†·PܷѧðèÛy²Lf„pLôv˜\7¿ùu¯+üÝùò·¿dÆ4óùñDZ]o—ׯÂÊØ™Ñ–åõ6¶5Ò3bЬّúÏ?¾öÁÌS“fȤ’D˜QfQ1Š›©*-¢ô¹F±Ó†(¦iUúÝýIÔ”¨?&ª÷PuÊ¿CB4¢ 2•¡p¢ ÕrT¢˜'b©"è H„B:DÄ J3QN DØ fz|÷ÌÁ¦B‘¦S”¢¤6™§Çã"ˆùá|yV(—ÙsÊì_GŒáé­#g G“°œÎ·õë[ÜúðÌL Â"<Ñɵ÷ιÝÜû˜ÛiZìzy»nDçé¸õMü†l`†öó©=^¾~é‘Çi9¤Íz<<½^^ÝÍuLíÛÍÓó=¯žÔç·ëm]_Gßzæ—_~xká£_¶K¿]–5èÑ™*p§hÆÚ%‘’"cÒ©©Sç¹µó©…€‘çÓ4"†7ì9˜8žŽá3ËT]H%rZ¬õáÌÑ„­-Ÿ>¼ŸôAE^Ö×ëË«Ûd½ÇC{7É×uj¿ÿñÓéáñÇŸþÃÿú¿èË¥ìtš·Û›P.#Õiù®oeξ ÙæÓa9œrZ¯ãÃùðáùÜtî[¦ã<Ý®ý:.³EªjM†‰ðt||˜óòí+×þÚô6áy:.§óù‡OÏùù—/¿~]$&]½Ik·k·ããá4q]m&Ñ3dnBek© ƒ…P1T¢{þþ»ÿøúùíûwó/_o)mž´=Ñ)ëš#´ÇŽoX?=L½KO‰V&ZÕãÃÑ·ÍÝݹ¥Ò²»3ÀÞ3zÈ*ƒ`ZÃy–mˆShsx£Î-“Pdr¬df¦F¦ûð5dC¶¨‹M*¥¥lIUŽŠ‡ŽQx÷="[‰ eÒ]Ê ®®Ã$$ÓK¾ë¶¢r1"²L“1{ÐS ™óòr439Ðr›BƒÒ”-aÉìÌ2†CV¦0r4d'=Ì„T%`Tuu•hÙ&:ЇHƒ9ÈIÆÊ ï9ÙÚwã]ÈÈsd à1 ÏÇcx&¼F$‚9,%á’‰dŒ1}O3!s”W {Z·”„Q$²Èv JJˆÊ‡÷ïÇpb&“£H½ØBQé!A¶bouïô-f­Y´tZ ‘Zž9Uï˜úíîŒQÉ+L/yM"Bœ‘²_>™¨üNAGT§`Ô¶ƒßêŒÖeêÎ3ßOf5rG+X¨ð"iÁÂwI÷õL©cv—q9…%ï Î"¼ ÊN]Ëž}¹“û¦ÖŽûÉ”õP’RþTükç¸ …V=LÞ?á ²Ž¯ØCw5D²*mj$D¼¯„¹¯Ó É:Ñï6âZáâé 0$K'–Ðb £Bj÷Ã.½¶tEÑϽ?X{»}².’«£J XVK3« P|Tº¬ª5ÔU¨^÷Õaîô¬Ú3æ^êØ×c5 ÈlÅÙ,?«]D! Õ fBÄvIøCDÉ€Bk¥Výe’´ØK¦IeUd RŪp­bqª*! «ZªrAEõ ÉUo#é©©#%½ ­I¯Lf¢0,$“B¢í[­ ™1’ºãýk,W¨S R|ÆšW™@kùX8–$ÝIÉÒšhŽÝ»­>m"Ù„jÈ4°…$D¬Y›T¦F …**†fÙT1Mª­5M61“-ÊL‰IÒNÏßed rr¥‡§™›ó8å[ý:¶DF ™«²ëäÉ«x¸ûæÙÓéýo~úõ/~{yÓ®ólχãËzµî²tꆗÛ7×\û–:7Ul·¸l¾¥g0† Ã3&I(FïÈÑæyë/——¯ëå>nÝc¤¯[xöF“[Fn}óŒTÆÒ8:¢‰=<äžÞ b‹ˆ§ÆešÍôõÖÁŒÈmË7¡px²eã2›DÓ6E2õ¹*;í°LçÓûwÏÏo¯7ïë^çÀŠ<ýðþ) k„Q÷ñÓ€NóÃmµË—8-öåó—//ÞÅr“DÛ\ûX‡Öx8ͧóÃ6ÄtJÀàŸNØÚíÚÞ^·yÞn7OÌh׃Šg¿mùðî§Ëê”q>Ÿ´)ÇÛažó¯?ÿ¹w²§ªÓ¦?üø»ëúëëe@F=>b„-.yã6ÆÜ{~úp>Ÿ¦—·ÍdÞ²¿^_ûHÑ&.ýéÝãõóK›ä|<œžæÃaºnëð‘PfY8¡¯_½ëy»"3ú­[&äЂ”¡f§®Â9÷¯þÖûMLL¸®øÓÏ_¿üüš¾®îW÷¾Žëºõ'¹z1µF³é0GjäÕ…<Éòth·Ì¹~y_û-˜t<´invÙl½æŽAÑÆÈ@„h*•D¶ðX·Xc8ˆA ¢g+).\u7ñŠ Ú½…nI ‹sÂiÄØ,:l‹1ˆ²rih&òЖImóŠi†šlÉðZµWá?%Ô½àˆh‘¬UGÑICB$«½Ï09ð ÂÖƒÉs£Ð(óñáãÇß®Û5†›4ω)Ì’TPÑö‹ BDà.¬îvõî©Òàš iÒ˜!êÈ¡¦,9FGŠoÎû} ^…÷á 8±:RHuqI/X‡w AÖ“E" j0b ÝOI!VÇ¢{=.8Uz½sLϵoî¾xBög¶ÖÃ|¿ÓÄ."Ie]J‡ X(ÚTH6DÔ‚KxWèÕd½¯0"3<’Q[4tÂu_yf¤P<ª¯ˆpá™û#Z!;)ën`®¦àŽI¢ä}¾ýA•Ð,•4¡{,ﮚJðï2"î÷ûžJ“NÙsøYâûÂfÏÙƒŽbÇtT›;KRv· Ü7euFœ¤„>5#Rvñ5‹X”$™±›tï:Ê®Î}"TIE±,Të4PD…$5­*jܯ•EN`¨)y‡§f Dîüé5–‚^ÿÜLtÔØäYi¸ú|»Rû§ô¾¶’{!0c'a_ví‹¿ØG@ˆÖ¡¶¡n~ûM”„ S©c¡I4¢A„©©’¢é¾ïþÊLŠ ­ ¤ „œ-†›…Êc‡Æ ­8ú „Q§$µØ!´êSTRhÍC"F $JŽi>‰ÎcÜ;½VXÖwáûŒ_Ï öfo„‚hI¤C 5Y9>¤©­Ð½šU Q“Bq-áN}ØêØk ²DÓ3°¡’âjT­ ª6KÎ*“NœUB„ܘjf¤ª™èÌÖœ[fBlã¡u‘غwä­…˜éQS]Da<$fΜèçcöÈ-DFfôLJi²ùö¶uëÈA—©±ÁÓG®o_~¹·ÌñpXùrýù6H†Ãf_Ñ7¶Ö–Èë6"5ÏÓ‰£Á©2>=Ì}Ýlžþóúã¿ÿùÿÝÆ:|dŽq[=®ý†T=šöžÃtDXÚæù”ô ÛÓqYîW6ÑýŒ.ÐXZ;OO½{"í8ÇðžÛÖ­$l‡E¨2Vzr‚}úá»ëeˆðŸ¾ûôù­g„)§ *Jz›ç§?ÿüçëíe6›Žúøð87™ëÖÏçÙn]þöåËr8¾ûôéOÿþóíòº^ßÐÎ_ß¾tZ¦sF8Ù‡&‚“áx\Þ÷MR¦ÀHª6>øñv¹FŒ—·>Æzí_û¶nkÄŠÛ:m×ÍšÛrxúãïÿô××-./¿^ב™¹ŽkF"×_¿¼´iyÿô±_oóÌçóCøíº®ª8L6¿ƒÆó¢ËéðáÃùz¹ß>li’6&™ßÖm1¼?|òõr×óÁ¤ÉÛ+~÷“ýüËí6Zø„l\"B¯×#§6™,B‹Á©5[Ú4üúô°LÚ6ÜÎçóazw_¾¾šê|øx\Ž}dŒfóñŸÎo9ÖktS‰„«‰NíÐïηõê[±„\bšI[2Fg¯b™ é ‰kd V†9Üc³žÁH¸3HgpYÂ\3ÇÈLÕÀÌ‚1—5Íå¨=jIek#hÊ݇@NÓ*}[#¼M/„!-…®™YR;H†A™U—ƒG…Ò`Z£r1+j‰JrqW ©JfÖP…”TEÉ«‚Q¹Æºf› éž-!:AK¦¥Œ¢¥PAãÎcà ÒÄUã«=DŠÀ% 9ÕiOQæ'ª6Ak=KŒ&ªá!žåÊH'v^®¨×po{EU„5‚Ùˆ 5K+Y;ËIgû€•­ŠFd‰†RF‰Åb¯á r€T†¯^‚Y˜Ñ1!E¢fT1k j 13Úmii<Ž6ÏOTè$J3åòôøqjdЦ£pQ¶6ëÔÆp9LÓQe~^h™\1ÆåBüÑÚëõÇù°më­ßR6õðÜn#FzŒØC[ÜÔ¶§y‰Œëm-¼ëòxzî}e Öái«0ƒ46v¥òááôz½¬×M ¤ÿé/Þâ²y¤ožçæ¹zO[§wEnÛ¤ôŒÃ´è´„÷ŒÛmKÏ[8}(UFvÒ¦Y™1-¸ô¦|~~OÓÜúŠž‹!_Wd“mHJêÏ/ÝLišÇÉRÇqyß{~y{»­7šÈ|‚XÈpøÍoþååv¹^ÄfÛn—··—íö¢°Ÿ¿¼ìåLJö:|¸Ø”Þ9ۦ×ÞÇmáaÀΑ—·WÚBµõv5[L/·µ×÷¯'Æ6F23r™ìãÃáåvYß¶íê›?><\o+äí_~÷¦cÛΧÜ\!Üâ[[½Î¶]¯b0›~ýúëºmÒ¨r°ä¡E/·kv;NÿöϳÛ_¿\×mÛF<|ýu»]Mno¤¯ñz½apžô?þî»§ór\û×È.h) ÌÉÞóÒØ~ûÃw?žñ§Ÿ¹ô›¦g}Ì—Ëö¶½ERgýîÝGÏñz¦~<Îïžš¦Á¶ðÉ&c"ûÇ÷Ï¿ÿýïþ·ÿýŸÿúeÚº~}½x`½å»'Û’›µhMÑñéÃûyæ‹zßÒtKÀ1!ót:˜…oCÔ[k"æ’žÒ21˜)!ž]Ü ¹%=Àšô=z\âróH.r˜trHL UÙAÌØŒ‘@dx š=¼y{Ën<Ÿ?¥{§‹|ŒÑ‹Ý·+±Ÿ¡2¹š"©‚’õÞ˜Ô!#vq0=*4¤ªÈòâ)Ê¿¯ºåÿgêMš$Év,½s\U|ˆ1§7¿ê*¡H³½çÿ_rKîZHiVõ«"ÂÝÍLUïÀÔ²j“’’éîaî Îù¾6Íw Tˆ‰™(}ÂÎA¤ͤ*3Ó“˜“™i® NT9$ÃaÝVïµbwîÈ›èŠ {&z'cáÜ@¦þùóù·Û:"ꉊR왣D²ÈzÎý¾ºâ^Ac<àè^Ä«¨; NDªP žšBQ±”Š#'à¡¢ºÛ{ïœIùOùrÖ÷…Y&õ′'×ç˜!ŠyUèÉ¢ÇÖ$–ä®LH]áZ*Q_˜Ú˜hq*ÙµÛ‚ö@}aE*xB«Äx…ÎV ²žò¸ß¼@-QCSöÒžÂÎ-VÒ;k‡<ñ~9¼ksRMYH­âŸÔᬂÅÕ:‚%yƒÖÄ"T£Å{Å»Z)ŠŽªu!sB¨ûzHP»—û/) š å>îÄ^ÇÛƒº"±ÖW™¨`v Dk»˜ϬÉó>{‘ý^€ìy)JÒ÷üx/4÷ä^ZµX‰jËî/…r_SI·35k?U*¢Èõ£¢|"¨ÿ,dVý5Ùuï,åÁX+,—BFA˜…žá^ç„dÓŠö£MÞ¼QDlBqÈRUàTP…ª3’Ä dfƒŠ"£ Õkuú®¬s¢†øá¬ù5[U{믮º,’é" «ÊQªìÍÉ$´j¬4ªECE ¢IwaÝb™ÎhHFD²AH‡Ðö¬$Ó²™… !2SÚ¤Æ)*³J€ÒTÒf˜ADr2LClAIx“r˜L¼‘¡ùÄöp8Ûñ´x˜N“ÎÔ>Sì4ŸŸžÇöëµ>öL¿È&¹‚CÌÞnÛæ®¹‘-¸ùÖ„‰¦ª¾û©_Wï1úØB±:rõí‘æà™X ¡´Ǻޠý& #Ò3¹m.¾-™¡trø¨tÄ@`t¬‹"Åu…€MFŠÈ$†±®tºKŠ[šHRÒMÛ|’ÃtºÅ6ÆzžÏb|½,áNŽ­û€„ÛÔçyÞ¼:ÿéó‡O?ýÿ}þ¿þGÞ~û¶Žíx:…ê–¤ ¦Ö†4ØAÓ=– éêô˜9éô÷¿½ùrÙ¢KÊûó,jÝsPža"‡†\’:8à ,ïkÏŒ>R ­=N=F]Âl’ƒª²¿%á‰Ç€0:ü6.Fªzf’ÑSÇ@>8Zì…Øù:J~÷éñÖ‡çôÈÌ‘8z¸ï8)u!‚Ââ ²~Ð#¥%TfÃls„ƒ®ÒÈ f"³ŠA´­î[%ÉT“‘¬”WUÌJšp‚€jQƒà”Ãw?þùöåV“dÆIÏN§¤2¢Š^Q z`„!Ô° Fƈüzë0 (UA©‘@ˆ¸Ž{¢…BHNju@I¦kS”le‹¹×ô²Ð¡–r'OîÉòÊcp¹WÿeWâ$Š‹Kë"¹£*”ÕáÏû®¢Þ”Gݯ’;F½hR{“¨´Oì|yT= €À˜ÝCJè1IŒ¨g§æJª’BRÔD.$2öhy ´Æêü}ÛZá:¦HTÇ®fP °´¼±ÃDw Ø‰…¸þ•«‡¶ù¼_©¨5ìyfâwÔÂN ­hvlfìVf Ùê`%d£U¡»1`ZTA!Ú.Ю¥ƒ”`Á¢êÅ#¡„ìû¦)EAH@‚Q‚FÙXôŒ„'FQ´°—ù’¾ ŸëµµCl‹%z×%ý~÷D €òú/öÀ®¯©õJÚ+¨Õ‹ÝS¹€H)‹dßK„a¡YÁo­ˆž²×H“¢MüN|g°ì@ªY¯$*jkQW¯T-·PEÍr‡ÈçÄLJŠ0¡"ªEep)?céÀª>!tz½ÖLB@”š)ééÜcîE‹ ‚f®¡µ®ÏÁ©ÆÁ/¿Hi TÕÒ;FƒT÷Ö32SS¬|ò»~ ELî`8£„²%ÒŒ¡œDš‰¥‰§óÓééiKײVšf³Ê hÈ ‘ôÁ›P 73Û¨hƒ±æ”l!ž)²:S$|y‰q…FÃF æ&¾9Y±auo&è‘£S¼•³›÷eÝÇ-3›Jfn9 Å²ò~ƒ[1BÖwƒY;˜‘Nk¾9Ý\:ÒáÒ5)HٷN·îtBg#›ÚðÞ×͈LÜÖýE[Z59~÷ãß¶åK£NÓÁe¸o‰Ñ>àiM“*›aRÛt~:=ˆ6HªªgJºXD\eLzx8?Š €éô$2NøÓ¿¼|ûµo1iß}xøîÃ'šÏæß®×ëb¯}KÎÏïLp˜™"$×MO§ùtx|8wÇù(ß}úp:¾¼õåmkÞÔöþiz»^>>4Þo×ÛØ¢oÛpÙ"®›ÿáó÷Çþ¿ÿö?–k€q]ãòvCæ:bR}üðty½Þ–¡¹Ý¶¥G|8ŸÏÓ¯ß.·qÄÛ>úIe¸¼]Éëð[_·yâeY–›Ü–ñ¶¼í%‚¾´>zLóùíu\–&8ŸÒ#×[ÌLJ®ËÞÇ5ø6ü Æ#n}oÙ÷eôux`äí ‡t#yt¡ûvðïžÏ—ËmÝÆÖ¯×·[ÆLÔ9sôÿóÿ¾}ûåë-|ܶTvÏ~[{æá0ç "Æý¦ìFá&iâ‚Èhè¯o×[ùátÁØký6’Þ•C #CÜá®Ò3b¢’葞.2œ¶9ú}K =„T!éè[f½2­]H#•!=zfÈu¤¨GQº—,ÙG½mk·Þ;:ª¶ïl9\š’"f{¾x×Â6ɤ’dS=Þ·m„µ‰¢N¡)Ú¤~8ªÆµ6£%„&Óf• "jJe©§žA$d^Þ¾ 8¢hMšJt£•ÅERë3Õ$Lq°Ä$5Ãx"Òc¤ ÕeyK¤")È„eUÛ J¸ß?=º-µ˜ZCÓ\˜™Q-‘ ß”ý9 îW!ëçjS*ÃHÞûܱãåY“«&ïÜL£4‰$3GñŽ:IfÙb$*Á¦‰Á„8¾hpQ´k’¹›p*ÒCî[¤û#¢ Ù’rª=U‰w"{ž²Eéû™e#f&ENÞibŒý_êD»¯»°Ëˆö•\ìãcÍ 5^ejkçBWìùª½ÜYgˤ–¿9…Mê„ïQYDõ¨c”¶Ôj‚ˆ¾mw' rÇ6( )žhšZ˜Ôª!$@$cG`ì>&f8#à=áȺ_6$«ˆÌˆÜW|5~k%õ‚¬ß£VÌâ¡Õ®¦Ž…†=²^³ô–R‰7­I¹þ+¬:“"Z R,SÌ@)¸$$%µŽýžðF'rç´Q¢˜ù©*QjžiÿЄd“ªN‡Â@0esÍà$–õ¤fNs›˜GZ ,e‚V4CzôÒ_IVä¯[I2#ã|pM«ÙêŽVM2$SM2‹ê´É¹ozëÜš¥(sUÎÚ`:ÂE%w'‘$c/²¾y…–"„4¢ dFL€ šAF­]nC§Ã;ŒP9 3Ŧ êÁh~Rm¦6ûd"­µÓI›’ ´Æl¹bNµ¡…ÈE›TFnÀ`.hÄãñ{6xh÷ì[úló|8èŽH·áý2Öu ©aßk¥‰Îù`‡éùÒ{Q`ÈA·ÇIχùáü¸]‘–Ô‘cš´Í£O¥½{ú¸,7¯QälB«1ólÔŸž¦o—Ñ»4 ©{¬‘·ëËH§ |Èé`éΞM!žè«Ø,‡ÉLx|÷9€¡­‰äY”ÜÚùØ´¥{;´ŽG;¦>½»ŽãáñÝÃ9Æ4"oëa­¢â[ï—Å—ul) ÓQÎg9°}øôqô•q2âpžódy[nóqúßþÞ~:ãþ¼I"2nýöù‡Zk/oëëu¹Þ„Âfº¹ªÓȱ*ÇëÛòå×·-B…ë£ß<â_þòÓ—··ÛëïÉáÃ3uuÎÍÄÚuñµ¿"[Ø$y8?®Ëv[· z<0znžj–î÷1tžfe1¼3Û>>¼?­×ë˲¬n±õ‰‘â/¯¯uÙ¶ó¡Y ÌÇÙ}Õé0¶HoklcÐâéݧÏŸNM)ã–Þlb_n_1ÀÃyšëqš€"ͳÙz[ÖØ¶[¿¼Æë/ëhÙ±m‘ªcÒöéiF×–mn><ý÷ϧ¿Ü\$º@Sx IDATp¤“>2‚f€äÈè«m#¨óôxôEÜ #3%|ä˜z˜'úhóƒBéÞà)&êA"­¥÷ˆÍ·ÛF¯þ<\C *„Y´œ²ÁT¥!à 2²;%¥Ð!HËìõ”ó2Y¦K:2Mu„ŠÊôñù)‚ž¾C!išTE(F5PfaÞ¶BÐXªjšø4¨ÐFD-3h@º…H"+¹ÊÔÐh2…Tx<Ô É]²yP:D#×N°ë#çØD Cˆ@ÄHDe­)$2ˬ:‰~þüãeY#ö0”Ü»Z’$ƒ1bT¤«ºaA:2‹L¾O±£°»ˆ±KY¤é>º«ŽÅ*†[ñYB@Eå³`ä uŠp‡DTÒ©_"Á„‘¤ôuuPª ‰?!î*ÜÌýhVÙ¦dþˆ”dF÷w1Š‚Ø±)÷IEÉ¢Ë1‚%êÉz:C… )¤éŽ[ª[€™Ìô"ËGaêDW[Ú‚eæ‹q¿ î "µµ‡Úuí½ûJ­kI‰+S…½í)å­Q`òú„µž+†Aû£³rY©;¿*Ô T'õ$D²ÊtYªÇ`†8t!%cæ-¹'Ü{‚ô:“ÁŒp°Þ¸ï Á»×ZÀÓ‰(“’È.Ä®) Öz"µÊbî‘@SCr¯LÊÏÏŽú3”5ÿ0ö º)Ó³Àû¢h¯AgK€Ù …KSo%Î+›´Ók3«FL¤@5MU4‘©rBu|Îê_3Dî8îXlRSPSipÂ25MˆI˜´m0‘}C™™ÉŽáÁdzBœd…35Å÷a3“±Ÿ¸˜jÙ+ 1µ‘’™’©TÅ<2U©EÊ8>Çe])Z-YPÕáÌ ‘Th«·ZÈ QMX†*Â*E€ª#{¦ RIh1›¬ hJè¤8ÕØh“Ùñáx‚&Ó”MF;4é&z<ò€$rR›°ÆŒ£ñ0á7p‰ÔÈ¡ó<µ|~|}{«÷ô)t>N£/ÝÌ 8Ì3<<Ò0!‡Æ Œ!ÑTíëšêk ÒGÌ<œ§/··>üÓÇï7[û‚5"º0ñáñ|½Ý"ýuyÅ–™32&Õ?}ÿ]÷›‹Ï‡Ã£Ùß>=y½lÝ_/c MÚ•Óù Ë–£\gÒÁvœúׯo>lÈé||þða{½’9Á®Û·Ì¡TÉ¡ª5Š;b±‡w:³åd¦ÔUÙæI#ÕÇæH‹øùe]F"RzèQåø aÊ )MÜ™–1ùæÑÇ* À•HuSÌØVUŽÛº 7óШ÷ހǦà`O`„)1I¯Z”B´$ ©ªT˜4QŠ!™9bß›%Bö7ÞJ¢‰» z¶i2Áçù²ºÐÓ´¬Vhµ’Ïå"ªÐÐaå¬3ÑÉ™°¦œ,©bF!'ÚH-…qH£ja Áà Ô›ÄÉ2$H7„gFd£DzG–b¹Êæ2‘I÷ZÓ…;‹L.Õ:/V=¢¸8÷t¬"å:–Ž•dú~Ù3ÓHÕ‘q?!13êm3(õ$àU‡«i@ËÍ,wÑž|SÙíD«…² ÕN¤Å(wö¤‚BÝ+~Æ )®w”͵º V“/|‡jâÌLJe³GpOL‘iwJ¸"E h!Ë4¶S­ZëJ«)eÇÆçnf¼FÝ5ÏE$ €š~o æÝê¸Û ¹Ûõ ^¿ßÉx÷AßÿQ_¾ÖNyŸ5îøýCÛ…yµmûçnáúk6Ð*„b{aUªjY‰9‚iwnY  ž»Q "‘gô@F$ëê^Ü|:œ@T.¯rWòûDUØöL¨ êëUߌ¹K™ —ÂZì/;& µ_¼OñYêLZ¢D”a•mäÈ¡;#¤¨åå(’uëÈð@…÷ë7B÷ïÈ´P°˜¥ÜëJ¦ì›ÅFfÝ3N{Z>&PC¼Ž¯Yá4TÕ ÔÖB‰"¶ChÙhE±e*MÞDaÃ3¸»3½Þ›x%®”ÎÌ Ùl§Ÿ›>àÌd—'â)‚bz ÁpfPÁôû´É))økÏÐ1TSÉdÀ‚ÌVËE3%!‚XFľn&1i;†&¦Ó¬+¨ˆ©ˆL]e:žÞåtC[SU6•c“™J™,S-UÃÜÚãéôüþâvy~|OO…ù,­â{š0³m;F•³Ù²úË—Kôè ÂËuƒäm[úX'Š©ØñÈÖb]Fº³f÷ÍN§'yºn×L7!B>LmjÖîØºg®É¼¬ocYÒGˆ³-ÇzÝVˆEFá¸'u6Dv—œ0z¿­‹ÿr[.‹»3¡TQïÿö÷¿}ûåÂÂñMM‡F,·—uu"O:«M‡ùñ±Åó:§ÖF‘ë$15|»DŠw_|s¥•÷á|:ˆ>úÜR ‡óóaŠˆœ$;Àwï>þõ/ÿÜ×õt:÷5‡·cc&Oótõíº^fÆãAÂÚ_xfÓ—ËËë%'¸¾Ù‡àz»­oâ>´õĸlëùp8æåz™[â[Û_ÿøÃ×/ÿöë—oçÙþùüþñËXûÖ/[Eï‚ O‡“6££’ÏóÏ/·Þæ?þåû_~ùzèŠu]¼ëÍÆYgM{¹.—å:®É.Ù2c½¼]lÒï¾ÿðëËÛºÞtøH¸¤È£ÐÁ$&™t`ó²\‡o3ÐÚÔZ Ö¥J$†ŒiŠl¾méÝ?=ŸØ!.ëPºKô£y`[»tž?qsFÃÈc¤ž²Ý°ö­B* Ôœ²:D +‡HR”µÈJ©ª Ù”JmƒŒdHr«þ!ÑØô»ù¬DÎL¦w5}z|?|¼ÜnõH _=³Þƒ¦kyÈ*]!u}043Si¢1ÄD(¦ —+8÷X=”4×Ñ T­IQ…†¨f¤¦#”RGàô:>IåR¨²–‚ˆÎ¨ÖDd¦‡$"¡aÙÒ3…‰!0Ð*zÄİ©1–,«†€ª;" áH$äÐô ½H`‚%PÈÈÊDqwŸa—Uh†Eî¯ý‡Q‘2xÏaï!êê‚Ñ D(µ,?ÜŸÉYzàÝ3'b± p2ÒwUtønF®äIÖíª÷@Òk6 YÄË;‡‹¤Ý7qû ñþ!‰( *¬ÎÄJ* *¤Ê r/ì"ÄO°ª2É(Dà÷;f2ó?Æ)ùOcVbÿ,ÌÎ÷|Ûåz;Yë¾[ÒO^§Ê¬‹QáLS$*„Z’†úò©2ËÁZ{l„PÆD$ ñVE1V‚¿«›wÉ]2 ‚‰LH™û 3S’Q<ÞdU y·WFÿÎ Ýˈ’”Âø2E2½Ðñؾ;ž•YIøý·c8#Ö>"¢PYe$¨ƒpšõâpU)Æ} -®9ÓjäjLeŠd˜04ÔuÒñÀ"DSÑ0RÅÒ" Cˆ0]2EÄ’‰^íæZe‹–¢TPé"¤¦ÀB4“ÂèÔ‘Hñ˜ê9Œáé{«"ÓüÀ&c¹›Šn¾z†‘‚`FŠî„_ÜL"“BátQ¡Çˆ¡J);*!HK8iph!þ$Íh¦+ CjkíŸþþ__^¿Di†&lhÍä,©HFdP„gΦå·5˜Í67‡êÁIÕ‰“‡ÀX!iÂ÷çCOZrWq“©}ãÚ×AÆØØP3IP}äØ2EÿüáÓëåÒe¬·Å·•2zxn‘c´™ŸÛêÌà†qÚÝhäL¥*$³%VDŒÞGÏLg°GÏH™Ótè¾2\&#WÈôô4ýñÃã·¯ËPœíaÙ6Ȉdžæ&¼]¯_¿¼fí}13U•‡ƒ¨©ZGöè^-œЉÙËÛºölsû§?|÷Ë×7÷i&SPš4Èùá<ýå~}ýFS›ì0Oý¤óæè§Tò<Û6VÕøôÐ~»ÝNóa>žçùøôþÝíõÍGÚ¤N Äë5ß.o1†µN9O:‡Çû‡i ùòºq>×Mo‹_¼Çp%òí×õe}í}ùûǧ_·.›}˜ZÀTõlÏ)Ë6ïÙs ÌjÛvž½ßŒÔ~ØÆ[“ùüñ]Ži~÷øîeÛ<12²MíÏúÃo/_30 Íôã»Çá6ºÌ‡ƒ©,k´5lÉ·ëzPécdN:Ô…1Ð3Í=З«J±:IëºmÄÙéûç­L œ’Y;éJÄj§‹ÔÏ6&PÉ Ù£žF [@c¸”0à ñÔ`Õ3R’^ߌcÃ{÷ŒT¨H…h…ÊÆ(š¸É0¤†RBM•ÚÑ”j­Î\YñEÙîbSÝd"E}×ò’kÚÑ Fz„Šu:“™Th“€€°$b¢f=³ëÚ…w&‰2v\“¸ä ‰$­Ѻóvz3B5ªdY„Gø.†#•ìÞ pÀõT]°PõÔÏøî‚;²÷ŃֈÃfÜgTÉ"HyáLRÓ";LDQKS’ îÄsñ%¸§°Âª¤Ï¨s"2` ?>Ÿo}óz,í$%I×L g»¯@‹‘$÷2£PS(w] •÷p4P{´,1iµõ Åé»}¯:ž‰êÚç¾ØAàwÊè]iÇÜ–µú¹; Kö<*P¯»|p÷ÆžÉ*ÈÄ]dÜi ^h»O{ E"“h”¸Ï“º—ÆØ–Åw o¨¿ó‚â¦!RG;<|xÜ®·’™)РênW5z„$sªE0£X¤.…Í£‰g×™P‘âß–·O¡jð°73$…å·Î ¶Bœž T¶f-¬qnªÐœiNhˆ¶Óôüþc¿½Š*"›´mD‚:«Mä4OoK¬#[Úƒîê!¡ÅLéÝ¡™]~»,½o¾eh ­/ëð€3³ñµ÷­gÞzŽ|:¨#ØfSö±8²My~<]—ÛÚ)M«wÉ(»}Á쉺•ˆð¨B›ñ|zøÇo—ë6Íž?}òæx˜OÑ„6ûØÖÓ1‡ç„m[úØÚÓQ²±±%û¡ÙuŒmqä–Ù™b*Û&ÛÖÃ)—5Dµ§(åûï—u='ÛúövybóáÔžŸ?G¿¨da¡!4³d.Û𾚎ÿvS™lÒ‡ùh-gío—·fò‡>xÎ*š1~y] x]t>¨<ìáA~{}í#:=?>Φ޽ݮé‹~:L¾áëåíÒ‡©üðþÙ3/—1™¿{x<=êÛH—œüé»ï>œ__;ÈÔ7¹Þ6Žúáó÷ßÿôr{y]nKŠ åíò2ú*ÝZŸ#S˜•EªÄNùawAÊ*¤bBj‘WDøLФ·ýN(Tùݱ“ð |W(:˜‚t")¤X*Á¢ríq´CŽpóuë>ày¬d1dˆ——¸pêRÈ'hHAƒ Ñ` ‹¾s¥öDM‡ªà%)NÍ zÕ} £'ŠÁååF‘ûXü;bì.Äá>§Ç•wå3 @m>íwÀÔýšÈúu¹+d²tY ld‚w¬hîJI¢i% WdKx=P“ZU2Mö³e Ç%³³†Ý(¶o2‘ôB[í…HV4@Ï æÎþ <3È=š}ÿ ¨™Ž}ãê sG²×¿ÜÇzÄý×î\½À:Ås‡*$`Q(û”H®£’õ'PÈ”ò+gáßu'í§V¢ ˜É0˜ ¦hz…ì”Ù’)MZBÍëýŸ4^&“Ps¯khõVÉ–QÊW˜š"s_=ƒ„—aH9„îžDdê€1¢,Y‰ªXtçp*3£¨"Y;¢Š2ŒGÖʱok¯ÐdÂTÝ™ Ä=Ÿ^ûiJdR‘" Z¤Ia,ªdl®ÐUS eúèV¸DSµ”hÐLs¢q̦“¨ &K¡¶8¨5ã42«iŠè”X›Ž38LçÇ7Ñ=qé!îà]@ÔɧD3‰GOˆE„cñÕûÊtÙ}9 µ®ÝpîÐ0dï[ˆýðÃç¾þº ̹nKgX:“|´o×uKÙÖ ­w[¥Ð&Â&Q1è¶ôeYn·uHÌÓù§MŸ¾|y}ÙÅR²Sa"Ó,óü~[_(H¬·>¼oO‡lÆOß}÷z¹úF6m:š®c¬C15Љ`þøÝO×ÛKz»ºJ<œ¦Ÿ>]//iMÏMTæ^²§v8>~~x|Œm¨Ži:Ÿ&žfˆLóáé0Ÿ†_¨óÃㇷ·/ë¶B4o[G÷Ìôár¾¾-™ÁI{^·Ë›¿{~ò¿þvyÙ®M"è1ú¶r½‰,l}íó4-oË»Ï×ËM!Ìm[{¨ä¸5“ƒt~øõ—_¯KO‘ï>||~&áSüÏý_[¿˜3}¤¿^¶Ãdç¹]orœONü¶Ý§ùÃó‡qÛ&81BÇÖ#·Å–eñÕû(#›7ýøvÙúz¥Æd Ø0ºâím½xÏ~{²m½ é°÷Ï(‡m}éMGïkêAc“Ïïüë_¾ûùå2ùÀõ¶¹ÆŒvxšãa:·ã;†dØÈ)–í,í<ûñÜ2›¦4]ÉF…ì½,£yŒˆdF5Å%F#ÄSé¾&;M0® „· Ó<Í[f:#Õ9BˆD¦2›´`Æ]vÅDÉD£™)•AQƒP0© ÁèDÊ.ÉV7h¸‹¦·ö0?=ÙÚÇnö2œÏÑG WJ#¨ã=e_mLÆ–.ØØ.*CM"¤)M€TK(èªæTõ”ÊÛŽLl-4,©Æ$$•%+L•™‰8ÎÓáx´uÜTL¢nÂ%j•Öu5$ … 5-˜Õ…ß­ÍG÷”4˜CZýˆÍD¢A+Ò‚I«dXk d’½wì率˜TÎÇ)Ò½¦°}>pfÒê¼ËcJNXièÜ]Ê… ¯Ý‹ˆh¥° º )žv •Ï(d ‰(¬~õÔQ*Y¤jßK™!Õm DD22ˆýL‡ HZÖ„‰ZÂPBÒ(Ê¡"²«‰˜*Bu·0í¼ôú\CÈLÝkÉ‚>á^LñØKwµÇ‰Ý€¹çÁ"v!ïmIü§ékG8h³'‚ Un¦=;–´öW•{+ý=D©˜J5köH¤ \`ü>Ýíaóß?»T—ÙwŽFåžà{dqç˜2s‡ó2‘Õ…ˆÜÄðû¤‰ÌºÛKîJô¢—3ï!ÄN»ÝÉô…/O0Sï×[BÊ£qa[B ’Ì pÏOªP¢\Š•gAVH°B™¨ë|ÐxG‰dš0­DØPÑœ$&R£f½hbMEÄTÍmƒ6¹G®´|19‡ŽÌòM˜„¹*¨â‘9%P!,A¸(QTú¦Á\2ÂÉTJ0‘a»ÀŠÐÌ„ é)Eͨü^i:™P£¯¨V+ÓµºDQ?†¨„†€pe‚RíȨˆ>KqÓÀ úNóçT6C¸‹ÐDU¡*2+¤ÄŒ’Ù’4ÈÓù»ïÿú/ËÛ×0¨°“ØÔ¨“ŽH45ä$£^«+r2ÃÔN+’”£™ªšžžß=­#bC„@9IiSXFÀêè½E¾Ñ5[Æ6 #ݑޤ±mkô„b€H• l!}`l··Ø:-›2³ç—[ô죹õe1ÖóÃépxÈØé±ÎÍŽgt äá0‹5gO]›ûJi ï¿þúÛ¶^†ÓD~øüñû“¯#t[V 8|cd¦Dïý¶ä—//ÛÐJòέ\zÍ.4‰©ÖE¶eØAÇÖŸ]—·Ë+2ævPÚ»£¬™–íùáô|Ðu[cH¶ùáðð·¿ÿËߟþÛ÷üÓû¯ÿå×럾{/:å¶¥dbícܼ»§Û?|÷.R·µ/·«6.×ëÔäÏy¾Ü°n×ÓÔÖ8û¸|x÷éãÓ»Þñ׿þ-ÖíõºøªÝ¯ ]–eÙ†  Öá+×ENÿþÛ?.¯_FF³éüðît:þòåçÿÇ—m½AêÚ_2ºwiíÈ´5߯øúò¶Ž­ÿï<ýé‡_û«ê0àåëíº]¯écÝ6‹¼[×%³ c>¨Š cKn€Lf·Û–‘[«çççùýiz÷áûý×[GnÉŒëe¡©éÌÐ-òëo›ÍTQÍÛr[תD‡4Ѧ§ãêÓåQ๖Mìé|ŽÔ㦻«%D0;'åñÝÇǺºÙn,Ý3ÌI*ÕƒƒqCˆä¥Íƒ›¦ÆVFŒØrà ˜X,`„‚#3ÈîɈjE‹jZŠ‹iƒr2—ÖF‹FPSµIkTR43 !„ˆQÄû-u•Ъδ PŒîôúQŽ1Ò="E«¾®­¢´b¥˜Y‰X¥1, LBL\Øà’» ž©PŠ´è¿M=(®š‰NI Òš*ÐQPS4´"CUÜR"»Ç¸döiž¾|ºÄ–âºçj„„zDh'c­®œï¶ uhrÁ‰3ä¨MRÓ ™´[€ …°©üxj—áeÌ­ñÝi¾ô^BàÈû %LíøÊȈW¤%#S ¤îÿ¥hš°î‰{:¨ž£"!,PY!å?пwÞî‰ßùQ¾çzˆú€£Œ?{‘‰Ñ¹lõH–eØÔ"ðAK-2кÃNAMMfJ²wa_­§Ò%„ôâÕS*êŬSéNÌð¨>à@Ž P!ë8ȲxÖBhG³ã~{­,ïÔY0øº–+`gÓûn7ûî4öÊÚ‹U .²â"”ý¿÷@ETD‘™º‡£©wˆŒ u8²3‹Ã®H¤vaÈÎ’È,™J&êJé™åˆŒ‚’!Öe‘ýŒ\×ã¨æi¡Rö™?Q·Ä;-K¡%Ðdýúê>äïî ìÊÁòåUè§Ò^éûË-uÿû•¬›uj22Pš ƒ˜j¡Œ©$•N(Z½;ÒbÓ T1‚)Î"Q0¥5aªDÈ‘E¿ë¥x2!šÓk2j*gê€x¹JaªeB=2…êé°ˆP¥ÓF¢33-Q·Ì KaìØB\G†x ªB•|,…8’ž0† á@†*J‹ªDƒTm¦-¥K–]YÛ;•ƒ¦BTËŒ-©‚™<üíÛ‹ƒ D;±)€‰#T•“Ó3eÒM ÂöxÀ|œÃ7=ÿöéqqD"=e‚¶“l–À$#Cóùùд­·5Ä£o=}ŠÑ·­*åKvú \³¹Çñ4k“Þ‡‰¨ÐÇ6͇ù‡Ok÷÷‡ë׌°Ûê>¤åÃóãeYÇæ݃Èçfuý IDATÉ´ ̇CûÓÿòúzXŠÈóq^–AÈÛrÝÖ5˜ª95»åº"à)ÐÓù ±­[Ø"Cí™k®L%Ä4¦ÖNv~|<4í© 5åÝùQ˜§§çØn›¯#ƒÔLÌóašð òÿ3õ&K’\K¶ÝVÕsÌÌÝ#<{ ÑݦP}QŠ|"¾§ü~úM(Ūºu/m„‡»5ç¨î7P \b‚IB鑦¦º÷Z!åôêÕwþxõ6• ï^—¾öí¶§¯×ùO¿|íýi]É(ÊKϯ~þý·ËåíK+Eà½m~˜J‹š6©ižæÏ_z[/cµ—/Ï7ïÐZ–§u:Ô·¯îo뺶9¸-­÷X#£ ê?¾¹{\bÛ`¸,ÍÙãpGÝžÖ˜—¦C¿^é+‹žï^)¡ãp8¾y9,ŸoÅfïÖŒá×§íÏ·r<–À§‡ù2{_¹õ.=Æñ\ÆÒ·¥hï Xa¥ÈoÞ,¾ˆ”?þîooó³…ÑÕlÞ®­ûñüòÅýáóç/_çÛå0qã&^îŽ÷ÂòîþîÓÃãåÖEî­êõz]µ#J¦A§ã1¢D ðiÙ|™äXÕt:O2lKÑ*‹RìPÌi*ƒØ´Í­…¼{órm[„Y¨š–êÇQýd(•îÝ;œ¤éæ¡ B×ÕºR´T¾ëÚ´1ÜDЕaäà¢äÕÐĥХ‹jÑBèPÔžm¼)(ÚYµž5З¦ª ‚æb­ÞKc/%wEÖ݃Y©K ŽÓ€Bh j¥šÕ»p§Q<ˆL(in¸  ¢¢ddm?Ë”âtnR”âT72Bhd…9-Š„y j»[µºÂ/!]¤ ìkŸá.¥0CÇ{ÕÍ©Bš†tt6!‹;„ì;v\Í;¯;tÜa”RZTª=[Y˜!~J±ìÖé‘Ë#…ŠBzï·µµî&CZu5wºÏÕ„î´o…¢à7×0 RÙWiŃPÓ ,iI!°› Ó íwÇw"#SR5#â i]C»<³<Áîí²( ês!Ï] Ì:¹æ3V¤PE¤À e ¥­§@KBÔ,ùû»H\ âÂÑ’Ñp2„ˆÑ“/±3«, )fB‰˜—üë³çáD÷ncÆŒ8V†û<Ðî äz";ƒùøËýS²T"‘ ›ýLQMwþƒh£H©a¢JŠé3ATRí$Ñ¢‘åÀÜï¨Ã"¯Ù=Ѐ„4E0…D²£çvv*v[RìĤΦ>I%Y$‘¤fÈ3t5¿ò"Ï»çŠçê‹„¥”@²•µÓ]ŸcÙ&‘¯5» €%‡~ÍDL´ä”€PÂ5ŠZQ+&ùÅ2 2 Y(FÉ! ¥Â¤ $˜e D ”j…ͬŠRÔ I•÷Ø«•T( ±B"æRª•z‚[ó é|67M¯SŒâÚ!{§À]Ü¥«ƒÒUHzHhܶCð¤[²ß„ŒÈàC„’ ‰ïc<¡‚ÈŒ1$zžT7ªÝv¥ZDµDOÛ@¦!Z9šˆÓ"³4’1 k)Å£W C†»ó«q´á0r )ƒ©Š… •"ÕÌÞÜ Ç»C§.ƒOçyAHj¶ÔÑ£&xŒ&ÊÐmY;ZÑ“ª®àP«ÓéMT.!]||õòMÓv–N8ÚZ"(¥èF|ùºlÍ{4úùþýr»yÑJpY—“± ×.}YÛÚ¡àåò[? ¶ùú8Cx>ž—­ßݽêm)Òçó2¯s‹Ã¹(Í[ñ®ÍÁТ¤H©úÍë׎®œZDx‡¨éx÷êîÕ‹Wÿòwÿø¿~Ú¶&NÅ.·§¥?­‹“+C­Ü©IµI´™ÏãôîÝ»¿ÿÛ¿ùó‡ÇÞ‡Ÿ~xó¿÷oú°ÜæS]?\¾.³ßf¹;…|ùâ(Œë­çÛÈa:HÔÙ7È<¢Ã©ŒRK“¢ýw?½þøë×ëLé­õõîîN‡jE¶Å¥ãÇóù‡ÏŸ>}ør½ÞßK)ÇÓÄÓãZ”µÆãýTËõºl›Ú0GÓZ޽­óÕjôèAôŸþÃãç‡Û­«”a*{ñr ®h‹Žt²Ôiº;|[aøò0o­FT±æ½š•2ÎóÓÚÖ*RFÛ¼ÕWgûçŸ^oÛ¼âîÃÇO—ë×ËÃmYgZ¿wwçéü°¶‡‡[-÷ÓáDø ÇÓñç?þøõa«Uz/­m[»Ñ•óý4(ŽÇÃhµqT)¯Og_=¤FÓ2â0öec‹2ŽZ‡Ð¸?ܽzñÃõ©;DF+‡JÙ8^yë LÕ~øæüj(×u[TÃCB;[Dg]Ì=Ò D¡”Ú¶hר•³‚n¤…‰Zg‘£$s€qd‰Q¡V‹*JH/ˆIh+Am½ù¢áùJ¦nR†áûïÿæúô¹3 i-c@j™<6`©’C­‡óùæ­yÄo´?¥‹þ–?V¨(HTIú±cç*›‚! ¸‹SE¥j1拊©ÇéíÛo¶yë[öø %C5  Jˆ8YLÿÑ9tªdò(õÚTº„BCÜeßIÅÛé"©ÌfÚ®Ên‰¸„æQr^å¾Pö]9©úŒþŠ=“¦¢™VÚ儦ÈÙÏ 0aÊZ^4ÇûÜ‚Ù~uUsc Ë@Ôä7g¶)£$^C‚A‰¢šo^iÌÚ×à")41KU³¦Ù^‘'Øèû¢èIÐI‹:E»°fMäñRèANVlÝæ}‰ÇnP–§ÐH‡3!ȈB)/twÔ¼ôª>wNI3•"%ó`^÷´„f°^TàôèšÝb°SÁ:ŒecëÀEˆ`8²³pU3« ‘bJE+EJµÒ“Zgu='Ñ£S¦ñðöÍ·o^^¿|?Š§Ã²4 -NSbT3›J™FG9G—¶…ßzo¸?¿ƒÊáîÜnX…¥‡ZAÃp8ÞOä¶]¶ev†Pßÿø7ÛüØV_—Ebƒ­»4‚Ýcñm›Û¦÷wwK»´Ùà ›9«ští­Âòx}êmUÁ±ÔËmƶ}ób¼Ÿâáj;ð¶R†{BçÛ,Ž÷çÓue©õñrÙºC›¢yôè]àÔ½:~8NïÚAè Ò]¦©¾`§´M¡V¥Êùpw~ñ?þßËüèp„¹õóéØ7_¹F3%>¬­÷ÞJ“2-]æë2–iñÿýå—yÝ(­¡w—uSS{{wW[µã­Ï=6ÒŽ“Ng½].ÍãiÑq@£ßÖ±"x{z|¸ÌÜÜüôÍëy‰¶>”—/ÇwGÿË—ëåºïNO·ÇË¥êár™¯óLÚéðâý·ï?~ºD÷7¯§·»»ûh}[Ŷ5NÃTÁ§íúðåáÅ鸭[ó›óv]—y^æë2‹nï^¿4V¼~}Þüé×OׯóX½¹‹ ’Ëë×íaõ¥Ãû7/Çaœÿþûoß¼<ýù—¦ýçÇmm>ß«•Ò×M¦¾,—ãˆáPÞ§'^¼ÿÝÿþ§¯¿\¿>|h·mqp¨m[žœQÊ8 洜Ӌû÷Û&·µ»F9Ê(a&/^œ[;’„vMÊÝatçÚ¢¨Ó¡¨–.`©"]Ö@?VõãÃåv›I8h®ÂŽ@5swéî ³2ÚZo›, E´FìLyQî2ˆEÊëC¡uê¨F„Â&U-eˆ¢êb4¸yBÅCºs˜Ä]ÔNu¿ñôå+Û&¢ÛžŒ–bÃÏûóãÓ'úž²¢ªZi„ô† Ðà%©Ç$ÜÝö's"±zÍ$G D¦ãY”Ð*£ˆõuªªÖ*bMšÑ„„¡—usnâô47JP«Š'¾²s»” ¥Y¸³ !ÒBL÷)¢/ž*@qJì˜n"±C$š6ã^ƃt ©º˜XòÉ’à+ÒhTJZ¤òšºïؤȖµûít_#!QYëŠçZ§<±ã2B¾· ­Žw;|ÿóïÂ@Hn+„"!)+Φ]n€ †Ücè‹DIdñ0Jä.³æýT!P˜î„ȱ‹Ý5ĉ¹Vhì³4EKº&T·üðrp޽:˜ RMKS£”žFbµœ)â»ñYóOºcBPŠAË.Ê5c1%S´#>“XŸ¥¢Œ¤¤¤ÃHªP:DÀì&+=û¶eG”Õ")Õ*ÀX* ETÝPrx•1äZл°ºFtD ІˆIZ„ (;’OÝžáyobË¥÷µ32©×%s&¡=:H{FR0€ +Ht£@œ ¥ ¹GÒÙò{²r·d¶MºhÉoc)‰ôï»U€á¢â²ëÃÔ]”8œÆ± }kLì L )]‘ËÒR¨*V­ê(j^â8ί^Ísµb¦UK½ ƒßýðöÅÛ7¯Þ¾þî‡ïÉêe w®MJ¥UŒ•o_‰ÙA´®âKS Xô5 Ë"á dµu5±b>Té¶ueôàåóÇuë…uˆÍ{s:P·žë8î‚6ªôvmh0ÌîõÕëëúäÑ ÐÚf¸yx‹¶õðæZÊ8Ÿ.¾Åꇱ…Ƕ)½snkîW®—ÇÖÛÒû" ‘VÇñîXÏ“¬áp›\¬{NGJÏÝåÁƧzÛúõiÎ3=e:MãÿúßþûýòõÓ¯ÿå ­&w‡á²¬·«xwÕ–dµèµŠ(íþtR|mý6/®ÿüó¾>üz™/o^•áxop+QÕj…i-ÓiÄ0ùæ§Ó›o_½¶ŽR-dóN5ŽcªN¯°é8ñqݶ5¨ª´Êr>à:?,ó£XPçË-^MfµÜO¿þú8ß®Zõöôä[ëÛPƉ±\txjK«…wÓÙÑ^ŽÛ‹a\~|ÿö¾­ë¼Æ4MÝý¶>­Û øm]æùÚÛòòÅáz»níò‡w/ž®_ûº<ÍÍL¬Núx[Ù·Ðmë.6¾¸¿{ýö»_>~QmÁíár[¢}}l_žE¡¾–a:–éíÄ/Ëöö<Œ²,«÷ÃXO÷wËêq[ÆËÜÂe[A9ã8š-É@.ðhÞ¶@ƒÇ¨ãáí›s‘ÑubÔaW¤UZ¨D{÷îT'í7{qº›{ï±QFž‡OOO×ÇÇž2íJöµ*<àŽnÑ ¾T¤Äç Ž ­’ZÈ·iJJc•¢E›²dU¢ª©a"ÓýçFÁ`PšÂР.ªÅ€NsŒÖ³¿ÕíJºãrùD ‰UŒ {„dpüyYÀõ•`J>Ì“âmN!Z0€!SÆ0 ”·/ǹµP7xXÅXÝà ƒÜ4$\Ö¥{¢:Õ]¨® ÁhPíp Ô.„Õî(]DT\D¸œá¤Äþx)² ö±¸HϸDÉUÉh†ÑÄ©"4 ªŠ r›ã=y‘&J>WÜNU$=[V|>J˜ŠHˆxºaL!‘ž‰ C" 'Ú¹Ê]–“Ág#C¦\/×YÊd±CQ2ÈÂÓ©, ÄcïçDAö¹vÈ^re"$4„úÜ6 $M) Yd"ËEL͹¿Q(°} µ+ÿ‚:~#s夔L‚Œ}ƒšïÍÌ%? Qâ™!ÅçºÀ†é*"îôª'šD&¦ÂüùsÛ=6BŒ,Qå¾P‚ FÝcÐJ‘³†–ŠcX^q…š C”)£’¨å¢ Ï tæå âF@ʼn¤L1cjiÝŽDõ§.iç­j~?~«Eü†«‡ÉΔ/€@ÊÞÇÈïd³ÓÝÙ½KDòƒ3Ý—þš=*e¯gŠ f^ ‰Ø!°YÍO•Škþž9ðª`‰ªÈ ¾EJJ!Ìÿ}":4u”`Q©äÛF+`á~y“žRm‹p±ŒJú£T/&Á£6e4ä•1[L«4 )i:tï½uÂùBã–÷pÉï]f=7©²i³¤P Í*òc€H±(fe‹Õ}õ'ª°{ü•R *…©c0ûþ÷¼Þ®pˆ¶ÛÜá Œ" ›‡UêéõëóÝýÏ?ÿÃÿóßý?ËåñA‚ë|ŃøP'¥xU-¨Ãyë®4+Cq† ?Ý×ǯâ®V¥wiR4Pºx[Tg4›{G+­E_fß:ys´g jY(µ*¬”à¦=D9[‹]œ¸) ’·†ÞB„‚è« ¸EkêÑ—&·­ÓœËL ¯E}][{”7¯ße¹ÍÖ¾naÓØƒkÁÆ9úÑDßÜݧûû—çùº–‚T×RäÕ]ý¿þÏÿþo¿üåvõZª˜YqùúxE®óuš*ê®{wãL-kÑjÇÃaÄ4Þ¿˜îÇŒñÅ«Wçéô´Í·9 ˆéÅýëyíÇãU/¡Ã¼yõíãª\Ù»ÿ×´Ëjÿú{|óòðåÐÖåøêÅÛ×§r¹ýåz½=]·RÊ4ËA§¡6”æ7–¨²¬ºÖñø2">~ù<¯[¨õ…ÇIHNC¹>Þ®Û\Oýõý›µEå¶ô@ðá¶^V±¡è›‰o·[oµÈá "bæ§Óéúø>7·Ö–yY}‹Ñä/_>Íó¢=\¢Ôzºûæ›÷ï~ùóŸ¡X—mËaã?ÿü+úüã›óâðXÞ¼ùöñáú4φ~wwtö¥_ÿüñô¥ó²Ì4 çñÅùááötýjŵúFo3=dÜ Û¶µ¥s:ŽB1+ņR !ãñp¾%e+¥þðÃ(Z¼_­SéÒMKÀ^JkRmÔã! ¯çS]èa¹}½n[°ÖŽ@Gïžßúb¨´â ~q{ò“»¡:(bÙ¢ 2Þ¼ýÃm»ŠnŬŽ UZ… Y!¨¡U …×(RMH €Ê 5áH§(Ã@é!gE„»·­™õIè¤Sû¥kÖh˜e ào‘[…æÛaY‹ŽØÏj ,W_Ý„’^[”¯ãhb-B¢F®`8€€t§ €èVŠh@%CÔCãd)Þ`tˆ]æCH(Ø‚÷Gfš=A =é¡Ut€¨i/£žïŽ} —–™f݉š®BŠ:4R—=ð¬€Q%ÂB…P¤j™¶+t@AIASb‡Ø%2 Ëg$Óc‡„%„8zìxrJäí'-3 hGz︯ÊòŒ&‘«™ˆpjÏ0ñ®fO67=[æ±Ã¶«=-Á,#µÚs‹J¥ (ÔvK´ F¾àƒbîBˆ%íÕÀ4±D*]C:Ä%ÒòB$Ó@¹¯—²)ÇnÈa@Ÿï¦ÕïNÆsVðÜ÷tUA‘ýÃLú ‘J-]MEaf­žÍÒÐÝÓ„ª™÷  ÔB(Y€Ð½v A†F^ =-È 2PÜÁlêiD†H§ÒÈû5ãxÌÚ ÊÎÀ‡±ËY H!P“¿&¹2À•gPQ•ˆ&”ýmo%²+yö÷#ý«ZÒÂS`jUXˆŽ}ÆÔtQ _)…f¢QP, ÏEE-ÁñPgˆRØI“*%ªB², Va‚RTÕzåi™ÊPÒiè€ 4˜º%v !›n¤æâ­'ÍÒÅ- ’ ¼y‹J„è&‚¨š*jšbáŠíÑ@é;kPˆ öt…ªU¬ãápඇñC-ТT…C‘RÂB­ µG¸=•‚¢µ›ä#ÌD¥°@ªX…¨h¹§ÓùÅÝËSŽùÓçËLJe]×$~ÿ7ÿÎ}›ë0P†~ûêuﶨi‹§VãHaœÊ CPæÞçeÝèóÚƒ®«lX—-Vï¡ÅЍšŽÓT¢¡ƒ)¶dXQ=Žrk!mêñøÒ¼·Óá4áp$]½Ðhb=\6k‚è¾x뀆s‹‰wG[ÚÚ ×ùâþp_±´îN ŠaÔãéz[#¦a*69ãÇŸ¾ÿÝïÿý?? ˜ ˆo®¿~üt [ïNïà}ÝR2ç[Ÿ%êýé`el}ÛÖå ,Cèn»ÕbJ Žu?ÍëÜ·ñT^‹˜½~õj[o¾>Œf÷ǻǧ•m8ßwcùÏ_¿>ÍVýûwoŽÕŸ®|º´¶¨ŒÀÚ¶¹7ÔROwãç¯]µ¨‡v}ºþçŸþXúæUw_é=æeUÔªv<•÷ß¼"írù´ô5_×Cžúum÷÷ÇZ€Ñ lA_h±ói¼?cý0NŽo-ŠºZ†ûû7·óñÛzš¼¡jÔbÕbë­åí÷Ãüèp ™$f 3-z<È0,«›L&±ú†MªF+[´­³A€:ÔÆMÄ'3+›ŠQC?Ô¡ŽSoO̓ÞEí0ÑÞ¹‘*ˆ„›o³€¢QQÝ´¨Ô%Hb¢^M#L”Vg—Á5Uj¹  Ý¢ìuAsÒ<l3dz³„ˆ óþ ’tH‘Ý2  #²"+ fdËοоo¬U«À,–Z 1RÕD5;Ýÿnå®tµ«’™V¥-—Å.5„vߨj€j`ˆ®îtu¤XT"|o ŒXËp$ë"vÍøÛ’…”Ð` 5³:4$Ÿ ¨Xhˆ¤ÊIh ì•N5g‚ý °bÕ4‚.‡hAe÷‰Ûž'í0ˆIÈ !ê<Ý݉Yçf….¢ÅL§Å`°ªÌl¤–·¿üùzùøár½®ëlÕJ¨3¢ÝP-@Ô‰æd†¬[÷2øáxkkÖjÞ½}ØØ·ãh[ƒ ECïÞÝ-–î¡bq’ÃÁþ»Ÿÿ—Ë—_–Ö%èêâ¡*½“èZ}ªÃ‹óØ—ÞÑJÑú»¿)Ö–ëAeŒv aB!dw5x[lë›Ç¶Å6o« v¨§u›çår]–­mµª¡›¶ÛÊe™µ§zd[7÷-øõãÇÏ_¾ œÒÕi*C3†á0ØÚmªJïhc­bÀ/ÞXn2Ñm­çqº1ÔJÅ©ªF{ÿÝ«è1D¤Ý¶vÊ?üîõ_>~IQÕ·o^þðöÝ/Ÿ¾ÎMÏ/ïÇáT´s£˜«9½{ôÏÛכܯùºº ûO¿. lGáûï¾=ŒøÓŸÿҶǧõV½8zï¸nËækï½mͽ«Â±ìi½l›ƒªéˆë2oóÖ˺jø:Ø–¸­ëòt›ýæðûr¾;óòðp¹hÄ`~Ûêºöª>Ö~7š«ÆêAÕòp»Qíwß¼üõãÇæ‹HÌó ô¢Å#Š­w]ƒó¼ zÈíòðp›{ë¤Gw¶ÆeݦQOu§z< çéX‡ÃÓ¥ÕÊúû¹|ùÚ–§—Ю6Ñ“ ¢´¿ÿ»ož¾,w÷ê1?9» v»„©üðÓñéÆêP¶êáDU1Ô€Š¤ÝÃ5D­öN3‡Ò×y©}spütÔ[»±º­¢¢´¶²ÄTŽÛêkw:`®Ø‚Ñ*NIÙµ æJ»ÀJT©N³"b•*ÕÐ0‡nƒ«"¼*+«K$’1 æ»¼LQŒ3ÀÜn(„S*EœˆÐÞ; 3ùÃ÷o.—•-Dƒ5»pÁ½P8DþðA¨ †w.Í‹Jåbl IDATB¢häK\1/Shws"6“&A ËŠ$̺yõ.ÍèE$DÑ5˜è¡nÊPÛTD¤AJ„Cˆ"¹ÅÑ4†æ#:Óψ"Š0Mb©Á¤šF)`g‹Õ"–<ë0Ýöˆ¼3ŸYLÊ`Þ²(’û ¨ïp|Þ<Ô½¢¸+u‚šÃÝŸ3FDäÏüü…!»:—LYøñ k$}OYì:B=¯<™§gÂ×—T°3’j ÕFTª,{V_÷ÌŽ¨™í¶ºB ÝóßÉÒ€«hbå#ãsäcÁ,¥åóNA e{Hþ¹b·¯öÛZʆòCÉΆãÎË’`þ.x^ZíîgÕP¸ ¨©ã•B ÀdwQg†‰²{yQBÃNЏæ’"PÍ•öübBŸ-ÁÑ ñ<´y^B%4ž÷}]ÅC<É${ÁoŸœöÉ_jžå=)’ñ/A‚Èr™šüZDB!BÍ®`¢]$Ò¸ÄTÔäÜåB‰£í¥Zî¾ †Ì3¥“ºû½™º õ¯f+U˜i¨¥ˆÊž=Ú9e¦FͰ4QjhAÚàUUZ.(Æ0ªÓ5À!´wXO`‹ìIDQ@<®Dbsõâ`G=¨>H”–§W‰}pü&OJ…QáF¨HDHXˆ™ÜNlkƒ3_„œ‘q7w GP"5åBs„‰ µŠlÄÚìÅ~xÿíív‹îRjBh‡*XD•A¤.›/¾À”´¢ÕX×22ª;̃‰»š"hëãíinë<û†7o¿…ËÆ%Þ)C=³¢ÎÒb5Ä՛ܿ-]O~‚l±^-ì0¢q»?ÔØÚ¼ˆV‹Ö¨êf¼¯ŒmkÝ¿^~í[:Ÿ*êtñRb§¢Âë2/±¦fý¿¾|öÙÞ×-À¯ÞŠß¼c¬S‹`´ZFÙ:m²QËa”o¿ûîëçOK›…=D³xÔjÓX:¶ªÓáx¨ŒÞƒ§óKFýéÝw«tEãt”óadÕéð¢uŸ[[¶µ7…8c°ZÑͽ¹3Ø×Û"Úœf¥Ü£D´ß}ûýûo¾G,*VƒZLüáÍßþÝ?}~øôáá²Ü®›êøö›o§ãõr¦úöÍËiˆë-w/}¥Œ÷‡óq<ýë÷‡ã«‘b¿|ý4_nÞ?þzÆû©²”¾?<]ñëŸ?¼¨½i¹)íxzÑZtÇ¡ŒƒE÷ˆ¾!p8žº=>n±¬ë|ëóñÀS•‡yë¦åýma±þpyzZ)Rkñéøòþî_ÿò—ÿPJÁ6wh¥††›ŠÊðBMÛ¶l˺ìtº»ûüð°õˆuÝÖu]z4¿ãén¼[W[–ëãêèÁ‡Ë¶.ÌLDÛ6èxƶm§··uéÒcuHyy,ŸŸ®·uñ­uSüý?ÿËOß½ÿüåñv[°hkëÓ—‡±¨£¬T5Ž„Áõr‰µ/C=¼ýf¼>̥ʡ–û©„"¶>LcP#\„:€Ú vÿêééÖ|݈YÙÜÛDMÍC“Öz´yÛ~|ûÍãåælf.\éû”’,ê1 Óݽ0<`Jˆ)£Ê} Ñ0ªö€šV…šYÔ­VSµ K¡vÚ4'Ì3?jD—ìRåB‡MŠ @õÞÑ…ap^®×5z²%G‘P¤^©jº”òl¯%öµH—|5h¤(Öî@b"TL³ Џ¹( áቈVQ½ø~¨PE ]!ꋲ3×ÕDJ´½ö(„™0Òž¡*ÅBS {{|Ø÷€æ %¥§1Ûp%¤šiõH1ŠüõžÇP¢€žÌÈçW–ÂD"\€x¾”í½$W5!, šá®”*ÁŸÅwm Bû‘Žx^×öñDŠjvw%ŠŠ&J¯ò¬IÞÿ½7ø­(Šh6í&Â\°írntH6…$"BE¹.Ükr9\<®¤ÃsΈÄ1ˆF+wTø+™Àl8R:Ï˵MB%§§Ý1°£ÍÓ©}™Û¤r*åÛ×÷ÍFÿãê/Ìß¼—Ë­Ò»©^æõ8½ü滻ׯ¿þ‚?ýéÃy’aãU£S9œîÔµjëðêåýynÛŒ‚±ÈÝ4^æÛÒ›¡0¶¾)¥ŠV÷r»=üzI÷†ÜV@¶ãDÑÃXûÇ_>h¿­½=ÍóÚæÃiüñ‡Ÿ×Û烛ÓéÕÃSÿüõ²õ.(ïï‡ÿõ×ÇËîNaz³¢ý»oÿ0ûéôòéú!oÎß¼zùÍü4{_Çá0…²Oçãùx}xüê[ÛÖpå¶Éåz;ËP̵¶~£Çõéúõéñöõ&Æ@Ex`#ÖÛÖ·mZØÖû@ê©ß“—Ò–ÕŽÃi(Ëu«¾{ð%Ô¢»k—Ó›áp2`*ÓApc‹Æ2ÏÞ¢k?6€­AáqóJÕbãp¸³©y 7é¢Ð‡§Å»j¶ò²Ÿ!«‰t†T ]$5£«‰¹Iž8ÔÌEc)Z•NÕP•Ñ0.«ŠºE" @ual¡:C¦¡U'„¦:Š÷¦â]J­¦Ž¶"øñå­³9,E,˜”O-ÊRÁW)D° Á„êH’UXSéR”…4j)ªÓ0Âá q£¹«4‹*ÖCÃгÉ(QBMÜùfªBD¨:"DÛN„fH‡ÁDœl Pºh͆ªMÄ%9ÛÖ³! B ï˜H¦àtªED¦çóù?Ôi<}máîJf> ¡iR±鹯A¾û FVÁ÷þ>˜1ˆ¨ðLË|N% ¨ûcF4 }?âY|JÉÜ/Ád$Ԫ̅&ÿ(ÃþP1E.dÿÒ}˜7¿Â¼¨ÅJ@LC÷§;¹/¾1‘ðÈ}MÓ¶P`ˆ}ÿ÷ü?Å„ï-@â¯! ¼ v‘Ý óLôÊ=„°Rîð\ T2È´£uBÓB ˜BÔC Z]3we¤j{ÞUŃ©",Խˡú|4W 5I%¦sƒ”A ‚±W öÓ&ÜÚ ^M=S^vw¬¥0q±¿9ÆÓ£¸_ ³—!J1+PS1(D U3zAѪrž´m¾£ß¡ ìW♓²½E*¡4…ÂiP•]¯EI}§åÀHH©VÌèTÓI’¶V‹¥QÔ’zW`ªíÉîM Uœj>0$ †„3M— G§³K£áyû„î9¨„V !®ìûµ3*¸u!B3i¡9…ï2P é]²ÃZÈn!¾gý<¿¨„:T¤šš˜‰tQÍS ™üü^2¢Š@+‹(¥L²PJPÅdZCËPME¬`bafbQjUE£‰F™T§ë¥/möAÞ¿}}8{wo¨2(GjïP ±®>éW-ôÞ×P¬œ9Ë%6÷Öúüôð±÷Í„.fÞ»„ïÇa)A„°‰×Öû²\¯mk0(¨RXÔIv4o¤³!±DM;Mw-æ1ŽvçÛVG{q~ñáË—[[Eqw'«×eJ„ d¬Õ}]–™ôª• QwLã(ðk÷$Ѫ“kk¤Ú ½m «+çÓiœŠ¨ÇaìÅ‹û¡¾¸ÞÞŸ§Ó›Ï·§ùB«6ؾyùx½Ü¶0©6?žŠ.:ÓY?—¯¿®Ë+|Z>oÐc=P©‡¥-ó¼\>üŠu¹>Ýâ¶ë²þñ¯ÐŽ/^¾ú›ßןññÓÓã6סvÎOsë Ê ØÏßÿá¿~ùp¹ÍóÚØ¶ÃdO7’^«mm™ÛÖ6‡·:h® p8ÕË—õ8ÚÒx]5ÎÇãw¯ßžwçãY¼m­=-ëíÚ̼h—ËPë`m½,Ÿ¾^}_–îõtœn—ÇÇy2ûît¾;=|¹ÕñÕÿößþþãçô¶Ì±´µ7YæØÐÅúºE }ë½K!zx4÷ù4Œï¾ÿÉ[g¬‚.Zÿøþ5|÷¯sGëë:ß¾¶ÃÝpÿâÛñ¶¾™PN"ú¨ËäMhyüãçã¡®FÙ´rݱ,E­ÔÁ¢Ñµ04 ï~g¬×e<o¦—O×¹¯NíNêЭä~º©õn<>^—«¡Ym-¦Cl^f—5ô•å}I¶¾¶µ§·žhl0HÕi¢g1jöRbëê¯H4ѳ33¥êq6°®Ë:™óT ±ÆìVŽSé@ö-pM1Í*ëŠméùr½¾\Ÿabl—µ%²©°p!ŒÅ'óz{[·fh¦P­v¨<÷KÌlé}ë×midé&f?̇ZJx=Ìó4Ûz9ÍÑ:Þ¾yóïÿö¿ýîw¿Ÿ*Ýo/—Kfÿú«o ?ŒØc5àĸB‡Û·ÇÓá¦_½»ë\£ó¿ÿóO]óËË…3Ÿ–µ/çÏç&l^¦ì½Î âåég‚lë:¦ëÏ—'a›Œ«®ç-׉“O3;]ÛºÆ×wÓtGÓ·_}Ÿùñ|y¾.›,úÛ4•_=DÄç§çOOÏ/ç–[ßâ²ôeÉãäw7”]–m»?ηvžEâèË¡ÖËÒÚò©ðxÅv¾âÆ[5û»ûãùò²öþæîP'ºM?þ¬ÌvþÜÚ—ëÚïÞÞÝúóËÒ6¢èòri±z‘"ÉÍœ]=×ÌÞ[÷÷oî>=_Æî÷ç—=Åv¾.` aª~:TS=”c§bÓñá¶÷ë\&Úñº¶¹…u<¯¸\z[—Þ‹­}ëæžhmÛÖ†C¹›Ëí ΗvyVCŸëád¨×õãŠ5Ûz8ÔéxóÿðÓçËVùÁL豞·mkm“勯NBæk°LÇûþoÿ×ÓŸÿ¼fk›R‰–Õ*«‰–^”«e-ã7¥Ì©ƒzeæÆî£œ^%RÅe03™8‡™$ÓÂ[#Drß©wWZ —¶^±©›ÓÕ›r¥!#"HØ«1Øñpô:oÛ2bÔ#ƒ4™Á¼í ^×Ï3Eß]>*©žFcÝ€m[2Úaz­Áà”¬y‘í¦µ±8‘±‚9åÀ3È»gvP;jWA²eŒÜt7#iJ‹êC•QGu¼ N ÄFa¨~Q Ø8îÁ÷¹Ð™£7ŽSy,õï¾ùúe»Dì·- ˜ÎØ„íž8ŒëšI ‚¹—èǘa;dt_–Œ[Û 9ä@v!• 2»9x<¨Ç$G Ë®ð% éUž‡Ümvúõ^#‹µO½iع##U†-Ĭ’wÃÈ1A®Ü E5‰Ì€ötø.æ £BJ1÷»KŽ “íúÇ?º¯z¸·ÇèÚ{C ´§â¹GãÀK½}Í/ÑkÐ#IêÈŠÓaÎ! ¦?»ÛG°gâËÝQlCA[£,D©ÈBFŽOR&»½{°Ó@x²0"ÒÏ=‡ã«5.Ó Vø¸¾Þâ^ƒóûÔhä°ÐìA©ýt»§ã(–Ú• Ú·˜)ÌY¦ÅNÑ´k”"=vrîXÒ™QDÒ2¤zÚw9dJ))#(º¥Ð†T€2éS‘'e…R3èÁ‰ —‰AMJ! UÎnìS5¼‚RHÝ¡ÈÙm¢·­PI¸å`¹ÙþÙCç8EOžƒÒ Ì=ÞÎáÈHþÔ¨NTueµg˜büR †ô¦›!KQVK##˜® ¢ÒëT„iü<—)p¼{3Ï5{+C'áNó2—ÓÃÛÌN4ÉY1Mnª}Ë=4gæF–ê>¹A£Nxðš˜*5Í;­¶„×Éë¡O».Ëß•;{ûîðô² ÜM±ívöûîþóóÑSì¹uzàeARªÁh±uAæU¦á¦>Ü=œ— {*/¨‘LQÑeé!:R­"Àle.ÕÌ;üîææùüKÏkÆJåÍÝãú‚ÖÖµwx:ìææí¶½LÕïn‘Hy¦ gQK©4¯Õ«³™'UkͰآŅXƒ}žB´ˆð›Óé0¶Þï¿.Åçãéü²ž/—ËuÜB«ÂÏ×åü]·¥øÝ7ßü½Ï•еõË4Í=r[ãfbz£Ÿþïÿ—?üù²µŸ¸ãeë§”|ºœ_Öµ¯íþv>p²“MÞïîî¾}ÿެ^I[zSóþþqòéüîOŸž?¦)¸äÆår¾õ–?üø~]ÚõºÜ¿\¯o¿y{S¡§ßÿõ¯N>]¯kOWž'à÷—e[×'åFàÍýCD/Óa*‡¾n¥nÛÚÀžýúóÓË//Û/çÕÅÛ›yÝ.—õ|¾¶çK§z‹bŦ‚—¶tÑKµÑÛe=?_/OÏÏ}íkÛ.ë¶*—ÃÌ{÷§sOm„,„D¨‘qs˜oN·ùÛ¯¾»¶ËËùåéºnÛÒ×µg1zDkl½§à˶Μ¢¤ƒË–×<¯×FÅ|_GçrwÛ¥ç6â ¦îóM‰—+ØZ*(¿žÏyù¼¬K91ÝÝe‹O?Ô’e:Ìó£ú†ÌÖ›’»»ádÖ‚²Bñéã^¶•e i²2Å{b 2îK‡v68Ó/v–ײÓxÊ@z¢gJÜ´'–r—ïQe³=ñe»‚F¯dô‘ˆÚûmäoô ¬´ÁšßUŽeŒHÒHƒq¡Œ[ÙÀŒ<–^ç¦×ìÕ>éåÀiJ6øªž#…AbP*s¸pv8}pxxñÊ1O Œ´ï«´Ca_Ïm ¼Ì·CÙ¸3ÍwÌù8¨9 H¡ Ç Ð £HçhE²`§ã+´SÍLnPµƒb¬¿b ^¥ êÿ`­iľ1¸£ 믔yCî_ îÃã@^úëxÀª¤^U^僆‘Û×U»§h(“()ÆÖlgtì§Ì=hµsY¢ŒÁŠ(O…¼±9–˜éÜ[Žõ•Cd ¶¡‘fÖÛQ ®…v\„É‹ÁMÕŒ•S`Lãs¢"R´båÐ7ËÆ”gTÁôŠþ·ìUM}Ô²Mi·®˜P¶ýÜÝÁdf ZÂÍ!ERæ &‘D$#š+¯ºÃøÖ·q©çþ>5>­y1’S•ù”m|K™ÑXºzÊË`æÑÊÍñx8{m‰ê6Ñìä5 ”u²j3…ê³Ï^¦zZ[^²›ëͼµøôñemBgú¥)~¾nËú%ȌŲ[§oÜ”+[ô–MŠ¥fÙbíVI/æ4l}0¡Û—˶æjܲLµÞäºP]%2X%$FEcž‹-íúri}‹Ð5-݈èè Y­°H$Sk¿fö¯ÞܧzíÍ9=ܾiÛV\G¯¶ÖÝQ«eÏÓÝÍÛï¾ùL*«¸"tsaí9•òÓ?^ÏÛËe!¢Çúòò¼, êétŒæ½÷É‹²÷fÇãŒ<'Ëû‡û/Ÿ/[[f©øêd=V¢ckù‡?ÿ¼¬ërÍ­ë7¿ýÁÔ³ç// å0=ÜYóøðu)Ó²æÓoË£ùçË5±L×…æ5/Oïîë·?¼ÿíÛÓŸŸ>y-±]~~¹˜¢·í¶·_Î_Î×mÛ–mÃÛû»³¶ÿüãz{^{WbkÑcs^CÛ¶®ïß¿uןÿúiÿý_ÿÛÇ/_¤Å+9áPóöpxYúÓ5²¯Ç›þ_~øê/_ÎÍêí¡öÔeQdd¤±´Ö¬f™5Ÿ|.7ËåÅÌët¸\¶¶m$zl]êçõ¼ôÅKÌk-S±F+¥¤9‰Øb2·yâ¶ ÈŠÜb͈]R’b9T©ÛÕ_šR1ßÞÏÔ²F¼|guz:çáì7‡÷^ÐiqMsݲÃ7¹Y·b˜ŠÏ­w-±d¬tµí¼­±YlFØÀõe[µ) ãÄ"µs03,Ò›¦‰ ´r:°æ¹2ѯkPé.•®¤²5¯å¿~uòÓñºE8=Œèlî>©fZ¬Ø|€v 2ݽN“{ˆãÅ›±“!-Ñ,;Å %£»Y*B$‘>"šáXé,b:Ô5Œqž&pk[l!Šf].Ãé8½%‰®è¹jfŒEQªOåÂÝÛw­/\0Ùxh µK±¬èa¶©oYrvã~HáÞt™¹ O+;Z¦,Ý}SîÓ™@2KaC˜Åét –öÇ&cðËÅ”j’ãî‡teÃP¹`' A33×ÖC±ßB,ÆXc{U$è $ò|—Öhà^w¦g˜%ªi@zöJឹߗY¯Lþ(s§Dêoo¼ÂÛŽïr· Óè#dÆn4À£³Õö’aa!ht˜™ÿZ󂉞 AÖó•‹ŽqðÔ> †±-1Ø»g· w€Ä.ƒ{ªF»p?t’€{½µÝaG°øsÈhÅè4}l¿ä)zkEŠeü™±92€Vv€–<Ê_Éœ–°0å¨P;j_îÇ-ÓE€YЂDA}ŽXùH–ã•«=&’¯ãaìË7b·¾nùëJÓÜD ÿé¼J¶ÿö­°ÏZ¯Ÿ™½ªRø0%!Ã0.¹4%Çð½§ Ùý~¾·¸öÙÆA8™é´28"È"MÙ¨Zinfô²cSÇ0À9?>¼¿.kªåHd Ds(LaÝëȱ‚._¶ôŒ Ö!0e¬'›$§JÇW ïs©t︎¶\–Žà@ù 6 ÊÊÈ£Éäݹë¸GÿsD%%C2jÐh®Ê)ÓÒq{s3²n¯‚…æn’ea6¾8ÖH$ÒžF¢pYÌféS0dÓº×RM¦œX~(æìÆÙx:žþ¤v³ IDATË¿þSï/}Êö­EF2D+i<Ê·Ò"»¬`²‰6öűõÄšQX–±.&£G*{¯[fëYYNÇãÖ×ì ŠLeš x¢°¤WÿñëoÏ祷õnÖÍt\‚ízé–$‹Íf¸?Ü&"–‡¸Âx8ÜD¤ÛñpsŒÞ§CÏ^º2Qk™¿ùþŸ–ëš6õõz¸ñ÷Gÿti‡bïÞ}¸.—¶¶Ö²m+9Ë8•öáý»-ÈR'‹ûØ%Ì"ÓV=Õ¶X·í›oÞ›•Ö× {¸»‡eMª±õžì¤ÖíÓ§ç—UîªbóúÒ²míÿøoýe=¿,÷ÒÏ·7_6¤–‡:…d,_½»ÿÃÏO_Îë<ß~÷Ý»ÿï÷ŸÖMµ–ÃaÊØ~ù²^¶$`­íÓÓv^>1R ú\ýÍãÝÃɪõÃñÃòrùôüÒ7þî/ùúñöëoÞv/µ°çëµ¹¾úú7/O¹=Î[>/@Ú4§C¶ë—µÙ©øß}ÿfé­Gfêr¹ö­ßÝž¤éÜÚòòì.+käZ­&sš¾2—BsÅÍíÆP( \Çùæ-²/ÛÛ‡ÀÛ¶‘k‹CëˆÖs 7cÏnòêó¥C-ͨ.:ï&¹ØuA»†Z4ç”þ_þåíq:}ú¼:óþ Ãá°e™aïoK/¥îæÒ}.‡Ð:ÐßÞŸ^Ú.™ •ZO•ê…SQÉ­%×T7n€6Êf‹µXînçÊßüôãË//-ºà4 L'Ò#QGéïó¥¿\{hHâ"SV§ZmªIÚáXîjõmUÉ™¤´–DÀP¹1ªÉiŠž)“…Œ.ëŠÞ™doÒ4Y-ìH*Lª0ÏÏ!ó!’5°.•WÿiîÄŒÞ2€)¤LDJ¤K’…ïjÝž[ßÖ_vÌP%Ý8!¢ÑH RÁQ™¿×Sf˜-wô=¬ˆÜ°h D·´´4£ÊPGEº¬Hp$g ªÓi0S]·ÔÀq›í-/íähˆUp0ã²?M ¢Ìö(Õ0Þ ÀzÒ4zyE&WjÔì‡ÜÎvÇ è„X&ŽØô˜1®‘ƒwùеÚßæ¥½7eãô ÊádòAûÞAW6ãFf‰¡,#eÚ}6«c)äV 4”*>öýSü %,™¢#Æp|tØ‹£™ÈOoû‡­ëüMt0ÒÆêã?ˆ¿eÞ鵞ˆ£²À[Ùð:º"6J®FìVÀœnFXAÒGÆÌ 8n²î²dR9†ÎžL1™*ÀÀ­39`ç¢ì7KE¼®Öúhír¤×_ó={}rÿ"Y¦…‰ÐØækŸÐàÚá¯ûDÁw¥µ“ã!á Ã_"´Ïë¿&Ù†Ci‡øË‡ÊÙ`é,a£Ò9¼äIT¢fÄüF°2 0Òeα" ñ³Y(£M*;y½Q¨˜Ñ E’€•°äÚõzÞÐJ ´"Š (UJbhÿ¸Ò zfCîy­œÂѽ§äë–‘44F÷ùXNE[1:&ýí‰>>L×þÛ¥[R!a†ZŸð±¯7+CÀÎPIY${ïÙE3¹•]éXTl¦JÖ ¥§éhæµØAi0˜ãõ…‡ñŽVq¨0ù¡Z-^ËqªuªŒ¬ÇRêmE=æ,½‡ffaÝ„§+¡Í™y °–òðø¶Ø²…&ø¦È­ÝØ“ßÔ“"-‚ìNK©º@®½³-B«Ð±¸u—%à ÆÂ\Úùº•Ù"õþæî—Ïz¹^¨†ˆÃtóý÷ß¾<]r{òŒômøÃ”î‡óËÚ û»Çyž¶öåXË»*—çOaè в½|þÓ§s´ 9¿\Ÿ.Ï«L¥Ns)Æês-ÐËõÅæÛŠÛ©j>Ôëòr¬4L³‰e`CÊýãûjö݇[Ç5žk9õ.¨XvxùúÛoNÓ¬ÒòbS;*ywºq_·¶ÅÆ›»úO_½ÿòrþÓǼ9Úóç§çË—Û»£Õ{XÝ>_Ê‚}<¿œÏùùã¹IÿòÏïþã|üòôË\oô¯¾ùqmö²žI…¶´¦¨å8?ÜÜýøà`¸{@øq*ÇÛú?þð»¿üåü¯ÿ¸\û|¨óñ¶ŸNÇ\—åãÓGÏhËòó—çëµ_. ¸ÅÆ2ß>>úè.õ–ÉCmGȾ…zK/ýþîáùËóõé“2Ýëͱ´¶M+å¾Ì…Ç ÕK™NËÖû¦é`H÷:Í•PG^.—¦D Z*2·•œ3ÒÌ*ͤ:¾}÷xY/F¼ÿðhÉkêÐ#œõÍýÛ†‚Ô|8Nó/Ÿ¯×Øæo·­nNÌú¼Ù&ÕNK\ŽõôÕ~¹X_rJ¨÷çU=­@ "» Çi%ˆK§PliVš’%»¢‡±¯ËúôeY¯>oÛ%}ôŽû‰7^„œÁn%mlÿån³W£ãÃí\³tl úþöô¼t0²"*Jd1mD«ºÒ;Q‘b£ nµLÝLóa:¶vá¨*j5Ã~O!ÄH Eá&™Óhˆž~ß1j ͇$-¤H“eô„ÌŠº¥<ƒÖù–†×p÷Èöú ‡ÜcK5†I°EîVè.Á‘®42.ž6¢dcG’£ /°s¬¥h‚+G™¿Ù©cXlja§O Þoc'—» ‘6Ú ;ÖÞ™‹`rÂ=ý´_iàþ©ãÞµd%R¹»l´o¦¶9*–tr`\èã‹ïF‡)Åô1r¶ðß0z£d+OŠé£b 7cÕ<¢o ÅŠXƒÓ%q¬±cq)›Ç3ѺqòåÃp$H‡eéFÌ6ªmÙ d'2؃\$ Œšb¤íÁŒ1÷€Á>¼Qˆ¾fÏ])¯Ãþ”Ä@Þ¦Á»8xq™>Þhn¹£¯ e @̧2Ûì1T–¦Q5UÚ˜3}r¹ÓÄœÀ77Çe5EËôžµÞÜžNÇ-»$—LHŸÒ`ð:ÌÜÊ\ ¥˜‘óì÷ÓÔL·ó º=ž[߬a™ÊT 4!Š Ù%hæ…‡ÃCÆ5‰’¸»}ÛyqGl}H_.ë1£SÇb›º™i•ÖÔU‘6o?¼½_ûjàÚ¼dÂ^=¦Š¶ÆåÒ»úç%Ÿ_^R›r“¬x,çËKëT+ÔÙa4+Í7‡¯ßÝœ¼õs‹¦Ì8æ¹ÞX)—µcSkklm Z8d­5”‚ÓñQé——ó<ñ_þîïùôÒâôx7MuòÇMë¥ÚÍ|2Û̵îNÏÏOw§»Ç‡ׯr,syóööŸ~úíÇ_>š©±g$•­GA~÷Õ›Öú|8ÌóaieÙ–¿_·-%1–§¿|úå4OUºö‹eÙX-M[^?|zZÎy¹ÞŸâ×ëåüå|ýÿÐß¿{¿ÆñóÓµö—7G~¼,·ótûrÞ.›îOÇ··Çùÿúr½\¯Ï§úÕ»¯£ïN‡mÝÖ-.½9í8׈5#$z~™ ë¶:üXç—õz\—Þƒ­µãqº;ï2£«vãóMõÒÚeY–eÅépº¿ûïËjßÞ”K¦XÀÛ³ºº¬¬Ë¹ËܦëÃa¾™¦VÊãMíiO5ºyn¡n¤ÇÓs]®WÓ–ŠåúâB=M7‡÷×ëK­|óþ‡5Š26'·46‹ÀDn«J_ÏÎ ‹;ZØ,ÅYœµ•R¼”ÉiœÀB*¤g)ÕXóHàq^;™(Õ2Ä¢®&EX £ eKOŽAˆF5Œ×“ -sÍ%±k`)G"Ìj°ðȆ°7Fq·%†3e2 éVtëŽ)Æé‹>ž¬Æš”E í²¶ÎÜeqY$vÏ´$-ÐÙ@²F²¸x›‘ÑT™ØgÊ·tk{ZM¨dXš‹¦]õ˺k›"33%YZØ>ö˜[äw”ŽL‘èêRÞû†c•ä’`é6Œs>¤‚»ÁÐR€c<1Ôg¯¢gw MGHÚ‘‰LËåa‚1æ)¡ïWEOÁ07ËF¡jLáfr OÁñŠn#æ?IŽ]i43#jÚi€Ý3÷È>)O&ÍRFX0GkÜ0CÉ4±ÈôDFŽèâu8D}#ø?¨­;Ák÷ïÌú1Nì¸L½CwÙ³ ºú`uÁE’¤«p ³è°0œ.VƒK£^ÁÙãe°)#c@k>âja4¦Û.±)H™òd¦5ÆN˜Ù-l4×°ƒÌÈCæÎÏOKB†2VTzVã$ ¼ž0w¬=ÃòUã´Ü½< EŠGŽ|`€u”E©‚$\‡ è×™•;s §ñó÷Ì-,XD3ŽïÒK‹™d.3¥ ÏTÉ*xº£ÜÒ]æ ˆ%hãÀnVE€ÅàîK>H¾/˜: ul 3ÑFTtô8¢8lݲ¤0â”!r;»›é4:eBM!6"Å@Ü,&r#V¾æù@+íÑÃê,Dwmª4”2~½É,%v=k‡Ea®ê^ܽ¨wu‚ÌâªÝDZü8O§@gus›JµZ&¢ÉU%Cbõ臻l—£Ïåtˆíê^œx¼½õ6`#}ëEé[¬Ø„f´€¼<—ð©Z[·.¸¯ óTŒÌ¾f²8‹‡o+bs„¢{AC}~óÕO¿üù÷k Ïå݃>]>)ÓÒm>><>üô›ÿúùùós×:•YÊËÒ^–ÚЂô§óÓåò%Ä×XÖ&Ó&NÓ-Õ‡é_ÿé§ÿøÿy½Fõ­M¥ç»7÷þë ¹÷›é®š»ò¨©-2¢÷Ç»·ÅËóõ¹o=’™Ë<Íß<Þ?/Ëu}þë/çˆk6?žNÇÛËò¹o)¥j¹)¢çóeݰ©Eﺩu®È´RØÑ{3+öæí×ïÞ?½|Y×Ë¥M÷woO·÷ 7·ûùþáaž‹šÅ<™q†¢Îå²""Ã*§rºu7«>}8¬bÆÚ[ºÙÑü´ÆµGú ü>*cSDc"Àá´-3“2ádC&cŸ¦_\C {MVé5{¯‘ªLWfŽiÎA Ù¡î»òs3ŒioËh‘[2˜WK Ǹ>ª _Ž9à &¡0 ˜Ã… 0_—ýófcÙ;(RbØëÑ:KÅ`DÈ“,p±ÐYÜd9¤O…„©uð.ö˧±Œ¸M ¯F+6Ú!&²0³˜hfÉȆž÷4f(¥&G÷A©Ç~hV¾Z½†/Kl§7ï¿ÊÞóá t9T|zÚ®‹ÝÞ½‡­ùt¼YÛöîþxšo[_N7o¶¦ßþý?^Ηµ¯è2ã]-—~…ìæáaÛÎÇ©¤mû¦jU›¶më!{¸ʸ^¢”~¸;þãß}Ïf½·~=Úôí›ÇÛ‡²¬ÖÎ×:ÖuÃÓ‚­çœÅeÆ¡Gc¯¥¬‰ÌP‡B¥cn´¸¦dä©bSF6¶°Õ€Ü›ZÛV´åÍý{´ëš*€×p3gŸ(YFé³M_ÎKïaÈ¡SÞ»檥p«ÎÊ(B°BQª&y„™gJê ¼ÛPÏGDxÐÔ¬g!Öݳƒ ›de¬$Ò{Ìl2SOcú€ÒJfÂ,Ìà!ŽB;L¨Å,bÈJT³Á"4—(Yp[-•J0ñÝÒm/ÀY÷ 7 1)ë{ÝŽ$¦"”ž¥oƒ ÚS!ŠèÈ–‘ò°ò óøî¥#ÂL°àèjÃq—¢ÔSLÅÅ$f+‡FÅ߆;sôþ˜”‹Œá„C‚9`­¹t%‚^­9;Ùk¤´ÜÓ@þ€(ùo‡TílÎýª»½GGs¬EóÑ}IÊ@QJg Ì!•&Xˆ„º  )¥ý_»Ÿ²)ÀŠ´wö « ¦Ãضq ƒ&l¤þ5nÖV¬“ÂHiè×D›5—C·á'T¬ø^"°Wö;wå(+C€^iJšÛ,s/>Òaäž—ò˜¾•”º×ƒÕO¹èÁL D*•1b mt,Æ‘:ÊÈHŒÖ¹bo¸î1‡ôdŽˆ¹ÌäîšLôRe2:£· 2A¹•gõbâ€b<åE)qNÑTà>—FÀFª‹r΀Onf`qÞ”´‡‡Ç7_½¼»{s÷xs¼§Úló U'¼Øt¼?Nªu>xhk­÷^á@‚וlo§²¼ ²õp?œJÉØLnY&Äe=GOJÈ…k mŠŒÍPèŠÞ¶p!A"¬Ódמ†ÓÓuqÝ"¨h„ùœëÖ3Ô’1ËíéÍr~zYÖ­÷bƒ›µdìÁuS Ô&H*Pïæž——‹€èç¶,Ó!K=ôŽã)ÀR¸9ðᛟ~øî›óå¢Ö¯×åþX‡‡ÂJþøÃo§ÛóùÒOS͂뭷eYþòùKïõtSÞüñ–ÿñŸŸ_ÎM›;>_þýßþîúéüéóŽï&½ütôÏçO//¨Ç»ç˹¥~ûO¿mëv;¿¹«øt~ùr~9MÓ­ÛŸ>~úôü)Úeª~[k-ÇŸ_®§û/Ë ÂRúùóÏ[ž¿zûõ¶=jýðîÝï¦/ë•^Ûºùæí£õí|y¯7uîœî—e{ÿöûOŸ¿lççïÿ믟>_/0½}üªL8žÞ}øúùé¹]._Ö-Ù6/Þb…®¢¯[ïonßßœþøñS©‡ËeûåéSSùöÇß>|9×å—õú™¥–?Y¼{¼_®=uvŽØtn}y¾¬­·ë&ç&ØçÏÏ-y{¬“ Þ$]Η¹Lß~ýŠëÒçâÿü÷Ÿ.Ý}r4Ÿæh%c[—çäµàô¿ÿË?þþ/¿ûåsëg–ÞbÛš¶m;* õpj-[ŸçÃì“»Uï+·ÐÚ”Ï×s¤mͧ:Yú ë.f+ºåÁÕ}mÛÑëáVÓýãz¾ÌÄû:/K¨½„Úòü©G¥mÌ<¸©Æ\XF³­­­åùºrí2¡ŒW+‚˜Ì8,¤Éùæøöñ¶7 1¥¥UB#QÀ&GQ‘z}Žt“%UÔ“P&rk0˜É”™ Ф9zï0Ñé“•ž=úЮÄ-à(ÿ Ô4B¥3Ó5$+ÃÈ5Â+ãéÂá>«ÛÁÕ? Y]i–AO`͈¢@Æb˜;-tD­ÞÚ‚¦" â@Ö¤ =•@Zˆ0T7úrm™)¦°´NÆ&FG "Ò¸À,¬˜hÒ« Ê4þÔÈK@FÄZáÁ-4Ž]j0r´Ì‘‘Ic€WøUb©ï{I‘ñ}þË‘'âµ9(ì NìLòWp§,·-%ÈÇs”ü•Êi;Þ•tw©ÝMHYÜldŸ ‚.&H¹?Ï”‰ˆ}óˆ„! !\²8%ÔG÷½#Ì ±WÁÊÞ!íTš×ÃÚK•| _íbgŒùž>îwÐèxüíYm7£ql«öÍd¿9I“½fÌœÆ4j'v¢Oc¥¶7ë÷Ož¥b4+DÛ¡0îStp ½% µ°ÆRC9ª~Y0Naã)kÌÈÑx“íã#I"G¢]tؘ¬FRŽAᄈa6jh3鎚c´qÜaGåÐ^!"¢‰9 Òø‘FŸ§›ÉçP’”9P ÉÝ1ÇÅË2 %]r7#-†jBŒx¦© Y²•Á¯ó1zúœN‹QUÃæ=CHtæß8PJ ÂzR‘ãû†€1@f !Áè¢Yæ8·3£eŒoàÒÔsä˜âo ý?.Æ:\|1R÷ã#+Î2éÒx¡2:Žn(Ãîì4™GVT+u&8²¦Éju¢Rµº»3+ó|:ÕtÊ›‡7÷·7ï¿{ÿÏÿøÿõnMv\I–ÞZî{GĹä™ Éf‘U]}›Ö´.6c&™^ô(Ó»þõÈd¦'ɦ»«ºxÈ<—ˆØ{»»v$땀ȓ~Ü×ú¾\ÃY†Èk-Œ f-5OãþýãÛèØR¤7·ÃÝá¡,Åq†ÇóµpÈc>¶UÆãQb-5ãñîíÒjHO¨-A—Ò@w¸ŠqLÓ㛇ӼJ4Ê¢!ûh^£±UR*$Bº„`Ys3sñ×6N‡A+Dü*ƒ¼™äåü4—u-¡ Ê0MÃÚ:ÿlLZ[­/f!îµQ£˜%`ÊÍZ•Ãà9¥¯¾ú¡µ«÷oÞM¹ IgµÕc=­K5·°yõiÒÛÃÎóõ²œKNÃx<oÞ ÷Ã0Nƒ„Ëa?X5†÷¿y(ÞÖÚ–µ¾\­Õëzš?_ŸV_ŠaŸ„:ε…¬†"a‰uysÜýùÃÏOWW­ï¾ù¾µõÓå³ãþ渧ûù|Z‰—— ÐnS¹žN§Ó1¥ûýý²ž³´ñ¯¿|Î:"†§ËµYK™ïnG×X.W¸-óür>¸e=NüùR¾ûê«â+#Ïk…#¢]ÏYZIÁiÌÍÒ—Ç¡†Ì—U ‡ÃNUîö÷—õtµaY——Óõ|iÍürºÌóVJ©Ÿ_ÖÙìe™›YxDm÷·o–²¸U!k)áuY‡u½†×Ëõzzy2s ?´4B\äÞ­î÷9©ÔÂ!åZÚól+ÔÚõº¬æ&Lû:5L#¥§s~~iw7»qÐ1.YÊl5cƒ‘1¯‘x£cÚ!+>Nr¿šÛ­ó IDATÇeP¿ùšë:¬­î…Ç®áôt»S·ÉƒQµ•*ÙöSy8N//ªÝL»OË˺^Ão¾xlËzÜïER]]Xó4¾ÿæýÃýÀ)í†((ë ÄnØCÒÍýý¼ÎyÇ77o¬çl £´ÕÌ:ê)"hh} Í$èá@Kž˜©N1‡–EÔµ1`S"…Ĥð '<²W-«"P#è!ìôcN#’;’DÐófäc£(ÕÈ 8U·Ç¶q”ÕÂ" tÉ!dsM" €²'ÃHÓ@@É,Ìâ N§ 5‡á˜¡f[8‰áÚψ„Dª9 ï‰0—n{³ˆN‹Ú½ëÝô…&µ ²p¢õoß21Aï÷DL)• ¾Ú±VÚ]dýüQ„Ó¥(|%,8ºª7méÅ3t“oÒ—FüUjØìÑe4Á.îH! Ôî\î‰ïæÝTzоàâ–1Ñn~–nê3¸áÞÙM"}ýÔGlÇf5¦{¸›“î!Þ9\-zJA‰­k‰~OcHÄÆûDaãpnËDÍ·õK¤¾tœîØ¡˜L¯0²«àTEE6`¡öaL‚!DÚÜ“ýO!àá!Š€k‘~ûcï…ÖîUF€ÚO“›Ý°¿ ¾¡oÝÂØg«ptÇN/3`h¿'éE‡h}—ÄWéÏFE‡…Jÿ {Œ®ÿ¾Î¬ê{Ä®=JìËPns=¢+¤[h×)üê!¢ô½kŸÊà„¨€Öê8î†iê Ò¸=Á¥é€Ïè+·m¥#ç•Ô)êBQñÜß:‘]ƒŒDÀ3“Ô$1„ 5Y åm¡ät˜‡6Ó {ëmM]³ïôüÓ…p‹€#IõŽ•ƒhŠ´ÖŠ Ü)"1 »ÐÅÀ,¾qDL9’æ(®ª–„*ñèãµÑHHJÎä)å IÒGòÉA7ÍÃðöío¬¶HM‡šÜ³JÊ£z»woÞüó÷ŸþÏÿýðoÌçëZ¬¬—B¦,»BÓÃÈï¾È§—òÝ·_Ÿ/§û›Ûÿü÷ÿþÁ—¶.nÝßêÚ`kó~|÷ÅyØ¿)§óº,*y‰ç¹Z°ùèa_.g‹ðhIu7©Öj w“Õê¥QŒˆ”&‘6x`'‹¨ÖH&©.5 k•s]YÑs Iµš…9¨Âó’uSª ´†Ë™_ßN ñøöÞ­VÇõZ›ENé0¥úû¿r»ž Õ×5·Z HÊÜOïÞ===Íë2Nƒ¨¾9Þ~ýðÅÿôwøåôóÒR¬Í¦ÝpNóêueؤx¹žëZ6òÒÖ¥°]—y^çoo>?' QfØçSy9}Zj»Í\="Òùtšçr¿O2úÅ·ßkÎZKÑ1M3ÝSýþÝãdëû-^ü¼È°ËC»¿)oÃ˵0l—÷—Ó©g#æE$Ò×_þ6íÒaH§ëóe½X~•¿9Ž_§Óõ¤Ã ¿ûæÛ—ËI¨7˼šÇ0´q8$jñu­kkrÜßL­,OûÃáãËê¦oöˆ&.ÖPLÍäññQ8Ìny­•¥í¸½Ù C+n.ñíûïÖYO‹%êôæ–­àr £©è² VäN,ëÚ¬f•/îS›u8àæÅ0 "FÛE¦kjO—ëRª¬­Åu>™y¢­3µÖ ¸WÒ¿¼ŸvwãîxÈC´u]æ¥-6p9Ÿ0MVjáù0 «Éê©î†± ‘!âP7'Ã8 áq·”D¢E æî ÑJôCƒ[eêkŸD„:k3 7F#˜$ÂÑž(MˆQwëÜÆØãº ¤`Hêü%ô†¹R„.¾1Ð[RM”‚&ì¾’PµÀ¶]UR•ôäˆäž¨r'ÍÚÖ+¥ÂjCßüé–SfÖæ°­¡¦ï“AXF ¢mVa¥¢SãCm¬ËçzFªƒÓà öúRÂÕ%Øš[ô½”04Äz«¿Ï1âxͶ ¡¾ÉŽz¯°õœAÐûÎ&¶ÜHß/8èØ2ÚµÑåÙ¯åþM‡Ó[.ôàÁ ˆhgW Ñ!Ôí?øÀM_ÌáÏýÏضÏb¡»ô^£ £ú±¯+lh.ÝöQ4t¡bl;š× =7µ…—ðŠnèéküŠ ƒŽÓ-:»‘Jô7—rL¨ŠªˆŠ@æP ÕÐÅæ¢d"h/£bËlC];žC\ùý‚éÆ ï„(n‹¶x17RìÑýmFã¶qê=BD ±ëšc|}½t·»s¿o´íúÖ…7kafxÿŸè«-±'ÂZûµóµ?¢Ñ0²iÇ©]ÑH‚©Ã)FЏװ€³{ß#D(yÈÔ€Wסn7ÙíeØRmÕ@ê{Q7šSE’gkê½"PáØdÙá.(æîáMLA1a2ô{na(™&t­2ku_b»7‡¹v›v„ù&[ Ð_NÏËõé|Ýï<,(õ(<Äõ²”æ1Løî«‡<ÜíïïÛõ¬¬½§ÄäwwùZ/·úý›ãçó2q5[QË<',Kmû\Þñí<ûã»ï.—Ïå|.Ó¡r7í›×óy?ÝìäjËóË¿P½Ôn*vŸ†LÿOóͧ—ÓËézmuLû7Çýº´u½ÌËlëɼ¾»;ü¿º>]_)9›˜køem×u=­E¥Ô×ë bE¥¹ÅÇ———Ëç,ÞÒdþî÷ÿ˜~øø$e|:Z×â¡Ë꥕aLû1s Ô†)eÕ]NcrDê4cmA,mEèÿø‡÷O³—F"µ¯õ8ÝŒuëÚN@+VËlf¼,k µRsÚÁµ,¼Î(¶†‡¹Jd~ùž~~—AÈß¼I/†_Î8_Q.¨Ý3‚5Y™=Vk°Ìq_š6óPÖê«u“C%'=ÜnïÿðÃß~ÿ‡ß}üñS‹r)e›{WþÚ Â*«BFQ3zã4ÒU[«™’¡H"©†e‡›}!­ÇoiÒF¸u£C[ƒ 7)}t7¶Üj&U@š áPEêâ”.çŠî”N‰‰17qÏš½o+²£_‘2Ñe@jNCJY‡8èP¡„¦í”3hJ¡Þ$Æ@í”À@”X#Ö5´ÆÎp@4šŽwYJêPÁHP×ÜÀ4#4X¡4D¨hl¶@ºÐ‹<šV†ÖÒd}sÑÛOÛŠ„}€xGuDƒÓ€ﳇõxÀˆtn}€Fsü5ý+мŸ{| …lÖdn•° ;ÚÕ´ û÷J7T‡zŸ(©#³B;׳”:X^=tßÇ9íKZ‘ÖX¶q:Z@=` ¨DZã,.„ªlb–íÑ9ï°À+²ã':WKÓx#ÜØPèç>J'Ó+Uú¯Î sgõ÷©H"¡&_¤Tº~ŽAqôBáÖ”èiÿDH‚{4]Rl0é•=l!i(]{§Tz:þ•ëé9ï$+ö» 7zývÈõR¼UŠPHÇ>ô× SQú}QÕs_½È€€®=Õ¶Ú'oy¬oï¥B¡‰Ð­‹Ší¯á"~{¼e Eu$†DN®L‘D2"¢Ô`CˆxÊA cŠÐC$¨Hqè¶T2X³N^m!Ò’E Q„5l'>ˆf"P¥P£¿j}Ôe¬k(UL‘MÂÙS÷ §QèæáÕVÔF/<ö“d_ÅšH¬]‚DJß<_–Å:íLv*²¡iE“NS‚jRj˜@³ª$Jöu·iƪ`&‚"yÌäk]>þôôùååj/Ëe.†õÐHÃþ¸|óðTêSÚëÍáÖ¢­ËµP²*#mÝEXNP¡i¤hH¹žÒfZ³Xg;Ï϶óÕ¹…ûÛÃýÝ}øâb¾În«Š7[ÖÅò8Æ `cu#Xx)RZxWRì§®DsZÖ(ëÚZÑaºóF¥ÕV%iÖ<äI9¥dÝ3•Éon×zEïqØï§)Ec­"É×E’dJóf?|õx:•H¹a"¦Ç·wÍÖÒl]¯LÓtÈ In¦·’s*^Jµøýo¿ˆ¥Ds„v·w{×øç¿ùÃjÕ"»'Õ2~þôŒ4~ýø¾ ùt>Óñ¥±½”=G½V×|\ƒËyõX¾}3zóúöæO×Åô­—?ýøáe^ ˆhóu‘Ãà!%âÛ¯~ûWïž.õtzT¾ýî»O§¨¸=â\žŸžŸãûÇÛS]—¥­%n+ ·Ç»j¾\Ö’d8Üï¦éþðûÃá½­çy>/×çûûÛµÌO/×§…Ÿ/óýÃp>/ãЬµwwÃeq3=—ZÊ Ñǯ‡ýÍãu^+âúævw]g`‡3‹pq¯ƒŒïß°¡\æJ$…·æØï°ŸRx»;Þ˜ÙnßßN³U“š]—j9ݼ¼œ=D³¶ÛXײØP«‚ùÍÍn©§°º”vsÛÑ"<ž_fF¹©k»œk“N¦¾¶50ë Y›²MƒU/Ï']×E…æ4?åÓŒbðù€ßóS´ óµ¶k50 [äÁZs§31 úÕW¿Ñîóþþp÷Ïÿüå?þÿõÿþ°ž^.×çëÕ³êqt‚M„Áp!r3-áæ1ŠöÇë˵{Í¢ÉÊê U‘V#¨ÃEu…¹:°ºÓЈÃ" )EÂÊx YÐ#L,¶Ù5PA&óîî9lvAÿLuí8E@IJÀDõžwÕîòìž5A•,€P€L¡‹ûã(e QO@ÍÚ¤ª‚„0J´(‰Á¼·!圆¡›y4)F7fÂ=†¾JjÂ"hÜȶYiDæÆ/N7‡;=àeÛA¹Ã½#º)„ZŸ#ººP‰N„n‰€õ"”oªÈØ6GÔ  œè}R2<¢gve³Ý½Â½·ã]ªÌ­ØŸZô l Îþýéáž§EhW"öC™z BCz.Çûß¾‡©ýïïL\›·ºó¶šéÁððØ Ÿ††‚]AÙ#ù[–å•Ìî=i¾a¬¼ŸŽ·,Q/U¢—ô6ußv9¦»Øàœd¤Ž"§BE¥M‰$EƒAéÆEäN¤ï~¾Çꑦ¾6E¿a© ú‘46[w‡·JôߊHÖ—"bêŒëÕ ¯Ä‡EÑ ÁpöT·h?köÌ#´Ÿ¤ápݨ[‚Í ´-7»‚9½²k-$õMÛf‡îÿ\™ú-“Ñ×h©3Êûöpó: é)à!Ð~ºV@{….!±öó5Õ@‡„77w¯­×áH΀Z„°Ï/ÛYUA²IcÑÙ¦)”Otm '4 „7(M" ‘Â]z¶M´°Í)Ù ÐD8lˤUÔ#ª(µÿtcE¶Eí,4ˆš¹Ã¼Ç AG‹Ø¾1Òk”›‰ì… ¡†rnn¬´œjýmªÔDÑ,œ¡&¨fròN –J0¥qwLJDRb”‘ïóíÓå9YDÂu.K)—ó|)%Ú(- CVŽ*—åšéûÛÝ uÖqwwûP.K¤Z®á«ËÝýãÛ{/®ÖDÊ@c–É›ÙP€P /Ëb× X»±>ëÐ8H¥Ã÷u]ÖÝtHªuõ…ë>ç¬éZç!“¦ ›«I¤+ÅKƒyI!Çœ[çyÝdãˆj™énJ@µö7Ö ãn±h†›¹9ì lQ58 ÑZ¹¤5s—”†s±¯¿|<Ÿ-ZÚO·VÌÝZ¸Œ)¿¿‡Ûj,Å•)hÕÓ\ê 7ï¿øâ8áççkBÚ|7=ìwI˜Ýê»/o?Ÿ×enVËnŸnÇ´Û¿”»áþ‹/»Ûi°âíÝýûœäî8:ÖççÚj¹.Ëõß?ž>žÏ£ÒX{ÞìÆ»»7×ë¹¶šš}]}]ç§óçëù²vâçŸ^£–²ìnöËür>{ö—åz~YÞ=þ¶Ù¥µEÒt¼yxs³?ÏŸ^^æÕ^®ëÿå_þúw¿ÿé—±ž¯KYj».hCÎ*íRN­®—Ÿ—ËæÒ²òpØýío¾ûÍ—ÿß¿þé|=ÍeðZj©5aÑÃîv^çë~öû1äp>ÏI§››©¶z;Œ v“|õp—ö7ß}ûW—bVýùrê ½þ3ÎüTk+Uúpw‹Ô><—çùêц”æe¶²Žw#ŒÃÀ„5g=ìîÏóâ˜~øöûóÙÝ–†R(‡ÃoX¨mG[m]–«›RSÔ¡š# “â»ßêÓ'¤Šœà (8}F¸ˆ¬„³Èâ1±Ïy YZmp!‘§‰YÉ¥¬ã¤£¤u-þ)þëÿõáéãó§§×kYXˆPMÎ`Î7‡·©•–=Th5³®Wƒ9 æPA(š+M¬¼½}ÓÚj4‡D!!p¡Àû£>àf†v³Ë;¯^J„»÷§áF¯]åá°ºµ]"D“õòP’¬bFSoº!1Ô`ôcŒÌ‰ØØ¡!l}þSPe :F*3c Y)ØN0&@†H(¦êJ2‹­ïãMÜÊX<à„+d :ƲN4cC«"}ÅŽT€F˜³h èî0÷JgCXD³ºÎY{íÝè$ÐPÀE\7H:¸å~‰I­>ß*m å(Ý‚íÿ+¼;¡‰ ‹Û¯È§-*¤} ²ÝÕ=žÓÓë¡ïÇTûõP@v2 …Ýáݳ]a×`wNwMWHWNwÊ3ƒ¥N¼Ô.‘paD@Ñ•vÒS`¿b#d>úžª;ˆ|›ãúm øõ”@hŽ[´»¿Úñ Òå¹½ö)=]¤*xçŠvŒ• Šhô¯”¢`ê¨|ÑþTd¯j¡œúa"ôø50¤Ué0§w¤C×ïÑqø¿²G7EO—…w@?½7]‰-_ˆ¾ç„¸˜o=6€Ú)² y•élVËp:% xt—’t(|wŠ‚QÂzî" 21TE{Aÿ‹¥hù7O¸÷˜'CcÐð-ÔŽjUQ²AÏ{Ÿ’Cú¡"LNéJè¦îMщä@Ká$\Õ Û䥞RÀ;4Ö©C%Is7iA$€t•ðÍ_Ô?wyÿkÚ¶‹Sx:Ýñz,„ö&* ÎÏ•!š†¼± :ÑF³S`H–,PB©) š•AIeV0ZJŒ*:Áó éýÛ›báÕÚò@ñë|’ðÙ®1¯åå|n^Å™(ð)çÁGKf¹=Œ 3«=“Z—ºQ)haHLÖk¹b˜BH%è²ê ÉxÿÅoÏ/OEW÷ÌAÜPBCåºK²®ÅZH^çe^jjµ¶â­´¶Œ22wûûj±ÖKÿ‘jŽZĬ }Üï‹ÅõZIQj«§ªŠ¸D‡}Îäq^j€ïÞ¾m¥†]Ӡ⨶8Ž¢ˆ~{˜¾ÿòËóR­•ýq×ZN ŸOO€ÞÞܤ,ûÝm˜­s‹d­°R¿ûîoïÆ÷÷:/RMRÞîwoo߬¥Z­1'ëRœüêëß/×çýß¿Ûj§Kýã‡?>?!¸F9½œ.ËùéZ‰x8Þ}ùö­W瀸Äóõòã‡_~úñOMðOÿù»ó§?x>Üî×â_ïå§?~zþyµv~9¯e–æc–c–K;]Jh¥4§?Üî^>?ïwé¸?þù㇥òé\‰Vì|)VË<—­>ÜÝ'†ÿÝÂçu>¾>×Z HûãíãÛ/Äy¹zk~< ?¾|üå‚¥®Ã¨dþë¾ÛOûÃͱ.A,Ëòy^Üën8ä}¾,v»ß¾Ô§——µÅí$:~9/¯¡)¨ûy> 1 ¸9æu.ÎÖ1ɪ|ss—àÏ/‹A ¡¶¾\Ÿˆ*Qw£Î¥®k­%îvC¤*®%RYÖÕÝD˜DDéY¢‚åá çsikú|nËì«AL—Z9LH ênŽ|2‰kk«™_TƒÞNV=l’¬ÓQ¾ù毞¯ëRÃ=ÕÁÊÕ«Ùr./óy¹\_žŸlP¬’TÒ´÷Ô¢J³Õ!BÄ`5¯° °Ba4‘èÁDŠ"­ÚÚÜ]Tˆ©@’šmfˆÁ%‰4óui‰E·ŠŠ…H[GÄ‹7&æ@ÿl<€^èk]£`È©ó@ЈÁé©S{´^œ¡ª!YÔÔP$"EF¿;P”¤“‘EHîŽÝLÒw nŒ´²†Š[öl SzÈ„a4,ȈF¥IÝNu¶4Hw…Å;;Ê{D=ASÔ°JoÖz†ÜY¨”ÚŸpý&Ð×HÏ+ÖóU³yठ»ôtûËäÞE-¯Эt‡­ØÌ®‰~ye3mdn“V$øÚ#ì1nÉŽ K ‘Ðõ‚9¨)dc¦ BºdF$i¿Pt,B¡ýI®Ñ©5a4=0e/j²›p<ÔI‹jŠMòíù=dÆNw8·F[¼Š–ÜrÖ½ X1uï‰Äžêñ„€¤kBãP IDATÔ[¢éìÔþâôD¼°ÇßU$b`ÒWI kï5P‚Ñ?¨0Ü·Uc‰:PIÜb;gv¥PŸòÀž“êêH¦xuS¿~&Òh¤éÉ¡AXW=S: †éÚ:n&Ç-qÈ-z¤¾¿ nÇÔÜÍBë+¾ž|”NeíŽo‡x— ŠHôû¼÷-kzU»'†Bhd×>E2@ tA¿6ŠC).œ8)U4I˜Ã˜ È"XpC£‹Â»pY@odâ$QÉ„õÃ.Ô©Bz ž$bqC4¯ ;‡ùËð8Éät‚FC<ÃôhñÓçsë"i︾Çj½ÁAÑý8Õ¨î’Ü.â@¡s\˜yHÉ‘4ÑEƒƒP)²)õÕoçõÙúPOQ Zgï{fNˆq#'¦ãa÷îíãÝýítwÈ>yÑ<¦û»ãán\`ïvûwïb®5Fd¡Ô‘¤+‚Qaò¹"Q²pw*“*Ö¢¡S®õjmVÔ…QÑBTBJjzÈS ¬Ê8„*k­ý¼¦´ Z´——˺ÔälÌQéÈ”ÙÜ¢j©n^<Ñ^©`䤈´EÍøx;•ÐI0ä¶6Ún?d1í\ºáNǃ¯ËËË|]Ê É#yX¢íJä¼oçËi-Ynï例÷hH—SKBÏåTç‹ÇZÚ¼Ûí$§³-uuøÍ!ýõ×_¿¬3ÁqÚÝíËùô·ûíªS©þþÝaÊÃuY;0íù¹H™ki—z½»¹ûÃ7ßþôôᛯÞ=¿\K 'ŽˆÛ»ÜæÓ§?—=íß>}Z]ÿöo?Îs FbNiwÜ#çô÷¿ûáøø¸ß¦¼+Ë¢û¼?zµv»#b8_柮O×ÕUÞÞÞ‰¶óùÓù²ÞÝŽ9µ»ã4f\êR®ËË饪œÏÏôõýÍë4îáu^ëã¤FÖe™çO O£¶¶Š`--·y¹|’‡•Æ^5ó‡‡×e–i÷ñéór™‹ùe©×奬–˜¾üâK³ùññí:_e?Ž‘HHj>ì“Öz¾Ö¥4>¾¹¼™U»»½«E¦ép¯K¥d­æu©K]K±ãTÝÍ<îïîÜ‹@ /ÞÖof?ŸÖ²Æâ»µ¶V²­¥†‹äˆ8èÚ ýÃ_\Ý—,Aé& lS›w-#IG„È$ª@”̤KÇÄ1BrǪ0C(d¨Šh‚ºÐ€dgÀE$¨É#EDb i!Gx¯&†á-$\Ǩî)œ °ý¸ ˆÒìΠA-Õd0êÖKŒÚ?m;ÇK· Óá[-=¸ÕT)¢yÌy<“ ™™©ÃŠ ª!«ôàcš×S5*¡¢ acÿšˆÌˆ}Ž,­¥A÷·û·7_ܼ¹}ûömMŠàÚÚ¥¶6·¤qwùÚ"òþþî°¿©ªYB›dƒ+oŽ·.»ª³°©J ’1˜¯ÍA¶ÝþðOûÍOŸÏmžsn†˜=‰ê‘ÓøöëïçõJÚaÐq.kA5‹©ÔêÍ]   iLîÐwD]CÜ<Sšµ4kL²Ó!¥ÄH"<ìs0$p¼™îöšâ«wK9ÕÅ(9ćP¥¯ )ñp·OÉ£˜tœ¢0k›ûznAÍZvÓxš×ÏCb´Rm Êpˆz]nÇÛ»{Ññv·?u®òpR”òóósŽ»¡¹|~ú¹ÎϺ÷ÛoÒòÂw·ÓÃ×û6§ûÛC-kaNmÝ?¼¯_}ûû/‡ýñ_úô¼”“6¥áöæxû?üÕs©¿¼ÌOO?½||mµ]:ìoï¿ÚíÆÿò‡?üòò)Ð~ÿîvRÐËZ’ûÉÎO§u]j-¢ðª£—˧A§Ûãøp;üôò©]ëåz)k]­=_fÆNÄ~þð¼ÌÏ€ï¦;d¾¹ÿòöáËRžR©ooÓ·_Ü×ÊÃQ¯—6¿û›ÿáßüo7ãøþááO?þñßþüã2_Ýcu+ÍE¢-þée}>—d>¿\¯×z{œ\’UWÙ¥²\«£–RÖEÂÂ%1ܧ»»w‡ýýóó‡æë<'È!x½\Vjž¯òßÿÇÿóç0»=Ü Ûnjã€V£64¦µ…•F´Ë>õÍÝùåòéÒ¬FãÑ*T4dÑ,Â<§»[ÎKcع·h%¬0Üû¡E-DäÜR4(Ó4ÞŒË\Z™s +wÃà™Fê KˆÁÝuÐQP‘(0J9à*£+ 4ñ’ ÍǤnaš‰É‰BmHâUBojÚØ(õ$mÀÆÞ ‡™°.­6B´õÃL(încI1£7%Bøùrñ Í6˜…`XP2ă˜ ɧ4%À}`0§ARöðµ¸yÓš …'ôè-]BûB ªÒjJNS¡íöǨkµÖ4Üz¬§»,_çµ»øû¿zYít9obu³ZÓ×ïFÂÓ>ƒNõQëíX¯5Þ riû‡ûÝ~ǧ—:ìÓw_~÷éùôáÃç,ãîp+ƒ~üèüã¿ü?üåÃ/Ï ÃùôÙ%¿\®žx7í˜üù—Uôòÿõ½–lI–Þ2³½Ýý¨«"n¨§RVe%ZUÝÅ8iýWÈÿIpÖUè&+;óÉx/®:ÂÅÞf‹ƒí‘5z!÷ž8nÇl­ï;ºÙx­“Y}z~>ŸGùòíí. o^Ýý?ÿø_ϧ“IÊÛ=U—˜/§OûýÍOßüeÜå.éù‡O?ŸKO>k”=ÆyZ|¹¾¹ýðþ×µäy:ï÷;”SŸùx:ÎsíûZΗ(K©Ë²T–ù”ûξøê×uöý~Xާ«]rŸç§ò´”ççÓyŽ¥Î¿üügÅüÇ/n^žzü¥Dðæj+’Ïó¸Ì&IöÝð¥Ê<XŒí¶WÔ¢RVŒË\Š/ËS-%„Õçœíût™Ï^çqNs¨YWÇñR^ œ*Qæú˧‡y)’Õß½ººÙPC'—…Å£ï §©±Æ<^Ê8 %ÔgßnZ°Ù¥ ëûwûÿù?áûï~QNl…VCV ¸¥Œ(Â+EL:H…ÓU¼ß½zÿþ~/u¶:•ˆêl o •9÷‡ 7IFQ Ö˜FWJ¸¡ù-–Ô%—Bó ³Ò³x²°\Û%D$*,P ƒ¬êgÒš€ZXD¤˜d}w sa8aŒ²¡ÐÕ¢ÕŠ+ ™BS]AÕª."!ŽªªF¥+(š`jÐMÞü›?üñ§—‡ê.±H™K¸'!+Â5k–d}‡+ME…N ªÕDg§p)%"¤& £¥–œ` Y”wù 7½.œ+€0Ò‚ŽÕ¡A­$•¬ ë°Cd½N¬j4XS´É´ (šDé±TT!± ìÚÍ´Á Š dõÕþK˜JZ«MYº6ÝÐöN-`ŽP1°¡w€µ1Ök›®fM”’LEEI1mçU„ˆ}&>4Éªš©¬Æm$U !bíÑ™FË9H˜x+1Ò(¾:ZH˜!ÚT}„’Œ~NNµEV;Ë­›»öcœmfÀ®ÁJ0’q³nsP…2 &Öä~jÌ­öíZ‰äjl°p$H¢XÒu§#ŒѸ  À`î-($T¡#ÚOhšƒ5@DÔö?]‚¡*pA[ Ã!A€NÐÐhq¶[¶¦|¤Õ…XO€*¦d Ý-¸ ˆ4žðZ6’çÐôy€×öúI0cS&iCt|†ù›¶º¥y‰Oh³”6e»Š(Œ-fɪÕTÛ 4ÃL 5ïDLÐA’’šuÊdjLPCRÕ¾e,ÕM ¦Ö­­Ï(U¨E‹ëIݵ@4zÕZ†dA|^>!,Y›3*È(!(¨¢Pc©ÐÚ:5„š$¥”kxCòU_„YHÑnµPA!fª&ANfI“ª¨%±Ð5–Ÿ~ùùÐ`&–DÔ’B"$¨yHï_€/ sûˆkfÝáúöfÿö‹·ÿÛßoþÛ·x~x¼”iY&IžÂ™D²Áúa³¶S‘vÃû~èEô…5ÚÒ¹Rz­Yò`¹ïÊ<ÕRª/W»áÅÇç§Ëè»íŽsÐ ôÂr=žæÓyjrƒß^íT–Z餇¹2¢qs¸«Ëe»Ñ¹L¦aŠZ'Rîî>ìöÃé|Œ(MMû›·×Ï'ŸAd‰›«}™ÏÏÇ—Ý6WDÙn7»m·Ýl¯6öóÓå´Ô~ÓÊÍݽ˙µºÜ¥Iµ»ºz•Y£Äî°=^.¥üÁývH›Üçþõ›ëðjÌûd…çKê—²¤¬Ê¹"¸Û\íûOçó0¤ñäÃ~øâÕ»¾?ýùz·ùøË§®ç`úöí——K™¦òúfçœáöËÇÆñRͯ‡-’ËtȃÓÿíÿxµíÿôÝwÿøíÿ÷ò|<ž–~Ÿ>¼Þ=žŸ_ÎÏóñá\F™uîvwSØåñå<=ÍÅß½ûâzë?=^:­_Ýþòóßüàòr>bHó™ÛínOã±zÝ6ׇ])yss÷îîv©~}óö§ÿ}<=WÁUNOg?žjH®u‰e™ëâ¾D­Ãnûéa~>ž/^IK"Ãfs½ßÃõùxI*¿ùæ«û›wã<æ¼{ûæCÒ)'O’ræù|öêT¹Ú¿}y™Î@ê°é7»^O§r}µ¹êú§— C‚DÅ+-/ÂíÜk²JF”e~™ë¸P’-Ž›«×¯ö·áâIjgSöEèê¡Á”6ªy™ì2»__]ýòÏ/„.*‡>¹"8½¹–‘\B5çA L”I™ÛyáŠ5g„'­&S¨¦È º¢(LC˜ªZ·9ì^Ý¿»¾>Lsªmœ^£)%º€h’$"î©"͵`Ž@8fK’:CE AQ‡3L‰H UèTÉ€€•‚pUq!W?cÜ¥F#EVQ¦°¶m!U“‘‹oˆH–”àÃóã²ï*©©õȬÝdÚOÈjÚÄ¡€éÐIJ¬5Â¥¥GN¨D®Q™mîoÉÝBдJ…#D<¬Ò©áQ=ZþSI—UY—@—;ÕUZl|¬vÓBó­±¹b!”ˆ¶ˆ5E`µÊºÑ>—á´ ›Û/å²îK°F¬DÖS`‹ž«kaPÖ“Ž1¤MWM¾‚¼:TÖT@Œªª%…ª«pÍ4´AŒm£!) ê£Hj|ΖNnJ"$Ȭâjáø9ÙäíÖ±.n´m}ÖguÃiI»6E[ÖH#ï_ŠV¼¤7äÖa#Ö Ûî—õÚX¡¶×ƒKíŸ* ¥%m¯GÑØfHôéåñ<—0ì;ûøËc™/Òí“”iûÄΗ§Ó¸ArÃ2ùq9—z¼ºn“³öÿéßþaœæ 9¿ŒS=>~z0Ÿ^]]õ]wñrÿîKªÆÜI „{6t›ÍyœÏóy\Ϊ]¶´Ý ÷W7C2ËÃÓùeçó21ý>ÉHH¶åîvk1Zê2(w¹,3ˆZR7¤¡·Ôå‚ ÙÓÃótž"<4WxK^VXÞnöï>ü¶”—RÂD+ ¥–¹º0 ›~øÕ¯~ÈS}ÆR­ÏCŸ…`ñHý`i¾ŒË,eƒâYJékñ÷÷Ã2˾ó.§n8,Õ—ÎðÙkM*ÖÛë}o I]!λZá1n4m:™–:ÛŒP Š^BB)Õ=j«ª%æö±Ê^m®Þ¿þðî‹·wý¾.Ëée„V–E ȨRB4* ’ÊáñÙ«ª)ÑË"á‘*"â.ð"°¬zèÒRk–ôÕÛW§ãe!-·Ç@ (5HRBÂE2šk‡2X¡{U±ªi+pk¥6ƒ„©QÕDæ‰nUBMP‹¶ÖInaj¦°#L³¤ˆ:/ŵÙú¢áÂT Võ~·KÝPæpÂTÄ“‚D amQ!Å¢x„– X»ûP¬3Ýoº©q¡€¤"Ž ±påX®lñÀz ¤4÷o#q¬k0Ä͵šhp}7:”AJ£óÄz8[»ùÚøßhCOƒ¹ó_€ø<ˆ58½X›\UÒë–M¤jÒ í¨$5ÖDÔÖæÂR‹¾4§¡°•èTE„µr£ˆ‚! â$•(ÚpÛŸ¯-À¨ŽuyÇuÉÆq]“ÿí_¥™‹À`D;ËÉŠ·w¬é¬V¡‡ ›ƒ¢ÑüWÕ©@–ÊP"k9µ ³f*‚âs›Dh¨M€Ü›!ÞÆ2_aòBªHPÖ•[õF6ÿ&DJ8ÕEÐn“"l?¤­œ„±`‚h¼¬5²×îÖèksAÂlekp½JjÔ9ƒÓ .BU¶Ñ¢qi‘»$9'µˆ@ b©|ΊHÖ,`·¥ ½4˜ocj¨¶^c{ù4Ô’¶S1¤ÔÈ,f™I»ÔwýRÃDU5#IC‹ƒ QÛüá*„‰PͲ $‹¦à"ÕÜHMt 5j1F¸F2™ Jгˆ š¤õvEÐ>ÓŠHD,£K¨®š{{ÁG q¸4p¼V-ó¼ÔªU“¢6F‹ÃÑ$ˆÎRR ËbYµ3 3¨Y†Š¦’j,¥véÇ$Jm]ÔPMÁ”½V ô ¶ÁŸæšlãu)Šéé‰,Çç“&Ip£™Y]¯š*y|Çgë6ª9–er_.±ÔÁv{UŠB¶jå2Ÿ‹ÌÏÏOóe¢“Î`]–‚99XhÂÛÜݶ‹¥rYjUjo†"K戲„+T™’ì{¹\Ê8ÏK¡©\__¡è¸,fY7ƒÕÃÕ>JdI&q™—”º”Ð圳-^Ãy¸º£ÔÎì0Ø÷?ýòñátº”:¹‹/î_}õþK§ ô<_¦Él®åá—ãÓùùxùy qÛ«þë·_G™ÿø‡¿?>=Òôþz÷ÛßüZ¼ËЇÓËûîûÍfsµ——ó²0¤V²x²€”ù2^~zwsØݹ’¸\mºÀ"ŒÊòô¾ÙÿùOÞm½.SðBõ:•åéùxY¦Óty¼œ|¬óRžÆÇçOåóÕoȲ¹ÛÝÌ¡‡®ö»mÀþòíwøòËÛï~üø—?ýô\ÎÒå7··ÿñßý»ïønžÊ‡û÷UpØïo7ÛŸ?žfŽôÕõõËe9Ž/‡þð¯þø¯¢ÊËeúþ§7ðÓ4†„º¨Lïo®m°p©5'<>>ýÝ×ßÜ`,Ô«C¦Ç¦Ïeœ†œfaä®3íså—××—•åååÓ4&õx¾<<~œçR˜žÎ§Ë´TpŠ˜å †>§¾jT.K¯Û›——çi¹ˆjÖ¾YTÛí0]Óp².sˆ¸hh€¡Ú'UÍÃΗée¾\„DŸßÞýôò2Žl7l†$]Þ´¦e¬ÀÐïïnî(8>Véó±”ãi>/QÇjÙÕiʨ²Ýp)‘¡¦¦é‹+šü ³[Ÿ5(¥V‰¢fN‰Ä`H5_@AÑšo7¯_ßßý_ÿçß÷g~÷ã_ŽOGiú3B-ˆXßÕ\‚¡‘Õ©%™‰H HQa&Ío [sA^H›‹“ŠãZ[N½zb«ve Hñ*³‰‡¸Ä"p²’‚ÒÂàÞòO ’=-îŽÕúÛ¶ Q0 Z©I[GSà∠9(nf€Øú(o¯O6iŠ·ÞRKþȺÒa‹·«Û[zûèjAæê© ¬4¨ë^ÃHh#gk൚Sk1¶ò’*b &ÐjV1¨ª*%QcPi{YI ö¹©¯ ¡¶N˜-x-Š @g4±´x¬ª†Oý@k×@¢U?¹fË„ smN¶]‡µüõgÑΊ¼X¡WHã_´y«¥êa›íMƒðCµñ»ZI®ÝDa&Íä"*  Khã¥BŸ?d¡×#(¡ÑòVí³MÀil¥P >'ô„×u>lɱÍÉD¥ ®Õ„ …Y-µ6 ZÍrMõAš(±­ßBBu] TÛJM× qÃ7Éjt^íÞŸ“[ývmÂÅ•âKPmÝC~V@­OF¤<³Öº@ë¶þß |†³1ULìZ€IsÓ]igÚ¥.YÊî¢)££¸¨©Yb²¤J jˆ¨Z2!¬£Š;Œl|TÚÂ*…ÁÚFÓ%aD ñ Q¥3ÖÈl!¢Â Ra¥ s‰OÿŽ IDAT½0„Pß6›µÉá­Oèt:¥ÖŽOXB¤}i/`éLéT&¹z³´éöÉŒISC‚Š ’E•ÑÚÊ+k’”$T“¤so ”€tˆªK¢AŲ*Óìs]äöjóíñåùù”™¨¤n¼Iºªº¤¥$®·º‰¼t[Ss°`&ˆBÑjÔŒ¹ÖS,I*M|,¾ÐQ$ dÑ`)í«l‘!‘´F.»NÅÅkîTÔ|dJ¾”É›íaañZ4´¢žçpT«Ar O‡M§E“Šæë»÷/ççñ\ß¾»³˜áQÔzE2ýb¿ÓB½º½–¨óeZ^N£‡Óû÷ïž/5dœb*(˸TÝvÛÃîn\äeºHwõ4ÅQK½œ§~³yzþù2ûvÓív{G=ŽÏ/—Ùîø‡ý«ó4I”ç§2 éöj7;Ç—_Žc·9¼>ì·‡«OO—éŒè/#ö4»Ü-:Ï“žºÞ.»›ááåqS©–×Ã8•ÂË—ww_½û°LKRßÏçËe:¦Üç>Æir¹¼~û…ûüËù±²Ž—sWr•æyùów?6ñzwu:OOÇóó<_ŽçWÃðÇ¿ý껿}~:½œN".™ö~}õÃã‘Ý~PÉþá–y“{˜ž§ËýÍußS™5åÝa÷éy:lïó›óðË÷çËCêÓ½œ¼Ö1I°3”ðwWCJ»ó\.SἜçYó¡”ótö¹Ôótrx‚¹ÇRg•~gMI¼.å,ÝÍõÀêçy<_æóeOç«a£"›>ÿþw_Xêë²üþW»ÌE,¿~ý›Ët™Êˇû·‹/`­n¦[1i0)xbh­¥W;žçqVaÖî<ϳ›ÎT´ú䌻»7¿þføùcýåt’™ÀrY¢œ»¹=lL–²°FÕÔå.*»»!‡3Diš=8Ûè^K +!$ÅÕ²%+5‚ ­gˆ™›•g¨¦ŽÿôÿÖ¿ûöñxzxyhª´ÔåÁBL€º0gS3ˆ·0ÒŠb‚…†’©qPaQkN!Æs8¤DU…3E’gjÔê Aª B4¢Ù†)ñ9Ý«¥=(" C„‚„„¤*4ÑD³5 b F‡ÕX‹I•šÅ´ÑHÕz£Á„šÕÌÂà"V£ 7xAÚ¢”âu©£Š†¶÷Ö*5 „yEU)ˆ0• V” …AV@PSa)H¥0áô¥ ×챤¶± W°ˆI 4´{8 pYY ¶4@hÀ'`®l_ü¶â Äú4¥bõÛ¶ëZô¥Iƒ´|´Aµí"DD5!‹Ùçœ|ËR5…½ŠQ%Á”&ÆXÁ£Øø­/(„25Le]jB¶À3ÉU‘¨µy™¹J€ &lÉ$PÉh…OƒF¬”š¨Fc¨¶_‚+j Ÿ7=hô­[U•+MÂúí­¬lzc‚µõU›ÏPD©©-©!ª’¨R´Aè#\!¬…íKÙºA¥¸ŒvâDD%=Ä®(*tª•„Hmµ¼5¹ß\Úü1+h èÛhNŸy ü뤄2 áÑÜß«)µBb»é4$‡ԹpvG˜V*Ç.ávØl¯wŽûÍ=-\‚ˆ™D}uðe!&S›%üùÌR j¯ÝX/Ó2¼ÎBxµ›ÛMxT/ª)©i×I¶ËXNǹr‘°œãééçqœ¼Îî5i 1C’Ôªçq\.^Ê|:^ŽË\®oïÆ©,3Le/ì®oß¼ÿð»iq•ÁÙ½¿Ù§¼½»¾ŸË8KªUjTR‡Ô_ívûC··›~Ø=?¿ˆçýa?MóÇÇç—ÇY•Óå©.ãóq>ÍÇhÃpÿÅo·»ûùçêÝ6«)§ºÌãi³Ýì×ðéÕa7Ígé7ß~ÿ©èvùù²lRúêíM ï’ôÇJ>Oû.³ÆÇ燲Œ×77Ÿ~ùô—oÿ{ˆ¤nû¿þ/ÿLJ«Û}W¿y÷¡Óœ‡wƒ¼ôÚ¹ê¦×eº’§Ýþì—Aq©ã<â÷ß¼Ó§Ëñçç³`Þo6¾,©^ þúæþõÝí§Çǧ§“—å2^»¡·ã\ÎG¯ryþôqæÓ8ãØí®î.çOKM§EJ^¦˜*D†l>^¢bþr>3ôÕ݇óìµIép¸%Ã# ŠÚvØtïo¯/“ªIr©Ü¥éå¤Ãp5/µïòù‡?žËé4•·ïþ0NÇ(K—ü²”ñ‚ív;ŸŸJ™¦2Ý]Ýô»|vô5 =/u.Ó"6$@µà™/eªÖÅ6ËËtþôÌNðÍûWÇÉ/—zÛ±Ô\(î¼¾F²ÐÑK åz³ï÷ÆKiÄ¥.3qqÒkèâÄ  ]·Ýäqœ#|꬧&¦RC<±êÂÙ«==ür|~9=O^/&b–ªzê÷(3M‹1%‘vºT¶œHË4MfëÀÑò%9¡˜¨5¨™"DÅœ «É@Ûí€@¨’!No›Mjà§Ò ÙšI[í¡Ÿ³ûÖh·4þv»Cª"š)Îú/1¬vFk£ KÃ5E[¨í uÅB4ñp»c"´X­}+6? ` „ˆ ”Æ&p•P(S +†*éí I¼) }4A5o›·uŽWI€ƒÂÌ !MHƒP¡xÛ۹蚱k"F…’àDË^‹(eÅ6ˆÇ_™ü\ókgÅ5Ì'©—Ó±°´ŒZ 9r¥Þ†hF[m‰¬KIoI;QaÂêHMþóóK©@†aØÕåó-R-E*P Ýa½Ý¿}©E I¼6}×Å4Q…ª–%is^[âVÙA³hS€F“wFh[ä©(m£š[Sb]cêR³‡Mê )wyÑ23¢$KWÃ@“a°mNêçÊZÃÐH’þæÝæÓiñ²äÜÕ%j9ga,œj]JY–âu†F‡HD”EJ™–Åçe&'§ÌRgM&‚vÛ! ûí~“ûùü"梄WHš–18gÍYd»–šç¹x8iUDèj¸pY¢¸c©a°¡ë<àPU˹ûâËß—z¾ŒKÒn—˜°ú@#o-ý*ò›»ÛÛÍæÕݾÛmk™{SÕN$¥ FI*ÇÑÏã4ÎÓÍ0<Ÿ«Ôðênï÷õ¯žž^æçMê+ç›ýõt9yáÕþðöí»Ÿþ‚z¹=c*uñå›w¯¾Øõ—åöæðÇßýêvóÆQÚç‡y~¡b*j“R÷ûÜåÁ½)¬rH·?<þøòãŸþá·7_|ñåW¿úÃÓ©ºÏãñRýË·ï^½º½,‹”ñfHçyÊé·y×õµàù4›ù ù‹Û›a³W_¨Ý¥Ö¾·O/çe¦ŠêðÍ7¿Züüü4™bÏ^ç!G­Ås¼¹ÿõÏŸ"Õwwï“F˜ ©¹ ÒÕÍÝÝëëy¼L³n†î0ì¿üúwþ®ël·¹Y ´.]JÎÍ››ý¶ÇóéX™§Ù+bZ–Ÿ_–”u—»©äð8_ Ë© zOOíS[™kãAnúm2›—ã\Æoß÷]¢*ùP}Þ'ÕLå¡ÔÎÕ»ž÷¯ÞÄ83Î×Jøâ5Yú›_ÿ*ëõñù2d½ºêÎËt:Cr0&“ž Ãæ©nºÜo¥Óµr [zpò^¢ÎIYk`™ü2.ç3ZçuIµ©îáÀtžŸçãô2—I¬ûðæÃ8NÁ:—Åd‡»ªB'CR‘Œª C›èa.&”H.j¢t„¢ª2©°šH8„%ª—A :`!ÁvR ÐÛƒ [‰¤øÌ†°9@T@qˆˆ¯O*Sn*š„¤Ô UƒI¶Pª©R“µÛ Å´,E5TRI–¥ùɘ¼#ÿ ¥0JÀZ4™ôÂõñVã«{½¡tñu BHu©^ÝIR13ĉ’F ñDAD¥‡¶Ô;oZn§6Êj+©Ò¦ˆö\F«)qMm­Íøö#ZÖŠp qÕ:GÛ~芇P‚1]B…HJ¦Í¼ÒÂ$0´ûŽ˜ò) ÁÛsѨ&f­³H…Y *=ÕXÛCÝ(WA@<4HW-IhíÃXä ´Y£U%¸î©dåÎdEÛ'¡9ql-¶‘Sþz'5¶ zSùY×]Cšf0W¿²¨ÄjZ^õkù²MY· €Ãƒ¬ª¦ …K–…D4u”B¢‰¤\®¡ ÕxÀ±ª*[~«Ñ®qâÐ6Œ†EkQ)äX£èÚªôVox­5ÎXïk4*•*¢UÍÖk° DÚJMØðé€h³ÄQKØòzÞâqÞ´ÉJmúJñueJI+¸)µ(!jPM°f´„eÕ,J¥ÕG-E¬£jÒ*¦mc)B‹Do–ÎPB„™†h…%à„…9k‰© Y¬µ—•ÂööPʼnR†ð¦}_|¡@]˜‚¡€S’ŒAˆ  W–YH‚»2„$YC¢ZHE‘q¬ÎÒ•f‘$H Úps]K§™–ÂÅÝ](¨¦nš…¢"’ZÄ„¦BU @»Lt"ê視 sXÉš:U“æ -{¤¼·Íl‹´º]–HÜeˆq¼œ7¶s™èÍYåå§ñ4/g¯]'/gB𡾖HBA0/`IP‡³¶I»FÔ9.Kñ!õ©ÓM‡ÝõaYÎ^¥8’ÉÍÕa‰²”S§¥Î›þÐ ¶ø¹z©^ÅÍTƒ©¸/\¢–Ã!«ÊT#ecµ˜ZÚÙDë²õݰQÝžÇq~y{µí§±|õúf»ÝÕå8ײÝíº„×·ö›´é3J}<Ÿ=ìî*çÓxYw·^DÔ–Rƒ‹:•­m*twsxýúíÍ͵Èðxy>>•é4M¬³¿}õöáåÓTçrœ>½œy“»Çó8zÙõúÕíJJ[ù»ßÿþ|¾•®ß ¦pO›îÃýýÕÛo]t3Ï%^oÞ,õòêæÉS,óÅŸžì’zñ·¯¶ÿéßýîáL3Ün3ôíýý¾Ï¥Èý‡_}úù|>øéÛ¹òpýîµÆR Ö™¦Ý~ÓE¡d¸ ç*u©Ò§,º×*©àù4;è5f »:\g¤:„Zv™–RÊÂÜKa÷éãÓiœ‹ûx)QT_ÜÞ>Mî1CJ—»AùÇ·Óñ¾u®µ¨¨ÏA€¥Ú<Æq‰Ä‘ÈÏÓRÔ®ºœR×ÙB\u—Ëþf Å4²F•W©‹À¸Ô)À30—®ë6e\–nYj Iý››»2 :g(Š ¢̃lí2Î…ôªPÕ¤I™L:a,ᦤŠiÉ Ø#F1ªC"‡'Í„›õ”NË(IÐ )"š 2D’µ|> æ oö@’ª‹D˜¯ÑŽ@(L”TmÄE“Hf%© ;xˆ ’VR©Hm>’¤Ý aI£ki–DP%a ™™“¸Mi&¯u© z@…¥b qÔú˲, ¤‚ ÖŠp'ˆ*ˆ`ÌAqÐáäê{«áÚÀämë«ÆbP8$ J:E„ÌÝ”ÁпrVA4â’ÄÊf_/M-íŽ`jÔ÷5z#íù-ÑrÐP±FëÖ6„˜®âÞoZMÀ a𤥽sÛWHKCÕ´¹‰[· ÞJ]®0C@m¹g%â¯vh&…‹°mãPŪ¤ªQ l!H¬‘¦ΠÒgAE5’ŠXØ@hÀ¨©U“Õ(Õ0;ÍÈ›á~sS,§®W ºE…PèF‚Vïn‡n£g˜γ‡Î¾ FE&ú%),UˆpÉŠƒa5ëlduß%‹œ”µK±8Ü—Ræëíáúövº–i)§ãLJ‡Ó㳟N“ˆì7éÍ»÷‡]~~šMzK–ºtsu?ä“q)Ó餰ÛW7OÓÂå|ÿúét|>žiõ›·oÓîUî¶?þðíxº$)Ë4ßl ÷û»Í%æë›/žŽ?OçqZæRæi¬ß?O—Y`¯Þ¼µè6W‡O?>\ÆãñåÂ:^½:Ï/^ÎËxñp•%åîõý‡Ý®ÿêý¯ÿüãÛm>lºÙqØï½Vz¤~·ßßDŒW[*t®U´†m¯éËëœÒæéù„d"A÷Ú+käùt™—c­¼¾½"¬ÔùËÝðÏ?/K­5è…–XÍŸ/1jY&ÒÁ`¡ÍΧ_Îõâµ,/ÈÆ|,&jw‡?/žûîpèPd°Î²Ìó„0 Ù$=F™Ï‹×®ÖJ·d Se,ž5•eÓoÃ4Îu¹ V“Ô¹H-K‘(05#g5§ФHi0ÛVÄ$$D(Ôät@Ðj{ŸŠ™Paá቟8‚’(KTSTam×ëÝ/K8ňµEO[]fNh¥‰ˆÄæ]+Tä¶–1e¨Öଷ{M„¦PQU+­m}¼‰L´}bOÊŒ$€2” 5¤Xï°ÆhÇ»­J‰Ð\,¤ª#P&–î „/Qjmm=2Ô ð¾ëéád%H«a-‡ ÂÛƒØ ˆçÏ>7¶ðw°˜bµ¢úî ÊÐUdˆòóu*ѲöŽÏP¤•õÙˆÜNfu¸Ú5Ç4ÚÁ¨íkÅÚå#aí¾¤¾ŠyµÝ…ôsÈ-äJS]µ,téÒÉ¡P*í³Â™’hlòÚàó‚6™´»¹¢Ém÷ŽÆgWHAH€&!i\íh—ÃHºî^Ð¥T¬n^²ý~«ÒÍ¥µƒ\S®&(Ò„•P—¶jõ>¶¸UyšìO¨m´´h„*QÂWÚ«H`ÅÖ“Äê“Y÷‡æ0j+(kœ}J©+ÙKÑJ¬‰öcëe¶v€€mBþ, 1ú*"ÔÏ4X@B¥!1ЈU±îÛ~ì¯hýVs$© Õ©9HmÄÖï[ÅŽÍ触!š"´ÑdÝ>»“M èܦ¦Ì¢b&Ê®·qÓ6I‡0‘ô¦¢&-Ì¥‡Òª€#Üc1GE‰€zÈÊZ©BWBÄ*ÂL”DUxm:Ï µ%ÃÚVÙo¯\؈).šjÛfP!®Ò “˜R­}ãÔº›MÞdÃjâb&T¦œ(i“쫯ÇyŠ QQ ˜Dh2‘ª¡a– IýÆ´'Þá {o3ë‡}œÅÝÃý†G»fk}_Ñ2¦ã—»[æôêjóñöËíí}ž’7q0Ë_?ß<÷Ëóõ6OÇ~Ê]ã}&Z^¾yûÍß~wyHú?ÿøáâ¼ù¿ûûÇÇý»«g?|ÿã—ý¨ÊT¶›æËà6ãáçÏ÷hÛÕpìãÐy†Ó0¸ê* 8 Ó˜öIóçû[‹™ ‰¸„\&rkÛv±|5åÓ2LÃ0óQÈÞ¶ºèV Ä™H@¹y|øbZ”Bh»¤´[¯ ¢Šu׺¦ÇÓÞÌwÛfÛ®b·pt¹PŒa)<Ž©X› %o޾èisª[l|R¤„Ó虆 llŒAsŽ.Þ}{ûù>¸»5 ðêÕûe·yqXAö’Tr,AFS"nbÓùÙ²Ü<•<vmʘ±‹~û`Óh¬Ùv;=ÀÕ”¢i=ß®¹YÒØeøFmcU A ³ª[EdSpI¬êJ©dP¶ÆB¬ Ìp2 A+&\T˜X…‚“¸Y!03¬ÎEê9÷¦‰\ɰ9Þ TÚV!†ÏךóTFŽ ."cd6 Åy"rØ<7H$âRy6` N%º‘‚"5³Kª·$ æ”9)¬öôJœÀð"lLnA]iò !u.”žÌuŠ»ÃMH”É& mùÝßüöÓÝMòL¡(e~ ˳»#¸ƒ]ˆ¬"Y p­"ŸÊ…r˜©jª«›`€z¡@ÍÞÌ›©Jîæ:9˜ýB:BÕ7Ö7ÍÅyª€<À‰ç8— J La›3Å$ÂH%OsG"™êdó3ÆÉÔ™œB y­R}»³âPP‡»þ¢y©,(ö2_÷0Ó6çŽÖ¨ÿ,[¬kª  ¦'ÒhCj™ðim÷$#œIms^ÃXó¸*¨)[=sIÅ:Ù/ÌÉIFÅ3»B5=Nnõ'SÉ•tþ’!f7±HíÀªçUUÓóõóe"8Ãë+X™&䦨ÔúAa••€(T=¡Læo•êìLR?Íb$Ž^[§uÐtq½V1ÓÜ\¥[.W U2`™ÃBµ!f£ Î0ræÚœI€X=¸b>‰Á„…œ˜˜Œ-0„šÖ‰j|>º»;W‹5‡"ìѪˆù‰8 $x1#ªè;gQ*É)‰›Uºȳ©€ ×ÈžÀØ`ZÉ*è›;×ësEx©™[€»°Í?uÕé×K¤µÀ¨0‰âžáf,n"VF”¦’›Yë§)g‹2G«‘Xt10”I™ƒŠ¨l7Û4e÷ ' d½Þ· ¦ ‘(94Fa’M^4Ô-šóõFÜ!Á ÔÓ..ÇRHÜ /cÛd¸(/—â ÕàfZK¯‘sžN§,š’6GÎ*ƒnFê–tr-šÇqM³zö·Ç1y)ªÙKÉYKC¯É}R™¦RÔÛ Ëó_¾§ IDAT¶( €]ž_®ºõ©Øb³Þ^_¾:Ž¢Ü,–§SÊæ®9¥¶kW”¥ä±°:߬69mž½ø~8õj¹ øîüL)NãtO^¹ænoŽë«gÿë¿ÿðùçýþ¸ìVÿï~¸{<üå§?¦SoCVË··§\l)Á™wëÅqói —a×—a<ž–+”¶›³1ò›oßüó¿LNýT‚÷ovÛ¿|þð¿þø‡¬ ^Þõ‹®{ÿlˆl.ÈSòªi&èóó—]NÓéúz(¦Ãp±Y ÅÕbûÍ7oü›¿Ûß~½›N»å4&wᦠC«"´p%ϧÓÑpúæùE9q_úŠ~î6+i–BÚHÃ:™»›c ˜~{½ù°¦qÊiËvs–òtû~œ$¦¤mÏ›! Ǹ]¿´R¸íÀíõÕ[ÂjÙÀi¤ÛœMcñ’9t]Sqnl±U—Ý’B›Syüúp:ÛØŒêDv¶æýý—¥ÑÉl³>ûóþͳóDÍ8N§Óaz8}ºÿºè6Wg›û2­»ušúá8µÜm7—/_]nŸŸGyÈû&òû¿ÿcÙïøÍÛg‡q²O)4ñââb}þ&ŸNÕYŠ„àzî'SY6‚™ÉËDÃ’ãújðÓ) I¦d7M[ N½EŸ´Øá”ÍtµYí.žåIOC)Ù^ón½hú“~üº†¾kãßþæ{@ö{c‰‹;£if”vqÞ4qÌy—»ÎŒÊóç? -ïoÊÈE=išÊt±Y_l»/w·j„Ћ!<{þª-:m7ÛóKYwz󨧢šµhÎYQ‚¹»¹¥Œà9”aÊSur#6ƒ)Y©×"DIØœIG"BôblyxÑIT*N ±’†”¸‚,¨*,"¸sÎÖh”F’3‡¢Z‚±Šg3Ê sR’ÌïÀ¡æP«='ue pr‰&†¢$^$™{†iu Ö”§)CAê5²ã%:D•ÅÄ!ÚÄxµmÍiJ(L6 QDILE©6½Ù ¡‰p77awSõjªÉîä0&Õªô>¥¸fÅ­ x `RrGQxÁTŒ¹"¾@ªêîcPnz£ b(eCq-n™Ý‰œ]™9ÃÔԉИXgë_Mt×lŒ»Ô Ú/¬ZÌäJêÀÌ¥YF[›ù Ìó:¬Ì¸:úpõWPE=Tw[Íf1Õ»ˆÃ™ëâ­òœÀ†™ºecx}dUm3¬¸M 𪩬óy±ö¤ôy—œu3gµ‰X­Bî²y©÷”KóˆÆ¼rûWá²Hl7@Ý©\ J<¯ñÄ“¯|öY3Ã˼ž(A¤l°z„‚TxÙ»‰™(™ äZƒÎâìNîÆõësSr‡¹€æI¨Û(´’ÞkæËgb©{MaÍ$Šºs3)‰U›@õ%q= J¥z:ÜÁ`3 Lj‡Àaàꚤªüd‚WèQd"§’uróPçY™ç4†ÀÅUà¨læ@fäŒ`56æ5Gi åšd† ÀÒT”àÈó|]½ÕêE`cФ*™•¸ÊDj¢îj^*߃ؽ˜Å(m3Sd¯?eõm¡–‰@#v)âÞˆ «À‹_8(dU è(Îjp2_6Ý2tJ<é)Õ#y`³±3C‰±\tÏvëÉ“: Ï…ÜZ%'fÔpF$#ºÔKµ ¤áÒBZ„à‘˜UÈÉ…rè’¥[ „\-6¡$hV¶ ~uÙf—¦ŽbA<«æŒß~w¦¡OîÊ€¸Ç¢ÓaŸ&¨Å´ Íp‹N£Zž&/œý<Šª†^ÉòTT„×A¨%ö|u¶zgàúò*÷#ˆ{vˆtê¤9i™\µ ¶\6E“«¹S„±”\ôÙ‹9—ûÇseŠÛuÓ…FU‹6ê>Ž©ÏÃ_ocRfrJf´9»l#ƒå׿úÕß¼ÿæþ4'A¾Ý®zµiÄÑ­V] €\ì6ÅÃÿþýŸCãP4òÝùåãi*©5íÒ…6m\v«Ë4Ï/¯Ï¯ŸiÒ?ô¥¼=?/)_=SÕ—gçƒR.—ànÙãzi¹¦úùÃÍ᱓øêy ç‹fŸuîîÝõùÅöüÙ»gÏ××—Ï_?»:¿8OŸ‰÷ýqôÅìËí&,‚/Ö«ýpj$®×;ó<öÃn¯×mÓ…Ënq3Ë® qýâü¬äa·xÞ´Ä –&„˰]ïd!M+!jS¶A³–añîí·}Ê1 “Z?©¡´à²ÝvMCL÷ýpÇaZwŽã”­˜Á=ˆ~óîýÙ¢rÞ,Ûíîúþöëª1/~š,«0§ãtr…P‘U·X-VÓ0 Çt,VÍ"vKyxLÃÙÉ%qvu ÂæYZz–)ÆRØÇ@n®…ƒd5‡HpÏUrªâœjF´†f8Äèì%3¤Y0C4+DÅ @°¨ kˆE)›‘°(Xc'F!°“ƒ­š¤cQƒ±*‘=‚ÉÔPà (f e¯Íq#(8ˆ ‘›Ì”H!ˆ‹3f%¯~cÛFÆäÙÈ……T,8kÖR™’k„…ß\ܤ*ˆÑP¨d Ô2)%A¹hÍçcvÀ”’ÁÈž‹iÖb`Sˆ×óV¿ sXvؤ\ØÅŒ ^aë¤b RwsÊäîÈp°Õ»QåÂÝ ŒÔi*ÉŠÏY(ªâ š6ZÍ×OÈ ùŽ5½¬@dت`AŸéá$Æ3Ù] XÝ ÔË "õº@ˆØëž«fÍ…Á.L$$ÌàBV%„•†ä.gZ¼‘{ sgˆs}â×n‚ Í <…¤jDûi›‹ µ—37C­ã¹×Õ‘Ò I{ê Ö¹ÃÈaP÷'à#Ùl¿®ÛÀùnÛÍl“®ñã9Qfεò@dRcUul«sSíèÍ~ÍÚž "£Úö«Àv%/îJ&øWœ<—yâ›A¡®FfUXS¿\¿Ø:=“Ï2Ìì™MŒf\kuS%þÔ1UíNU"i¥]jKwþϵ'™9·‘å_³S΄øé:k\Ë…$bWñ¥Íਸ¶ÚnŒÙ +ša¹Î2×2žvÒö@Âpâ:)VÔÜ*±Î$Dj¯—ƒs¬?ÈpÒ'«ÏŒoe‡º»A¸¤œ…ÝLÅŠ“‚Q›‘£€˜ÍŒÄØIQêM\)Ãôc#ž'VQ_‡Š}N)1–b^ÖhÚvÊ®\{¥"Ý¢›ÒØóT¬/SêÍL‹eæðæÝËãÑC3ôGS”d©bòlišˆžÑàýëo§~<> ªÂ¸¾¸§É²˜ª-Ø–Mœ”Þ¼~C{x|SÑH|u¾> ©Of`¦A)õbl.òâåë~ýo>~ú¸Z5ÿõ¿þ÷>¥››OC>÷'R_®–÷›o>>ÜœNåõ‹×hK‘èé/7Í$â%'ú2ÿ?ÿñ?ŸŽûv!û‡¾[ tÑX³Œ:ß^?3÷~´·ïv-ºE,«}=ŽÇaÈc‘&ªåã°oWÑïNÓ¾ï5'!~õúíÐïõæÍŸ¾|BÀùÕõåÙnx8|úúõîô³xÚ4çŸo~¾¿½»½¿ýæ›oõöÅýÍÝÏw_ïïîÆ^Zn„càå6¾yõv±æÓ¸¿¼X÷Cùoÿùw¿úöêãýí0¨’wÏ.ÞD=ýõë*éÇw/¾~øørã?ÝÜÄf5ö^î?dë‡iË´–ðx<^¬¯^\ŸýÓ¿üËp8¦¾±DhGÚþõËípJî4œR?LJ¾G´NVúãØñêj³Ùm/®on¿¼xþþnçðÝî²[¯V ïOÓqê¯6Kâæìâ¥æÔµ’ŠŽiŠÂÂñÐ/šmÓ,þýï~÷°ßOýq‰5—Ì,LDLSÖ”ÊBÉõLnË®Û-³ gÛͦÙl.OÇû¾?x9­›fÝ„~<9BÉ·CA¥ÎlÛŦ¸N:yÔˆ#ȶ‹Ó¨i8Y5:sâAÔ˜šœ¦b€,rªÊ¬Í¢aϹqŸ¨ºBÕƒRj‰ÌKâ†èÕ¨!tf#Y, iÖ]h|J N. Î,¡ò¦3Ä­›:‚—ÖAˆ)$r0D–1"ˆ)¹¸ÓHÊk&eup&O…ÕÍ¢¬@œTÙXÝ™áÞÄ™‚´ß¿ÿñt´¸ÚÁ²ñV<«B \ F&3¥>S6Ôð*ãÂ\+{¡Ï7Dˆœa(DÂTêaÉQìC1‹p1ÀH£‹‹¥RÓÇdFjVÀ&¤ŒƒuÃEàÁYàŒÀ#3 @`°5›eCJ£9`Æ$‚¦[®áDÜt]ËNQY@†Â‘DD<¨”` s€D&$&h“3³Qv7!ãu†Sq*Ö îÎcS´L¥Æ(.»†I‹¶7M‡’³¨4%5ÊRÉ™"b0*"X2¦âš3ecÊÜȇS±TRQ´@IÑËãþa,9 ɳٳS ¤£¢ä˜”J”¸Ú¬_?÷pwÛŸ2™—’`Y‡(²X-}V¥)M^NNI8¶‹¥»Æñ8fgg¶õªy÷æÍ0Œ±]¤ä«NžŸ_üôá§íz]ÊôùÓ_?ùøéã‡óu×´ëM Óþö0Ü=L©ˆ–éÔ?¾½9žÄÈÆÌ×—ç‚ðîõÕ~Ì_n>æ)øth^.šÓtàØòiºÿöÙ¶i|±l¶ÛËíâj³ÆíãñîîñÝË‹»û›Ë¦ysyöñæ1LzyqöòÕK´cî6«Õ×ÛÛÛáôêêü›Ëw›õö»ßýüùg3 Çiz¸³|øç?þþþx³Ù5_ïO¦ËOwÇýƒ1Qï¾y÷wóýeÜóªéÖËŽÌ>ÝÞuËÍ7oÞ ûû~Z¶Á¸éºõÅ6ÆxÓ/ºðéóÝ0¦}6¸_­6‹æÝëëñ”î½ZÎ$ž|¹:ûéþóíÃäy%k’²Y-2-ÿôÓϪe±XO6$KÙ24k1Y¶Ëçׯ.7˦ódy8Üêð8¦‰ˆ×­|óöõ©?õ嘧t½nÿÃoõ—OwcÉçë(ħ)å<õ#I³ZŠÙôånß52G-ªjD²‰Â¬`/¦‹nºXÊqJ%DnÚf¹jCoÎ@Z/ÖcÒ‡ý—v¡«@l©Ÿ†?ørßïÃiH¹iÐRP¬ËÃ8Ähë6¦‘ X9´mTŠË…Ìg»–ºõ.Z¦”©˜·¼¥Ü ë6S#®$,…Û–½x(jB‰“Mõ¾TàÁU…<©“ sa&uHÚ‚'XI„‹F2’`T`bJlRŸ#Ü ©L01G¨ÁŒ ‘–DŘ-· CõÀ­»Î…²6‹f¡®¦õD"\[SÐfy–‡þþî&M£ªjíjžœ¥sæ,Æ9Âà…Ý)\êwäÜp”È/AÝ ÔJ0B.Záä.J3̧ÎNj0R£0³<ÕÌSñÀlF…ÝÄ™É{G".˜T;ž(æ¨Så6™Õ’$R›—â6ÇpgƒÜ:[Ü\ª)Ð*‡€«®jò‘¡ÅÌý4¤\àâuCB5‡û‹ô¬>b¥¦¥ˆjrÊí >Ó+ ªVþ©>Tk¢½Z™™ÜA°ƒYˆ¸Š1·ÌEB•À1„2Ã|"…³]«¨…@f^©œêõv‰Z $›q dsÚ»ºªQ-‚O7‡;«9”ÌØÄà`«L¯jpÍ@3‡U cUæÕ)Öð‹„Œw—Ø®ø :PóFTG9€ˆ«Ù Ê^=P`„ŒNDlæ\Å=LÅÝkƺ†ñÕ½²0@h….yE;8UÞ››¹œ˜â|àœ³òD&µbHsÌžØë‚•@#VkÝnÕ3i} Éʼnà0öʇ¥'—¡»ÔNEݤø\D¤"\$5½Wÿa«šg#{ Už(E…™2¹›“Ø v ÊRuÏRµ…\ÓOõÂÂbDˆ¨­Ö©Æ Ž$ $h(‚3¬E2ba¸¢¦åØ‹q ÔXbpS›LëÄ \J= ºY.æ¼Ôü"Û,½d2@Ù<E¡BŒBZA 9zXmÎOãÉ,;‹¹“p#rˆY}5œ!Q\¯wÍÝHÁ BlBÛÆT2AD×»‹oÞÿf?˜äßþúw÷÷wfG£€nÖ¬ÑÕ™­qr’v±\²æ¬JR“M»ÃèÍjº†(E'´!Á5’H v^ì.÷%CM¡\L("hö\b66åÖ)hˆ¤Üð4M@°³FiÊù8ŒC1¥ìÙ¦„Òµ3kr8Ì ©wpÃùåŘöp—ØœŽýíçŸ<Å4û¤™E\7§&Äf‘“[ñ’ói,9@1º*å”*M‘%Léa]³¶±1ØñtƱ?åøá_þð‡û»{ôíåzô ·CÎ#‰xé5ƕ¸p³ÙDÔ6bðûÞëeöáðÕ“_¬vÛÝaÿà>\¬v Ztáo¿{™©ýãÏ?=<wûÓ—Û{2½>_§”6¥‡‡CX4?~ÿJV»‡»#•©Pºyü˜®Ÿ½>ÛîHš‡›ÏݪÕ7¯^œ?öýn½èšpù|£Î×/®§ñôñˇ\¬iZÒµáï¾½¾åÿýç·_>ÿ|wóåî¡ä|qµîïo?~9ÜOg­ÿæÕº]œýþ§¿¬ºÝí~|¼?þøÝ÷Œ%·~óåþÏ?}ºœž_]ýôùÎK1Í¿y{þÇÏŸoOA|öìÚàÛÝÙÃ'KÓáH!lή×Ë¥0 /Þ<³F–óîúÍ»ïî÷c>>>ÜAÍ\Žù¬±?ú¸ÝlC¯j‹ÅbŸñåóç)§)÷“ÿ’jh¥«,E·,z²¢ êšHÄ)ÑÍý8åcÛ4]9g‘ÈNY“)F ¤–*Io½éؼÏx~uÑ€eŽÃÐ4Ø®|±hÏί‡ñ¸‰ZJÛgh¹LyÊJ) :;/w‚‰‡qXlCFt IRIuׯ¥Xò‘„IÓÁQ¦PH‹Z±‰ó¢dêNUÙ:õœ³’«„Àj*F+ȉœŠ”bºY,»E7¢ðÓPAœxÌDœ ĉ ³›—1wsu ¨g£šäÍ«¦Mfã0MZr)”Ä 5ž *îV´dM–¡ÈŒªÖsmŒ-“Õ¤ƒ VêSÍMÝf¹‰ƒ³»q.ɦS±l™™+‘á$ÌâÊÎIDÕI\ªˆÌ]œ3AD‘ Ž«õd)Î$L¸™™’ÜÝܬZ9;îfZàµÚWh®ùÑœú%Í\·4/Ú.×]Vs(9æÛn,¨…úù×…‡B©1!«›\yVŸÐÜÚzR Ï2A"w6æà$ìó"$†§Ð•¹3qB ˆAÂ3ñÂ)3™#UJNUù˜\©4Nµ£9÷ØÈ¸b·f΀CÜI]ÙaNêîJnu̓™ _Ù^%!fw7€æ¬*¡Ó!¬Öi¬²@Uò71sk®Öù#v«ú¡\u6ï÷ ,õs±A~)]zλ™+ÌÙ«vY©˜*êœÅnµèä¨ß°×M˜iE ¸j=z9Ì+W˜ÁFV£h ª¡ŠÕ!>€¨p,­¬.@ØŸÖ‚¨½§Æd…W0UR¨ Á}®`Îà,03\](LUÄHÎUiiNjüăw!†‹RA3`^ Wgsf'÷PÐÁ]ÌDu'['÷H˜ÙbV0j&ID˜„ œÝ‹Xp@ˆ¡æ ¡l^\YTAlæZÌÌØ äb…œL³A=×vEavv s]몕 †˜9çêe28¹!Û8žŠe2̶h²b†¦¾/¡àD&3!é‹’š2S ÖBi*)›{}sâ!*4 §“¹ÝÝßç\Á(6Á±ÆNvRU ‡0Si¼œ´ŽbA<ó8ˆÂ=2!»å Í"¬Ÿ]~{: VS±‰$±•–Û)û"PNp0y#£ X¸].… Sd'+Y'ívͤvKJoκ¾Ïã0iÉ”R¢Œm m×ìÇÁmrbØPgYF<[.~|ÿêëן¦4©Zð¶ÇcJ•«,›õ °iJ\ùS2w5'swαiЦ\U"«ÐQà66Á½ êÖª¬V›”Êdv÷õ±hyq±¦ýÍI‡iLš -Q€óbÑ^_>{v‚oÏ)Énµ;?»6 §Ã]Ö¤6h9Šž¤m®.¯¿½Ð^5—É®/Îd³jWÛÅùætOí¢}øt÷å4BŽšÍ959Ö1 IDAT²ó—¯7CñËó¶dZÅãׇc:¶]+ì?¾y¿Y,û¢‡Ã§wÕýÓŸþòâù»þëŸ?ïÖÝØBƒ¡ œú‰­Ü=Þæ”JyÝUûÿýáfÝâÅբϴØ^^m›ÿãïÿáÿù§ÿYœÞ§eêBÜv1²âîñT&F‹:/×›ÀÊZÈ´ªA"Ú %ýÊ„âu·ŒzÔƒæJ4±hRm,DjÚØ¥â“jïn™ƒ1M® ‹èÛÍ2éT¦–’0”Â%Љ“D¦Œjv ŽÍòå³Ë>Ÿl2°”™\]Fe„,ì0rÌšQØçËO†Q%±™rž&SUS- rpÃIܵ6ÔJ2'w*%Î(#x‚ºÔd‹iý{™Íc—§·åÆ`wW«ZgbàHäÄn¤Äâp¨¹Õ§ÔÈ#;ƒ%ŒMM` ‡Á…Q{z&ìbàE²ÁÈV×a•¯@>Í'##xqxCÕ‘ÕNžõy©òÁäp¸@äÂ’óŒàqžcKu]á®î“º½a"B¥iVÄèÌM õbXáàŠ/³+ÄÜ 2C£jÝP( T Á]êŒ"d³(ÎD2‰à³Ò ˆ¹3æîåL5LJ(ê4S×ñ¤×­™,”ZU.s#ÐA•Ü]©YôÄõþøUñXf''{r{Ý– 1n«ˆÐIêʉªŒQð4¹¼Uˆ:UßГÖqAÔuW¶y-êæ:ÕÕ@¾Øë§«n£úÝ©Ïί³¿Ô"Ól2š4µ×@O=ÊYÿíR‘&¿T$É|v©Sœ¼RŘëË]©T{‡ÀŸk3ôbÆgÍ^îꡤù…iEŒÖm¥¨“C©D31‚@u LÊ,†šÚrˆ°уLBÁAQá(R!lBðÈÄ΀GH V©—`å`$Qa0Áœ\¬ÑœÝÔDD*03S/9ƒÈIuæïŠ‹YWXaT5ä<¦ª@“@¤qšC|¬… `VÄêü‘Pù§µh &‘z„dGàÙYDÑDP·~ì)›d· ð$Æhæze ä‘‚BšfµÜÂŒ4k ~ ·aÐØ˜g7Kd¹,cMŽÅŠ‘WLF€ØÄ¬TZ ,š°,¹¥f¡.©k×ß¾Yç“ižáüŃëƒl·çÃñ§aš\Ë–L6r`q*c¦qʽ2˜ª²s@ä±~:ã)åUUfö”\áÅ´k›¸jª ¡jÕMÙá1B5˜j`4Í‹&évÑtëµ+½~ûòr~8£Ýjó0ô«U1œŸm)#—b»Ž`Y-vNè÷÷¥˜–¼Ý4ož½X¬ºwã8ŠSב‡p}¹>Ž§ì‹¦K“µ­tMwu±ýͯÞýñOŸ‘um·>[lÑ~¸ûø×Ÿ?Ý=><>~]mb:>6Ââ«φq8L,M‘ìÃÃþáñN¤™”KßÇSÒéøpÿðu‚ ®¯/ÏÏâýᤑâj¹}¾ì6Ÿï¾Ž‹%‘ò¢Ylwg".HÅ'kiôæl½Ø<<~:[.>üé÷Ç~ü틳fýò¾¿/Ó8æüåááöþþtØo-jU‡a²Ò…õ¯¿ÿׯ‹íÙ©/¥OäØáË®{ù컾¿Ën—«ËU¤ÿôëëûS™ÊÅæ\-¥d"òùç¿x±—×Ïÿ¯ÿÿüôÓ×ãaÊ©ãð«gûãЩ]l8NCÎÚФ\º6\M-Fé¶«ÕîüÕú‡ÿòöåÅÿþóÏ©?L)+Iqëºîö¡ä2®Vg¯ž=¿<~8WÖu‹E×°ñ³ë@“ñªé6Ë]ÊÃ8å«ë×ÇÃé8ê”ô4ì†ãiß~ûêz±ÞPXý˜KenC8»Ø>¿z{¼¿M> …å2>ÛíV›óûþ‘Hþí˳eo÷§Ý24³(ÝH˜sÎ0Vª&¿ï·«}±ûœBp‹Ø6‹ÓiÊ:-× ‚ŸŽµ¬à’^ÉÙµWùš«G¶Ëmq³ß% 2ñ”É-„® Rxî[y€bDNÊjân¦&–î­É–‹ØÊ"Mª¨fQ˜»[¦PÛöB0U-u>2vŽlRñ‘€«“EQ`×P!8L°Ê§f«+ ¸›w­ŒvÈD ³í£ŽMæÕæ„‘]fS_)5Æ )p©5A0X3“ ÌݜԥwÕ ‡q17W3ÏÎ…Ä`m¸ƒ.(5 •*šÑK /3aAëñjÝçÏËPx¶\lfIÂ0¶ÙX=*D:«…êRm ÂÙÜB˜aÞNÄ”àpÔaŒV `òD~p0jØŠ8"T¨ƒ€A,5 U¤ž‰Èj†ÙIkMÑêÏ¢“¡Ê7ȹÌG'çâän+Õ8 27“[¥eVŸ"Øg¹%³+j$™fm Áh¢ëTWÉõn5Þ§`=I¹ÎD0–Ðn™õó°&Ð+Ä P‚sÍPÍOlòš$·ª(ª²G—yœšë õ!#bÀê µ"‡J²2¯$ [ σťÞû*2ÔŒI T–›T:I@-ü±êo!»,lLB3ãêݪe¿°Çœçì|=ƒgH½TjÕ?r 2ª@gNâØ@‚“¨ HaäÁLÍ«5 ê ‚™×¼xålQlë]Î9Ì1v"™+p–Úq LÜЈ€¸q­$­ ;k}sØmR^f0ƒVµ|!jÉ\S1 hq3h!l%Q‚Â#+ óâ–ÔÙabV™½O‡ã æPJPæâ"äv”«*©¸…H¬`±ª–R8ÀÌ 1¶ …,Eb° 1ˆ›Ì–õ†!ª/Q0âÐ61§¬4ã™Cè,3'†s1CpDF³›õB‹¤äZž‚·º´ÐX!‚¾¹8·vå¦0ø.†<µ§þÁ#ÚÈ †4Néx{HeZ0ŸRÚ}QÎTt‚“«s&M¦צUŒÙF™F- ÇTÿzÖÒAK6Sg' »EÛI7RÍ,7€¥íZv7u˜¨'s)j¡íš_½¸|< cÂqH7'þ´ŸúäŰ[v¯¯_Œy\-¯¿{÷x|Ø.»»Ø÷6äY÷°]wSN¿|>õËxs½>ß.î÷‡»‡i&âv¹X¨æt]»]ujIËÔï§¢¼ÜìRéâ}8žnúñt\.¸¨K믟¯^Ÿ-~úðé~ïÐ볋ÇýIóýé°X®‹ûîì*çr8<ˆÓz·’²/(_ïïýiìSŸ÷C:õ} /¢hßgõÅbݶÝj³Ìÿø÷ß¿xóëûÏY´Ë×ßþîîî§ãxhÃ–Š¾{õüãþáó‡ÿŸ©÷h’,K²ôŽªÞûˆ7'á$XFF‘¬êbÓl3`¬ ìð—±Ä tWKUuV' âÔ̹÷ª*÷y6D27’îæþÔTÏù¾Ëõ¦éšuy{ÿcQ]mÚ?¼}õ»÷¯.//£wûánN)ûÃÝÝýÝç<;JÇœ©[÷«¶³~ÝêØv±m;_múÛ!ç±€Ž.«·§'!6®åîpóöÅ÷û?|ý÷·Ãþæâäxn·wOûŸÛ†÷ÇÇ“Õú~XuüörýÅÍÉ827t~¾KeR_üìw¯/Ïñ»oþò/ú#æAš†¥9=Ù´Ú¦55÷}3އiÚóCrÙˤaÕÆ†ß¿¹é¤9ÅœÆ)MÓ¼;ÙoŸž:g§-HÛÐï){šRlúÕç»»qœÓ#5]{sµuç!Ó0>Íc¶k=¿ÛÊ7û|Çã-A‹y𿇧yÝ÷§gÝ0>q‡ M-±ZI¶'9;=ßßÍ%[+Èó0 Å(5,“6Ì–ú›š¦´ˆƒØŒÝ¹™4iVÌi„¥Vœˆ”À«¾ 똵PnŠÖ'6;3S™2%¶Z-2 –”QŸ» 1”æÉ”ȥ▌ ƒq=4x¡‰îÆUÉJ ¶e„ìNfj/¶'Óä2¯¸¹º¾¸=òá8èL¥ÀЖ4ŽÍáxÿñÇO}ÛE‘q Yé×›W—gOÇ”­¤ã¸'¦„¬ááhÌ8ßtÇyÌE ­2ŽMèöÇ9çc·ÛCÓŸÏÓø·¿ÿÅåvóãÃÓ4§”=•ôøø8)†±*M¿Êã4§)Ú®t«&ÜÒý”JÊ:I.éPŽûi>:ûl]Úï‡aJ`æÐ¼8ÍZ`À½X%í;ùâòC­H˜Ã½‡s}ŒÀpRÔ81á¹ÈÕJh(]P`äÄîKЙk¬iYâŽX† Aà »-kµ&3ÈÍ•`x^ãþô¤$TöS­6Z}yùÌÙ´ÞEîd¤U¥Sï‰BŽ•CºŒ~RrSµÓDpˆSݬu)WîxPµ$¦À຾‚“ÀêŠS@Là°@ø™`/£hmRE9€Xw!¢š§37!ø‚±­Y¾ç®CM¬RöE‰M‘"XEP‡H[¥ÌæNŒlà1°H,/Ä+‚qƒ`ˆ€P$&7&Š¡’(Ø"„œ ©; &î×ÝžG7¨ëB 5bF¶*7Òâ^ŒÍ™ ZýÕn6‚IÍa`3)\ØÉÜ,g%ˆ)UúŒ-]yƆJ$Ž`6ò:ÖHµVVHeVÜGCB­ š mºÍî&«™#N,Ù Dì•ib§Hâäå»"±kÖ%šHq‘&'öÂVíÖ뮕ØÎS0nÑpP/RD¸daÀ¨Çézó?>Å}½YÉ Óüfw…sÎì^$…y&MÅ £ŠH*šKÖB5ŒBà!#';EfS¦R+L €âÎîÆ¤¹ä’úvÛ5Qmîû† j¤fsžš¶Ës&¢u»:oHšµÄõtÜ éöt[æù8S§C>͆¦•¹ÌœÙÀqÎ4޶ áââœiÞ»³mÚU¿jÃܶ[V°PQ߬ÚõöÌ=±ZI~÷tüáv|J##ÆOÇyÞ­û«›_~þüýx8¸ÔÀ-š¦Ic:_ïÎÕxsv~Ö¯Çô”‹Rˆo®_o6›Çý™bƒŸ¿¾þío}º Ó ûé°ÞtÿåW¿H‡Ç½óîÅi΃m;oCûê‹/Ìøq.·÷ŸPŠŽæ-ûa„ÎÇhyžó¤þ~ý‡ëõüÝÇû³ÞÇéOßß~÷áÛvEо;¿dn> ãÇûSNë“þ0ÍÒrÛ¬¦i<¤ry²}õb÷ññöáp_T7›þÝ›Ÿ¾*äÝéæíõ/ÎWÓõõ‹4ä/®W”Çͺ}œgz:‡ "³ðöçWÊ0%õ’ñf½ðì~¾Ýœí.qN›¦!ãã¬Ù,•2ì¥àrËcòcÊÇt ìÙÇaLF³pøÝËÓûã‰~w³›¤yùæW‡ãý\ò~˜rÖ9¨÷ó8[ì]Kq›‡âŽî¤Žã0ù~,–ĬP’fBl¦ãL]Éa¦bä%ä JÙY-K6`rvBOTjû‹Ü A%ÄõÉÍ‹©ˆ, ±y&21f%q%Êw+¬ÉÕë;A%#…º[a¬ØŒ,ÙË” ëBžâ¤¥šp%HW4»(±V¯¬ÅHÒ8«™›»'ËðbnðàèF.õB¨°ÚÖ)¬"›Š d$0ÀîDì\Ï6/ _.WêŽDw50À©føgRŽäîVaã&f%T&¡y­s«1Èêc³…Î &w«ê5§2™Ä¡Ž?Ž@ôg¯5ûŸ½^j´«¦˜œŒÿ=‰dK‹¨¥4¢Ê ¯§:ˆ““º<ïnž|àg ‰+ÖªoïÕ´+©’™ …«*jTG£Ö?‚Ìü™uUˆ‰Üj1¾jóÌÝë6õF»,É©8Àf‹¥Únð„%µ„µê³ÈÍ*}qûÒž¬áZÁgp…„¸[^2z.[Ö²ˆ!Ëz¨&˜Anu÷ºpØ—“ªØêæÁ D0Á„jø«2&gf‡²H]0VCwMÚWe¡UCÌjųú”ª8’×¹ˆ¥òèÍ=Vô”ã9¾çå Ç•x@ÄNæ5ÇæT÷v&Ê rĸ¶KÙ뎶FÔÄLFˆIgBॠ@âL. Ö–PÀ5æ‚)ع‚Þ„<ƒÂ³…Ð|IKÃHHŒ!—Z*WCu7Œ™ÁZfì¦ÑÀ°¼¬3¡Åˆ‚¹†ìTû©æn03›/‘>ËÆ…<“Q®kmwã²y-+f +äÐÅbY1½Î*Õ{M$*v±ú' 1s¨wÛúÁ3ƒ…nH˜ ‘oˆÙç)ź­5 ^Ø… n¬Äâ­å¹HV H]Í skQBß®]Š„Ò@¢ydv+‘d%5¦¦ ußr '+ƒÙû6¼¸8;L“Î.Û¢>ú±»•iL-±Œ9{øäHn $È\ Of–›uËù0%RgR#±‘˜"siÕun6$‡z 윽Ìpc ]Ã! Pæ’Ó8K'»]Ú§a^õëRtH{%ËE9Äl\Rî×¢¨œ~*%aݵ}·*)+³™ÛõÙéëg9{×yÛ¯”¸[sn›94­IÛÄþl·Î“ÿîÍå!—9•ãtüpû­üòçïÇqX­W¾¹=ä‡(Ú GKi[Ñåû7 Ùãão^¾ÙöÓì}¸‚="Âõ«—Éèúõuoôåÿ{»½Ýžl÷ƒï5ÍI^¼|÷Û/^Ÿ¾jû›_]¼œ4ýçøÇËÍE~÷îåæÝ/þÿúúøy™r6i°¿Ûýøðñþ!ïï§ùÃá°âùÍöd¤îóôÛ_ü~LùlÕý¿ûÃ÷÷Ó`Mln®Ï7}ÿ‹wïþ߯?÷cñÅÅúúêêç§¶_¾yýt÷ÃÝýÝj3L”‘ïÃ7ŸËâízÝŒ£¦ä éOÏÚ“uðOŸ>š›[a²î䦀¤=†ÃõéÙiÛItM4hI&™Öýöþ) c‘0Û®.»þ´Ÿ½ó8&ûœÔÝþÁ<™¦ÀM\µyšS:ä<*©¡ „”í0$ÓØô·ûûã½N¦š "çÈLÙmNûYANúX~ýrý4ÍYÜÈi†#lÖ¬íÊWbC"•õ”ȠÜW\_Ý yÖ‚2kV§Y 6ºÍ!DÀ›œŸN¹‹5JíØÄàÙBžÌ0+<1e&!sŽ…˜×'«³³‹~ÛÆ¦ŽCáÌsÊž*Ô±À‹‹®‡¯PÈQÚ¢jffJ e`˜¡¸ÀXæ¶¥<ÛQóf·Þö«qx„gc7NnVy dP-°br#1‡QÙlSÊNÆìdr%¬îNæÊKÚÁÝÈØPjÆXAjÞ¥.Jœ+™êæ(Tå'ŽԀ’ÕƒN†¬Ì®äÜLjö†ŒDÙÜù™ü½xwˆH*;ò É© ÝÀI‚º%ba’Ÿ.p ÏGŽœáÞ5½°ªe²:.`g3g­ë!h$ž¥»Ïç1øbK¡çÏÙXlQÑÔÇtÕzÍâ³ÁÝꣿ¢Õ­Æ¤ò,ë1«¥ºåþEnLnõëùÓr ps*ðĵ{éîndÆä°%ìNâZ¹]&ö›'w2[FK'˜‚ª÷¯’D ËæÄËgý\\ KÞàä"1lÉYÙ`Ï"°Õ¢dE¶ó²‰bò yg#uTÃR•ZF·eY]ÎN€ª˜tYš¦ 5€¤æç½:ª»œÕ–]mrz¦Õ¯‡È™…jT°îóêBÒ`Î숓s ðs‡s¨ì2g[…–ÕãòM!`12w!µ!Õ¥*Wô:EbmhÚ… È…¨ÒÅa£Fú~³Û^ä”òÒ# äâÄ"™ð"ÔªÀàDŽhBÔnDýF¹¾e·¨Ì1œspXý«L&º±"¢jP‡êòmé¤ SçTñ¢Êõ= /UšeF‡0¡°×ºlqg–ª/­ݪ3 aÙý±›[`!6áe%\˺!ÆBÜ€™!ˆ¼‘º1Twâè,,¤jÝ-4u&€0¹K‰}ÓX0£º¹vupÏ,Òp0w¦@â16,+&Ä&€¬•V‚¸4½_¿}ÛÂ54d˜ƒË4[,n¤ÜÓ8ÎCØ Üƒ¬xñNé¨e–LÜu=¢¬o›U×§<Ôz’Iž“GáØûÌࢠW3ô!‘©´«æâü‚˜³Ïm”Œ%6m;OjnæÄÜl×g›~õxú.¤9F·Û­^¿~—“MÓxóâºi»¢9w ‘éõËÝÃþaNÅ\ƒÄ¾oß¿ú"FÞÏÇ/ÏWëó/|uséÜßÝ߇ù¤Û¬bw8LÇ)ç4iÛ†yJã¨)2­b ³âôôðø˜fó¶!Õá$F aT­*÷?þ0Ìþ·ß Oû÷ï~ýòü$Ópµ9÷‡“íöï÷7ß}óÍßÝÝï÷ù~¸Oó8®o~öݧoõxÿ›—'÷ªÇaøþÛ¿~¸ûpÒðu—:Y}xzúô”þòÍ_>~óù—ï^ýÇøMÓø¯~ùËßU¸ IDATüìÕv}¦Š“u?Íù˜r'Á…ùúäLUwëîÓÃÇ9ó¶Ý†6|¸ÿl³Ÿîú?¼åæúëwS*œ¿¸¼~÷âú8ÞÃüâüô7o_<Ó„öÝËWOãøåÕÙf³ù¼/m»iúhFûahÚõÍyßwëM×~ýí_ž¦IXB`ŠýÍ«_ß<ÛM÷òÅU¿½þ¼öSMSŠyæ¾ïEgMh¨Q‘n½–Õz›ç”SîW-K<I“eMӨγ*5ÑûõzÝŸf+Íj-ð”¤#ù˜PrY­£&v3â¸Û\¦t°BaÕ °çä¥h!+Çbº?æ\ƒ(RIGÏ”l.ê® µ»¾¦y?+”„„ÆÜÓœài2Ç‹“³è„íæ¬ëº‡ÃSº°¹:oÓ<ºfS.¬õ¢åÄ*ÄE „‡„»Ó×W×§çn…[ö÷TØ‘Àk³ecÊæÎÑHŒÑP÷úí‡ñày2_N8‰ »µÈ™ÈKˆnLÅL3·ISI¨¼$‰jŽî f@&w,ÇW‡¬ÚÉØhdUP[XJ÷KȪRDG·ÅfC02v‡‰˜ç —`hªLWÙݬ^(ÝÙ‰5¨0ØàÌäd%ÆŠÅf7rƒ¹×Ý[õˆPÝðÔhWY²3„é¹äW}€äĶ´â˜ N\Û^5ùdD!“ª;JÝi9›-ÊÀ¥Ã¿¨Ÿù ÄRIÝ`^"ÆDä*`B©!¥Ÿž9  Öȹ>¤@µÇ¸,­ ^›¢õnFÏ;˜.†i‡±;qCn¶\ÒìYÈÇZw*fÏÊ®³jÃYkõ÷²:5kmÊ×…N]/Õ†ØÅ¥Êš€Ü¨>déyoET+xõá(aÒ Rªç.fþÿ…¯j@M¬2íMêeLfNlÆ`U†‚‹„Ç—ýc}Êr ‡qM´/™7°W%L…™þtíÔå€Z­›KÌ%©Oe®1ÁœÉ-&Ab!çE}]Ç^†³º>¿bU’S9\D0B!€cl^]nŽÅ²ÄkS·Òú9p¤’” àŒŠ%7gaÊ\p@½eF´¦@`bC„¯_áb9ÀÜU(:QúXD¬Cmä($áõ+SL¤†DĘÄH ¦$ 'ˆšiJj gV¸*—Jµ5±Ò¦Ëlândâ­ub¸±s “Øf2b'%_(»Õ—­î Z^`©}°±9˜ˆEÄ 5Ì£aÌ¢,,ìA‚¡¸PR%8\!Ü­kãåÕ󜒒QàÚ z•ÈäQ"‡ÀÜ0#¢sj=H×vöH)OA²JÁÈ¡ŠÂ µm8?ÛN£šÏ–Ü0+98CùÊ%ÊN”ƒ$4âFÎZÀE-ç‰$²QšÖÜò4ÆU<ßõ¿y)·‡±mq~vîªC R %9bo}DÓXÖì%u!BBÎã”J€’³„°Úó<S±œŠ¹3açy8Žäsˆ²Þn_^¿†GqÒ9æ!Í &„À #¾:ÛR”®iÛ†ïÆy]‚8+—4ŒÎf»>Æ.€ziJ›5•’ÞÜœYáh|qvbM×µ 26±±9ùåû/6äÉüýÛWN¸{º‡sÎà——åi°,ÿÓÿðŸwW-PrFšÖÝÉ<—»§Ïï®ú?÷áiÿ#iÓwëÝfw{÷±ïÖW§ÛiûñÓAËtœ‡ñðÕÏÞ¾yÿe³º±I¼ýPF½xõúËW7ãüðÃ÷Ÿ¤=¹½;þñßþ|<\­;GB7£UßÚÉ:¾¾ºú/ÿ·9££Îh8ëËãýS¨RÛõéþþË3~ÀÚUƒàó0|ùæfwñv?¯No.^¾Þß}þþã§Oîòëw_ÆÀ›f÷æõÛ2c&)ùižÝ¬išTRà†C'”ïo‡ý±m¢–2ÎùiÄãýÞDNw—}E¥‘üf×Öüìý—Ì©”qšÊ$»ÓÃq,ªQó6ôE­ip¶Y¥<m=kCÚ4ÅÕC»;9ïÛmNCš§ý½ âð’UG Ø´+Ë“["ŠŠ³VÜ‘”Ýr»—ÓFºÈf2«jž¤¡íɺ‘Æ’Ù<¥ÌM”¢y>æãQw»öþðÄ)ÿüËͧŸºU\Ÿ®žÆ;+“d@È<§MµØÅ)È"~qzB¹´a»Ûüþïþ¿ÿo›þçùqº÷#v6Ù 8J‚ؽ0u”3¡±lp=îVL‘M¼¨yi<ÎÒ6M›œÍ܈B²<§L𽤑ÝÍTH¬®MHKÕÖrS¥:º2™3Á¨xm²Š ªoðŠàBTM­â6“iQ*ÕƒlÅØêá †’$‘a wÒŠ^rÒå¼¢DÄ 9Ue±%rožKÝû›“¾x©˜æTð,¥&cP®ªÀÅP"«jX,Ý4b'!&‰³Qud3(T•P½HÕô•»ZA$ÜA W~Nïþ"~ÞZÕ@XÕ3/ "òEO‡Å#X X|Ïí;)×ÙÄU aÉóxͰs]V:ÜÌÚùÜbˆ—W¯ÃAaÃb˜)gI0â°äÊê…Ï…½æ¹j@;Š­e(éóÉO¸Â ÏèöøTwVÏ÷Á/‡Kˆëš¶çEe³ xõWg7#¶ªíõŠˆB–`Fd³šJ&åEáHõßÚq´jò©i$ÕZ©9ÈóÄüf%#3»U!¸¹qQÍÙªÃÛ…eA_pT¹™¥œ´ž‚™ÀäÎÕ-ÝB`« %qgv–ضa¥)ƒ›X aX¨%­þÂZ‘€Ã=*3 ‘”™„©(™ÃÃLLç@‘‚[vÎê Õ" +ŽY ™¹q бÕ&q}©Í-˜«Õ?«š>7µ &g0ƒÀêDÑÙ܈(¸²P°(ÄnưG3DYJÔè P}oE$Å+Uˆ¤D¢ìM³nºÍYæyÔ®ìN0nZf#­{ ApF=;‡(b 4Bb wm󦿜ÌB€„6ŽÞ³+غØÊÉz¥sÞ"}ë)1))® ‘ ÌaÒlN.õ¨€ö±O®yž(zt\È~t/®ÅUux|LîZ ý·÷Çœ&çÒ"\õá8ƒG Ç’ò¤DÅÌæt,Y—ºP6”lë>§B%©k„дÄa³Ù¥4À…Ø.6Ý”Ë0 ó¸—®dýªéºV¨X˜ÅÍjûz×|÷0škºRT"«éñ>6Øž¼p·§CVޜƶíMÏ!Øíá!j9[÷!w?î§W›1Þ2ÿÃ/Þ2χâëØ•’ÏNÏÿîÍÉíÓöóŶ¿|yÉÍÙŸøö_þôÏþë¿5‘^_\ýñ¯]…ÔEY]¾¶Ø°•]ƒû§Áù&\°½|±~ÿ·ÿøîg¯¾<ßÝüúâZBæ9{àû§ÛÚ÷?ÿÕ˛뇿þÓËóÕ‹¯¾ºÿ|ŸS?~ýõ7ÇѶŸ¦yœ­”|¾Þýê«¿™ž>mNN7[ö9ÒЭ_žlûÍúË/¾ÊûOç/þôo?^^ßäi@X¥a6æleš%•1µŸï>þÇ÷7÷ûáá°»ë¿ûü$ˆã<7¡IyJžÇ£AuÚK Sñ¦eBT &°Mß=Žù0î;¶›ýaßH4Cö¹e.û§Û”¦¹ŒiN$î†Íê\(Ü?  TR*îªýéÅ%<0¨ëZerKÑb¸xq5̺ŸÓéÙª §‡iܦ’R2ËÎp3’T4´ãÕù*&+»Út “ÓܦB[Ѿ¹!Zrà‰È/úôã8kV9;íOÖÝõÛÒõÃí¡ Œyæ"SšÇч‰£}ÿø4Úa­Ìûý4[)Ì ‘„®AèdTkCSŠg·&ÊéÉ gáíÉ ÏŸ»ûñÃø0¦ãÈ  ޏ{)`¢IÎÜ H rNG+'™ªPʈƱŒ%'ÆÉùÅ8Ìf Êî³x0d‡C•À¬÷åzRŸ¥¤í£qª• €˜yQº#×ñJl¹™À-Õ#‘¡0ßÄRmÄnîZw*nÌä*Z7â‚èÁ•¹0˜a&6˜CHŒ˜c“&Xb¸ÀݹL¥ÌZ Ù3W€EHåBÁÈÌÁ rh "J‹ÌKf¨†È—œUÈ„ÃÔIkä7Tá[¨‡3"‚»“Tz’»Ó¢r^v6ËúÌQ}Ä@^^^@â ÷áY‚]ñ©ÁYŸÊõŠeU­ ªfv‡?E|ÈÍ«“¯Ž•Ìn¤Ã¸/¶`å*.Ï»+¶géž@RÁåf¬Ps7]†m,OC«‹38ÕsZ…{j†ùqZ‹üÏNh€k#°^pÈ!¡[ƒ™ëÄ…þíÀ †ˆPa°^>ur­ã yåF,xÓÚô¯ÿW=4s­N˜×á„HÝhÙO:UýC®>F‡TLŠAëŠÎ¾EZÞZhÅ@TÄYC ÂO¬ªf0Ü9µrí è²-t*¨¿é¢ ìÑ«& JµµN°.€xaܼ¾r¾¸+Ÿù´âìÔ¸W÷s=,6¨_W³‹‹+Ø9TFEq' ‹“: …àDÔ™@Ds&²„ˆ52‰…V`Îp­hhÊÀ¼Ù´)–ÍÌAìœs2'%#.FkϤ¥BÂDÂκol*Ôœ!22D¯Psˆ‹S "ADzj;k¼‘®\k`Ž™˜Ü™À£‡V"qÍ,Úì ps’à`n×’²ÎF°ªt%áX‘…cß3Yd05\zw1¡Òô—›Wƒ¦’K™Ú Q¨-®Äû“óçið”Ñ7«³“t|âF6Ûí\T²‘T]“ŽÉ,™zè7çb9»›àaôiL9y*©Ì³Óš²—TfÓÌÑÔFB™5 “ºÃФ)“sT (yÓuM·™çYHSÖlDÞh)7!Ä®÷ÕªmBL)u«@àc*9•6Ú?¼; ¡ ÒvÇitó“Ðd—®_ïÎwO‡yœËa¬ÒAÄ]äèÜJ#,®6æy¼_Éåå»Û§ê[’¹$iû×/ß~L‡ó0 CrH!O×'»þðxÖ÷ýðñ8^½z³Û]L†i¦åáé‘’õ!?>ܵAÈŠ±]üñöón}’¦üÍ·_ßOÃîòmh¶Ý~ö¡Ù}þþ»Ó77ñäõnsö—¿þ5¦ó“öî8Äv£‡_\vQíÃãC’xFü8ä•Ï¿xs%}»Ý^ÝëÍæõ›—³‹4ý÷Ÿï»ÃÓçÄ·‡Çã˜i{笇_¿½iw?û?ÿô—aÂPޝ^\qÌÚUÓÌiPH`Ù?~Þï?“ðãq>_è/Ÿ?›ÚÍÕE0æÐ†6ÈlóÕ¦šáÔíÎwïÞ¾Ú®û’f.¨=ÛÈÕ«¿9œ¼”ižSµêó¬išrΣ٤]ßüzç—%Yž´í(g˜5äMša—·/_äi"÷¦¡M»ŽÓa>’•‡Cúpön¦:'s"Žâ g67Šm߬·ÎÙ4ÏÞ¤\LµNF]¿úÛ¿ýííãcŽc¶!‡§‰u˜5°Ù8Ï–r÷éÃí§4•ÉS!ïpÃíº'¦UÛ»(ͳ²åñ;1Óº;3Æã8i1µ¬êA}Ðq.6¦!êããøí÷ß÷ûÃPÜvÛÍ<rËF3%1â&ÄMwÒ­ r²ÙÉ`Åɽˆ yÑDÆNP*Nw7Ó4ª§â§i®¦Ð¢°"”Zãu¸*$îVÎך`ÌIUKEúb=[òZF”½+Ü­¸uc‡™;¹ýd¬e# dÄ^8D"Ïä ‘ÇÂ.îÁ`õ.æ/‘lR×7«e+”«šFIØ F®yÔRØ™¼Q. ˜’³œœ•n¬Z™ÖB *ðBlëŠÂI­F/*}gþT}«ÐÌgm²–„›ÔE²‹š“œ«—p ‘> ­ÊQœkü KàÙ‰G,LZÿt^®³Ë¯cÑÉÔ‰¼†n*Sk!?ÆÏñå¥"æ0ñÂdêîÕB¥­$p­i²,†;‚¹»=cØ«,Ž /7­BKJº¶SP2ñ\™„“{¹HÝ_Hh·^áÌÏQ5©÷8/Ç5Ë+ú r¯yu•p%C:‹GÛQqóìNÏlÒÚÜyù©¦»ë> N ˜œlÁ¥×ƒ£<#RáUŒXÉ–‹~’Ù­¢ï–׀ɉÍ`ä±6!Å¥öUµ¨)Ôä9ÇJNæìp$¥ñ^ÿâ1ƒÌÜ­ž@º_䬤Íz ­a|´fëH|¡]¤AÍ×+33VÉóµBGDÁ™D8°0„ÄD½òÁÌÙœ„LŒX@B, u#¥à&…œëÒ% ŒllK&sR3¯ol°;‰¹rÍý‘š,×k°’;S±»j¬ßzF=Jb‹¤Ò%8Q«¨+FäØD² …HÄ$0"››‘Èì¬PdšIB$õ“H=âÃhÛf*–u"Žìè%Ær6 (È,f0I°XÈ)5ÌäîèN†5¼0Q85ͪ­øìÊëéq(HyôýãcšÊ‹Ë³ÝzcÇ'4̰qžÃ,*#S£EçãàâLI@EU§87¨±2™+„£$¢Ä0™Õ§d¹¨T“„¤¢ r‰-9šÀ"H”4«ªBÔ6'õ>DŠ–sŽbàßýöOŸ¶ëUÔU׫¾ë¶w‡qµj6ëîLJ9å 2(Ŧ%Fžµqu¶Ýþ‡÷¿š1?ÞßÎiVV]m~q¶Š«ÕqÎÇq˜S ¨ñðêõÕéù—×oÞþðýïßýlcžNNv_^ÞìË4–ý·?ÞÓ-\_½¾ûtHûãPL88v§]³ê1¦‚¼[7_Þ\ã4÷Wû‡ãþiƮᙚ7ëMÜø~øÄë5Jw7Ï:<înŸ>ž÷Í”Ó>…³óŸ?¶2÷)ÛÓ—oÞ¾ûtÀ‡ûO¿üùWÝ/uµyñòËwçlÇT6ýúlÛ½>?{uvvýî*¡}Lºêã¯~ÿweLuÛ½ú—Ûo%¶ôôÄÿŸþç?þõëYùü첈¦áXf›Ìbˆ7/ß}ûùîø8ÈÇy¦àæéxL:Ŷᦦa4q5ŽñÝë7óx÷¿þ§ßüÓ×·7çÛÁç4œ´’ÝÕÕrPŠB½0í÷GÇT»+«nGœï@¡p~Ú7±?N‰)®º9¯Ü’“Sž³wŒ³Ýf*e˜˜–¬KpÌÞ4k«›IÈbŒ·W—ûÇýažM§ì<§©(OÄ”‰)Ħûððy»®!ÍšFû¦ÉÆ®³k9æ4 V(c%ô›ÍãˆvµŽãfp#4Ý?ìK™æéø4§qÌyIšþòüÍáx(ÈP'W/^÷bŠ‚Rˆ@™Ì¯zv&µË•ë\á q–êýŒà\IêDn¢ â$ì‚Jgp¶åÃ2©…çº#¢çÝóëâÏ;+*DTê<µ¤æêÄó 1„ÀNìu$c2!¨ޔ™Ê2‡WF%1»TýÒ¨£RqUp'¦Zˆs²@¼>ÙØÈ#ˆ),RJ‡Xýý*ã XÌÝb …:F¬îÕàhö`Dõ»E¢ptP€ƒÕ´¾ƒq BìM‰Î!p]Y9W|×Ò“aZ¾R¢x«ÁuªÞúÝ`Ö‰­äIë¡ fÓà°Bn¨}V8™8¬œL”˜Ia ²,)CÉ#±²8œÔ™ÄÅI!ìŽÈ,,ÂFˆBÑÁV&•Â"än$uÆ5F"g•g˜Ý%Xã@Èî™™„¥ëbÏM ä¹ fj$Š4I£ª 3AˆƒGf#‹¤PS¢A9…†8‡¦"D–QRÎÆÔedÆ9K*Å™Œ °¯®ºYËa|Ìiv-‰\9™º5PS¸À8+d8‡å<¨eqm6ˆhÕugëss™§Y#Zu½Äè0(!´»Ó0x ª…iÞ¾¾±ÏãüútgÌon.Ù~óí׆à³Í–ˆ^¼x}<–iœƒ„nûÂÓ­p”Ø8…ÀÍÛ«/6}ÿ´?F±ÓU¿Þê‡Ót˜²ÒÔŸ\¼}ùvÝóáé.;µë¶o·ÛÍšx³»¸!¶ùo<íhwu¾a)ÐËó«¾oúÕåû·?¿½ûÐô+?<}8ÇÃ4섋廋‹—/JI_]_~ûùÇï?Þ­»³Ì¶Ý_¿ÿS¶2‡±ÐéÉ«ËÝiׇùø¡)öåÕUèdÌc£sk©Ûžþ×?ÿ_y8G¤ðó÷ïóT~ù³ëÝûó¿|ýñ/?|×iÜ®¶‡Á[ê>Þ?ò÷OãÓ!´Û½×››»ã0_½¿[oŸ¾•$´;‘Õæ‡?ÿÓ0 ÂáÛÿºö¡E3RÉyš7»]ß ·ýdv<<œžžÝ?Ü&í‡ñ8""i³ Z2Q{²âÛ»ýíýãû×Î'd¡Û6ÇñØiC×:™27íõÙÉI:sO€wqµZµ«^ܽïÚ7oñtÿÿ1õ&]’G¶æU3ó)<æÌDN˜NÅ"ÙUõzÓ›þ÷oÙÝÕ¯‹,NA §}°AUE¤j*lpNä9y"<=ÓÄEîý¾ÝrurºÝ><Ü“u‹õ”’¢0t»ä1éb½Î‰wi(%ÃrÎp Kæî¬ïAÎO»b9‹­Ä“!2OIS.#Å%iM……ËÂYÚÎ4zlƒMN¹ ]œ®b£(¤– q‘DêJŽX ÌŠb9ó0ö“–ÂLÈ­ØØ™a݄¤©ÏÅÌ‹¸—¢Å³Y±\0ÅìÝÓjsZLÿå«— ûÝÍã‰tiØ™ë¦íº¦K#á7_¾…—q².rx³Y¯«6®R‰û¡ßï’3Ò4•l/ž]ÅF;2_­V_¼xþùgÛoüGÓ,ÓX8DUD)›6þö›úÓ÷;ö;LÃ8S&æ‹õéÃ1«L#µmøúë_ ýaߊÄÐo®W{§œ4HÜï’k¡€<Ñ”=‡Ø†Àî&Ô¤2™ZŸøáų—_Úñ å4Ɔ³»PäF^^œ÷îhQÏDæ±õ!«µæ‘rqvGH3ðÎÜŠ´FV‚,Öí¶6X?¶Ü-N6©ø0eK›|Ôd’™²·Üpxu~±Û¥£]Y::ÈRïãЫ8“IPe!ž‡AíÀê\ܲ»˜¸±((ÒZ©˜MUƒ2Ë…²y*€ uf_Dö¬syŠ…(* N¢àù3 #‚g››) ÏÝCP$Y·±˜š¹1S nÚHæµúÏÜ@̉Výs˜Ü¢“H0& 0®Nf%‡3#J3â@ͨfyMäH­%nnLTçˆ5¼^o-¬nNn.Zƒ?€A‰È¤Þ½>k ŸÜ V§ Yrðd XÍfÁoBLj0§ÒÚçUÌì®›‰›4“âÙæ/1»ÌÁo© ›‡³êË!2r®·6®Ý©¹Œ8A NjÂ0ªà«Œ(Uª;bs‚$Ôg¼  ŸÁQOI¹z"óúŠÍ“Wå2ˆ±:WÒ§¤³ÙùiB\gªª;\æ¬ý¼¦›q5N&’¦YK¯fˆâL eá*ñeF¨M»ñ¼t$ÁÓ­šj;Ó1S:ªamfW×WšêeМ+ÌC`â³Ej÷Q(Ô‚ ‘°Ï«ËYÜÈõ‡™k•­&ºê[cþ!!3®b~‹UÐû&ÌœP}puÕXñ_(u8pˆ q˜·šˆár­$§n¥ Žp3…‘¡~ž±Àž5JNÂ$,BÂê `,p½£Bñ™›Â‚XiìÑаˆ8@Ø„<¸-˜æŸ«³ƒ©)©ðŽÂ¦jâWŠ[Ý×iS!Ä2+ØŒÅÌT+4+&&c¤¾)Ö3TÉl”áVöCûâúr˜²—BR·†À‚)Šƒ‚;1À¼—RÿùpfX˜s¹Ù&… ³çÌÊ,Ä„h+ÆÎIØK¡Ú$ÊìL­F&Sk…5„6À•]8up…GÉÒpüüþÍCšúÇÇÔO€Hìø›ožß}È0M0ËC ‹aYR×-Ë” ÔÅHÂÇa̞Ɇ”SqÓ ‹®ý—_þúÝý‡4̺҆Vˆò8 ©?ì{‡‘K¶úW{?f“€åºÝÄæù6tËíXh³e§ØtÇœ>Ü=²„““óíÙ Üd·ûXбH(OzèÓf³^°ÜíîÔØáMÓRP‡8ÂrÕÜßßú´Ýv¡qó©$5£¬æfËÕ¶mËôæññq?º£èå’ÝÍ'=?}þñãûœ²,Ä »Û‡ qqrvs¿3Lì0ŒA tíj¹ ‹u÷ñöqL%rd Âî–xhckÞŠ´ëÃx˜ro™Ê4¥b°àRŽ£ŽH±9‰ÅWgçeÔûþÁ=íS*(«¸èNΦaêÚØ’ä2(yl¤k‘)çÌMõêJäF8r\ó‚J*æÜ.OÒ0æ^ žƒ) ‘{ñ SIe4¥THr*æäÙ8šÖõ‚“+SÚ”¹h>’;/š”U´ÀšÆa% d!«—ZçäªÅ˜Ý‘ǹÏϰÂ-Óš1ZÕHõ–8à"¨Ÿ„i–ì¶uÛú¤J¦DP­é\u£ÚÅé4NnVgJZã"B!Ôª?WDMs‰‘ÂŒôdg·9¯"³ûxÎT×ÉÁç” ¸Pu"ƒ ©–a 6šSJLP‡{ %gŸØ€”Ü ¯¨z¯¦e7®ÍÁ:…Óü¥šÛè¢*€KìNOQ"P ' @UºÇOÛ(fæºÜ¤š¯2‚‚;œ‚ƒ˜ÄÁâ žkjdÖNsÛu.âiKRÁöÕ”-„À3`ÌäRÁ¦õ˜+\7Ĺ‚$Ô×ÓÍ¥†£gí¹‹13<ÎÝQyâÍ7bžFЊyƒ]LD¸æë²8ÖŠ&ƒ‰<ÌYBŸõ’P!äÌĈn¡%YL„á‰wëTQbLàÂÎRQ[†(‘˜˜ABÚº›k]Û 1{0 Îä%3JEÙ›‹¹š«(“׿ÉP'7vc›fšQúN°9­—+–ê¿J.œÄ«Œ°P­QÖí+” BlmýÛ4ŽcEA!Ô¬§0 NÄæ"*ê ra¢úvu®¦îÊ_Žì‘ºt·àÁ#G碎¢ *åŠ)ˆÃ9'i¸°Ø]T[0»pˆN¡5€""ñ"HŸdI"ÅdG<<Ç£SI¥¸9³¦»»‡4åÍfÑ𲟎)¥¨:®i4s”rQËÍ  0…\Ë÷Óñ„å*NSúÇ”zµÐÀÕ9žžžíû^@‘ÀMËBºZ¶,ÁER)®jŽÅbóìÙË»ý‡< yÌã”ö½[5ü°ïTœše»H)šã ùú⢠²j-»J\/–]Û6ç—/y³^/ÚÅv³É˜ŠJiĉuµÜ|ýÅ—Ç)­ÛöÕë—p鮟¿\u|Þæ©¤â© ÛõÙk²áòêbµÝRÓ“UÎYVüêúÊ󰻟ºõâ·ÿòó?ýçw÷÷ÓA¹ W'g—˘ÆãÅÙ³ûï³>ÜýëË‹&J´qô×?ÿÙqyÚ%J//N†ûÇ2ìß§‹¯××W×occmGšýôë_Åë»÷Ñׇ~üìŸwøxsýæ÷ûÝÝõÒÎ6«ña÷xØ}ýë_N6lÔôñcºyˆ¿ø§‹áöýN³ø¨±m¢[$£‹Ë϶Œ‹'Ýâth·8ÑüöËç÷ì‡þ8 ÷»ã§]"+×W»]ƒ4°LÙïîgÓѸ‰_½>eBvm›F=O£ŠÄÝãÇ<¸y¡ÎrJý؇°b‡’“€(laèÇ!ešhŽ²Ù¾¦´Û÷N¶ÅriäiÒœsV7’õ¶í¨§ô°ßÓØ¶ÒEzÜ·»ã±˜°¸€\XºhLDÒ.Wëa˜ÂgÏ/>îï ¥kÛR-&qËB±[-7›nG³’&›Ær(CÙOÚX2›'4«T&Ò’Ý!MlÖD Z1U õñR¨/ó'K”ˆù¨SN¹¤&*VÜó|l³L:-¹¨fWSËMÃÞŠ¹z1IV¢Ìjð¦Îi4MŒ¢œC`#›Ô5çbpµÖlN ®„Â&ªÁsÖ†r³&ùÜ(6x ng^6¼é:qã<%­ÏOq#sÓúl5Äœ²zv¸ «ÕÔ Ã¸Èݬ Z¬ºœ%V|gMÏ@\j:%lÂdÎ`'F•<[õùQ!ã ÕbWÏ=¢õ`Sɪä\ås³SRS'ƒD˜°`Qf©Ï†’³3ÀæÄlÕ&,5ZUeh5£0T¢I­È9öä<öÊŸr…1?¥ê¬›!ï4·íÿõßSf\~ÂÓ̾¬U† l~Î31“C…½¦óá$n•›Zñ ®«U ŠuJäy·aìsvý‰~:CËjAï‰Zk¤™ÜŸÐµ>7gï|þ)æÓ—CƒÖÍü§)†*}|f(ÎÛ5=Q¦œÄk{ ÂÄÄÌ)T™‡z|­ý÷þ¤›ž'‡ß&¢è¨LAݾÈK}!k°E z6#c ¨Û™ñß ’9IV55îV•äì5$¦$ólÈp"­EF†ýŒ…˜e`sfvVÔ˜”1A pŠ& {æÂÃÓ@Ä:gÌD— }¨§Kæ:±ÔΆ3WÛ“¨ÃƒW”¹»¢Y_0DÀ­æPƒ"ÂÂöÆ-È$’8‚M( »˜(¥  Cͤwg&Pj Ó]aÄ.L®\X³ye»[Q3¸ÓŒÐ€‘;…JFugw­²ÍZGP;©‰‘2?ñl“ýôBXêr×H]™™œÍ<0  ÄÆêý)%WÕ>©qÑRú¦ì€º3ÄÀ ¶jÕ68MC](Ú&w’PT¿>]Ÿ¯_enO›üüül,áêììÕËÏïï÷ü뺶yñÙÛãá~z(qs™‚~þûßàãû›Ãñaº»ùæË‹å¯~·-ùßÿðm”e[†“« yõ&ìF|øöþå‡_ýüõy¼8Ýþøí˜¦îäÄK¹»y¯¡¹¼lMlÙ-¿ÿp÷ñxì–›E»ýt¿co¥‰ˆ‹óË7Åì‹·Ÿ•ýMoÅâJUMõd±zqµ^vòêúÙù«×ÝÃ͇‡‡ñ8 ·wÓ0Aʘ,’ÐfäÝÝn÷ðp< ûrØ6”’òÐÆlfŃ$˜8|H%åw¢ö³óUÒ0ïG-Û MÃÛÕíý>¥âD1œ0òÝ~—³5Ë®mØ´^Fô|³ÞnOrÒ\rž,¥t÷¸ÏÓqÕ…oþé_úÝnL‰LÔób¬d÷d˜Vm\Ä0NG+Å‹g„.D_}ÎN楕BhE4±³ÌMÛ ƒUshR›¦’˰X-‡„|ÕÍ-:Ù}b³ÉQ\!d¦^,RNÉÝ-G°÷^r̽doÕr¶Ìn£dãB­Á<Ûà#ê*G¨kØÕÍ„V€dpWËî&& ³H.ˆBBÜJq¢Ú–›˜œŸnχᘽ8Ø,sawa«-3Oªj ÅÙˆ9pÕÚxšŒ•ž)„àÄ¡ mˆ…´’Ë‹sd¸1ƒ•Ȉ²³Ô*—¹Sw•@`5ö¢„@âÎÆVÓÝ‚Jæ2%Aµ?…bˆ«É¦ÂÇåéLW¿D8 1»ÛŒp®¦·™GEP ˆÍÙ­îµÛO3œk–ç©“èOó@ÕaŽ‘£6÷ p©õ4• Vƒå\…ìlD$„̪ ûiߤê5´SvRo€r8 ØX«û¤B´½d¨T­:…yU;ÓÓåsNiyÍÕgIí÷9žÊ‚ùÔÃQŸõ•ƨ=›AP4Os€‹4k"væ§­RoqÂÌBì,"dBóÝÍ+-­Ž¨2_×˜ÌæŒ¡€kŠ_ ^*>«î*æW³Ê’ÀÁˆ«^Bµ_ú´TT78¹×ã¶„zú«gDaªU>Ôh>iŽj¯öþ*0µ~_ ÀKEõêÄTu=ó˜*äÁ‚3™ÎX׊Sd©Ù#åùRÝ’(H@䵆XpöI܉H-2{åƒÁŒá¥:LëÍ•À$M=8’ÇHìDÔ’÷XôJu䊅ê&rr†\Šcf¸Õ&‰²¸»eƒ*•Z Võ 5¸ ¸ YUÅÉØ TÈœ „XV27/Ä µ:ñ P&V&&PýŸ*È©n +ø8˜Œ ÌÆ&ä¦RÿV,(ˆÙÙÉMØ‹Å舙0›@-¨G cD!0)£ÆDg”ÖjµlˆÔ’bn›XÚÀ-Ú pD$Ñ›¯_ùêùÃí­’©[$™Aa± ÿú‹·ãÁ“uËf½]¦1g˜ Ù’"%b×@Âd¹Š®Œ^ÙˆJ#žK¯Sn°—LÑS‰’ú~„¡ièds®ÄçWY{h" aÉ!ÞÄŘpÜMl™¸Ï…ÝÆ4™³·]³hVËnk”†±œt¨)J®d&Ëõr¹Z§’ÔÌɸôCNÇdS‘WMhÿü—?—œrñÇÇ=̲å®õ»’m³‰Qpÿøp<î†i§!4XºÞ¶ïînCÿxó¹¦tÈS™Æin“„‡!p´¤ÞOyÚ￸Z½|vrñ‹ovïn“Npó´X7?|øñdÛ~¸¹Õi›ÖKE ›Í¢[x¶{ñO¿/ûÃq>=>œ^¿<Ž9“|ùÕW÷?~(»,i|ùËß'~xÿç?þùdµ~þë¯ÀŒ1Kw£ÊóçoˆV÷ÞºíNO/·/Ÿ¡¾ýþý½µj¯®Îon~øÏoÿغ†Ð i§t¶===ÙŽ¦‡ÝHqÕOåâüy·lß}ø8hZE^ua¹Y2†õÉùß}<Ž ´¯_¾L….ñë/~~ïûá0æéñpÿéͳ«›þq4ŽWggÏVé”ËÐçryõüq¿mˆ£åõÕgº»Ý®³gÓ„'sî‡d®ZÌÈA¡YvÔ4P[tÝëWWg],i?˜¹Çï–+5ÙOû?ݾ()§<.¢µqOÖàˆ2 G›4ÁÓb½Z7r¿?î>ݯ7Çã=X…­²n¥ø˜J–°Šâ»}ÞeoâI»¡€N¶—íêl*eÊ¥xųe³Z.Çlç§—E½‰Èi2ðw-ÂrÊVÆ"Ä ×”]ܨ‹hD­z9„<…¦Âi*”“‚ÁI=¸J @áìÜ2Ç ãdV×”2 ÓDZÑIpÅDpU”<{Îps‹Ô™­ÂæC@¬dN%#7Ÿ†ásk×½»U泓©  .ó Qu*n$U–kn O8ƒj s˜A‚‘ÆÈÜBt¶Pª¤­c(jÕŸYAf Œ=X,â¨Ç,¯‚çÌF¼¸EMy%»HˆÔ¥J”™Èê=Õ¹ÊjÀNÆB) †‚°¬Þ ™àf°Ê/À|ª3 ÏdÎ:¶¹Õ[*É‚æ’øü„…ÃVOUTq ˜û5_5{“g8ç¼ü«Ékpí§› Â¼ªªÆ|v 8œ] ¡ó‡›ÔϾ c3‡W)uX%š £µ,?Nónj+³¨:éÉdˆ§ž¢÷s“²Æôó ÁðºÖù/ŒHÚÅIv1ƒìé&ËL–*T&ž9OXd³yÐç,³{ s1ÕñÆy^”Íè~ŸUô´ „£®?ê ¤nê«Yé•R÷Õßãâ\êÅÎ@­„Î?odÙfm·8±ÀæÚ`ÝV{@ÕdÖm}Ùê ·rj+?že»èV]˜tÒ:Z=] ¨*ÇçJ€üä"bç¦ qæ:*x~ÝQ TOv Šõ“AÑ;KÍ„ .® 9#d#‘™@!`.™HÜÌ‹Ö7IiJP7‹æd%G#µ*S Vfpzý`a6§Û¬î°Õ…ˆÁ…kTÎ ˜ò5·"H™àld!p!!o†®Y#öŠœe#s[f8KÍ‘RS'd†+ Nܳ( Š3•–¨išìKà%!”R¢Gâ΂7pª/5\ÔHX8xb”7//'MTJ¼Moß|~yÝÞÝÜO–•K‹Ð0#˜»ã”r½;{v÷ÉLBAÉUŸ™Ûöô‚x§]°ÎiÌjX7P{j»åqŸSÉŠ›e'òf»z8 N"ëe˜¨b|âj½ ÚÕù0¤a*ÅÜm(,ÙªâS6ËÓg'†ìÅÉ- … È9Êæär¹Xž¯š® ÏNÚ‡±Ñu×_¾|»’´¼í“ÌŠsìšØ„˳SMª¯.6/Îb?.O–£¦ï8öe,†£RØ~¸½Yž¾Ú ».Œ‘…Í‹ç¯rÉLxàÔ›IÛ„ÍÕöÏ?ì?Ü´Ñ*!¬Ö‹øpØŸ,VëE»½|þ›ßþ&j’îüßþýëwïÿãÿñâù——§q¸ûŸÿþï7wS*Ÿ½x™?}zO?Üüí¿|‰ógý÷ßÝ=¾k“•³óghëÕj¼¹)¤——×Ä«–„Ø-š¸=ãd1Æ/^^^<ûòãû[sæ1·1¾ºè¦ýÛ°Ûݺ)(¸˜³×ÏðÌM¸>½º9%™©Ú„iœÆqJŽQs3+Å@b'Íî»q:¦lj°ÒP죶6)„Ê:¶äH9 Å-Š™§õfùÛ_þóñîaè{óB©xI…Pœ‘3ÜÔlâ’IÁì¤(Z—þœ`¤†2&ÕâjN9•’y²/ʼn9X$)9‡‘jÆ ›<(™2`Ha7D!Gq….Yaw&«ºÃŠk†e³Ê’ÔÂU‚Ì(Ö„Fˆ¼˜så•g̈PîDh,‘Áj5Ì ¢™+«‘Bň70&¸39{½—Ô”±ƒ¥~è*wª2Áa„¸°9¹Zp×TmõRŠÄÂÁ)°ˆ¤ r–y*‚AÙ« ð©lVÜMçô  v)ÍmºY•3«RjšfnéÍOaøÜ<“ú0癯¿Bó*5 TWõü5ÛZ×9Vm³Af„ªŽtwr(9˜æ”0B1*PcSÀAÆu§µöZë”kmê>gùçì6þÛFŠCmV] ÍxÇúmZÞÕXÔOÓ£´Ý†ëIŽkè§ê Ì‚44¿=™æ¶¢;ÄÁN\½&òëkɤò?ÜÉÝ„ëžgÂ,8=…ïku ~ó‹õ4GÖ?ºùG|îT¨:K…i1ŒÉÈC1®0™yOCªA@V‘¦±ÞÄœ0ËyæØž‘;œCÀÉöDuÌc½Íþ7Ú±™`p#ʈNêÂ4cÙ‚q}3¹W Eü9p½Œ9°×xÝȺ³‹G°F°ƒI$²Ek¶ºÁ5T y u8+Èjw†sÌ bu¸QQ#hESäùúé¨EܧõrÎìd≤s£dæIÔF‹qxjÙšÄÈœ‹0ȹFs]x³Åª €8‰: h¸FÐp@…ç‹©1 pK p¾hGU KBà®u ³PÃæ ‚‰[–Hf¤˜ƒéBòT\¤qñUc&÷ï7¹ï4Ö,žu°@*N¬mìÌ´Š/…‰c£-ã4UsfO»ãM±ÇœË"6/.·%ÙØ÷cʪzÚ m M.}!f„Ðȯ>ÿâ0åw}\ž±ù4e"KYsòå³/˜B°Yp?•FxÙ YX´˜F{ìÇ¢Š!õÐDÚn·¦ÅɼL‹ˆ¶‰‹åjrÏY—‹eÛ­a:Ýv»ã1»ã”“âdÑÇ£Zaóëóç)߯Kû¸OZR61°‘0Ça¯“ç’Óz¹ú¥<ýðÃÝ}ÿüÕu?¿ûöÏÑŽ›“î‹·Ïþòí_ÿúÝwwÞo»Ó¾?æ¢'§íòâåsËÞ{ñóÏŸ®V_¿:Ï<ôû϶W?ûÙÏw‡‡›‡Û“íI§ûÜn.οzvqû¸sÁ&®?†ï¿ÿ>•´ê¬ÏÃÃ{Ì–9»ÛÍÍý0M›¥¤)÷©|üøÃñ>=¦ãx,¹2«/ µþ±ïÛe\/JZ6œ‹G‰çgÏ6 j#—lÂ!º~vþÕË·VûÃΕ§4ŽS§éfwÛ‡EK}ûi‹sXäìËvõüò‚P†iä°ä°¿ ¹v IDATºyÈ»Ç>ç>«fˇ»Ýðx<+`))IÛ–drãºÈÞ²{Ô4šÚècštÌœÄ$§qÐle€º£…tljTÕ÷¢%Ѹ€iëↀNyô/Å- › Ñ¢^¶§§çûw?>îJ25˜g8ØÊ™\ɈÕL  ƒOu%X2·Rr9¸`¶iZY4Ë<³ Q.AÁ²Ww0‰³Æ¦¦z\‰8 3ÒÌ\Zu2‚:&¨Šš.C½+À* 4ÇõðÄÌjLp.  ‘‚„H@t¢\Øv’ 2G³„ÄŠ»¨}¶ÈpòP¼žáf´Q1Q©õ8 ®ŠÚÅçšéR69+ÈY}¦ˆ38Ä(BÂT-?T[xÎNËå¦ä ›u-ðR?F»Î¦_2†ÁÍà2ïîˆÈµª^žü"3֢ƭkGí§UƒÓ“NÅðßî†Äî*È+ qN©3¬^ á 6cC ˜AdŽPýDeòzfƒYzܨî|–7׉BëD)óW •ïQ-ómª¥³vfž§*‹ëòм"«È¡õr9£[ëÔ-N¨ªµ‰Ì$$&ÂDóÔ#3§£Ê ˜jÈ®‚7+`ì¬&§0é*1¢Vy¦ÖSœgý_#ë+Ë ª(¬x³j¤Zx òPù •»>ß±jæö´ÆröX;๠P‰XO¤®ÙÄ gR®¬«ä°`îÌ Y69MÇaÊðŠBe!â¦ú=fŸ¡¢NˆŽ D5ÙN`7¦*€Æ%p` !ªw¢&ØC…f ;²°x3 BêÄŒŽE™) @‰ ŒÍ³k•±31›º³¹ÕZ;1j|QQà:ƒMQÌuþ¢`©T1‡(ȪÇʨ¼*`æ¡jºAîj*@8J@†©;CÍ*dƊ¹6^!ÀÉ©pÀƒ³[Þ„ŽûIÁ“³‚ŠC‚àlUµ:$­t¡‰¯¯_ìû#,!‹rðÅ¢ Þš;‚FëMÃÜû`™”˜DZÁ™sÓ„Õ7/?{G Š“¥"TÔT&Gñ6RÛnw»O–,;ãxÌ»iLybäØ,õЗã4©lmí }ºÿ4c¶2唦ƒ‘›kq‰Ìd‘ŒöCNcl£{Xt"³…HÝõju½ ›cžtp͋Ų[¶í2¼xvq<Žë“gPÏiœ¦¾òl]öãÔu2&Ò4îÆÃ˜5'51°™á¢Ö“Z ±IM4]s<úÃ1ŸoÖMÃmwöÅ2ÌñæÍëáþŽ9í;*,j§ ÿáá±?NW§gÿöûߥ~Xvaº÷w7Ç|\4í8¥ãííyäØêÃn¸?Ü<ô…bøp{{{x ~wì«Vz¼;ôÛ¶}§×Ï¿4áí²-%ñõ›ÇŠûÛ—¯ÛÍ©†åÉþǦ?}ûîd{qùû[]]#.Ðùó·_½~~ù«_mN6KïNOV›×k„ø¿øæâô›Ÿ½Ž¦Û‹ÝùÛOw‡óó7ÿöûÛÿú{ì3‡«íf<ÄM;•üúâbílíòÃáPÜ–]÷ç»ã1í'{ö½m²Óããñls²]ÉýÃÀ _üæ÷·ÃR:îÔØîŠ】$Z 3Ê0$µ\|±Þžt+”)å…—²Zu¯ß|3ì ð~ÈyòÕ2¼yù*ŸîwÃT­d5·â6º—)ÑS ÎÌÇTPŒ@“NÃ1E#Kêj‹ÀÞÊ¡åÒO…´Xqm"Ÿu1¸$WËÕé2"/§Q33§°?¤! ²´ ˆ¤¦ ]" zàF¨#‘ãTXQxð BŨ33[Ei—!„rÉp Ɔld]G–0Gl*Ùš)ÒfÓå\¨ 0À†E j4•ƒyç¤H®bVùíêTæÙSKT‚CÌuÒaÌSVs&GàÓ'ª]" °`Õu8;»µËBÆ–¥xÔs'5WBkƒ…Èf§C•ÏèZ70ã 4˜1¤.µßVEaNÎ8‘±8;¢hV4Šd†O‹KDˆN!) IuQ :2€\…Qµ1ÈDJ+2RfâÈ!â!…Z¥‚ÔÂLùv! ³gSHýÈmä^¥Îîæ¨ª@ª]šdö À0ÀµU^Á l6ׯ*ø’Ÿª]Hñ´åª£³ÃœWä™Ì”-æ'€89 W#‘1ÙÓ¦HëBÎdó!j&A̺hƒ;ÕÙSB¿2Íê·4¬*¿Óœ”ç» *NÎ3¾€Ÿ¢HóÁJ¬ÎÈN³Ð?ÑØçxÛÖÀöLog€‚D€1óüÚTËW9ª¨’Ó„«"™½¢5ÜùéîV—…ÄÄ”Ú'd˜?Y+j D˜<>A1jyÁµr¶'´™Ìñ™ü@¨šR¯":!õš6Û}h~£ ˆ`êøÉ©'öJણX%OÄjFBp‚"e)IQSp™Yœó$µ’[™Uê$N H5ƒaFšn±´is!TưDnb\e+ì ,Ø…˜Ñ²ˆ»¨z!e7uc8#;X‰Uœçw?#“()—z†c³÷¬+DæFóyЋ9Õ‹½UDš(ÄÝ\Ü@,ììðH á3ߢö(벓Ü)’»×%° @Ìfë:;»1(€cÓ´ [dx.\;Í¡ajº¶»8½8… ¦FƒQ¼­’[®ïlW-Çþ"‡&,H,^¶ëf­ªbda®MRÈÏ] Ö DÖˆH& D¦ÄC.Å´0šFTªr,4‚ÂnšÊ Ùmd[Uï‡2¦â–„Zod±h^¼~;5tŠ\íRð®~2ËÓ”§lê>•÷bã¨SžØ¢Xú4Qa¯®ˆ¦ ÎQëåÅþx쇽j‹QIe’v«Å‚…!pNCß ž?»Ú.Zõi½>E\–¼›Æiòñìò…[s,Aøt»ù?÷¯ÿ¸½¹ÛúÑÃ0”éþþF¡ÛÍâñîXJ8?;Ýlºþhën{Ú6›‹Å¢;ûtóû»ïîïwœiÑ5_¼yûp÷0Ú$ÀmÊW×oÚÚUë©§|8N£*~ñÍçç¿ûÝ—ëííãCszõÕóW¯?;?£ƒwñbÙQÛ1hûë¯×Ï.VWÛÇ}¿Ï´ìš³_¾†¬ë?¡X.Ãn½½Ø¢hûì’OO1 ’§~÷ò´C÷|ñå7«—çùãݾÿ—¿üßß¾û¸nÎBáÿïoßûÿþ?ÿøöÝôñæùåâ~Té–wûé>ïîû‡»Ãt|˜Rn%ýöË·Ç2}Úí•f*u‹Nó³sÖ'§ízyq¶:9Y¯—á›Ï.ÿð÷o?Ý߬؃ñ~è•ò?¿½Z.ھ߫Z?y¥¯wÃÝqrxˆè:ùñæw·Ç¡Øë«õ”ÆÏΛoßÝÜÜ}>„-¶jšU·X¯ãØUsÒÙXÊùfyu¹y8¦ýN6MCÜÕ’A}Qe¸º’¸„À˜:áHÁ¾~v1My(Z3+‹F9R΢…ÍXQ`c¢L\È¥[,.VÍ”‹eH1àåi|00yi­1;[­FKäY¼l;Jãä1uç(F1ã109ÄÕRêI®Z,39³SІ…0N ›Mð)é˜ËDîÃÀŽ2)žÕGÖ‚\ÜÊÈnÅ ®Ì5 Jš †f#ÜŒ‚9“Ï@63g‚"Ÿ¬–6ö¤æÙE C‹+»gW°ÐÙi;ª1Uï6ÈjÙi>¤(af7‰ÿÿœ½WeW¶¥7Æœkï}lø4$“®ŠdÞ2×të¢/$´€þË‚ô$@ ½¨ ¹®ª[EWt™þ˜mÖšsêaí`ëz Dšˆ8Ì=Ï0ß0ÖåâÙRW8”PˆŠ‚d4 ‘vi Ú±¥D,D%©Öé È ‡£PÓü3¥J_Tæ‚8]«á)@mBÕÜ%( C¤†¦q7¸Îu-ªD 89UZÊxmPܪÞQhø<á<±«j´šÎ™ñ0ðŸR\3òŠ0¨¡@- QËM",s‰Zûd…I9#ÏïÖÍ@¹s87üæ˜|€¡sÀ;0ÂkÈ.êl$ReWA "+& >°ª¦ãó݇Ù|b=ç›%à3þ½.»pn=E]·žçãj˜?Ôåò¬êF¨zUÅŸÏ!§*ÑÖ@}¥&HÔ§©hÝÉaš7Šæ¸vmô+EIÜç!g„sî“V è<-3gŸjgÁQÛ"B+‚pЃ$LVÛ{£ÒèAŠÏç4\hÅëb„Ö`HÌÑ⨸Z÷ bÞ(‘TˆDeÔ·#ΠÐDÕ¬hA…B¤Q‰ ° 0!$À`˜4æÙLÎÅxh¨ÖµJ‰FH'áõtIh<½ê‰èÔE»yþìé0ÔBjTi±4’D"+»î‘DBA•!À¨‘,yt£ƒ^hÔèÀÄŠHÓ€8¡ð§—“jO? † ¼â­(¬[¦?‰)æb‚$ŠCB=àJñpŠxµåË“bŠÙBOÅÃÕatˆkJ +lB¼ÄnÜg+Òt›´ Xc-Ä£x­„6î#„áB.?úè³qê݃­;Û05;ÖqD-ÆP("¹¡º èÙù ‘RC õ†MC"‹E§  ŒLM>9;éˆaسL.\´“2 4).êñì9Kø¨Œ&)ŒJ†Åz±],ÿõ¯~öÝÍÝ4¥tÅ ƒpMެêa³€¶¬·Ï†ãHއ1´Õ÷ž½SJ&Ãa%‚llwmSzÿåYvôý$ìVËEön»Òç§›ašoÝz½Œ|<Ú8æÉí˜4Ñ¡ÒöÇa¿œò~s²=99={yvývßO9åtV€¶u¢ß4¢Ù®Ÿ]^$÷‹÷?²¶½»û."^~ðÁë··»ÇÑG€Ñ÷ÇÃí?üöï¾úñ‡/¾ÿþÝ—ïüöw³ÜEÉmÒóÅæÓ«æùZ®ÇÍá»ë7×ݯ/»³Ã¸ûâ‹ÿóùËŸ‹¦ÿõ/_ö‡'.º ¬[_ÿá/W§ÏÎ>ý|úóŸôñƒa»°zqÞEƒ!ïþü—î—еX­Ïß¿D#8 h:,Ú‡oÿzýæ«×ÓÅfóñ‹›ó—i™dÑîïO›”£+†¶Y^_¿þÅ/îw‡Ë«gçW/¯^Þy»è‡òÃÝM£BíÜ'§§yÚýp÷m/Î.?|ÿùÅÅù˜§þ°»¾ÛÝßíº´~±QwY¬W£ãææñnt&IÍzû¢¥´‘aÓáùáèÇãÑ)áöÐ÷½ašÐšá†X¦…§¢M{r·Û£˜‰—ì“Àÿþ³GK}o!Mx]¥1S81œlÂk 6 ½•ÉRöSž”Óh‡!/WWGËcmY"yDC®×Ç4NS)E“-<¤“³ó‹ûñ>OP¶EEši¤éÔ­¨±=ÆQ£«ÍÜÈTƒ•h‘¦¦Õ:lcnmŠuÓ™yLnTg‰Œˆœ=0:Ý%ÌŠ·1©„x¤@1‰™^œîG¦"W­Â£Q0Š{rq8 âÈU«ð€™€PRÛ¨ÒÝÅ9rx±Âðe#Ò–á`µ[å5 1VS)UHA¤[,€º?èªrºZ”⎀$Aj»…¶ôȨìn ˆ4d•@´²Eƒ$”j©LÑx¤˜ãÀIÂÙåDU A„ÃàÐYÜ2B=Ü«Qàa âÙv9— ¨B"%9¥]ñW ©Ùœ'ËMÉ Ù*œ£ö1ãÏ£ˆ¸Á6;t3̳Š8óÑ©ñháJnª”ЧͦâÌr¯ä+™'ñ4z4;ouƒ(PýÊúÍ“*ô ž¶*ç ›‹„Ï (Ÿ ÕÅê"3˜Jˆ§ H„ ^­nÖiÌÂ$€Ôð`5*뫆ROHJF!K¤±y)º©u»ú[Ó™ô^£n^ózÕLuqgÔv\æb¬¥¨®ž¸Lj•¢Y½ñÚ¢Œú|¬¥Ó˜}åúá¡áâU:3Túe$I•ÍT“p«)1¢)©Â=œ Ø,G* *Ô¦µ*4J¥@©Ti$ÁTw¾-„…ÔH¡Ð /‡ã|eKb“SÒÑaQêqqóQS7$$Ïo3`¥þ_Iwñ 3Æ`bªLؠ¡)¥^Dj,”$ôil1¢R´z‚šÐJbÍd¢¨º˜*M#œ®„ Å4Aæ&LuE\ Ä‘(ZÓŒ%Z 2Zi©1ŽÊUÓœù4-9¹0j ˆCjáöÐ?NžARšzD³J›mÊfm“B(©õ0&nº“.µŸüáÛ}ŸG† ‡±Œ ¸t㳓!g›F›0H‡2†íMlÛ©GÇ)ˆ„–‘§ÞEsæk+cxƒ’@)¾gQ÷éîîñað¶&ÂhBK£lX(@q#î"y,n`›T˜úÁr o»¬êŽÒu«¶[5i(D“ó°XÅÅv1é=ìš6éÏ?|O¸ºÞÝËt–VŒp:Ç”j{E»þ«o¾m<ݬ Ë~߇J»Xý~,£òÕ¶yýp°á°[7©dÞ=Ühâ± )ç«õ¦m»-­Ü\ÿpssÓvwßÿ°ßï_ßÝ~ò‹ÏÞýÛ_•>}ý–X¬ÿüõ?Ÿ,–M>ü§¯ÿüÅ—_è~÷_ÿÛr÷úæp¼\uÏ——oïÖã ë³ÿð¿½ÿÛß`Õè³wpñ ›ÍOÂ=Ok\­Ï–éä»ïßÞ=ÞAWºÞìûñÓO~©ûáí›a»W/_=qws½]tî¦aJDjÒ$;=IÃàË®{~vÖ-N Þö‡\brç8¹L°\H×ÔhjÆf{ÿpØ.ÖÛÞ}y»;¼÷ê´2–}Šv³Iú²ížŸlîzwUXw ³)AÝòp*¬TJ¨oµY/Ï,›…^\NÙró8•‘´ŒH]³š–ëßýü·×w7Æxñγ3L\­›ÕBGGÙgQ—ࢠáB^mš~bdéIò0¸ùÈ0‡Ó˜û±0;'zÃÉ‚ æ¨þ¨Š« |‚dè4 :Kå†'æ¹’Ð(î!@ö(áŽ6µŠÆKf-à·‹€wIŸ¬ŽC¶BËÅ ¼z 6cq¤8C0s)B›ðY$wéâRŠÛ<8Ü cÑm¤a™LU8s®U–`4 šHÞJª6hÃhD¨"&­'iRmµ ºz€"M TP(!’ŒžÍCI4žòcxq§Ð!5˃ ä9k4³¢* j’á(pz8 ñº‚B/""ëÄìᤄ€$eÌÀöÙ°Q¨ÌbAüd U«Ð«±ù´“JÔdžì(Kœí¹*ÅA:0ÀÝQ"jøÍf„UüÄ0­cÂÕXÅ,­«5xZD„£öC˜UŠª´‹¦iWŸÿú„ÝvµHízú⃇þ°Ôœ,׋./úŒíö²nú>çaDHÛèè}ÁêòbÛu‹®iÈhDºÕºX9[lG£´‹Çýã4ùfÑÁuš¦ÅrŠ›‡}Ó.ëÃc?> ùêÝçûCßûþ0åᓟýja|8܆¬ëfœÊfµøÕßÿSüwÿýÿ0r²{óÃë÷®ÞmÖÝÍíãzµøìç¿<;»Ú½ùãýÍݲÂþo¾¹\]-_]žÐ›wÞŒ(iFíS¾Áòõ÷»Õ³Ó^(9¾ü–ÄñúuZï2w·Ÿ}üÉþíí_¾ÿÖ¢MßùÝ÷ß³=}¹{xsµ8ÿäoß??ûô7§ iÆݺ»ÿñú÷‡ã~ ¾8Yüí/þêÙåbÑ}÷¸‹¼£›ò³çë$Xu—ºDìðÇ/¿üáõëÝplã°{<öÇãîñî0•â>žmΖkí'ûÝgŸyÂýÍm×%é½÷ñX¼ŸŽý1¦‰Œæ0õž8õýº•R&Ñå²ãËËÍ"-Tp‹ÕÕÒ¹<ù0 ãýÝ…õýaSA»wÏ/ûþØOeì÷¹x4M»íÊ8tÍúòêê¤ÕÛ¾”ŠLe@”ãxÔð1Ûé é²?X8B¡ÚP%¥fÛu¿ûè½7w»·o_ëÄ&ÔÔÂG·I›2•Þ@41´äP)aÙm? ˆ—’s¸<Í©^²×‘>ƒÅF+ Ò뚪D„j A&Aš‹aÌ`‹V’ЈFBBtþi\ÑRÓP$J4”¨ÏÃ083Y„–L MjBœ2é"º^naáp²8"CÒzRf^Ž‘pÒ­„= ò”ûòtñ¨ÄÑR—{%ÜæPouÌT HÎ9|è¬)ÕŽ]Ô„ sY±Öª¬U™êO×\Õ%b~´F„Íâˆ4ÌO‡UC¨UuΙ[Î<&TÊ»WЂ„u §Â¯*¼n~S´‘€2à6—û+‘¢ª Ð*Þ£ÎþOç ×ã±â¶$êz{Zãñ!R›n$*³M:¯ÑM¢€†Ì+j‚;çœê—UkX<±yÚk×u¦³z vc.K†Ä,÷ ÔYt‹ ©nPÖUHg]Ta];®?À%fBCHP˜B 0,ê›jÆbÖgñ¥ È3~b.qêÚ’PD•ÊP$¨¢¬sÎ1«’¡$„MsÔ™ PÐHÚÄRëtBƒ )R¨$$«æf ÛIª ZÖÅG@TÂ)4’é„E0M™Ë0q8,Ò2s£ ).Ao¢ˆÄgÔ! FôýáÞ}Já5l˜‘üõ˜uVÊjjä½wÞï¤U°˜€.!­y¤]aÁjó u¾\nÇ(‘])Ú¦vµ„Ä8N‰L‹M™l¤%/5Mp¾}>Ƙ3YfÓ?\RJ©ÑË“«~ì)Ҵ飳Åã©í(lSjH»Øn.O/–›‹³å)Kûñp÷ñ«wcÛz«S„^]½2`*á¼ÈÓNµÙŽ'ÛgýЯ¬ÚÖЯÚ^}Ú)Žû1CS»8;Y¹µŸþë/¿ùâþÍ]´<é¶'›Íɦ¥ ž_¾8îï?|õê7]üúÓwLJëã±MÍp—íúòd¹mù?õÍõÝ—ßýõùIB·‚¢ë6·7/Û¼HÝêùÕþÓx<<¼¾¾9[6EÒùûïÿão?G9lÏßÛÝüXŽw¯žnÏŸ+õã«“íßý@syõ$X¥9Åüt]@ïÃ8,/ÏpóV+ ¡›Íz»±¡ŒÇýG/Ÿëj³Òýôpûvÿp²Ñ77o¯vbúòòìO?‚tËv[|H9Òp¸ÿúæ_üùøØgäO?üÙÙɹ4v{¿ÿß¿ø¢?öï\œ®º.Mo©dNðÝ÷ý¾ß?ܾy3l¶Ûÿæo?±Éovûýq˜ÂJÛ´§—çÃáps÷ö›¼½8_­®.Þ{g³øæõ[xÒ¦µœ íÅùóýaŒðâåÅË—Ã0®×§|ìM“î;wÑ…ä1ò4íú¾ ¤³%HA‚`0W ã¡L›Ç£ß¤"/®.œÆòbÛ3öã@ W©%‘Ùʘ¥‚>÷ãa8Ô´" Eۮݬ6IÚP¢e³àî8ÂR¤&¦S¯Î$ IDAT(J 5g‰¶)OM.nÙk¤Ú<xÃFuwuŒ,BGñ L´°pDqu‡—i,eHè'u°€ZŸjâaJGD Ì<ꀔPÂeœ<„x8¢ï{ÓT¦Q™Mõyô”מ-žðÑ)3“§Jp §õ¹_Ó¼bTX¼•½-Þš¢©è§ªöBÑ iUÒ Iª´&‰„$@Z©(GA2E‚ÕÈ–×A˜¨¶^TaD"Íò¥w@%@'T$rv7 £E€‘è.3ßQ"`tFýiK¯ZTD…ÏâO ¬Þ/aÕÿƒ˜Dè%" ¬vDÕ5faGçi;!ç Òkæw†x×wÖ™5Å`L¹“¨i«§Sà‰.õTh›Ë}õÇÃ¥Ú™œËŒs³WYsê—JM‚°âhór1œtÐYÅš…™ösä "uðd¶Êf µ¦Ñê `=‡æÐ“®Ög!H3^½&ŸÈ˜‰ä äÌÐðÑŸº|ZSE&˜é U¿™åH«3˜”5ªQê:ÕT¿ ^÷ªçA£ÊŽr0<ÕÔÖúf ËW©>_H¡œõ@™ag0}~8ÃæïG`V9ëWÝç¸ÚS3#ÔóçJ…P`JÆS!AD :­ÇB8ÕDFÝ-…2!AiØÐU¨` ©BîÉ‹“¢H5F¸ˆÃ:·0q¦ä"ZR5a=yðBOY„j´ˆ0KÕrv”°˜Á¢Á Ïájga0Øz´4ÃaáÅéЊ­ªo9’ Ô*©Õî‰÷û2•'%Ì⬠J€Æ)!E‰Ð¨H oT¬tñ‹>¾ÞßX“04ãát*ÁÎB"lÕEŸ Qؤ"„dv›v«mKx류4¹øÐ#ŒÝ2±¦d@J\·«‚Š«‘F¼]ËcMÛn¹²<™£xˆÌDÓ$r1.P_ðiš<ÂÐÒfâ–<¢e$I6Xˆ3eJdàИ·Ú´‰ÓTÆ|D ”œš5Ü…Ms¶Ú:SkÆÊÍ“!Ú÷^^Ø4Qt¹\7’_žm™0ŽÓÒ‡‚¡‡›ûû·w?ŽcžòP¬”ÀxÜ—R«ç×o~ôp›j‘l-›÷ßY펶JM×âÕ³õî0£5m{²\>ìï¾þứÏÝ*m—Û. lWÁöý—W÷Ïη9—÷.—onîß>ÜÛ0‰¡Ät¸Ý½¹¿véWÍ‚ÛíùÉ‹ï_ÿ¬IÏ>^®W¾¿Û÷ãêtñùßüâù÷ÿ³g|üâj²€s³jn|÷æÍÍçÿê·§''yðËŸ½ìr¹?öç?{…å)þÅGþ/WÿâÖhƒ7×8»œ­Ã¤hR÷wû»¡4Ø=´ >ÜßÄ4\n_ýx»Y´ƒ¯¯žŸ¿÷ÎÃùê˯·ÍøŸ¿ú¢Äjh¥ï§±Äýý‘Úlž=ûË_¿¿{s7ØðË÷ÎTÒY«w÷»F7•éxýÃãÍááþqH«vÿpÿí››¡ø=L6Ëæã÷ž›yÿxÿãÍ.‰¨NÇc?öqèËâê•o;(iÙµ“ùÐ$ÌÜvÃððx}è]šVBûÒŸ=ûèúíЋ“áФ\t½MŸ]mtÛ,Rqõì]·¸|ñìÝçÜÜߦiѦ¤Z¦ÒÇÀÀ~4]5g—§§g¤wÒ¡‘<–4KéB‹»‘ ]j§ `µ8]tg‡<öý‹ÕŠ{ Ç,L»®kS¢>´®¦¶°ÆPLSÒ²l–ÃäÅi.iâä¹·2ºÙ1R`ž-˜Åf0ñР£40 /  Š0Jd¯\è0”‰DxŠº_A¨¸‡h¨Ârq„çä®ÔRÜ˼ÃÌ!½¾Ïpƒ].–`”*ã45$ ’0ˆ™¹¢îbÕ z°ƒhTR×9$¤ÿ;­K»HµFMõ9‰iF36€Š  NU¡¸À“$F4I"´ü hÅ#Õl/„0u'½F_<´26"fc1ïæÍIH"ânp›éêggmä‘Ð2ÇÁ+å Î(×iG-jþyÆ-ØpΔ¸TéoF|“¡«Ã!*13‚dµó'šVeÃûSGnŽK±z|x¢ÇS0Wc>7)OÓ75ùV³ïŽ8Ó (DëEAΖA‰="æHV¥º³ö%¨¬CtóÆaxÅ`E=RkÍ2«R5D>7ôëçH]®/uùžÁm½š‰5Ûï@MX‰*æT=ÉD¨@IÐð4_=×2ùĈ*rqnÌãÕˆ¨›Gœç–.sðÝç/3çaæGˆ†TQ5¡âõ^«X³(õQž\ƒ‘Yzª5ÊzMy Ø ­xø bŸ·“×ƨí>DJVCH„€Æy ¼:nIÄU8¿¨#TaG C™šŠ¿ål«)½Ô…š‰º¨ºh x¨#¼a•“’±54£8ÅX@ºˆ…«±¸Ãé‘l>Ê5ïu)ÝʼeN‡D¼¾ÛŠpBB!¸€L®VCX1/71„Ò6ê¡^_çL,4@ÄÜÄkv²®ƒ·É´} ôYR0uê`”€-4hq/ˆ¼íwÖPÑx"•RBÅ5"\¬Qa«ÎžKcÐ(`*Ò5íÅË GÁ4‘a“Å}Ñt'«¸¶ k”‚V-!I³H§›ÅÙf9ŽnÐwO_½û|ùúº7”ðPA šZ’ÂFÅÜÚ¦ƒ4û¾¨†ûÄ"92 •iòüÝç¿|}{%çqzץ˓öóÎ_ïîŠMNnÒ6±ìÚ”R™jâW›Ô\l¶«õIG¡4Mó°;ì‡ÌcIÜ÷Óíaÿ0íúãõýÁÂëuɱLìk»®ëR ŠïöeÝv`*´Løx¾Y|ôîÅwoîC–.½wyzßÛn?6Š•ú®g»î¦a8=] lÇü0L×»ýë›»ñxøü³÷´;ÿöÇïò~³\Ù^ž/·ËM¡‘«Eœ ¿ÿÇß÷õ_ÿù?óúæúq‡©¿=ì>øàÃ5—«×ùý÷?ùþöÇ“õù7ß~‡él%9ÒÍc¿’ÕI»øÃÿô¼kåÝJ¿îÊâås¾úÿáL‰M´øühZhgçÿå_ŽÀþQŸ¿Ø=ÄIâ8nŽÇïíòäõÝáxx\µËú7ÿôðöíß}‹épÛßS¹y|¼9öwÓ”ï÷G†]¿‹½xöjêïÿÍßúíëCoþõ›·S¤´Ü¤³7oow×»»»o¿»;ìúì ݲ¹Üž5­Ž‡¾‰þn?ô–×›îêüj:dbÚž<¿~¸ÝÝ_Óóa°»cèÅÊþØ›÷0ÛK¶\ ¿Lûû7ÅQÑ6uÍr»>Y&iRº;ä±d©Õty¾z~užtÅ„ÛûÇõªmH@´kÂ2Ñu-ýégûa¥üõ«¬‹n3†åjQF°ÈE¦…u„¤ví¶ëwc™J¸ˆ¹L£» «tel‘£ñ"¥ØzÑ}úÞ{?qT³ë¡ddŸÌc±°±/)çц5B.—-&ŒV‚„•b«u×&Ê +p ¦à<Mä(ÒHrs“RÌâ–PšS ªXÕa¤F£‹f1Ö÷ã)Tœ A„yx‘˜w«ìv(FjHÌš™!Kdxc¤HW%*ÑVÑQ™L)"šP«„+´vñ!`tA½(”F"™ šÜ ‚0½RCwˆ «D©dǹöamÁ£,ÂäZûk­ùúdf^ª*Ú<ðQ:VSÔ°*ÙÕnÔÌþæªStO « ˜X#Ý|J_Í«ÆQ£=sºîŸÄ…pf d޳ó‰MõtdT’–·$jÚ+BfW]RBX§‘87sôºÚ~áÂÊCzXˆ(ÝçTýÓ§‹Yœ%´ÊRŒù¿•-®«Õe¦™…‡ »2Í4§ºXIý º`5Uˆ²çA7Šx< …‹Œ2ïxÿÔ‚ŒpV¾¼ž¡TÂg“PªLèO DªOùÆIÇ\ž ¥&Û‰"P‡Õï–ºbŽ­Ípÿ€3È`Mþ5ØQt­èQwÕš4{:ía}å?½F«´lÕÔõZ”¥ŠƒP©ƒ D ¡h4èÀ"`$­å a :ŒV¹ö2µdaƒ X#Žª„Áê:^_0†9j¤–=Å 0ŽQ"ÂÜ5øÄYŒƒ# V× #‚ÕeÛPRê""!jvÐ ÄFÄ3A 6õÐE –ÈiþÆ„$iÑ`A0ÜÑ’‹^^¼öC­HD0mZ‰*1“ ‚™ƒª71JqCmà ‹$ÐÔ%$J.™žŸV˜È𚘛¢q,r”Œ›¶:L­:¹=]œ®O÷yÊ9l|8nÝJ¬žÐxÓ46½x1e£ Ó84B]ÎbFZœmÖëEÚõGÞßß5Ág˱ŒƒxLàÍÝñ8öË…/O¶^ÆfÁm»ýàÃoïýT, ˜²û8–2 )q}òüp¼VR¢Ëæˆ8?½to²€PØ.»Ew²HÍ¢ÑÃ09%x¤.“›·’e½]·(›µo×ëÅâôq7íûý8øõíxý¸+&ëíjÌΖ/¯>>î&My:DŸGO‹ßÿÍovwd~õîÕûW—_¿ýñîa_&~ñâþ˜Ï:ùä“xØ?”²ùìô~w¸y{ûï×Ë•«Ëß]¿Mºü§ûïÇãüãv÷·Œ|}{/ÍR‹åâ¿úõ'þýßz_¾ü櫯¾ûêý³óO>W‹÷ß[¼|^ë7gヒأ;Ãÿûãýþ¯_µ/_À¡ž·¾ã?~ùl»¼·å‹Ó RºZ.þâ…®›¿~÷Í›7?>ÜMãÔ¤®[=Ã4\^½h§cÞïONºÍr•­(äòŒÏ_~0¿íË4édÑJ·ï›ÔóÅåÙCŸÇœOÛ_ýêÓß¼w~{0u<ÊÎó'3Û´«iò¿~{ÝRD;íöýÃqs]nÞÇcñܵÑ5´˜VËíª]ÓÌÃ!Ë“õ¡ÅƒaŽHƒÄ˜šØrQ4M·\¬Öë%`/_~°Ùž~ûí_îß¼é#_]n¯./v»›lC)¾XPÕnoï†þeô¬wFËÓÙfûv÷˜§ÞËqÊÓr½i:­ó"•*my4‡J­K»ÐÅzyY‡èVË‹<BM‹>‡©¸ˆ0!2ÝÝ#â(ôó>{z[¢ Š ›O%û䎘 înðÆK·<pyù|lœêJqw8‹Óàî–½8á.EsX1JÃl©ã‹ÕÖ„<­ÊÌIÁõ4ReaˆÀ©l…¬}ÉêO‹Ô)q =æ‹"‚uŽ!ór¢0ˆpnÕÖõ½jbÎBœTn•óxd­^TZ¥îÖ±l­ ƒ¢œ)(Á”¢¾<›ŸXôUt +Ž7šz¥¼VK¢¢>7]ÃÑÀ¥FšP`Œ Üœ “‚pI$YB£ïLr¸›ÕUÎOÿ@ÅF¸" ueR¦Åjáð’=¹Ä<Ç iÍÂYçuTSëpe„‡Q|ÞOª±+©2¢qD<µ”.ÈÈœúIg®4_’!ư¦Úïô¤KEY§Eˆ( Y¬SûêÝgÇq,îmcÁ†É¡Ôj !%¨B°iÚÅrqæM*yŠhÙ¡Yø"•…´˜¦é0¤DÓ†cºÜ,.–mŽGÏœöƒ³hר0,O†1jé4¼FJnR·øåGï­ºÍÛÛÉcjˆw^\‡aóTrîáîâQÇ ¹!›Nîïï†2§FVÏßùèþq7 9\†iš¦£@þγÓóíZS|ôî«ûýÑKh¢[Û §¡{t‹† ùðÕ§û‡>`d)wHÂÅÉÙ!ß¿~»ªMj›>nÂŽž–0h»è»ç/^¬z(a—k}sý:OþßþÃoÖrN¿øáËi_ö‡Ý/?ûÙPò›‡~÷ч×Ã÷?¼¡÷o¯ß>Üßçáa»Ä~†aðÈï=[ÝÞÜ>_oýýWÿüçÌòÑGŸ<»zw¹iÖÔÕbquuzûã_¯ÎÖ?û»¿Áb¹óЮWÿÂ ì¶øÿôÑ,ÚË+ˆc|h/®@Ê®—õåÛۇ~·(Kn.ÎRþð—oBW»¾×2ùäÚ4 úüòtʃíÇ'‹f»Ã6­¼Ø´Ñ¾½Û¿}sŸl›tu~2íïÆ1ßõ»ûý±gŽËóõ‡þî‡××÷cž ÛgËur€‹~hUTwÃñùùörƒÇ½HýtXvÍééf½¹hÃxqù|µHk·‡ÑÝùË_•>P<¥Ô&P¤/2eÏÊõ²“”Üm2Ñvµ\¾¸ÝÝí­¾‹œo–û¾¿89}qòl¹Ö¡ï I›Åãäý#ŽÃ8ûE'4‘±9YœoºpXQ 8СÉB¡Ú&-4›®Mh9NÃáhÌ šÉ½wY.¶‹•gB<’›O6Ð&:lаÉÝr±hL&ï£^`¦9Cl 0xxм/Óqš`Óq<ØT²Mî^^G)æ2+.Áº— %ħð&±éH3QÑH& 4*¯ïª Q„z¥ÝEÄ’WQUˆ™{rT¸»)šÖ?ÃjqÕ¤Ô¹zó3õ›AP55"ª)DIi:©ðQ'›¶¡ªh¬$aH´¦pMµæµ¶åÙÝ] µj)BŒ@¢DªoŠaPŸ¡õOp¢02é: FxHžë•¡„´p þÈj³¸{e\zÀjz†O¸+… º9u›Xâ''ªy¢Opò'Æg…‰aΛ“ŽÙp©±ö:8 ü4ž³æ•_ãuõ⬂9Å‚Róé^©QQ]QÕT*UkþyML* VíºVOg‚Zƒøõ€kÌ£‡i^^¬‰½ JŠùQV«vQ³h¯ ¢Ë“«™ùÉ$ j‚ôDu­Å:ÖÕeA#3b*æ¿úyü ¬…Ï„Tý  IíQKŒþ¤RÔFlýŠGÔïi‡R… *+~ÆŽÕeB”©uKx-qÖØ|]”¬;·:ˆè¨Ç9jýoöu¬>k˜hݪ]V°[¤R«LZ1ú@èÌ€‰ D}NEÂCc¶‡]bÞý®ï‚,œ•ÖI©4GAÌês¢ÔYÀ,2³% 46 q Âi‚âAWGã©æà‚t«mɘ“£ŽPY%{íMÔm‡ˆâBªšC“‹åbN4˜µñ*©î7H¯¶>ªí¨/‚:¥£­P=y0 « Jm¼€ÆTwI+éÍ;æðš"hÕc•`¢H"i…ÐJg“a(NÏ*II»¤j]Ûè(Ò"N§*ËåÉ³Ë +ÃèHšÈhW­6ÚY1*]éåÙ…†~öÞÉÛÇÞÅMâììbâ8õ†„óíÉöâr·?.¾ÀÍPBS#v1É4ô7Çaš"ÔJÑÔ·Íâ°Ûç<¸L²iÖ‹“Ãñ˜G÷e^6:嬘6ݲ„îeÑ,·ËvÊ¢œ]Xé½Ú%šîŽ÷ý¡Oêm'MÓ<¿Zõû]?Æz!\]²ÙBVûÃý¦ñÍæ$%k•ÔlÓñ8ä6ÉåÉrw8šééöÌÌC¬k·µ\|÷ðºXß©ú”éÍëë|Ø5)úáøúæ»·¯o¾øñÚB.ÏÎV«®¿Ý¿x¾,c)iùÝwׇþ¸Ý,~ùlu˜¦fóãóóÓ³í*Oþéû?g™Žýq»Y^-eŸó×7o¿úÓŸnï_þðÝþôÕg/Î?úWÿzøñF{v\Í×U?àqÕÿ>G³#$m¾½}ýx{ûÎÉæd{r¿³Û›»H¶?–´:oËøòl9YÖîy”±¿³ï~¼™„ÑÈË““ÇŒ‰øææ¡Ÿ†Åjù°»õ(¾ÖFËñÇÇã¢]÷Çå¶Yu[·cŸs-Á­/ƒ•»û)Iœ^½›óq¼­<ÇÁ’6W—çø[72–þqÿpȽYšÆýãa¸íiæÅóã~7åɼß,›ËM÷â¤Û¬—Vl˜¦’a0˹‹”S¿?<¼é]’6%ÔÕl?ô9÷›nñüòÒ)ÇÐÔžæœó4öÓ±äC@ONÎ eœ/ýYå4dLÒX5y OÓdiÒÒiÙ±ìÞÙvÝBy2‰õɦՅEAT~ 5ÈšsÑ@JÎ`ƒ®aÈᳩWcS0¬>Á¢nƒ°î™\¬jùÿ·àº=˜rH„H̰ƒ,( h•0™1WóÃ$j}ŽiÍ®Y¬ª0‚Š%gÌ-CÀ°šˆª×Xˆs¦ˆ«‹S¬t-¯EDˆs–Jå'xÔ=ðißO‚שGgÍ PÙš¨‚â ãÌ~=ªˆ]­ŸKÐ’& Ä…BJ(dÆ%ͦÒlþ‘E ¯¿ðœì«EŒ²6é†ðjÆŸ˜/˜¹ön¢ P®3/KBD£ê` gˆ‚‘BæX}Æbýõ¬Øe™ŸýU. „ë¡zrXU€­^ ! ¨o æ3®¾Þ5'ï.pVY¡“N“&Uß$IÂ$"ÕÆ‚‘ÖY*XÁÐIu±(AGÝS :ªt@(&¤p)pñF” EcM’!Žd¹™A—s–©Ì'g= •a.QJ„ ÓGzxxÄ”C"^‡,ž6Ó+6E"iwzzâf,—Zå´:|éþS«CcîA<ÀQÝhaeC¨ AãÿâìÍš,9’,½£ªf¾Ü%öˆŒD"{¡ÐµN‘Ý-=#Âá<Ìÿ8߆B §«¦ºÐ @"רîæîf¦ª|0F(ÃŽ|@H$€¼×ïÍkêGÏùŽÃc"'ö¶šcd'29‰ƒØ$›šÎ Z“„–¹wW†³Ä®ëº®cr’BÌp'gnMGE0¥œYÐK옥‰LŒ¥P ’á‰i\´Ýݤ«XiÜ»aÚIIUv_­Î¼ø¤;*îÒå-Z܃gÒN(.C³ÍºÛm™G$·ÃÎib<;Y‹M@‘ Æ$ªúÕU3ÿû¯>àv=å&9;º¢Ø“k+¥˜¥!eHŽi:»40•Õª[­.ûФdGGÇÛCîç­Ðþ0G#ÓRbãg«c‡®O–M”‡ý}öìtœ¦èRhLãh…C”¬Ó0N7cöa̪ùâ¼/eøäÙyÔm*«£Ëë+-éW¿üýÝf¿¸Ûß¾»Ý´±%9ï¾yó¶°üâË?Üö«Õò“VÁË_^¿RÕ“ãŽÂðñùâÍ÷?>ܽó¸~~y©ã¨¦9úóÆÂâò/ÿíÿ8Lj³+Ѐ£ÛtXÆOw€¡ëŽž}PÞ½úâŪ—÷›á|½mµŒ:¿ûê‹}ƒôòîfRÒ4…?~v}¼^–Ž¿}ýj»ÙÁ,ÆEñƒ³ ‘…Š?{òá__Ýl7‚‘.>þ‹øíß¿¹jÿéw_]/CÓm†Ã´Øe£¤¹lwï‡qÇBOž|Ü7¾öÈ»]Éûq“I¤o×ýâý…ÄÃW¨©Q·*ë`vÝÔØëÑS÷J"ó»³~ªüÍ"cã¼ "„Góz5Ë+U•§^Bú¹Z¨Nƒ«è¸©W:Eµ”¹TÜì«§YɬyÌǵ2´Ž™Ä`&«×ZUc!]ûìÔµÅjw"æ QaB '#R*î*€ñ,êdWw7vuƒ@…¨º˜“¸Wl+ÈܳSe!0YµVFv ›±Í5+£@ædn 5"llpóyÓ›½ì<› @jR Js¡ û¥Xjœ4)Ô)>Ö·3B¨„Šã@îˆàä$µ‰Û ì™büHŽËĉ˜ûÀf¤6gŒHVû‚”gá8¨±G–§×õ«Õ8e÷i–‹®‰í”w,3ÕxHk&.fn.#©eBߊ°tÚ@èˆ\$Dˆ™›h*Ó E=‘s)DFÆÒ ]œ^v÷®Ó&M€04L°‹5ѯŽ}7 ¦ ,Ð8šæÐJv759‚AËÇ}²ß`Ëàû›ÄÐ\/ŽŸo·›’ý0N:¥a:ŽS>>^¨v%O1xßúññÒ-N‰`\Š%Õ¬>¹S)isÎ~öòêÅ~ܨZ ÑмxñáõÓÐw/ÿºÝ' ¼hbNéɓ㇇l–#ØB /*"–Ò& ¦Ãd<}>¤ýóÞÎMhÆá0îïÏÎŽZ9°§q¿&Iyè‹E×›yŸž~ññ§KÝþøî"2¨™îî¾óܽê¯ÏÎ?ùôðêMl’ÿõ'ª6¬ÿñ¯’ç£E¬[Üo°½…5+”ÜP.“o“‰û‡íwwo?º>}zy¾¾8Íä‹ÐÝOêYµGÇÇ'ËóõÉC&åöa»7·u2e=>=ÿô³Ïïoß^ž-GG_ÿð251ö‹“>½¾y}s¿ñáGÛ‡1ûýýýð˚‡!÷1^œ}r±º=L$‹Ýn·Ž‹£•<¹zªv(cÉ–`ù‹§'‡´Ý$i?{²Üí†\ Âr¼^°ê¨i=y?0ÅEÓ¬¹¡Û²éÇ\T”¤#‚0-Úp?䢚IôäìøìøtÒ< ™‘¢DS/I#Q—Í"öëóápx÷p?›à¦…¸í#©AÁ±i£ ¸FbQ×ðñóöÛÃAs(ÊÒR ÓÞrQ±/Ù)’«'=Œª mŠî˜2¨¹¸äR ¹$·äìÌ Úƒ‹d7·l( $ªªN‰”²U‡‰¬ªxJKÙÜAfVj@'×…… ƒ„6g% üxß¨Ž &jÆâ.Ë®)O$¸ÒÚéüØcgÄÆì`vÏR‰ŸöPqÆS gŽ$Ì" D–(" Plˆ#Y¬\Ç€–Ä©ºs‚1‰©PL,EŒR[Ü`ÄîFZ!³¢îbN¦¤¬¦j¬ î¯cÔl‚ŸkœÍE*ª:ÁMÈ) «Õz>fW7s‡«‘°W¥Éj?0„}®î0®3WØ$•Ï vðãñˆ.°ŸCou5»¤æ^B®[@¸ÒÏ2NM÷Ñãa^ÿáäNfõÀ€ÍD…Yø1y-ŠaýÙîSçÊÚ$ø8W±CÉÙ¡Ä«$ìÊŒŸr \›Oœˆçhhru Òcõ {•Áy£ª ¹z bH  D Xå ÌCê)Ï3¡Áj$•Ë•E­¦QÁâÎ:ï–P‘Vƒ‚¤ ®½Á¯˜ø7ZMè 2ò¦.YMaìqÒ*lÂÁõU¦Ü!.u<§Ú'5rRؼUU"öŠh#‡×ÎN›µµ*ÔÕÈl¥S¡àµ Üçð!À?ç ÀäõAˆE¥͹8˜ su7y˜»ÜÙÈÅV̕ݽ“1Œµv 13 Ï £ó`jf¸[`ªõËNì e*Nõ^ ÉJõƒ1L jÄÑ\œ 0¯z£yQ.^¬”Ú¸àÄ‹jp„‚Ïï\æZ!ÄPdW"'HæP3Èl‘Ø(±rˆ‘,8‚»#ˆtÞ¶““º‚9{$jŠPµ_wA ‚·&n¦LÔxè›ÇLÆ”†´ÛæI•1˜k)€qðÂÞ‚92ráÈýâ«§W·)÷(Kb LhEPD¬Ñøä¢ÓXÒX<¹!0-ctnëë; {³÷® ìѤ=[®ú¶›RÎìZÆ’sÊÔwm×&Ä ìŽ$U›„[¨+k)#SŠm¥R@šf‘s~õöÝî0 µÒ´ÅriÐä®Y)†Ð4mßÊbÙ?½ü(MÙ‘Ë•ŒÓ6XdB ž¼½¼Ü§a"NäDA%À“èéÕÅv(EsøôéõÃî0±d b¡N»ìZ’°N%,B<^/Æ\T½kÙ šãEΆœrq&Ÿ’ç2˜dââ¤yš¬ä`äZ2”(~üù¿»ÿªø • Dà®.žÕ)Cœ²'U-NP8~±^î6»I‡¢R²âJj™X–ë#[*.šWÂ3¨ÁfU×Û¿O¹×6öGgçÓnò†ÚËW¸‚%Ù¨V˱1˜K¥dÍS†³‹Ç ¯b"!d–€®6g!1©Øje Rµ –VX"s¤àA;6¯ =° ÌRœ:º<¿<ìö–ÕPJr"HöÙFef0ubcÀ)Àع–ÌÒ¬—U•fW¯ì(‡YUÂ`G<|îçóºÙr1#'beñZɯ‹£™¿Í³z1O.^Y3f€j‹Q ¹ŒU›þØçvíÊSw š»Í 2·™=5ƒ¢f2¸)‰‘?j5³)ÞgÇRƒ¬R$à¬&ãÙ­NÕÄE\ëMjqO}ðììU¯ªCÏ\5í>?¥G=¹Rå²iÖW bf©ûG¯,'®­‰bîŒ8Ã&ê Ä åŸ}˜ç¬‰Ïî¶š¡4®Ze‡Ì¹Kb²@P'€tN‰US~ 7aw(;Ô!n ®{èè^ÍŵV±b¨j\@‰ªYGk×"ÜU*tVUK'sšykÖ5&×.G!F݃‰ŒëFÖµ^Xšû'!.5n l€¦’t(¨B#¯-5ºRÓ¡¤”\«È™ ¨^r÷ Uz­ŸDNdnÄÌ\Í…M@bJpb£àduBWqe7qW"†K%ˆ°£ÄÆjîšÝ‰¹-aË®[D~² Ûœ­ì*ÑŸ¬ˆdÄDªBT܈‰I«+°¾ÝêºX@€zžŠCÙ5T£>£ 89«‘ª N´o\ÉL½v@3ÕW±Æ‹+S!ˆ,à\òÀBúfäDÉ`¢"q,%¥Ð´ôáù±Š‹[¡$ä ,ưF5 h”lhtaÕ6NÁ2´u¡Q̘q}²6Òq2õ‚ÒLºwENÒ F!pÛò*ø &î¤ §SNÂ|~|¬T´ŒiH,X»¹[iÚåÕåUÔ|¿$ò²…[vWµ= .Cà3SdîÇlB©k»Ë«‹.LeòÒI$÷xzz´Z­šÐüá—_üðî~·Û+Eéûq¸³i r,¹ðjqüäòÅ~xðì÷ÑÉÄ¥üòËߺŽÄùéªÊÑb}tvJ{pÚ:ª†& ŒGm#M¼»ÛÛ ]§àx9 û@tÜÈ?Ýô«îäüxÅX­–ß¾zç'ëó“埾}û°á¸îÎ.¥]™ç»÷7Ízq¶ÏŽÖßÿé¿~ýëïïoNNÖÏþþ³;¼ø ÐJüÿùÅñöÿçý__®— Àõç1?ûòæî­%Ýn¶>Ü»w{êu¿ýáõÝ·_ÿåõí ‹“v±nx—­èƒ,b ñÇw¯6»wë"~~r|ûþ¶Átû~sP´MØlödÜ€–GL|s»™òôüâøÛ—¯®Ïc !ÆjA‡ñþÞˆ1î§qÚwþІr³}Èãô÷éf?t‹ íö·˜Ì^Þ™õ]»h#¡¤œœ©ihÕ5Âáæ~w»=d§a¿?Lj9¶}{q¼~~ºØOi;ì"ËØS±bæpËÌÌ„8¤¢“ #†bع$s" oàYÉ£Ä~½ÜiJêæ:f¸ ¡i¬u gëS-É´ÊÉãë´^,¥YLî'MÌé0™²RnH9‹«æ*!H#¡j¶T&%%¿{ÿ*ÛàÅZ*…<Šå‚ÉÉ ¦Ê\8e‡eårÿ°Íšœ˜ØÕ P3Sr* G15r!°óc³°j51«“ÃÁ (ªEÍTK‡!™AFê”PùA!U)ÀÁáì ˜Áu0ª(„9#Æ…YˆBÈ0 ‘EXkés[9¢‰˜BìÂÌ v7 ZÏ@JBdÈÆÊŽa<$M–ÝÕA€VÜÏìÞR7·039]¼‚=‹Û W@“[qUC»ÃÌÕfW•¿POZž›ù Ü"Æb$<×Ñz=ÿ|¾5¢ÙM g«žïù¾‰ÈÝþj°Õù¿ÿØÍÖ¬Î.úáûE#¡kVËåW¿üݧW«£ãõù³óŸþüÇû¡üý¿ÿ_ìÕæÇW»ã³#ôÍ¿eº@è¯Î׫)âä}‹bˆÀɉ½|ÉÓk 5¶T¶)í%`6÷ÇýÂbóÛ_ÿ¦]öÐÝæáý`éÍÃðôät:Ü/: ýþ‹Í«éô$ì†Â^œµw›Q“>¿:‘åù¯_=ÆÍnûþæ>Dê›pqñdÜØ8Þ=Ýv±¡a8À¬ïšË£õÛM*š~z»¹Ý¦®[|þá9ÜŸŸmG:$¬ ²^z2ãb,«~Ç8)qÜ SQÍÐà$!5¦jªÉLáNºz¿è,VGA1á@2C2ŠŒbÎF<µ±×ÚʆRã[ö÷»0¨s>+®i?<ôm³X·­4¹Áh4V*^2ZB¶ÜJišŒU‰Í”Ä :‘YQefJY=4´\ž\_­›íòh¹\÷¹¸ªDQóÂLËelº‹1IJŒXuôÁ‹Ïoï69iÛ„¶m"s´©ëé×]ï÷›ËµŸ\]¯/º®Ûow›ý8eVÃm{HAüìâüäh±h|úOžèNïúóÓ5Ò_/¿ü`Eͱ§ý›ï¿¹}ùöõ~ú‡/±øüטâñÏq÷ÇGø· ºZ€#Üëôú¥m»ýÝÍè7ïßíî~ØgñïõÉõÉòhÕŸF?:¹.òêí»o¾ûN©é»µ¥°ZÅ2=|óÃû·›Ì~~Èòõ_¿ÞïNí]Áý€ä%Ùòf·¹Ù&ݧ”vÓøôúìòì‹?þë·oï7·‡4îCÚï¥ièòä"6üú~;T=xvnÂõÕõÕiûúýöåÛmÓ,%–“7}8Y-ÕlÝÃ9 uŽf?ä’µíݺn›ÞŠ\B1ÏL’5à µÄD­@A¦Òu¡hÛ-:æå4) ¤É9EÑåÙe#2¥ ,˜ï+@[(dãØÄ‹‹ó&4Gm[¢=9{ºvnÒö]å0Žê)¤]}úù—Ûû»®]®–ýÝÃÆ‹»çCh¨ãæ~*jšÈ‹jV),ëý0ŒšŠÅÜËhjnÞ–f©iÐ<šzÛtGÇÇë¾wsM{*ÅÈÔ&OPÄÜ S¡J±1r²©¢<¸©²3[LHÔŒ™«#¦Š L€ëÌg&(3^œ&™ý¦5ÉU“‡@aªÖ vƒ›¹Eˆ" Læ©ë APPva4Ä,,˜T„"·A„¸ëܳiAÍßUQÇ@NÊ ƒRVFꦢ{ª¨UÍ5ëæ¤€2¹ë\Fg¨ÔÉŒXC!5­µØ…Ø]8sR#b2#À™ŒµAnæ| ³KÝ™0â0¹øÏæï™üinòˆwâ™Ð^ÝâórÉaµM°þKF³–U dÕü=û±á «1*7«tš[n¸*)¥r*¨ –ú̶뺖ã:1Õ0×”=;9H”H¥Ú°˜ðØXÉVõ?¦Ê Ÿ³”uø€;y$xe6ZU9ÉÙ¸NŠ&ýòš™˜ØØg4ý¼æ„+þ6UÖæosˆH꬀ê«RA jª%‡«“ÙßÜ^ PA®‹;a@B¬ +‘9͸ªö0=BjüŠ*჉ԫÉúè‹3­ãÍ5ˆõõd<¶²‹1PÌ \5Þ@× æÒÊ:«PÑ_©½TÔˆ“‹‡yñmµžr&š“4Ñc}@èk°h©¾7z$ì;Áê2ç\qcÌÄ "D*>—bª½3´b4C”Ӿͅ¬€\”¡* ¶ú¯î=«QÌ™áÑ3ÌQÌM‰“_´q½èK)V±C&6­½pœC© G‰‘8ġ̨²ªºG8 ¨ï͘óüd8‚2ËÍCPªÊÄœ`Z\%qÞýÎ^šÀ¤îBžë- '¦ÀÔ²ûB‰ ®¬Iˆ\!L.aŠbW»…”e"1W“ÜC¢Cç‡J -!ÀY ¼ð ”Ìír¡ <”ÃÞ´L[^¨7uš€òüúÓýÃû1å¾íÜ4Ü™¤p¤ãÕºØ>6²ê–ä=4KäĦi+Åž#‡ØÜ¤m»’=¹]^]~xØÝLù8{Ó¶}».Z.Ÿ~¶ß?ä±ÄЯºT,knC¿lE‰”p±:º½ö)‡1i™zr OO.<)…ÆEy³ON®...—Ë‹÷ûûw¯îvï† ›ýááz}üâ²sʲ¦õúÄËx´^?ûèWl; ,lÃæ0ŽÛ…óÉZ¬$7º~öá˜õë—ýàôø‡77Ó8>YñâôÉOßûúîÝ77Ûg—ÿôŸþ×?ÿéë?½ü±ïšÏ¿úíÙ÷ã~²LÓa}}†íO6&ڼû×G‹ã”Eq¶põ|óÓ»¿üðrÝ6þæò°iö÷{]Ъùè³Oq}uû—º¹z¶>;=ç°n—‹»‡ÃÓëëË˳'ÏÎÞ¿|ûîaL%<9¿ºû6¥Í4 †Ä_mÒ0n£7Iót؃Ý)2¼iÌ3?ÿäã—/¿»ßn§’»ÈËe:^v˫ˣ¾[§¤»áP²!ÃänÄËW¯Æ’¸ar5­– 8ž¯—Y%†Ø¯ºív;–,„âǢ;YŸ§i*(‘±Z-WÇÍqçãTÒ„6ø¢ãœ(Èâ÷_}¶?LÃxÈŠì““¥)€’r"Ê>#£øh{Mé#ˆ»H»“ÅiÓ†“ã£Ãa§ÁÓ˜!}³l$L êLNÉîÞßM‡’Æi²Iéd‘Ô¢Ø(š’&d˜Š©L†’ÕÉ2 ©.… jËÕúüè¸ì÷ŠÌÀùéÅ‹ž>yr:jÚÞÝ'c&®5z(¨NcÏf®ÊD.ŒÎvÓdnMÃ}¤¾‘QUÈ ,µZÆ ÌÈV'öžÙ½RE*Ü‘îâ&!v}SœAFfJ5ÕßJEšÏõ/Šàþè¶²¹¹." ¦¡HÁØM˜X…¥Â±añ@I ;3 {NªnÄÆêJNÄitN–­dUBQ%s­|VV7d¨U'¹‘Á¬TŸŒÁç>r+dŽŽN¦q¯V“}Z©HÕpc•É`×Z çÌÄÊæ,uñj56W¡æõƒ€SñŸùsùà7ƒ£z©g »ÏSYÅšÖéÄ«9¿º™àZ¡X¦Lî^õªŠPÄŒpšáô3¶ŒkÜ*x…® ‹DÄQ˜…Ä̬ff@¿ñSI ÑAÌóÿNY Œ¿Åä Á#ν–ɳÃH ýéu][Vè—ש µÚ·úÿë/A55F5$ ŸëÜ „£%ºF$ph´XXµ\Œ$Äõéq Ëg}vw÷vJSÉæÐó£3o†ÃÉÙùo>{úý«÷·w»‡íŽ¹í›•š>l·ûRjù‚xÀÍa³ß?܇.»öW¿ù»·¯ÞnÒ¬Ÿ|ðìÉË·?Ctj‚OûÇþÙów›÷ûÍ󳓯>¿úôÅ/^¾~ýÍ›» xùòu& ´\¬Ÿ^_I°gëí0hIÇ úOøbóþöëwïyÒøìr³þù/¯š³«œ¬îÞ¼üáâù3zþN/à²þ¥¬›÷h&|wûýŸ¿ÙÜÞOûdöîáNw‡"Ü®ÏÖ—‹ÅÙë×ï^¿¿v—O‡ÁÖ÷âáÕý~ܲØ"¥ž4ÞüðÍbyÜ/–Ã~c:ýæÓ/¾ùáeN©(© 8°îŒ§§Ëåñnÿ~m{˜Þ¿{±¦‘½[¬ÎsöCž“ó\ÕËÁ/>|ñÛßýæõÍ»¬Ä@#ˆŽºß|þôÛßgÕqœ8€ívÇZ®OC¿>n…÷ƒ¥2•HÌ=Z•âΘ@±8‘²¢¦Ù<“!TTjج†i'žÅýüâòÿøŸúÉŸþÛŸv‡]Ô] B®®…ƒ›Á8©ÕN»º#¡Í0532aIª ‚gRz¦ËU3f®sr¨©¡¨ÉÉêhÒ±0f L³M»Ì>"“šT÷¦Ra‚™P þØzb>˜ØC˜ùX`v°ˆµ bsmÔu`ñ¬uÏdìFcgg'p¢ì`¸s¦RLŠÃF¨©Ã S`€HàJ³ÈÌ d¬Î,$µþÏH­îÆ!gª1¾6.ÃÝJ3ÉØ*¼òFYfSµ×ºçºEsg“[•žª+¨ªQð*Ì©ÕïmŽÀ ¬u˸³‚P—гk«"7‰`·ú]–Öe€\ˆšº)f‰3” Tý¼ÕÊ\±Cu9F,ÄÕMÎT½z Ú¬L3¦×U­çõ•-äDäN&‹å“ªHYíQ6v0,81yÝ1ÕæâêXª=Ô›çR{\œÎà%vrƒÒüꓳ1ÃçÑÊŒeV•Ä*.¡JJLv†‘0›r|êgrPE-)£>p‡›ÿllóÇLèü#sR¶RÛëá3nÔÙ‰a³p窥Z!Ìn³ØX{c@JN•Üê"Õëd¨›Âˆ¤ê¢ŽišrmÉ 6ãºÈ N%CÄÈEÜ™ ‘°ÙcÜÕÄŒØH"ó˜‹X0b5¡R²DfÁ¯áZx¡ ÆFfÅÁ(pVªz7 ^Æ<=¾9ˆœê3«}šJ“TØÕy%w™Ì¬2s‹Nä¦ff(Nœ™ ™yÍpäjTæ­)ÐÅæ£>Úl·5ÀÌnâfœÁÐŽÈú lfFä‘@î\¹õÄL¦AÄ£¸¹pKFF {]÷'¶ã¶o¥mä諳·B¦¥ïW§G‹b9g/iª¨ —C‰¦©íÃññÉ&•4R‚GÆzN–|s¿÷R,KeOÉGu.`–¨în“©÷Z8åi±lBæC†¹ôªn¬êNð£“ËŽöãT¦^xÙ-†ñŠ/ûÀ‚4%b.Rò¦í¾üÅG%—ÛûíxHMÛœ®Ú×7w7ïïP\áÂ,Ñc:$˜9~ÿêîa·+fËEÓ )Ã~,Vœ|y¼ZvÍf» ±999Ýö»íÞø`¥4.×çËßüðÍýÃvó°¹½¹¹¼ºøÍW¿ÖiOF}”äöþöþînwßõ‹gWO¶Ãt;åãeÿöáîl¹üø¢}ûŸÿéß?ÿø×+ ã6L»aŠÙ÷/ŽOÏZýöÕfŸÒo¾ür¿Iúú¿ûâ… üêÿ}³¹}³»}zqú!¤Þprþßoyþÿ⌎–(íÛï¿iV«7¯Þ >JN‡‡7o_¿>LÎÍêpú¸X4e»?Ün§“ÕI¶éäÙÕp³e¢£uxóþ~{·»¹yÙömÛ´çÇÍ?Ýœ?{~÷p·¶Ä1J«…Úv¹èû¢*±Y±1ªŒÃaÕ2±^,ƽ•5m@Ã42$†˜'õÉp~~Õ¯ú·ïîûƒ•)gˆ áðúf{÷»1—² QJ¢ýöu;èüè|3q×uæC½?Yu_|þÅ”ö,07!FØd\EÝtÐp.@!˜±fsò(q—S.Jú/væ0…«€Å‚B‘Ðr–64îgmŠºYtgQæUh?¼~:¦}Ò‘¬œ-ZÜN)-æ/©k‹NÆ 2zžKÍÀªb¡Lµ¤Ikÿ1¨i›÷ïnÿòõ·w÷·7·¦ Ssc+ ʤÄÔŠÌàÆRvsu"u! k‚EtýÚÝP 0a V4i1q8X™ÉÍBâ¼ÍS]y˜[Œ}Ó‡”'Òª•à “1S®M.RàŒ $ˆöÎ iÚ64½Í°ÅºyjÐf©Ëâh‘‰,D‰} Çg§iÈ©jî†` W…›Q±ÀÕ(ÛX” ЧìÆ^MþfäÅȽvj‘:2†ìfsgî Ü4äªGWÍ^ æP&x]m²œÍÁ0dT6¸±pv±XgK(•Ú-³¨ñ3 År˜ã÷T™>ØÕ ê+çzrר?ƒë(òsTÿ‘ïó]“ ôøSþ¹¨3ª ûÈ D¡öþ‘0“°4àúƒ@³“¸²®ç…³°:*ï±Ú…@N^¿Åü§SÕo¨2¿êR³VF[}ÖÒŸ¡ò‘j¶ÏêÅh'WâȨ¨Žjµ˜+E£ÀöˆÖ/†ÄITwKàÏœŠG&× £‘I…»2@õùK!f"æ0—í;1©CÁÄsX“ØÍðÈt¥â.2£U±ÌGªÏ2Íe‡†Š`¹ÖˆD ®Ö¢tÏåÍî>¯YQª‘‡:`d^7ÝÎæiÝ“9jŸ»HlUAÌ1zÇ­"äd± S“mÔ"Ü4 LÙ5åÈE ºê<¥R’æ”'×~Š%•Ì9¯—]ãT;Z A,ðžbÌEÙ`9“Wp¸îw·iê/®®®?\”Bð¢fî)ÃÌÕÜ ‰)8D y6s¯ $Àˆj¯Æ&Ü | ç\’)Y!Q㪰h§ R! •žžÒU6·É ‚19;5,µï_óº¥&†!°sè–¡íR601·ˆQ\Ä)Ì´ˆ@ŠÎØÑÜsšÆÄ™9hnNÐE4dƒ‚‹»ZÉ– ¤„"TÌ܈=@( ú¸<_SCý©^r.nlõÉ馅ÌL]ëœ3ó¸Ù©¢<æ{¬fÏÜáÜU Y ×ÿ°×Ìiñ:R¡ºóàFó5Ë¡UF|7Íë¬VYG3ÔŠa=ëÐgÇÊŒúœ)­õ(ˆ»gxÁUÁXHLÄ‘‰I¡’Äç4Òb†|>|ÕJLÄ좠:]Ô mu,VØ¥AïJ[³íVîÞ’œY=Ba¡¹¦‡YýG0«Ì !ªtÌz~ªtO'¥Rk…Vîà£u=dÁ`Eugø©d.£9ž‘²Næ !&‡ÔÏšˆ¸RW¥â­Dê£Þæú?Ȫ œ\î fó’êÒ¤¦õk$ —y¨ù6øLGs8;(€®\Ùuaçlu#(õ»©p1s¸Á…H™´@á¬îue÷ ÄÆ ìN“%?‹Ç¥rÄf\<¬êƒœ‰¢«Gòº¸"òÈav&Qt "nȤñP3rÄÌÉÉ! ¢F榎`ª¬<³©¬P(óœý7gscÏ^ÀMRÁ­KQ#–bf`(qDa'­Nu#Cdr!G;^F˜du“Jßb"åàJÒ‹)JɵOj5§hT³vVu&S+êÁê7¸3±* «9bJ}2U!s¢%b‰ (@„©y‘ ¤4DISG¨7]÷ÿÃGˆÝawÝuQá†P­®žl"7£in†äTJICOžÈusñ)[ÑÑRŸÌk„AÈPlÜ ¸[,Øcß§Õf}HÃa»Ý¬ÖmÌBœT†~¯y 1v̾êh‡â:å, FÂÙÑZŸ.·cÊy³Š›£ã)£ŸRàvs|úòÅ‹ûg]I×»ávš†\ÊõõÞÝ^=}¸ë·R ˆ&5,ÚÈ,——æ[S0u«öáÉÙõaK^š¦ýìÓ6yŸ,Dá2q>¼þñÇ}ŸŽ×'}"f?L)eÜt›Íj±2lšæñé±yܬソ8_„ðÝùå"ôKÎ~½»ÖÒlV¿ýíoo¶ùôÑÓíaˆÙï­?=í¾üò×øê_»þÕóӣńîìï_aÔCY,¿ûêO§¿ø%ÚÿÜ'¼ËÃþÿ§+dì.ðô1ãû›°î¾ýú›¿~õïHiÈÛ«+ùä£_>8={ö›§û×o?ûòËöɶ7Ö÷×oÞONOîta˜v·o‡^ËPì ¥]Ãüöû~ìuÒË«‹Ö«&üp›®Sš¦/~÷›ýÅûa×ßÜ\Éê,¨œÆ»ÛŲ×õbCÔä2mûì¶hWÇ›“‡÷×ÇN·ýÅõªß»·øýçÏú‰C×FQ§U×Àp~ñäÞþÉË¢]Ÿ=x@Ó`‰]·hÝbàfÑÆOï-?Üc¯ncæW·»C_ îB¡8€ÑÂs9¤Ü(ÐMêê®Ih)šŠå}¡–=JlNކ1“¡„’s¢(ËõÒ’Oœ£X4ÙXqX¤ ñùýulmu<\m}öârNE”Ý E‰²–bì­–ž'ób‰Ü ¹ Y,˜xå.Ù4†qÌfT,WkîÙ’™³†)•i§œm¾óhu½²Ûf³DsÊj¦%x ±µ’YýqÄT…ŠÀA…á‘Ä•‹[V!ggˆÙ´&HØ!Ø1¼Ê™-ȘىM1MY­Z¸Ôw}i¸YÀ™(³:Øc#¨cK°@$ ¡Æ ¬ZŠ%—» ?Ì4˜ÜÔ3 óò ìd Ë… ^Llî–º{ ¦âä!±)ˆæ…¼ðL­ª‡©¹ gFVÑ^#75!6ÓRÔµ>ÀgÀÁ¨w­‹,ÕÐkÌFfV¹W¨ÇÁùÚ8ÿ æC$f2>|^ÝÝ‘p§Ñ€d>jÕç4Õb`$i‰…)L$ˆ)2ÿ‡%±º¤É.µÂëË`ÌJ6¨»¹Te /\W:+'ALZ«bw]:"Y.p½$V³T9ŠÑÌ‚¨fȸһŒÜîÆK…’—ºsØî4‡FN•úTOÏp™99‰×0—+¡ú뜄Hê4•…bl"R&#vW¹_¾ª(hEjUßWõd~ê"².Ј¹Ô*v‡á7žhuXQö.ÎuóYÓyR'T…¹‘¹¡¸ ¨0¹ùÌÍW9Ȩ¾ÆJTS[V#pZËŽJ+“º»õŠÍ­‡B1TÝàdB‘8 ¡"‡€]àî…(0ž-ÞNl^]<®J8ºAƒƒ„ÍIyÎ$ª¹ªr3c•Z‰¥YŒé¯ë˺¶„{)p!0“qR%„ˆ„è®DlJÄŒPT4˜˜s„Ö]ËTßÊL6¦ #¢Ñ©8©ƒKmz¸dÄ(!J 35cÊÌ„„©¸4 +SÀ‚ØEj4ŠY ‘@Á¢"F$V5ñlÞÐÅy}saTX,ðq1Óâ ØDá‚H¦}§Eb—ÑÆ Q}šF5WVR63_»ƒEBŒÞ5Ñ4qg'áÞæl̇“†Ó˜úiT-O6ÄåíaRÊyÙ6Â4NeL# uMcHîì±€äôÞãbiÈ£»ˆ,6ÇU5åO?úÈ<­YÂöf¿Ýï\“Y!VsÜÞ^ŒCY6þÙG¯†œŸ<}~}}aê‘`ÉÜ﵋ cηiiÈéx½yxÜvm·ßÞºrÎÖ[Y®"¦üÍûw-ëÙfùôÁ£m?(ÃòÁØB;}'þâá“kµõ:>Xój!¯ß]ì§ñ°›ºÐL˜Âÿõ§ÿç´ ‹Ø,©ÿòùYŸšƒšõךÏÏ}š>þôÕ·ýê)øf¹ùø§ï¿¾÷ð¬¹w áÿÞApߣqì.Ðáø ÞýPnûË?}µ½øþþàùgó«—¯Oý›íè®Ó_ÿøÓõ !oìðîý®h7aøõ'ÏzóÝgOº&¬o¯ú©Ø—?1äýáYŸíöºh›]qóf¢fuòb?¥e /Ž—_¿þË÷ï~šŠÿêÕóç/?ûáÍCÖ‚+ÔÑç҄Ū;].×9ëêh¹l–G÷ýúËÇýËyž&Þùêö¦/ >µ˜²qh7«ÖL­èÅõ¾i$&µ¡?쉸ùõ—¿»>l»e»àEŒùý®Ï& hIt[Êa»s´E4ÙÑF¨kÁΣ†Ûí>gw‡7"ÇëÓ>í‹A¨8Ð-”v©ïVrºy,a1&UuPh»£’÷H£±‰z›<…¨«e˱éÇm2‡\LÇa? *€ó¾É­!ši*>¥âZ(²!SÎbÁŠ@·,1ªDiº~‹‡&Wd*î 5hškµ²0ÃÈ‹¹1{I6ƒÀá¤EÝ’Â+ójÃQ߈ÕHB%p:¬€ !¡i¥úT×͘+•êáKHœ ÔD¸˜TŠu4æ õŠŽÔŠ8)Ô‰ ›,ÜÆȲ CÛÀ½U¸Aݨ˜˜Ô-›™"CÝ\-(ŒÙJ%n¸)©Wæ=`¨àÊùSÅÌçra][U¦ ~£Fý™P»\«w 6/4¿SÏ ¬:(˜×™AI+ƪæÖg½M­vÕü¶Ï¡§Y-Rodµ7_+¹Óç&»Ï»»Î]­Q æý±Ã¥"Ÿ,D˜ÿ˜Ç Ž5ýòó\Sm~³òÚëìª TwRqŽNU\_Ÿº¤Óšv¯1%b™ç@0Uòy0wÈrqâU®dÕ·ló¦ +×ÊáëÄY¡"¨Ã½°Õ ØÉÄhäêK2|ÎNuáËÆïZHÌÉu®Û-nêk€ª× ¸šÏ­ÎŽîâw…P®XR›=[bu»9£Êl¦ƒÞ½¦w¹4Èl…®sH8°3ïSµOÊä »¨À¤*Á äZ×’Æ6S:'¡nµX¯—9M¤Ìbázÿ"3AŒI‰‰¬ZBE*ÌEÜ!\)aÌì±vKÃlù¦6j `'ŒHQê;`ÉàÁM¡‰ J0"WÒºßT'ˆp&2#Îx*šƒê47²³AàÎ"?:^”R²z×u›£3Ÿ¦b*Ðb ÎP¶9€hÌD[zr¼œ lÞ8*fkÝTA’ÈÕ<“X @Æ7G‹»7âFØ#së&!€ØM‡È Ò4dÎ9 šœÔˆœL—Š4TŠD–6,º£û™$%-ÛÓ2ÁÖžb\*9ÒÌ¥0GëÚ›6„MáhˆÑJ`DõœÝÉà7ýÀÒ®×¼Û'•, cé‡ìľ^/Mɼ¨ ÊÍòøäÑ¡c$+¨?ì“M:@]5­NÕL-ß[uǧ¾þúë~8J±2/›ÐælÉÙ­xé ² t~µ/E…ÚvaMðT#$™Ñx´ÙDõÕZlš®öû>Ç‚A"ƒpÿÑÙ>ãp0 ï­÷·{oƒûO´èØcʇ˛í´ßoV¾:Y]|xsq~¾+M*v¸¼¼¶Ûi„|³¿¾½<Œ‡}Ú]¿~ó!»Ýßtj}yü‹¿ûyšö‡ÛÝP.Þ¿ëo?Þž~úeuyöx3¾¿.×û0P¶Ûÿï€ÕD@p¾… ö>¼ýæõû‡Ÿ~²ûîûWŸ}ÑüêsÜôßõ–ÏßL…ö„ýöüýnìMc¹Üç—Ï~ÓuËçâão?Üìí´]ï"ë4Jß÷»‹÷·G?y}ùáäѽ&p»¦“³‡|ÍŸòÙíöüêúêpð?ù|(´>=¾¾:®oÊ~HÛ<á {èbœò ÿøÅÇï®oß¿=¨MÛƒ/–]‡8ê½™Š!èñjÍ%mš>¬W¸ýèÅþ/žÆÅÕùnãÖGãOoù&ÝÝõPºÀý‹ÅÙËÐ6txó”!ß|ûÍïþòõÛËßþþi¢,W›õz}ºlï¼}{sþCöÛqüéí K<ãŽÛó½ÞloÎî=nûÛ‹›«”Ô^]][äGëMñép(ÑÇ×oßú­•rÈÞnšÎÏ_¿¾¸=X.7·ûnµùò‹ß\]]ŒS‘T¦’¦NÂQ'ß~÷áj{»»¾¼Ý_ôãîáÑr˜4p㈫±êvè·xw›4%Sãõ*6 R.ª  îIá÷6ͯ_žšÑv쇩aaN†iÌùp¯NS!&‰rrülœlw»-v0æ(‚í§~3,r$Mð¤žt‚S.eÊEu"ëeY„XÄU¶BŒ °¶mB¯#3-6kUÉŒ©Aªï†…t¤(O‰­8©ÄÑ͈›ÆR13sQ‡³› îÊšSšJ¹Ü_•ÃÊÈä’›yõþ™Û¬Ãã9 N Á܉@VÈI¬âª*#Ñ õ÷{d1Rr&¢Æy~d:Πb°lFÌÆÄÞë§\40¸*=jIØ‘$8¢“j¸4 3H8xF`b€òZ@1"pi.šë2®°:™:)$u'-\ ĤZ%…ueåì=Ñ,¡Y,O£4%Vÿ)‡¸“›ð<ãÌ”ÌÚ©ª±+°£ná$5^ÉUF®!„ƒza Ðyµuàf&†'EUìÖõU­üîÑ;ç„ü?ä{DN3¢¼bŒjŽœ+E*ý€çË ¡Nõ^Y¹èuUõ;Lì¤Î˜sðuQ“ïäì^5…Î.®B^ùDU¼Ã W 'êNÌln>F•VËs* sðɉàT‘›5âæ$íú^ÕT3ÊüŸ¨P1w0™T|}=ò:×Ð6@pS6g&'faÆÝ…¯ÖíhŽkÕc"¬2Ø!31GÝX…j¸ CuõÐŒW­lÕ*£)Ïb"sw©TÒY¯íµF[‡užóþN® gb+^‘âD.q!®Ð-‘‹Ï“+fZUry½V‘B<àŽÏ•¯Ïͪ+Zy¡Ü£³»Q­ÈÔÿµêË"á%˜Ùƒ'‚ ‹ŠQ`n#u&VDœ‰È„¸­R£L•Úiä.«‚`5¯äS!VóÂ(ÕNRçîÊÎb¸2³°29L)ÌhµÂæ”ÝŠ3ŒË„siÒR|–V©•ÉÕ $uÒ63Ü*»M\ÉÀ™Õ ÌÌR‰Ü)¡ÀA,æ{¡ñjp*Òm»Ãn´IÂ̈„ÅUh!‹U»ÌZà$‘E*Ý®0/™[Bo•Ø… E$XÓrÜ퇭é4%LΤ!g‘˜$Ú™ÛU*Y­k¨#N^ ¹¥ÃR?iPøB:‹,;†¨åØ6Z ”KêVK@·Û¤&(Qµóºx×tŸ~úùåÍí0æeã!ÆãÍ&M&¬¡‰]³|q¿K i§4/š}]W¦yÑ•Õ" óÙÙ†ÌU¹?Œë/»€)ëºÃXXÀ'g›1Ã4­º#‰Bi:<8YFË·iˆM³½|㚊³¦”'_Ä`e¸¸¸¹=ܦ26íúÕ/>}‡ýõÕå°ßöÃ^ã0ÝÞì=ÛÃáz?}ôྗý8÷=~öø©Á÷Ùǃ—Ý8ÝôêÙó”ûõ½Gÿóßþž&LJo¾ùñOg}òøWÿþ§[.ÂïþöËÿúÝwoß4¦G'±¦f{Ù>ùââ§oOς˷ÿò>8[Ž7×X\\½þþýít¹.‹ïþðŸ2ï_ÿ°~ñââ§o¿ûó¿d÷_¾xtÿÁÃãOŸ­îžZ¹úàÁËg§-/›äÝš¦f±3^=yðÅo^6auÿÁÃï®ZÍý˜†dÍjýøÞ³å‚K&+IPò¸S´‹«Û+GQkÆÂ÷ïþïÿÇ?ýåÛ·ï/Þeµ³Ó3nüÃë÷¡£ElÜs±Œ"Ñã¡·÷WÛŽÔ‘.CêË”Ó.ëõnD=|1M×ãØÃ¦'K»Ü ÙÐ-Ú’2ÈOWK3'ÄÍ…ˆ…Šn'º>láÍn¿J.¦‡]!•6£Y@È ŸÆA]§’¦RÈÔÙblÍbN£Rk-'À#…’]š,±©µ±d>NSrÝGµÔPT'¡h‚Ðr·è˜¬‹¬æ€w`5-ÆUA§dÈžƒšxQ6‹¾øì777ªC–èÎÁ¬šæêsÑÖ%C2WB!¨×Ÿ¶”{ÅN)¡¸c3Pí‡=ܬE­þ‚§z2‰‘P”êsYæúŽ µ¯DEœË¥Fhæd±[du0˜²£§R÷,æ^ꢦú^…A$R!–€‘°ŠX Ì$‰‰ $ ¹xpv&#gc)¸± ×`ˆl¦‚àBz&s HÏn\ê&«¸»™)AœÖ'õSïê󆌠^A4_íÜÉÙ ^’ĵ+@. @³F£¸¹AÁ¹Â>Ti>VU—Ÿ3±»Íë*¨Ö„VEýŒ¼š‘ æUÐÃ@ݧU²Í4+窟©ÀP‘Ìô,®-|žul³ªo^R‚aPVP•ÐÄzÑ­ùvš »wó <À…,Bí2±Ì4L y¥HÔ©P«ÂÅï(ñN %µ;™„ͤUªi9i§<óîågÇ*1˜QýÁ…fáQ œÌh^3‘†*U$7ÀˆD½âÙ¼¢Î¼B쉸*É„àäÌõ vw¾sæ;½Ï2°I5 Owiy šmÔ+ýµ©_¥Î^•Åâæ'¯ÓO]5:ˆtY±W-ƒ\Rñ¯Vÿ¦T¸~ýâyåQØÝƒÏÔ|f€¡NÂZx"lNõûƉÒ0˜¹Apaº#ÖWbu†Öò{¨/7`0LA9n‹1Å@Êäìì4°;¸6•kxÞkY£Þ¿i¶+̈Tavgc÷BJfÄ¥QŸ; ¯Æ5E* 5su˜×ÝI‚ ‚™‘ç苊­oûщÅN sec/lêaªŽTcHÀ!ìì,® Ë ÍC’=\”›°±ÊÓdðv É,so„ÈÉ ¬a^ W‘–Sch¢Rà]Ë…¶²hÛØµ(,MB·é–Ç îs"# z­iR÷‰„ mi³jܹŒx±Xuë2uH£©ÖŸ£¼k‰@hÖgíû‹)ïSãdÑ€d¸ªKˆ¸º½Ì93‡â%†nуÕrRçãÍÙýG/sÒÕŠ(Ù'ûi˜PTW]HÚíåpÆÁ¦bgÇ«—Ž™Ó8æ ¡Fóä¾ ò)e¿ýâ—Íæ¬Óiszôîââ0îÎÖí£{›/^}zzöÛ.p¤ny~õîÃå‡aLÚõçŸ?3[œ¸Éþmä“—©ôMˆï¯.lï¯øêú°<9j‡¡tGŸ¼|~²^ ‡i4:ºÿäâý¡ø_þæùñ飿ÿ‡6þúO>aÛÝqw2¤} ã/?ûUóðáßüøúúv»OKØúñö ÷šýû]™†nìh÷øyx¼F×ÜÿðãÅõÛ'Mh>{yþíw»ÛÃÃ'9ûÑÃ.Ï/žÜtrühsÿãÃ娸øö«ŸV"ÖüøAÓßÞ£ñ‡íöúj8[m¾8[ã¯~ú÷緗§÷i·Z6BÓ¢[}þê³óË¿ÿ‡êoß ý.eyrqy~ÜÆM»œÆ<¤i{;üù«×oßmó”î¯WÏlúIÇ´[4‹Çeƒããåò¨ áâv{œÈUÊ|Ùòɺë§ Ç]³Ïc'‚1»Ñ …|È9rϼX®?ûüon·ç ç!¥Û]ÎFÛaJàɢHâBÖ”<{ax &¡?þÝíõ›µTÔª0Ë.vCA%#\»N¦ÆÄ0g£ZmwbGMÙ Í­:!Â=°8yNDBŒHØÎ1Öº<“E ìBâæb`–z¤"!Ø…à æàh5Cf•ä¥ÅÔÜU‹×!›¹V¼AŒ‹Uɪfl0·Rtšúl®ÈdlÎÕ©«Î‘ ÕpWÕs‡×\ªV„f‹¡*«vÝŠj©Û¦ºT ¯Ý}óÑv¸3ªRÄëª'wz·-«ü©ùHꇮX†ºàà9ÒN˜„fÍÃ*p½>Üë ‰ˆÌ@`Pýj!ˆ±°Ô©ÈØÀ\w›Vý:`¢p·S«úž@Ì.¶Ê ˜¢ °Ö4¬‚ʪûÐj*߸b§î‚ýî€Ëbq¯®mêŒ`¡î|Ž$Õ£ê|¯t©°ÊLªyòZ¬dæ;´“ߣêZPª‘ˆ„ê·–Ì!ëz''¸1‹a1b¡ÈóFu¡EìBµB³HÅÝ u_EÎs"Δ¸®gk¦©ùÝëw˜ƒ™lž …èÈàb Ī1ªÿÂÂ1›¨"ýá±æ†êòÉCí$‚êPW@ózQ™…‚!ÌûÊ6›ÇTaŠ— © CG„ÏêmâB Ä ¥¾j™M˜fà­:“»2¡Àfe+IëMÓŒ¼žÅÝÙ=*;ØÍ˜=SíÔ± âB¡À³Nî…œï Î`1#—RÞFp‹ÎêîÎfÆâD¦LdPˆ ¸vÜØ™´RÁ*Å>ÂH½“)«‚¹%x BV ϺbV.êgE”;„„5¸•ÂX‚’3y.(~ùú‡··—ï¬ÃnÿþX\Éú雋˫ÿæËþ‡¯ÿø¯þñ¯ßô·7'g§S W‡¡£öÃõåû¯~øúý¥9òóŸßnw×S>loˆù³_=ß]ÜXÒäíî°kâêù£³Åb©šwèC – Y6rrzòáz}Ö›l–²kiCX^öû¾ -ãéÇíú~³x¼ï¥¸«vM¾é2JZR¶Ð-ºåQÎX7-IÜ)†¶i–I'¨çé’´¨:³38—‰|,jSš¹(›‡>åT’ 1z`7÷ëÛë;TêõMgo!`Áæò:+H…Økl„‹WÐÕ%”»k5×e˜›ÃÜ­€\1ÃPgÖÀÌ]¨Áyüü«T[oõF6é…~ŽÁÓLûä9t\U6äBp¯F gŠ`’¹ö"OÎäB\‰Üµ^‘‚ Œ03Ç™l¾*ϲ"(f¹—ù”é¥NÆUæs§õv›[‡]wF„ëÀ©M¯ Á Ÿ1¢ólQ—Iæó©³k\¯ÑÕ~ f˜iMÀ#Rí ê *¡‚¡D8À™k±âÌŽX¨=¨€ªy[03ü+ƒ‚j »â¿Œçåݬ¦êrqâ™È.l&õóªXNbŸ!_•ÉËw^a¯YvLê šÜî΂^Å«Ôd”ÏX‡J‰5@ØÈÝ!% ÌĬ4SýëË.Ä‘ˆ‰CÎÁÌqq’@f£\<ÖAÓÆÙ™…àC ƒY-3«rmvÖ~F`  ‹)a¶ï7qw÷ qªêèl 5¸¥P+Ôln¦J.ä.ÆÑ œàFLV7äf®îÕÈj@vtb¡JNÁLf\5äUvåx`G¬û{`î ivT¡“rp+DBq'öÆ•Œ$ÂÌ#r†YÅ ÀÄ ˆÉR"_Q¤ a~^'*ÐÜ vÉ| Õ0–KaBa «xÿäT(­(@¢:¦“J×DŠÇ·y àœ ëØ,¢-¶]³ïw‡q:Œ}S&- K ÷OŽ®÷ƒN7ë“£c rÛo«€~ºÛN?¼w±»$Kâ6»óëíùõÍõÕù=oC ±ù»—·ý¡mèŸÿéï¿ú÷¯ß¼{K9…e7õã'OŸœ=]nþí§¿þ·?þy9å_}rVnúeËýãÿ¶wžD€q„qº÷Û¿ûãþ8LùäÑãÏŸ^}ý탧grÿôðú{¼»ìz/y?ìúd壓e[¦+Zt¡4mºîDw?üIšö§ó«þzßvÍõ`Þ¼>XXf¤/÷Þ__£¼|xòo?\'È“õúzÚ’¤ûí«Wc?6|Øça˜’#¦ÞïܺwÛí~R‘ÒµòðôÞ8êÞßî6 Þ¬>;jú>9‘ðQ*y½^gÚLc"ÐêèÁíí6—rŽ!Þòæäx»;·‚¬»öxÓ©ñv·ç€®‹DË…¤qw('G'SÚCÊ 2·#]_5²5kiã"g[taÑÈvœ%1v¢€ –2O9—)(|ÊÓÔ‰Y'ã&:ËØJç&¦Óq)wÖÁÝSÆiï(p?ã;¬–G9g‘nÑÆ6ÆUdQââÞæ,[ÞuÐæèlÁñ¸…Å`Öî‹2…Ž-»°çœK1c†¨OsîÕV'c³ÉÝ3Ç¡¨Û@Å]K•˜˜d'c+Ž¡«å²”œKÖ¬^"H×¶‘Tk‰Ð ÅJ6s1@ÞÄxrtLÒ$­’ Ìwñ*±€“k]Þ£TdŒÕLVˆ`ÆÌ p¦ÎVÄтן§H@ÌÙEDæÖuq!Ë&垘Ù(0â‡@FswU7OZÌa`3¹—\f^¦)ç¤ Vš“áxÛðs@]E Ì0ãút«º•U2{-ãCS6x-'‘C¼>ää(Næ³¥ØPsñV×Y^ƒË¨ h5¸¹‡9a5ׯ~NÍÒênùO¦eÔV` Se޹˜¸R +n 4¿ºõÙᘉÁõ^4«o½Æ¸*N‚X˜¨^$!v‚"cˆÈj¾ÝѬqöŒ:öTj¦x¥$ܹzæ; Î.ÝâÄÌBDÎ3¬ªæÚê§msŒ VÃCÆwd1È­6ë,ãVjoÀÈ­ÐuÁœÙêáTà A=r¨Å>f°Ô-,‘ÕËW¥ˆÕ¡Èì|7G³šz2B¨ Ë;mµÃµÜWÿ’ˆ˜εtPo¿àªOÍÙ!{`¦º%¬þ¨B\*LË«6›ˆ 6Oh•@aÚ,–MX–l,ÎÄRë˜d —Ê7!'&'r0 Kk¹a„ ä­ B`¦á¬Õ J¢U¤(YLDƒˆ©ºÎépB=D@c ¿QÍ_AÕêO.ÎææâX`-B2S«Uú¼±•êµ¹)`p).U¿ ŠË¿ŒÉëÛA„êQ*¨·òöƹ8 `¦v3gqa›‘$0×¢NnÁ îVöˆ™7â’1D³À\æòB!gjq‹Ç°äŦ ¼dUoB㱤å6›#@ѸÙ4G˜$õ ÃmðHŽN»?¹:ŒPÒ2XšÆânîH™Ý&²bYÍršÊ!MyK ×ÂÝbšÎíànîtÛÍ)´á¸kWM{ïþñÅùn˜¤mÚuÛªa<䢹mp?]ÄM‡CÑ’›Rv9/nžœßìû©ŒnænZˆÙSÊcJ–¬L\|jç”î­yRJ7«ÕÓ‡›gýâ/ßýÕËayÜu¡ùp9x¦)§&æe³¦Ã&'R”†ã˜É5·MLÎmlÅm-¼ê˜ÌNÞEn[è° <^òÕÞûÃîíåöíùíÙÑò݇7Xµñä…Ã/_=÷nR<;:úÝ/ý‡?ýéÑÓçcñåÑÙ›ë«Ý~ßðþýÛ×ßÿËW~öêÕþjÿÓùÅ‚ñå³'ññëïþ|su}ñíëwWÛçÿð’Î÷C¾Îåãg¿À«O?|ÿæùéY\¬ß쮎ŽW§í»Kíwý›ëÝW?ï&¿Î9¼zñT–'ûièÝrǧ>ú/;½½nˆž>¸7Ž7C~ôøéûwW}Þ{Öämˆ|8ôŸ>{Ò5§‡ÛKáîÙóÏnçûnùìÙ/~|ý.«•¬Çˬ!œ_ÝJÛ}?Œã¾L¹$7ËZ$Fa³2¨ê0Ú~®×cÚí¶×77ãbÑnNWÿÓ?ÿ¯ßþô6OS¤˜5eX_Ôú”rVóR²¦lÊÔÆx-j)†&p‹i?ÌÊJ ÐÝ82dÕ¶«u£žÚ´¾^±´û yЧr;™™9ÓD™‘BÌÛ©lu*ÉM§i,ä ¸zQŠa© LPÒéñ©˜s(Òä^ÜDs0RÏÂæ†¨+¦\`fer5wucÿ‰z³&ËŽä:w¹{ÄÞgÎ9kª4€F·š”ÄA"Í(³+éÊôrÿ8_®Ù•DRj²IÌ5ä|ÎÙSD¸û}ˆ `¨‚eæÉ¬:¾Ý×ú>!æd†€ê‰-j®9Û3Êœ¨¢/MPBu5‘‘WILG‡ˆa~ŽÅŠk•|¸ÕuIýkÔ„MÀF„ m\ˆy®*1³ Bæ¢ÎÙˆ­öî ŒJ•Ö ‹fgBôHge6»˜¨)R`F!1%&w'±âŸP(ÈY#åâÅ]“:™·Šá%5…qX°e+°À>RU\Ã*s^@µ‚fómNkʨº®MÉM‹»\0/!ªm% ¦êVÁ½š籪6ëÁÈ fæf³Ê­áµ™IöKH§ž4毼Ú<ú¸Q\a¯$ á:p=ï)È™!yN&âP¯Œ5ÅT? Ôä¨âg"ó3’”U©Nx.Õ{Rcø>U‡?ó ×Bu@V=#àÒ./æÏ›˜«[LᬕsjÆî… ‘º†¡^QÃQZ[†Ú¨Ÿ#9Äfpv!£:ö:˜ÈX V/Â… è aa''c®1-6° %QOán22¯ùûgp‡h5T2)]¾xõíûZï^m×|ºZœ]l›v÷Ãâõ'ï|VÍæüü„âùëÏ¡’8üËûû6†U”¿ùÓß}óêtwñ*ûâݱûî_¾ÿòíÛ/¾~ÿðïînŸ\µ=ÝŽ#‡ÅÙš^¤‹ i7§WÝM—±Z·]_>}ûõåëmîúýÓÓ”ÓñépŽyìá›cwìG_ÄYÊ™VkjÃÙëçÛõ®ï†ËWŸ¿Zãçû~TŠªVœSJcɃ»•”§oú9MÃÿâÓÕ‡½Æf½\ÄÍzýxØÃÚë³PÚw †Vh³¢¡@MÕÈ %y,ӜǤ¦»ÍÇe?ìkýe×àüúóóóëûw©L©LÙlv&°‹‹™™Ρ¨)„DœLœrl3­Wëeh*Úü0=Nž‡nÕ‹ÁÍÐÊ&žˆqò2 9eä&,`);ÎNÏŽ]ÏÙV«f, AE(˜‚,€Ì‰)”›òÍÕöîØ%×bJšÍÝ­€ë¦Bƒ»¹ªÑœßQè\·ç&pTg-‰\ÄÈY˜A d€j™JHÖ ³8œÄ…f#晸´Xmšv™SO¤hÎÂó#+‰ÌÝBMf” ì%.K°©.WFÞ1‘à‘Î* A…²2W€UwS*N XqW©,f§¬0u£â0ŸÃæ„Bd¬>6Ï*‘Jû'w…ù³4»bЫÈCÈɬ8›jf%˜¹0Kµ¶ n6ÈÉÈÈZ³þ52^cÙ#ÔÙ P«H¦z{¬‡+ùý™!J`1&¦zC{^…ÔKÔC\OF©ª§ÚÒªÝÁjdPxާ;1“××0¶UêÍ;f(ÎöÁÈ^{ï•Ïå e@Htþh^êâÊê;S¯f·þR"t¾¶º;=‡Ü Rå? ªeš+‡Ì‚;±Z¯qNæáyVõàÏKX Ž¬bDæâ®·š³üD¨ݪ.žËÄ"àš¬g±ÔÂæ\„t÷„uÝâìp±…z$$v1f&˜»×€>Ïñ2‚×ò$1‰9fñ’‚0×-gÙ×Q_à¡ ª]\@ph‚ª×§ c2)aä^ `®É9&wbŠ ÄÎD`’:naAÄÔÖAUL{5)3ؙو!³&PHZ¢ ¦Ê˜ª»³;"X`¨ºÔD¹'wqcSXTaØÎ5zȦ,Ò¶Ë¢®)Ï¢H5R3v/ ·Jœ«r.sFÍŸ—¾îuM¯Z«!ðàbc6v!RBòªO0u‡Z¢g3›¸??_T^n ·^¬T.‘V ‡ ÆÌŠp¨èéÈpjÉB ²˜M8€—q—<—!7M[‚/!¾\µ«FŽ}ï¦ÁÇšà¶àFMÌ…aê©p³ ZiFõ\QðØZ.$26Æa½¡6ôAÄ /Waœ†ŽƒK°íf¡É’>ucÇ$QHÕ»ÞHD=[áf¡q6'»2BB“¦’]Ó4õƒ[ÊÕ4^.ŠÔy½Ûu½†á~|8Æi{öj½ðR¦§vÚF,þü7§£•Êa·Û¦Ø9ô–Òj響ùòEèþçßÿC³ þ‡oÞ¶íîÿð±Äu$}ñ›¯Êû‡qxJïÞ<üt˜¦OÏ_¾¹>¿ Ë%4Í÷ßþÝû›Ã˳–øüÝ?¾|óº=Yc*:=î>ÿ,buýú³ß~ùùêä¯ÚöäüÝí÷û›w÷Ýþþé¾@tâ‡Ç¾ë~óåYÛ¡¤GøÙöºtOãðpÞ6»ÓÝíÃÍícè»ýÓp}ùúøÇ’4'" sß¼¸L6”T.O.ÆBýÈ ™®Nv§'W»íÉ·?½†§û§ûûýðånÑåfsù¢Åtvz>û±ÈT dfyÊ¥LzsìûD†¢fãp&;Ùl§äOÃ4• ³lV«‡§ñ0t¥$JÖ5‚³ÓËRr)Ój±.6vÃTlä`ýDÓˆ›§ýÔÜ<¹ ÜQfp 5¹a¡ 2Þ-#7Á3µ‹¨h‹(2ĬȽ—ˆ¢«I\aš‹ÓW]òPÔ,$¬¶gekzȈ뷢¾Ÿ‡ ; uÐäPeÎ fŸcWDÊÆ¨^¸gÁb]À8¡vJ3œ`µÁWƒòõ˜c>ËÁ0­iV§Ä3n¬ÆØˆÈxN/×*'$TSƒó—QW€6[m*¹ˆg<9»²ÃIëÆÍZG¯üÐyìÏÚD&qb—J: Sà:ÑVxPª¯†ÃÍ*'µö[•¼Žs4«@†Ín ¶úéz½‰‡ ë¬'Up™3 ×Uƒ¤vÞž_¾ðÜ[ åÕÕÅaì Ù¿× ÙnV†L\‘²<ÿpK¨C3b °P`fƒ°kÄLîÄÙÛf!BfVñú}61"HÕ0×K4ƒââ@¨ÛRLªJ@Z3iæ-˜cQª!îzöu"âL•jl^rʦ…œõ9Ã'µ è¤äµ+ ¬u­èsð\ݪ(‘1*¹3ÁY •&Ï$äìuÓo¨Þ{‚rÌ9‰ƒýY߈àıYÇÆ$ËàdE@äQ$€X\‰Ü„%Kস !€yÁP2+m 14ÒPl–i¸ gÛ«¤ÈKR'n¤ùÍ›‹Çƒ)Øa-¢¤‚ ƒ+$Ša×JIšÁN½cÔ¾„%µ(’IS)EÍbÞ]¼>žÔÆeÓ²vµ(ªí‚[ñ”sÓ¶!p²žÌ˜‰¹X°Èáúäìlj\žïa*™É5+Ìùäd•3‡(ÃT’æØ¶íª EŠ)¹‹0`¥PÒ¼ˆR¹\‚øŸþò÷÷‡‡óõrÑ6—/ÞHé[ÁR¸m–»õºÛ÷îHƒ7,«O®NO·«£Ù±£Å®y{~uèö‡Ô·Q>»ÞĶ9Žüø´_µéxœÆiÊS ±ùïÿùÿ9÷¿þêw_¼yÙ\}ªqý?þîoûþêz·‰Ëq*‡ããÃa¦í‡¼ïáÝC}q~r÷Óûwïf-Óÿùþ‡8ºÃÓÐ C×ï·WŸá|Ë÷wÂãm÷ñÐîo®.ßýøÓáœýêí÷ÿn?D¶a8îZüíßþáýØ?~øùÛê§Ãù2Ù0.ãúëßü›îñn4ýw×g'›ý¤ÇI¯®>ýd»,ezóúëv¡±Ù6íÉo®Ž“?íß½ÿQØS  ìƒt9 tCúêjgº=Ój½Ú`ið“õâ5e½¹»SvM™ íÅonŸn§”O×i›£š•‰ÛÅšx‘J°dMÓp”ج‡±ôƒ™f8R.ýÔ‘4®Ó²i–M«:$óq %´ë·¯ÞôÇcw̹X`o؆>u}ï¥Ö®D5NÓ4tãAn,¥(C!Ù”³ *Iu¶òò¼È‚»Ë²½ ¸ »ˆCj6Žuæ¹W93*7un4†gKÕ#.jºÚ3Á©–)DIœƒsóÌEp•êF$ Õ'„–6òfV@ŠÁª§ÍçR_x륰r6a®T×¹ñÜo¤yñÈTÏ÷DÎB3›•¼_%CäìÄUÃJ©Ì ÄÙ‡æL‡a(¤l]ÜÃŒÔ4«Ó§±8% Ì$D˜@AfUú‡ºº ŒàU.íÄM„Wô;S¹y$mÄÅ…ÎALêR˜…Ô……œ¹â(¼–j˜bcvRbÃa” ŽìØ)…b ‡T´‡ ¤+‰®jS'¸™¨ñúÔQ/é.Õ?´XFñú;rEÚ:A)WU9EF$eÊ0¸+±©:WrÌ{`”œØÔÅHò3¥ƒ<(©OМ<ÕA/,pu!D`õ !'€›¢—6´P UâÆXÍr÷Ý8¸ Mýv¹žÌ&à@íh‹\F£,Īhã‚)ò xlw/äÉ'r¤,Äè­( î¶kׯöòò:àxèÇ!srY.BKÉc‰ s(ã4Æ6k™´¨¬iÂnµéÃêÒt ”ûqØ,O›v±Yï¡ûƒÙ˜§r¦q9˜ ЖšÝÉv:E„¢îæçëxuÞß¹?$§®ÿä¼ùþî‘¥ùõ—oß½ûpè¦c?cßqú,MÀjuÒ§±ëºnšR*u0Ë%Ž9ún·ˆgç7wO÷ûGCyqr¹XnnŸºî˜½?Ý6qµ¤xròÅëwÿòÇú°Šôñææá±ß¬šaêÞß=”Õj·]Å/¿øÆVÛõróÍIxóÅ—ëEº®‰þæõi,ÃØ½ýäúb»Í?Üw·?oÆ~srþp,x÷¹üÕo?Ûþðôãw?}øøóþpV4MßÝ=~ÿãí»››4Œ<Œ›U¼Ø^ø¢Qů޼XGz{ºlWW/¿ùí:.nÞßÜ=¼ë÷whÂ~ÖË“Røááþ‡÷ S\¯Úõõª9Ù.Öf)e £MšnûÔMÇabiN&§2ÞuhôßþË_ýӷߟoΊå½{ºAJ) ‡ñØ+%¥«óW ðØ?Y)JTÔŦ‰)(žyÌ>jr5÷Î »Íºh?N´\,sÉËÐ.Z¦òx|t"Í] z}~õñiråÓݲ8•ÂN²l‘å’ÀêDl‰m‚ qˆÍz³bv2”Ö#/)4mh³g°5<0Á¢É*61ìv§E3jG÷”iJb%åÍ**…d[9ìãÝ“:#&±º.3Q=±(7iæË¨/¥({aEI^¼c‡©›ÍL=Ã5’'ö@mÛ¬Q0Ùä`¡Ò´BNNLpg°S=͹À‰Èë‚„ªì„ªµ:lî×[K¢Ä`W©HKgF@BA•(³Pô`u‚«9èR¥ˆÂFN “j䊖0kF”¤öµáÂðRœ 9‰ zF1’¸tÓŒD0‡Q•öRñ™Df䢫 ššŸ‡[ʨ†eq7c îàP»kFdfÅLÍHݘ µ¡Æî=ó?k‚ÚµTâ:™W^§×Ú\}c†?»‰€êË3pýD35ža5rí2¤˜ž›â‹ùþÊL#BÎ\¼àyŠâç»Ø/‚j ldìÄB¡¦Fs`ŽsG2€˜kæÚ‰-ÒŒ  hðh¥b“AáêP²š«vFgR“ØL¦Ÿ{‘³gÞNÁ¤Yæ4ؼ$â9NƨÔN¯\¦ºü«¡Š“BæóúÇ£…– uIäU.P‹‘¿`w¡¨ê0³Ìé;ÌWx0Ìf)Îð×ã:'¿æ™¡æÆ˜ëÎgøXÅü»³¸ÖZÝ[ÍC¹ Õ9/ù9ëgOA‰Üæk¯ƒ€¬P.Õé-AáZ× 8þåŸ A Â`©Ú<Kvfn™…Ý)Jaf6†ÙL›p#0”Áäb,N‘D„g†AÐXd¸Ç@ˆ¤þ\’8È L!Öí\ðè,¦ ˜"UªÍr¢Dð@æpš²›±³i¥\ÑüGg?Q sÒYŸ¼¼î§¢i„¹ ±"R˜â `q3°™9ŒŸ#¥`ªÀWj 56T¢ùŒ.ƒÅbL$mÜÀ2; ™‚‘„(Å0¼q†Kk‰Ù‰h¶"\lCR_ ÍÝ“'Wó’3gQϹD #q™zc=m ÑWÂCfR-$ÎLâìüÍåÉУ⌆Â8©º'íÜ|Gr¨íáMîH»Ýjš¦þ¨žÉpì ˆÇ\4%w¿ØéK‚•¤i8ô†\´é:ú>r+±=vý0¥%‰)Ô5o–«c7À¦1OÇÃÁmáfSRS q}Ú4£MCß;óÉ6x?–‡¡K¥ÿð~ØíÉn·™ Ÿÿú÷ïß=®£ŠûašJIcé’–nê}èÇ1OS/d%¹=?ÝŠ,C?¹¾úÝg¯~~ëKN{7ïûlÚ\¿x½»ÞŽwÓ?üxs¼¿¿g³ÕR®NO®OÏ.Ö›Î/—ç//N¦¬Ì‹W—Ë¿ÿ»ÿù‡oߥܗ< ÃpFoÃ&,–MÜ}òéá¡¿Ú„Ý7ßüð/ï~º»ÝlÏŧ š¯[¹¸?ÜšËÇC´4ëó¿øË¿YÜ<Wg—ðû·¯^îÞܚ͛¤íÙåÕÝÇÛdåéÃÓßÿÇOç§/ØSšòÍãÇ7›¶}ùò7nnÓôdy³]n»¨ŽðÉ‹ÕýñÕÖëÝTЧýSŸÆ^ êÚÿñÃþpßÙ™‰˜#D\@ŽØÙ•ÝeÖ¸tþ³‹‘g“oGq2X瀫ª¡T²Q·šÍ!’Åâ‚X@aö Ó³;QPŽólÇöl"¤Êü¬ª7X™“õª)?W‚×q±×^@ ±‰Õ©À,*æg¼PÏ“™›3ÜÌ̘¼³“9;ÈM1ÃJYëW>ûç]jxTOx ÂdGª*mä,䵋Êö€=˱+¾RúÝg¤„ÕO\ˆ âµTjÕ#MÌRÕÝÂHÕW…u€ÁNH˜y]× …@Ž<ÃèIæ×›¹jµîµÁFÍܸ $¨†Yœ¼Q.q63 %b1B Ypg¸Vi$¡@ u=/FFàRW»&Æ®ZPœ’ZrwWe&7%…š€µ62' ÄêêÃÐiÉîÅÅá(M*Ò¢^¼ŒsÉ ÷P)¦ˆ‘CêOɈZԜ۸’J„/í²Ykñ B ³ÃK0-IˆÔA‰8º4DMhÙ+MÈÄÞDò°3¸zš˜’0±¦¤£QÒÈjn™úÞܵÅ6œ.‚'N(ãPÊäÞYµpµfsT÷¤Þ÷“›¿Ü.ºž²a»:‡ñiœt»^ÿå¿ýæáñ©id½Ú–¤ Ôž,ùØûiR 'b‰L1OI »]ØïÆ15±qØf³eÞšæC× ãXÌÕ\Í/ÏNÉœxl›Å›—÷÷‡'¶Z®Þ~òæiŸRÉ–¸Ú­ONvk³~!šE\îÆaêÙ—½e²‚šrg «Eà- ”©¤VNÿëýoìôwÿøícw¿ôòg¿ùõqÒ¤™r¼ß¿ÿñãÉÅš E³hã¢ù¿ÿú?>ݽk¢?ìËËË«»ãÿúçï.8y÷‡?týaÂùëWÛ–~÷Û¯.–»ïßÝ^ìôýûýþ±ñÐŒo~Úé8NÓá8|û¨wûúã?˜‡M#‹Íò|uÖ’u?¾»ß¶¼0³·Ÿ¾üðþvÅû§‡±OO»ÝåuƒÇt<[6û~8Žˆ$\x±X.CVüt{SúÔ¥‡Ôér»íuœC𬤠D?8LèV‹“ÿþŸÿz³Þvýq軤}›e+fV†®´d½Ÿyf¸Fm‚Ö|êƒÚ•s0´ŽpáÀÄîÌÁŸ!@½ÃÍ(לœÃŒuì X©b¢™žÀsO€5 T*ŠÄXd®’PuˆóŒÜªqzöZ«%ªÍS"öœù’ùóÎÄV©Ïš€yz‚# ±BãRñ`aa&©ÛŽ ¥zT¤XØÎÄH¼FÆœLìmMPp’ÀF´zïtfe°§Â©‚¥Øëb4ÀsÍ3@Î.00dv');±»Ìë@öùÛêêZÉÅ3¿ÏÝ`ŒŸ´ÄjÏ…â@ZÜ=U¹±`µ\ä¢ðYßhpŸOÊfžÿ,“Õ+3©ÃAÁI ‚ÖNÇ]l`ðæ°jÛ!ujÊBîâF p‰¬Ì1µB"M€4Îk¯Ú7&2b*ЬÔÖÀA8 ËìBÆ,gÛ˜¤¡ÂyiëÆŠe M„&Ц€•:RɹH¼[m'&í&![œöf!^ì.ryt;k#ŸäØöé©”LÄ7ûÎ˰\Ò±@0õn,dCcQnæY®~ûùŸ|÷ýwiœ8Ž:f*ËÍr±÷¯._ÞÜ¿»ìW˸Z/M•ÙÆ¾8ÒõJöÉÉÇþ`>%M)•eÓ¶›Õ0ôc2%/ëÅv±n‡®ëÔw‡ãaË$Ñ×±5 m¤¶YuS¹jCðÔõ.Θ IDAT}¢ÅæÂ,x˜ÊáxûÝ·?ÜÝ÷?||—ºÇ/^.oîÓÕ§_ÝþbÛ|ûÝû‡þû’Ë1¦Q6|²h>ÞõOÃãg_ün¹<[_¾ÕÛû÷‡.íûCl6_}ýÙ«·_/‚?ÜÝz.·Ç»}7n×ÛæÔ.>ý÷}÷ý;ÄíÄ­2½8Y_¼xõáþæÝÍãr³ûíç×ÍJ ôÍ7¿xº»}¸}}ºº¸ÞÜ= gK~ÌxuHÛøÃÚF.NÎåäìRS‡ãÕÕ‹g'Sòé8^ž(¿y:f-mÐTÜL7'ËÅrµ^‰Ü 94ü椉Íòp¸ŽN™i½^­[Á¦mMÛ•‰%LÉM ¢)©ëÄbC*OOO‡I‡åêúÿýßÿgrÛÄqJݾK¹WÉèíÙò~è’º•度¡èÔ¦.=ŽSqiÕr×`týbu±\ÚÇcY6«¡KÇ®§¨d,c ¶Û._ŸžíûÛaê ð™ƒ6Ð"®.7Û©PXÈïß¼}LÉŠ…È>™ Vµ”pŒmŒK²ÒrNµd] T|Ñ®v×g,¼,ÚíùÉË©7w¢&,ãBƒkîk2YŠWÏ0VF¼¸“< —©¨ 'uöRMLV+™à±˜¹ Lk3åÚ s³ªS"…1ÜTÍÈ5þ¥NÃÓSÊÓœ ¶ë‘FâÕë/‡|PË5¢NîÌ$d×Í€‹‚³‘2B Ve&s«Ž"\”a‘âÆHBà¹Fç…Üa9puQSÐÀ©k8k²2Y]¥²'"¸Â«çG<Dˆ´ž›ÜŸ˜Ø*æ,DZ x\ëe,sØÊ*ØËj­Í¨˜×${½3PÞ àg!ýB€ò:€±WļþâÊs—_n„Ü*¤»²ÄkÁ¾ªf4ƒ0C@ÎÏ÷ÂwK˜+†pâZÿšyb„à Àìq˜!D$õ Å­v‚Ï62€¬ÔtXŠ P!1´¢. õƉlu·Tñ¨Ìd€‘™Ì´‡›4ËS®!v u>T©k¡(Hh>×Vx‚«gº¹¬Hî^™žutÅsžÉ˜Uh&>jÀðùä)LäµÀ€\2¹‘‚ à^—XD2k‰ÌXQÏuŽZ5x0”Z}¬C*˜–*¸³~ÛˆL` 8sâš=³Š¹ vr«¡ÙuSaîΩK2§yûIµebƒ'He¨s ¥¡•@Klì† óN²m*i¶¦¾ëX©@UbS§ÏE,^JX8E,„86Þ©I£Ü L†" !Q8…Ô²9ÏrxâÚü R†yY¬L 5P}Ý õGš‘áDæ2£IÔ¹>ÃÍAôÚc®O&€+"jÔÙɬ”ìä\س•³xˆÙµþhÀÈ8;Uz~p F¢3ZÄ1 –mL©W7‚Øn׻ͪŠr L€€ˆˆà‘(kˆm»ˆ+V5ŠC„’µQÝXÝ\Ô,.ÚÍ‹ÏÇ|@qPÛîNJžŒËBš³Õ¹E]œ¼1+Å¡Êîn¢„r±`sú²\†õI»l·¤ÖO4«)‡áÍ®mãYge½nŽÇ}?ìË8åB!än*ÃøÔ G/üötóØû©OJ‹¸ýͯ¿Ú?¼ËªSîÕÔÍb`ö²XáééNK¾¿¿;†•$Äš¸ÝœÃ‚p”»‡q,¹Xpy}ùéõÕýÓ¬Ù„^Š<ŒSßwnãñ0óCחöËÍãþ`¹,›ö¬iBÞ~úª¦§§~,}.£R< ¸Bо¼~ýòåÉåáðøþiyºZµ‹•ñ·÷Ýw?þôñþLJ§–˜ü¨Ç;?öX-NÛÅÿõ_þ*uÓ‰ìÛÕå]¿ÿù‡ïàøó/¿øñöñ~ÿðåÛÏ"ÒþùéØ=uùý¡o'm‹?~÷m\œ~ñÕ7˜ŽÉÇ#?R.ÖÍè›OÖÆeÚ—¼Ýœ|øx×4Ab»¿¼ËÉ•OvÛòpÿÓÓÇûAÚ›õjê°Ý!®ÎÛ“Ý”&Í©O‡¶mŽûÇCáÿøŸþìáãí8L£¯ŽÃ …s°UôV"‡EJöéåòËO^~s?¼¿»ý8åíꈻӥf£qI6ŽΣƓ““¯®w]×M™SÑaÔ¤¡mR}÷Ôw‰É²2KÛ,—±‘š¦äÉy˜zW¬ZÎZ†!•lCŸŸŽ#3·Ò„PY/šÇazÿa(n׋ÕÉÒÆÉ  -NN·š;Ò|LÝa<ÀW§˜‘åç_ýžC,ã䎢Œ6h²)›4 ñ°„–Ñ“k¶¨¼Ý-˜˜iÅ!l._ Sdó‚áÈI`ƒ§È1,ZggÏ1Êàþéåë®ß·‚’‹ºC]HÉ©ÔHdñlÈÙ3g.d(ÅÔT La‰È Á9“× ²©aÆHªqUý2WNÃ)p©)¢’HjЊ"ÇUjHP1ƒ‹3‘Ôó›—þx0/Ñí9Â*¨RÌaTªL$‘Ã9À¤vy˜œ‚Ø¡1‰ÔåI0×ÃÁÙƒ3Ü ke+ Å]APfr¸J‚¹pvWg  ]Í )99©LëéJHb7§à•9í,æ³ÍÓ<_{\àæT‡§Úƒ3™3¨ºé`³ŸZ×\µßLuܸÚn  ²“*§“ª—µÓçµÿäe Öh2Íc¸ÂŒ¨:ˆEªŸP¸¦Ý‰áÕf" vF¬tyæçâz$avPd¡†´ÉÙ…Àæl½;‘¨aF§²» DDääÕ÷Vk4Ÿs^ù `ªñ¨*ª¿ù€ìÒ.ψê—a˜ï³5’™$_jQQ¡^×L³¥˜ŸY¬óæŒê:Ž #!B¨»,cfTúf@¥®U¸9ÈÅ Õ~;ˆÁõ£ (C Æ•|À #G]O‘£ „MªažOD’X×E<»  7`B3{ k‡€ŸC`pGõÖÄH FªÕ‡ÍÏX5"‡WÔ¯;&š+·ìÄR!˜B‰㳿‰œ~)HWG5‹±‰7¨ö&¥ˆj™%â@DÁÙ ƒ\4T_3<‹ÃEÁ ì^œBe‚š‡13Ù‹™UÈ’³;AÔ uI!Ô'Bf`Ž®Tgsbq°‹CªÀ›ÝÅ™Œ]™ÜY¸ÒhYkÞƒ)‰3d8Pm$ÎJ‡¹¡– œ©ãA5 ÙŠÉj»ŽBØ]¼óž Ô„æëo~ûÙŸ†ÔMSˆÎ waÄœÉ8XF# @›ÀnêQÉá-$ ‹G&w&1Z¬4”S¹d¨sË«&’•aÈNŠFÄ^ÒÙ²•ØÄv«Ó`¤ÛÅâ,R—¦5cyr.†ÛO%M,{^Y´4Net]´»a<°Sð<å\,MS§„jä–j”Æ4•æí›·üñû’ºÕ:vc MÐÉ»íéãÓ¾= ½›Êqì÷Ç4 WW/§4ƒYvÛM‹Å”Ü\ÚÈû£Ùxû˜ÖÛÕån7ÝõéŠÄV‹†¾›&'Ï›vóêÍg?ßÞ/cp²Ç1O­¬ùâúÓýñéôôôúìòã»ï‹§©Ïƒ ›õöüâÕØïS@ÀÐ¥ó³ÓE»Îydèù’ÿùá ¡¬TøýÓ¾¤ý»aÙõùæáqêú›ûû‡Ãþd³Æôtww¿9»Z­On÷‡1¥¿ù‹¿òaüáÝÝÞÛ‡§Çǧ»×——Çq<æ1¿{ïöqÿíŸ~µóн8;M|ÿtøáÇ1Ä—Ûã4–äÍæ¼ižò«UKËÕÃñ°Ûn>ÿ_¿>ÝùÓcˆíÕůnï^¼¼ºþüòý»Ç›§¾hZ¶Í«‹c?šxlÏÖÔ܆ãÓ8tÝ*.ãæìp<œœ>ôûМŸœ]=|¼Ýw½æ¬žŽƒæ’9œ1ûØO?ßö½KÛž®·òÕ'íÛ·ŸüÉ׿ö’–«xŽ–’¡LS‰‘6§›cw»\®V«õ‡UóW_üЬØÝ=[$gÕìàE³˜tz¤ÄSN^¬!›Ò´'s ˜Éþá1Ù¨%C1HC´j–d±K —do?ùúáøêÙLsÉ\cd1ŠHlɬqaS)ÌH6W^CSX…['h¹¾~yì÷ä¼\5yè"Q§dûÃà'—ory„òj£4Çœ¬Âb†°&M ª“›9¦®Å9€Œ´8)¨ðbùRU‹Fd˜’1ê"‰Xˆæ£”Qµ1סâ3ÑPXÇ8æ*µ¯ñç„h¹\šze±Z Wò_¥VbR«tGg‹Bd°EÚºç0Dxë&Tq*…@Á©ϼ8ýÿL½I“,YŽ¥wÜ«ªfæn><÷7Ç‘YÅšÙUd³Š«¦lrÑ+þë^´™Ì92"Þä£Mjªz/. žÅ!fæþ\aÀ9ßGn¨Ñ×2~¼P┘ÅkÑD•®Njž §ZÙÝMÉÔ´ª@™M\(‘\Ó £z|›Uâ ëðL<‚93ÐBTÃkRñï+W'gs»¹ÓLþ&$H¸ò0›aB`¥è~­2TÑ„x`/ì)- +Sü¯(´(óC5,À$˜÷Zqíõ O…¹Åž( l™(´ˆ±ªÌäC¢áF?hFã!éå8\ »8?Ùaˆ5rH +ˆ9wå5&ÐÜ%@\P•$H ð§¨v€F»Å8â{áÊ!„ö’ŒY±wgÀ=QŸ‚íIÄ. }à ³8;$ÂêBH>«çsFS)ˆ˜„%bì4ß÷ŒÜ¢9Hð(çÎ%ÈÀrÄn$‚4‹Q@å°áÍP{b™]18¬‰µœ§™i»Á‡ÌÒ“›`ËjdTÉh: 3Âã)ªs N¤AdÏ$ ÊÑ º¬ƒ˜3‰D(ÏsDí#’Ç`q¨pãD} ¨ÇYV˜“,-‘(QÊ€QN`B“ÉÅX\h…7$lQø¬L¨ÐDy!Ê3ÅØ\ ìl7Yª¨®{òQÙ ‹ï+1@E÷)VJâîNHbq¼ÍŽä¢É•):#2g-’BlÉ 4Û¨z!—'Ÿº»Ã…¢í¡ Q‘† ]Ç£†ãV½BÀîIÒÅÕ³,í§7ã±Fžß ­çšŒ\]bf!^vøìú³¬˳nÁM*E“îš­i)¥ÅdÊZšUZå$ ®ãŒv¨ªÊËåúÙú¤jy~ùüòÙâqóI}pŽ65MÚ©ªä³¡Ž»Ãvѱ°ÕR]Ê™6‡©Œ5qú‰¡«“ÅÖËHIR²"Œ&e·¡Tæ¿{½¸=PœÞ}zwÝTÇiOfÆ¢Ð~Ü2——Ï_>nª= –]›('¢‡ûC©‡Ý¡Í¼Z®Ôûã`U»Ë“¶k›Ý8ŒãqwS’¦kö‡úìüêó/¾üôénšF÷ܤÕîhÛí®ÃööþÓPëúìäòüœ¦ÃÃæ>swyõù?ý~»ß¾xöb[¦•¤_|÷·ÞýðPއݨËÕ‰TYådÌ›»‡Q%ß?>ô»Ã?~wþ8ÔŸïv'MjÚõ·_¼ùùã»…èÝ~úâ«oFø7ÓèkÂÝãMÛ-_]½¢ñ¡?>~ùÕ·'—Ï.éñl½:ýå÷/^]ßýðîÙúô°ÃÍáÓgÏ_SÛ¼9¿°Í~_í7üp·»9e{÷ñ~?0‘/¾èNNUiÚ†ºvj÷ê³×ëçËÝû»ò¸U[·çƒOÇ~»ùyÿ§oÖK¾~v%íɇÇÇÏ^~8–7_üòöv[Ž…„ÏNóÅIóñávÒCjd,vw¿Ûí»‡:éõYûwßKVÅNV‹±×õ¢AÄÙK圿~ýùqœþ×ÿí_½¼¼ßÔßýù·¥L—gii˜êPöýæ~6ñÜÞ?”ÑŠ•áXûÃöpU­éæn¥”ZQ­k¥YtË“ÓÝa[ íŽýf˜Uº:¿²ZÆq”6™êd‰Š“/Ì›I§) ŒU7›Û¡T8#¥â™¬¶IÅC/剑1·"Í*91‰ÔêSÙ«N)¥fBÎÔµÍñ°u±ª„q?UÔÂî·ýC#õó‹·ãáæÓÝþðÃýÞí§wï~þøç®]åÅzµl–©Ùln†:¾zõSÿa8¶ûË‹ÓÏ^½¹èÚ»~Ç~r¨…-u«¼ÆŸßßõz¼X¯Ôå÷¿yÿìóÏ_^½út÷ÐuËFe¿Ù|ºÝOR‰üp˜îïö‡ãÝn³^·ð“õéá¸5맺ÿôxx¸¿W£×/ßü⻿ÿÓãeX.©¥Rø8Õo¾üî|¹"ʽV’Ü irž`»ýöÇï»øx<ôüãû÷>üì(Û}!Îß}óíLJ©wÃ~ªe2‘¶áüâêì8íûã8 ‘Å×"ÂÕÕÅ¡?Z±q,‘&‘›«‡I«kù˜ˆO ¼<[æ´:N΄®mQ…i³hÞ¾|¯''-PÛä’—,)4mMK,Srjrb iö’²’¸ºt-§åª>!W¢±–6±PSFJl'Yö£U-«´xþâú¿ü_g‡ÃùÝãa¿íI̬4T)êÓ|ÍÒëõU™ê`S­Ì•2©Õq<”Á¬˜¸‡k˜9Ø›œs“FWwe3‡Lì2‘·93ÄÈÄ&Dajý¢åa4u¸¹•ZÆÁ”˜˜“i P$›²˜¸Ä;‡1'@Ì`æî¢Ä¹¸sëb-¢DædóbÀ3…f¥r€ ‰Ø„añ࠙ɪs%0OÕ©D ÊܤµâÎ\%G9yŠD `j`*3¸[¢çg+g7í÷ƒMã|Ydá 2Ã_â|¾^ãXo¶X‡HYâC´§UOа„,r(•€šòò;èS3€Ÿ WÁ›d · ¥DñôxåÑþQŽ”o,·œ’˜c;Å f!f¦–“$²:WHp‡ ±¹‚£ 9¤’™¸ºDžŠc*YLAêñ¤‘‹k%A¡>ÛCüÄ9¶]Ï'RvÀdÆÑ‚„™žVb<çꉠ4cKá OÎ<ßa±;¥¹3 1¤º“ýÿî‰LÉÁ”„™!>·‘ÄÙØ†Îì.[8…‚Þá.†ƒ4æI„‰‡fú¥ÔàŠ‘—œs³Žz¢ ˜<3Ó“t2èpAø%wXTÜ"¥eIžYDÛ4§¥C\Îm¯“ ÈB!¥°kGÀÎH\È$vƒîæO%ZbrÑdìbD’œ˜Ix®dg”DDÖ@±sråä Âjä"ìÂ?óa,»îôò|š&¯0cÒL Be‡‹9Í"îÔubRl2 ˆI”-á.äD‡ÁŒ™ºÖj¬;‡a¾‚ãKÄœ0oãEÈ……)ÅE˜-1{‚kf®¼²:!À!“„L)IòŠ4™º1ÕŒ¡’j):Lpã1ÔŠfˆ›Qâ–[!?=mNÍ‹g×/_¿\œ1‹U±‰<“¥Õ²u^4y±hȬëÎ.®¯'«eœÀ¥7g'«6V[4ëýpœRÓ$Ld$0ûã4ÖÑë°ï»$ÿúÿñãÏ©‘—×gÛ‡þ8õ']þþû,ãn¤í¾×Ä­ýf8úÃÐ{1g—E×”ÒõCè¨mÂ7/º›G«HY܉ SÕz}±úê‹/Þß<ÇÞ½4’ûºFÞm·Ë…÷ÓØ6 T1XÕjpÒR­2iÑ1qwv²Øî·™ùÕ›7ÛÝ0Œãj±z}º¼¹ÝÓ¡`ðê}¿Ëmn˜àc)õâ´‹³Oìòic/ž]|óõ«Û‡S{õúå±NÛÛ-Qi9¼¿Ý—£MeÝ2ÆÌ§ËE‹õéÕçßüõg_~}÷᧦Yå.x÷áèã—gíTõúÕ—ÓX®/Ûón ¶m­ÓT»íîØÿêÕ«óWÏ“œ|üp¿¼¼x±¼øt÷þ7ü¡K¹ŽþæÍ‹¯>{ûúõÉoÿøóûMqñ†a}õyV{óòúÅ‹go/ÏúöYj‡ãP»ÿø¿üËûw7]×>;¿xÿþãþþøñ·ûÍýæx<Ëë«ç]×IZ•)íGhÝVëh]³¸XEJÀ§:*8‰4NVÈÝò—o^V3f‘LŸ=s»ýpyz~ýâÙn»ÍÝš‹$I¨2媙)3Éùr1Uæt–2A™SÃ妚›™rk#«ž5sºº¸ºëµT›Ü BJ"¹Y-ÔšÛwe³ûø¸ßÀ´ºÃQ}êljÔÜtêu²ƒÚPª:¬’h™Že¬Õ`VÔÈQÙa¤ÑyQ‡MP·JæJ$æpa‰0…9 ϺV £ULœ6ÇXnìœPc˜x“Ò"‹G`GÉHKà; ÍA$£*pC¢"H5±‰J"Ée¸: 9ƒ&¶¨_‘€<3D‰EÉ,9 %7¤Èĺ« fFn!øÜ,yGQ1˜Œ äÕ0?'•ftÖüåv~p»¹y©Å+a&;«6îäÎîÆPŒ“VÕ§'a<šn!–PÄD¢¸ÎfP·3f¤êüxå8’?Á¼Ã…ØPö$qæ‹Z"³`~´GŽ~^‰9#\r†xòVóÓ‚Í£uÈêa…#"¢L* !2±s°Æ£X„ÜŸÖFD'a™×pÁù ¶*<|“ìfPÀæEa‚u¨4Ý:†¼ð(³lÀ“&:ø§,3E#Χ1y ™ÍTû§wGóÅÚfXU,™SŠq“¹áùî:cZAö8Íp|z‚Ëû ™©æîæV|æ08¹“Ì€˜hâ‡(NƒÂ+žÄŽÈí Û?oèbㇹ]þsLl.•£:/™ç4%v¢Ä‰3©ÁdH2säÇ!Ä€;¨ ?©Žœ H†ˆû‘‘Àâ5'agÊñÞ[u'cÒøê¡BfUb$ãf>,²‰;ñ˜3q¢L5¨Ó¨:¢âiW,Î Qª`Ÿàê¥Ô:qñÙšµR/ +ŠyUóÀéj2<­Ýˆ-3¥™ +I‰Ü‚™H˜„Ún¹Xœ×q3ÁªÃm†>¸Scd&$ãøç° ƒœœÝÝ¡X`!”’ddBÂB &%OÙˆ[VZuyy²~vuñü—ï¿úòìÇŸî“ã¤e4I˜¾P#µÒuˬRûqŸ¥Ö™L¥–4‘2›»=ëÚÊâ#””«ÔÜÔñ`Z˜ìÝíý8™mªÓÝöXÌÕo?|¼ÊÓæ8ÜÞL‡aX-;¶Ã4 „ê•;¶—ëî0 Õª’ÓÝ휨ME­[ä$Kµ2Tª  ¥Æ¼4í £{vþl·Û GHØÌ IDATýâõ/¾ý«›»OÄ´êZ’±L&Yk)‡!þp·ûý´8Y9··w›þ0ˆ,ŽÓ4Nƒ4k›J9Z)oNëvÄùù3Õ±L©ùìòz†ýáææîvÑ6Ÿ½~s{ÿq7ÝêåÕóÛÍÝ~?´ùÍ«ËRÆI5KúæùyCtº¾xöüÕíã O:&Ý?ŽåxÚ¼:==‡ñ"Õݾ\<ët´Ütóí/Ãt¾^þí—o7}ùxw¿j—w›ž[‡í´·Åù¢ºÝ}z÷áþöÓñ==KÝú8L¸ywûéSK®î/¯Þlöÿþö×»ÍÍ÷×'oßþêí‹õÍcÙ=ì3ñ?ýÕ/u®¯o®ÞÖã¶íÖÞv}+’¤I ³õÅå‡O?VåÓnåâ¦òx¶‡mÎ Á†ŠeêÔýT6»¢˜þéãOŸTüåe»»¿ë÷µNw›Ãá8xEßo+üx<üâ‹ëÉt½^nv77w¥¶Ä'p_,VjUÉ`“¦jéôä\Í(ãôìÕã¾ÏIš¶q¸Ö2 ;U5Ô±ð8y~üùÃa<ŒÇnì¤f‹L r"¦·¯~y8ÔÅýè˜ÎºVÝwcvUFv[(#w­äœ;˜ø˜G‡§N ùär‚ØTYMӶͪi$£zb2ñ‰,U¨A“£Ù”—"õ”•O9L†òéVo÷u<*ঠ­6ðh0³jšë0õ]YÝÔl{"W 3&W8su¨P²‡¬–$‘»²É“ÝÉc÷Àjð‡SWê,¡…{|Qg»šCÖF féˆ:ƒ“Gÿ_È™‹ÅSB0MFš˜YHŒÖ8³‰›«¡0§'ùm–\¢ëî¬\I.¤jRÈL ÕœÜ\` ³™»&af„9)™»T¸¡FÅi~ÒFÄîÄ¢ó nq#"Ç_ýè D³ÐÄ¡˜x2UŠ*",ÒPæ6à cë ±Ö={`bJeb'r‰ÄDU-“K¸ÏˆÂ¼­D!/±âÉ!ȉ¾ Øï³nR˜ƒõGŸ¿`9=ÜÎ"á …¸{à™"䨳›1`šÒvRT˜j ‹BÙ¡ #&S€m†›«…[š0&y¹ ª‘»Ï«&"â°í†}šœðärw÷¯›Y~2Fh’Íf½ Çàð† C@"í‚„Çðd¬îO¤9LÌÈæ;/QpÜá–PÌÁnr²èD-pÁ‘gu€ÀAI{ž½>ÓØÀN0›gÛßy!9+•$ÈxZòÐÌ:o ²+X‚d<· ÏC Êhæ©Næ\™çO-&Bèo.îFÈîN,žF3 a6¡ ‹L‘|¯'g0Ùì-7‚'BŽD?’21'n:q&wVâ–i$Ò¶]¯žµë³eóüñ~¾Ûïv‡£êˆE»VIF:%^.9‹Ú4MD¦´]>]­Dȧjî¸ -k¢ k›eöM$ìVÍu˜ªN{¶:;š›J—º~:ÞlÇÍ^Ça*G?;ë¼è~Ø,º65|,´ÇR……!ÕʹѼ\k57mò¢k3¦QÇ~´i»›´ªj­ìwjŽ6énœ~úñÓ0”Ï^^üݯ¾R†áÈUØíX0UÝÆe³xyõâÇŸcmaZŽ}ß/ÖÝéÇIOëÍþþ؇:¾ß<öÓñaÈ©;Y¬·Çr ‰6Û»õÉ’óéTôñqÛ÷e½:]/d::Ž'9ýõg‡¾>»ºêÇq×÷§'úÓ¯ûí}C\j¿hVß~û7˜†ûbW¯¾lÚ“‡›?¶7Cåæ«çgºX=<î^¾¸´‚ÿú»—]îùð¸/ž‡ž¾z»ü«¯¿{3UúñüzQÇšj­n¥úÃæq)Ó¾?ì7LJþXÊ ž›”/×åw?½{¼ß\žœÜÝl~úø~¢¼lÏ_ýêeé³Wo~øéGÉôOÿüÏÏ?»þùO?Óððîö±møõåÉjuݬw7»:©¹®²úñ0zQkú¢êYm?BÚ¥ú¾§ZGÕ<¨˜ãüd½ìVCÁ_¾ùðáýíýýf·ÝöÇa(…Ça_ý8ZéÛ”A’“Ê8hq"´"Ûý~ÒÚ4Mלž]œ¡r?Æa V¸Oª˜ŠK5À´¤¬Oã…ÔZê¤æ¾ÙoYHV6Ë9©T›”¬Þ4"µ*¥&L2ΜÄÚä€>;¹ÜB“$E6âÌn%;%j|Õtg«Tt,¤ -ÏV'‡ÂLV²$n›Éë‚DÊh9Yæ:ª¶Ãn¿Ùm÷½öÉ’U/FâæŽ¸º[)äEÍ«¹‘š‘›©Ô‹;y—²y5QfÉP±0Ø›5`bÔ W×¹Sµ&rfõ Kš´²Û\¢šÆBf°¹³1CÀBI JÎp‰ËG_˜PMÆë‚1ÜÝØÅ9DÃNìÅ e3çÐÃqR,=ÌÈÙá"¡ „žØÆ*‘úrc7¦q.F ¯ä5®…|ª±÷ày„AÝÝ ÎlT‡EÒÂÐffÕ­ÏÄ*)¨ÂÍãJÞcר•Íÿ“ð*;€4· Ù]£@`JùRLYLΔrܽœ|ÅÂän™zv t%t„™Á@bÊñR#eNbæ6¯AB&ŸE2%އ!…þÚhÞx?AíEnqQ 'ŽE6ÜA0ñ™šåf.^c"×ïh¥kψgs&!’ܘ1é œçO™jˆ«DIÄã†ÆK¦ãC(<*ôïhz'â4×.Á$$d¿Í÷¼`uÝÍ]œÙf´))“²Á@ ŠïŸ"w‚P a™xþu÷™ðIsÅ0c[>‘³\É»F·@á’Ãh&»ÿBä!¬7; q„gÆ'J;Ám'A<¸ç @ÅÝ4ÔÒ¢DB.™án•Ùag3„T:“F.2 EU4°º,$&ìNç¹¥ÈMbN s¯^È:wR°§™Ïë®ÙÍH•àfV æJdlœ 1(©1±™4mK$Ã8ºƒ=FbÄ,hX2˜({h©‘L˜ Âpgeê@¨æµƒѾÇR)ˆî`R"O3“â´ë›óxÍ 9³GÝUE“K&°ÈrÏ/›ý@£$S$s’$ÊÔ€ÏÎ^&N>Þ|xw³ÛnŽ¥ìw}Ó¬¾úöÛ¾ïÕ¥ãÔ¶K5U§ZLժ疵šÀ¹6C­êÜdKd,)qbÏÔœ¬^¿º˜¶:ù˜½52ðò´Mì˜Â‘ªÜ,_ìwûÃhªVi†ýXz=¿¸0›Ôkõ”£•’È*1ñzýì?ü÷ÿúáýe*]êJ-Ó8Ù~,Ç~R3bŸj=öÊt4ÔãTê¤EÇ6¡mºiDÆi˜>~¹l‡ƒÖ#›-Í8Nûa_¦²hJ—šfé×Ï/ÝtwØnöGS¥: ü¤kVM^,.nöŠÑÊ`zX4Ë7×Ï÷ýæöÓ‡~Ã,l(Fë“Õ2Knäíg/ÿüq£Nß½ún×oûB¯_¬Ÿ-N®ÖíÍÇÛï__ååU³º¸|ó‹U³øŸþó¿üî׿~÷þc›O.žYŒÄŸ~zßtRlEˆ³ vDz`íKy6-/†Aw‡ã×o^6ü‡¯^•ÞÝÜl¶û³Ëgç‹Å¿}÷êv§÷Ûéìül»Öëó“Õr<ÜÿöçOÕó«3~<ŒgëÏ¿xûíŸî:ÎãÈ¿ÿð{÷òíÛ«Þº»wøùÝã~ÿââªË§ãä»Ýv¿»ïûžó"¾}ûVÚöîî!Qsyv~vúYK™ËÅßþÝ?úøþârùâÕçÛÝáÙùºú”óÉÅÙy“ä¸Ûßm‘Rw‡¡‡W3v«‹ÕšI6»û}?ŒeŸXú:‘Öaªã¤»Ýc­åâ¬ûæË×w7§-|†2•$Îæ®¥¨³“›¢š*à)‰¹ÕIM ;(Ñ¢[°s>;_{‹ëå)Ÿ7"©)f†'‚%O Ëâ È¹ï ™çÆU4°$neô¡ŸŠB¼óÔ5êMö>#› Q¨u Ô|*0ƒc¬cEmxR…)U+ê6QW1ÕêS11›PQÉ&˜W'2LŽ ¶@-“ÁjžØd¹<)e,VÉ$Ž*ld2A]âÏ›!6]áÛ‹ÄçLœ© Ì™(Ö$ÄMÔ’#9¤âîl®G¹Á¢gfL‘”°VòÊmv(’ÈI]d씥!æÌ® V˜g‘êÌVR.€»Á8V#‡TNŽpÔ1È8Í©¸‘+ù×-c2w ˜§oÛjä ÜB†Â•Ãoc ÃLŒò'õMô \AqRT‚Ãg.D@ȃøà(E l.¯óL}’h¤Å¬Å Xç«3Í“ª4wú5ò×±ŠÜw”òiŽqŒ`»*bˆˆ¡"&gsŠÚ~¸m‘æ¹ÁgÓh»¹“*Ì-Î3fTWÀe†_9™“¹Q ]P¬æ Ä®ˆËiˆO¤YœÆÄ™ZÍ${’yŸš`RöÊ<÷ˆ]€9*5C2 5“qÄ÷o’ N„4kkŒ ;BöÇÏ3ŽÌ…ÌÍU(ÈG4o DîD‰]ã˜< r€Ý=qÄš$æ¾4ï6É]ÌUg “ÇâFÝ(^:“„FI9Y„¸³³Å¦l³…š!Ž m¤4§4C¹8@ö!‰/,BÂÂ,YÍ);„¢À@Ljà{Pàçj"Vv1@$áÆb2×ø¥!di²QV&Ê ÎÙ‚Ì70*œ Þ„¹œ,º¤jl^‹šƒX]a¬ìTgSbü"UX` 2æ–HêX€†]‰e¦Ã1ƒ…æ‹5I2…˜Œ ,D’#~7¯ÿB+ *0c@©jÎE$iïˆ<“S8Œ©Í,$ ¹fIv(e¨$ád¨,€RË|ùü›aØû46ývÓ÷®›~¨ƒ™yßïë`Ä)ÉT‰^…ÄŒ´É(Ð4•:h­njr¶–‹¶A†j&i²$–Íù±­Ó¨.dn…¼a‡GUh)•ÊdZÄÓdæ:umî2Þ¼yy‡~¦Ú¥Dy¨Ü.Ú×—/†Ò#2ß>¼GAU¦RÔMËYª&¸»'sžË¬2(§|~zöö´yè{ÊtœêaÜ­Ÿ¿‰>íUK—IdêǦI*3^\­<µ××Ïw‡‡ÍÑ'sxCgúúëïß>;]¤ÓªÓn·†édµø‡o¿úþæK©¢Ã¾ß‡áH™èÕ‹«$§}ݱ`ìíPëWo¿é~úðî—_¼ù·ÿü¿þüÅ@—÷wÇã_÷W¿ýÓ›ÛûÇOýfÿÁ¼äÅY×ðõ²M)%ðÿü¯ÿ°y÷S©‰³sZlÇþôäìñ°O‘á8èÉå‹®YÈéõà ë¾ß«ýòË¿½:;9=}ùæ_üøçÛÏõ«“ÓÕãíÝ»ÛÛRJ³X|÷ú¯~ÿáƒc|ùüÛHyõñþç¼9mœ>Üoï?¦a, ýnûb{Ô?ÿð“ÕéÕõe5ÿtÿ0 u*0£ýæf½TÒÝÍf·ŸvnµTI){™ÜÄ…–§—ã°a.Vi*Ç~èG+SõzlØV _-O6ã~»yÜ æ:•R]3Ñ¢9-r U"KRÕáМ»E›Î:/Æ]×pj§2Zb"wφÜOaçòbõ|}Ö5æŠÔ0î¦`J]jWvDUK2"o¥åÒJ®ª>BÎ07‡ãì¤Ñ©LP7e%Qƒ!‘[Ô šZUS5¨j!b7s‡™B©ÖJkoXXx,•L‘–˼àZ»¦æAu,£«;¹–‚☴¸“WÎS n’4pÖF,N’O(©U‹s1Q-ˆÅÆsxÆd¥–‰©yŠbØŒ'"* í &SׄPÐ1D®¤6Wò“EÓ›2%ì95Äb ¡äÕaŒ ò$ì^I@C&3÷Ö^s d#›:̵ĵÎ…¡¬ajŽË‘¹»º™[urà^I•çÂX湆'B[^£m‹ ŠƒÞî˜0·2C“-ĆPöxr  1yaþ2™=â?¯ÝsÜû§l4ž"=_Kâ6ãb¤þ”Åa!wƒÄýOØg\QT!àùEÇnÉÝÙf5q’s‚‹f!«£jOHUgŠ#[…»Åì#IîNÿR•¤§…ÙÁB,õ"{Ælpœ#e)ø®Ì.€Hãsš±RÐÌðM6,Ruø„(0 ²Ïˆw ’*ÜX݉˜Òü®Ì™Â`d‡[åè,FP¡1ÑÌJ"Ì]€XR|/  9 œæ}!<Ѭ›Gš7ÃÄÄ‚ÂNΤK'ŸËlÃ-åY9>Y,H@ÇÐÂz=Sß’.Dæ¤î®Òm‹ü›ÖÅç<ÛLÒ'&xvÐ$&)$–1,ÂÙS¡É8‘âÎ)‹ÔĦFcÿøJæyOÇ‹E*Éæï!"ÄÆÊbq† •Tݽj©¥"VLHU‚úì`ñäGö—3ÂIHP5['—š‚Å/e˜?‘¢£"ѱ“®kE.._ŒÃd^•‹:%´àÌÒ%(KÃ칪¹hU„ÄØ2²JÚ÷‡ýaÜíúb4ŒÓc¿Ñ‰¸f媆ÖÜ2™k£ kÇ9‡ Œ¦$P!Ê9­/Ö§g]ßOËܶÜqj”Ú£Ÿ* ûJ™«—R&kÕêÙW‹ ò5<õÃWßüj»S£éúùJ(øt»:¹¸<[ŽV‹ý¤k®ž]_?{Þï{S«:í› gi•ઞaVËä‘®9[µceIžsw¾î.N–Õùn[®Ã0ív‡±œez@IçܵÔ5ž8 ûdu¿ÛßÞïË T‡‘—ëîrÞß,–/˜»ÃáaÑt¥U^®Î—¿ûóLJÝÃQoïöý8²t‹ó“õO7?î}F-g‹ÓÏ¿øê÷þýq_¾øüã¯ÿÛþñáÓ‘ßonÍÇù?ÿÓoþëûéÇŸGÕ¿ùÇ»»»í'þæëïÿã?üËÝö£&ùãŸúùÇ ùæaw<.¥”~œÎÖ ±äÔ¬.%µw›‡·úíör)·ûãºí¾¼>¹ÿæ§?üß¿ÿÉÝ¿œÆ¡]ÈþpüæÍÕñ±v~èšn7Bãgk9P;Õû÷·?ñˆÏž_ó‹_|º}LZÆb‹E{³ý´Ùî^_¿úãûƒž¾U(yßï?Þxªè‡Çýnô„U³¸X¯?ÜìÈŠ$¹{¸ƒK×êtÈýazÜmvýæÓýá8Usw,..®‡á8òùYÛÓÔï/r†ÍJš¶?î¬Tƒ cÑ–ÅbÙvóð¨ª)¯Æ¡?–"bôä)'æÉKŽûL‚ä–ÊT…sJíq“t9cßjÊNL4Ò4m%‚[5:Y/^?kKñÁ™†©màb“’åÙ'/¹±ÆSjIZ œï¨íI»;ÚT•-¹)K~ýâôp¬•n4Ø©±j5UõTÉŒ&c/–Íj5…š©U«£{UjQ€J¥Z>íH¸ôÇ¡Kn™j9Ô¢`…‘¨Ã¸Ö©š£81»»À-)„b¹C¬Awiϯ^ ‡½Uò$V˜½RpºÜªÀAØ L 5ÀŒšá ÂÄdÖ8Á“gReÓðôάk,GBMHQ['Q'&XbvæDÐF"v¦,„ÆEؘ\*…Y=šê°ð†`#Í$øbœØœ!æwÑxúT#v(Á¬†Y™]"z^Ì\ÕH]ÃÂb¡ç©à‡P„]ã4÷§¬·åŸÇ…‰9>4"&’HU¹H‰‰)Í6JÂô¤3‰N¼SÐD=…L æÄìLQ¶3V²<å}½à O¯ (©Í¦<'2ç9nDnp¢@6‰z\H"’ãnlq0…Ã\áBHN\}¦[1Yuu"÷ g÷sbéÉS˜Ûa:ûá$m^=Yüx¾ 8ÌK§XR˜Ý=¸!Êc˜ØLü 4%È“‘1'℉ œÙ1'Ùv®»6‰+¡2¹ º„ g63Å<ø\A3ƒ˜Åsd}ˆŒ\˜•˜ƒ2ï¼FJ>ͱ/ z;¨2§˜4α+™Ó÷!ÿWbVRÒ@ô‚‰¡MP®â×*¨,quÔ°¨ƒMŸ¨Wp1r56”Ükü€ð]… ælžˆH’>*3ŒH¸IB¤bdä"’:8ˆ=ÞCÃfl, ÷\%n“ ,&¬ÉJf5(:þT÷ bpaˆTR'ãê¤B³\QntœŒ5äÑÌ91qÎLYŒ®m1!ÎâÜHÎL)ÅIœŒî)êMAé©XªÂÍœÂT2´’̹©Y¥Ä”™)â $ââY0œ˜“ R`J=²%&Všß<¬N/K¦?üñ7÷··}94íúÙ›/¾~ýúd¹z¶²íáÑ”¯×§¿úüâöp¤¦yq}öúüüu'ýôøßýêÅÝþÉËŸïJ)·7}mò‹ew° Ó‡ÛmÓuÜ®×Ëv7þt3¥fµ±´zëv3tmã‚ãPî¶»ƒ™*gÇÍãÕ‹çj©î.ON.×Ë?þôþpÜ “:N‡R3ç·×«WWoÞß¼g¡v±Úìñ"çφI:’¤a*¥ö^)§–Мu«¡Žýá”qÔ“Ó“i´‘lÑ­†~­ì0ëdÔv µ`i†iPó¢nN¥XÊiÕ6pó:ÚÆ<R&K\ˆP]بª·¹]üõ_ÿ·÷Fh‰ )eKÌý8íú"´q85Â^ –Þ,³¤%éâôò‹ëçµ.æÅ MšÄ’ª–*U’ÖIÏ×Ë"ttìÈäS2±êåÖ“Û$“bRµ 56âѬT@¡6©©­#©> UÇÁ«Jö“ÜôÓ^§RË4NÇ:rÓ‹×T]9‹™eA%¸›G`2£™3xe áŽUÝD)÷î…‘…“+ WäœH+XÒdll.5%W³l³Ó.rË$.pc¡Ä,ñ,Urq„D]Œ˜)K¶@ O‰­®Â¡ æ¤H*n!½° ƒÍ@.­D«óûÓ+=®A³7··ã4ÿÍŸýöÛ‘]×o¯oõÖ-…”ÂÈrþòë×ï?}ئèEÓm—ûĹ Y‡,)Ó ÈÕB74û~È›íÅ›çÏÿÏåß>ÌsM?ÔeR&,c„$K¡ð¨H„éÍU?{Y }Ôn“´X ºÏ°ZC/ʨáÅ¥Ö:… 5› 1ÏŽêD ˆ¦r*ã(Á¬1‡ Ä:†No®/K ÇœûMÔÒÌ´í†aQ¸ƒÆËËguñðÌk&¡ :R¬ž¾LPÔBc¥,>‘°è_G@ÖvV­x Á;ÑrŠ&Ó–.J¬Mª€I×òÃ)(’tíÚ“‚ѪU% Åuk•Ò²í AUQ·pÒ„ÎÂRéÀ["¹}”†HHÛË…×vV ¡(âíp´ž«Ð":*+±PZs­ê·KFÒ$bMÞ²RÖä”Z˜¦fPS6®‘­³‘ˆ®¼(]gèØrñÍ2×ôu–«›ðiLaóú(´©ù &aXceáNÌ]¹M š4æ*`Ín"mƒ…v CP³€ÐÖûC(¸bžòW 9*ÊhTö§±k-K¶¸6T,÷i)²v¤J‚hƒŸ›FCÔZS¥iÛÊT”HM$I´íHs Ûªâ#C¾]i­™Öâl¢a C¿RÐ.I ((\%: Uó$À[N¨ PëÛ=W½P„m®j ©Æµo/rƒj(ªá«£›BÀ¹ZÛùSþ]‚ÑJ‘ŒÆ:€ÚÓ¯Ú¢ú-DTÓ ób8¢ôȰ6épšD¥Š®?^óm?tÖG-€P“EûàL!Ize]P%ÐŒj¦–ÜÜ„Pz:Fo45HN”*a©Š$jÒä¬m£¨HxËú§h?°(`@+-‚`Ë8Jj\Ô,¡¢û~Û¥T dPUµ$ ÚAW]6UFY–XCµ‘Y`^ÖÊÒmùIB/KÔÚ&vd@Ì& 퇮ë7JJrí’m’¡IвY/Iªªyg1¨ 1K6$µë“ÁJ—’JXWF"œlÙ…–Ì I™T‚±Ì,±³@úMºÂ†N¹¾~“v½, ØÓ‚R9žçe.–,ªKT}?ä$WÝt}–óù诖šÂ«ø2ÕÅ42é®KõóTNuY|Ù]Ýø|®ËL,ÇãñþqLJ"B¼8®/;aS—¥z¶Û¤û~è¤Û”:Î@—Ó8ŸOR–‡âãé,¢ ˜nú˾ÛG`³Ù3æßÞ¾›æS©Åc)Ká´œ§¢Ò«ˆ&=/³‰ìv›Ý~{:Œã(¥ö°‹ýå¼L_¿zÖ'Á¼àwß~uK¸¦ÃXÇaú|8¯¯/¾|}ó8ÎÓ¼˜ y“?ßOýþ:o_|ÿÃÃb\C·¿z~>¥úíæâ/ïÿåÅÅõ—ß|ýOùÍ ]¾|ùå—uÁ¦Û~<õ¬»{ÿy^DZ¾~ñL»Ë¥ú·¯oöÛëý®ûéýÇýù|¹Ý½¼ÚÚfsq{ûøé|a]uŒ³æÝnóìOÿöó?ÿãOž½þî‡ç/ï©ÖõÝë/~tÈXçñ<ÇÃR ^²ÅúùXïÏÓãy,û>»#J\ì¯kIÖ]üúáÓyœ¶Ì“ŸæÊ(Q˜2Ja©e“’S¯._Š`œŠZk}÷ù4MêÓR_?¿©Õ¨±LãÝ8ÎîѧÁ’Ì¥•”eq/¥Ì z 8Ncg°CŸÞ<ˆI‚ÍŒdPÓa³z;OË<#wQ›ÔJM“=>>–Zª;‘sî4‡cp•®×Þ.‰T$<™¦®—Î:ë{W¡äKRÿûÇåÓ‡3R¼ÕÍj¥ˆfñdŠm`ª~<‹¸1S·¤¨Ø¶½óR gqs2fŸ½ÖªªîÐR ŽË’;Kù\éô`f –q^4@.5jÁÜžŠÔÖ“V]–©»æÛ¼a­Ÿ ¡¥~¯jÚŸk «Ñ¬L¥ÝÇ´mEš™°6K6îyS´è™$­-«Þ‚cÚrÒºŽ¨Í®²Æýµ}=Bmwî‚&•„D´QSÑ€mˆÕÝVb’áLT¼%ÅÝÚò2,PŒuÞÍ\á--'!« ¨Bdo’É $´=™   Â@—Z@‘ bFB*³ ˜"ÚUw…Þ*ÖmÚ‡µZ†€tcmca£¢< óJ JpÆW ƒ]oú¤âbîXdq÷$ƒæ¬D2é#©xm)¶2aUIÊÐ"Â4)4Ä¥=_ ™ ÝM«ˆ¡Ù²Cj$·j ¸0‹¢6”.…l¼~C§bª‰¦¤@Å šÚ«ÒF[—.’²zÔ2ÿ;§¯Œ ŠŠ‡€Tq†¤Õ—Cq§»I5"0¦€©Ø¶ësF±,NĬ3ŒiÛíºý…Ï.ì˃¦’ÕÎRJ"É’D5uF89‡¢À04>›åÈY;дSs0WÕ,A攟Ý^ÔÂ1á¶Ë©@ €åt*ÓV’h:•E&u„QÒ»AJD©2Í£/Ëaá\Œ²)uê²î÷I¶Ú%Žh7@*<÷)çóH©¥gÉ9z[ ˜F—âpDÊJäíþ•Ð,Êù4§}ßw›-`s9©éï¾ù.æcîúÛë7o¾üò<ÍóužºNß\§‡O'ÏoŸÏ'óR t DóÆŒ´¤_Ý^ÏÓ æ’U^^\ˆâXC\lö‹æ‡Ó§Ý°{8ã8/Ë)¦1ånÓ_IÚxuuûùÝçÇñ¸Œr~<Ï®/ÿôí·_ݾúÿÛ?½ýø ývÀ<å:u>JZ–IÞ¼úz®üé—¿¢úÝé°öQŽeºßÞx¼?M††v»ªéÇïÿf×ïo¾{õéÝãá±ôÃv›zé·}÷öÝýÇ›ývªõ¿üÛ¿ŠØçOoŸ?{ùùã'_j¶ÓýcWÛÝãÝ]—7Ùb<•³OÓRË\t¸üñ?îež]r².IV;/…ÚÅbÇùØå¤ô/_í«ð~ò›ÍÅŸ¿þŽñ°TÎS¥Õ}õüÅ÷¿?îÏçÇ¡ï7›nœËfƒíf{<žãù¼”Z¦q>Gñm—×á‘Üç§¿¼H¹ë,!0 ‚a+ËÄÃé±h$Ö%\‘L]•µNuYiÈý0ØâÑ«Q"QµO› mûí·ßþîþñT%J•pn»a C‘Ô)•›N“n&¦y™çÓœlp3dÛ *)¼:‚ŒWÏ6–ó´,î]ÚZêº7©s‹Oc ™bž¼ºFT .e,A/ ”bÊÍ~[ç ,eòiK ¢zË\eBTJQFËh 4±uµî§É¡‹—Z½ÂÚÃ’JW!¨Ku§åüæÍËó4GqA´ûhP:"RWc®YO¨ž\ ÚvTZZ|¬”*£…NŸ@N‰EÀ*D5Bh0±Hkimœ˜&P˜U‹J8[ë^ô\53j !º¸„9…bÎ^còRk, €P'QÁEZÓ‰tÑÆ8¤xZ$ÚõsY§,ÍÓ¢›¯í÷Ø,f|ºŽñápZêR¾Ò¹“ˆªtÖJNÉ ­,§cƒêºkB È·›—¢é‰S›ÑÔÚ¹©H!+ºRš¥]#Û™J !´ ¢€TL¼!„lÝ9ÁÖ“ÂZ(ŒPºPЏ€¨2°ŽRML&…²ynC©lå²RjÛÑXéõ²ÑŸâò´n¸ª@…Mm •¼ö’V:TIˆ¶p›Ÿ”­ˆFÁU…#H¥ú Ç5A¥¡R$¶o´íÕOHiED•mçi$B³2ÈôIoòWÌUˆ·ÆÀ¯‹P·FjPTµõ%ƒµ ܼ9…ÑK-ôßx"Â\A–'¿øJ,…ˆ)Š™(]V};‰-pÂ…baÒ´£\§Jm/Xã „ª‚:Ã#‹Mîn’ͬÍo SËj’BSꆆËHZ“µð›ˆŠ›Y0€HIC•¡*P-kc„BÄj‹ö A"¬´ k1I´Ê¶F4Ø×“ŸS°¦úÕIªBÛ¬GU ÑÔä:O?¹öabÞÈ_&^ g®R-¼1ÈÊöâÔ4Ášù8T™UA'tË ’–’$$$µ,)ùìTK 4$Ø£‡Ø l<%I*ªI-µP˜u ¡W(rëq›Kä,ª&ÎPƒ±-ºÅéÕ<ªHY¢¸Œ¡]TJLâ0wëé½Ì¡½ô* ‹IÝn/½Æá8–©JÊ6l„zyØ-¨B«^cœk0UâbØEh6‹*ÕUv]ýâz3ͳ%OjAËÉ7Ù©dbp)KÄ|8ŸÇZÇíf÷üæEgúâù³ñôë²ÌoöH›ç·××ÏJ©_ÜîýŒ÷÷Ÿñù¡Nó2bª‘¯¶ÝL.Å—Iº2Í4\ôÛ¾þnª]Ìœæ._½¼ý|\îgÄØeëØwe«»Eäúêâùþæaœÿîïþ#œß~ýå¼L×—·WÛáåë¯?>¼ÛÚö&É0ìuùøþ8žîO¦<§Ãr"¿Þúÿðí ùýižïË_>ÿz<Ÿ¾yùÕ¯.ïçs™MYòîrÊ;Ù o®‡Ï‡¯^~wyq™—¢û§k”ÛW_ßßÝ}ÿÅË2ëÏŸ> ×ÛßÞÝ'g‡ÿõ?|úôøx’}O'–²d­Ó2Ÿ¦r*eÓÁ¬¿zùFtвMƒÍ§»ñá}]æcÔ«íöÕÕ³.-CŠluY¼,Ëß~µyýêåçÃt³¿8Œ¥ÞŸOïIí¶û¿ùâ§÷çññÓÇ_çsqñ4ô?|û§»ãýÕåöþñXáóìµ.I©Ñ.*B«”dQ*r·±×Ïn¶)Îsé‡>Y\÷Ý^ô§ÑÒ$Q4j ½ì÷Û”vä’ºîbSŠ—ÈÕD’N³†•Ôî¥æQE][ågš SN–MÄe]Es¶Ëg7I†ˆ)hêΈÔKš-NK­ã<‹[tÛKEE)H’4eÑÙ u*¥Ä\k)ˆpz]²A;•Xà¨<ÖŠâ Ô)|v88ƒ¾&!Ü©ªè6î…¾R.)„Â$6i/0 Lèh-pæVF(å´œ”•„³²C;ujxD¢0”°ˆ"”UÐ4QÂ*ÔÜád…6xANíœdêu‘0hZ¡Û oŸºSÊ¢¡Xoe´¶l0õ,â"RÙ*H5„”%„Áh¨hà{×@ .¨•kwœ•N¢ˆðLbÔömrRàÙµì2»Î6Cw®u<ÎÓã(›Ýå¯÷e”¨ÇÑ÷FB’κa(]úœ^¿º-ãÝq<¯Õ‰ «°Î2tdz—¹^\êÕn§i\&šç0ì·»lR^^îÂ¥ÐKijK©µîú\ÃÏ“Çy™º:¾¿?–e™–Zn·C©KõÃÛï>Ü/GŸËiZÈy®Åðç¯^ß}¼;Ôê…•eØôϯžo‡>8/Sé7ÝæöÃç‡e<,)å77·÷ã¹ÔÉó|šçÝþú9ýç‡ã‚²Íù?üñö··ÇäÑ¥ê‹ãüùÃû7/_ýôöçmÚþøûï·¾Ï÷hQT"énئ«nøæ›ßÿóoïþå—Ïgú¸üúîçÓüy7Üþî«/^ûæþnÙlsçËa¬þ?ýø?ü?ÿø_ïO;‹›ÝÍþ/ÿí—¿¼Å|ÒÍ0Ö¸ØÞ¤Ü=»(§ˆË¾}ùêå˘—ùôé<­_>ßq}±¿½ÜMS=-¶WÓ8÷}Ù¥e©Ÿ_~ùçÏwÕÄ¡§ã¨fsEßõ§yœžÆE*è†Ëwwõ<ãt<1¥±œ‹OÓ”úaû—w?Ý}„ÖíÆº nóÞR¦VNeœ§âÒínË|:ùÓ¯>ŽîPØÍ^Ç)Í%‹,Û^“ÁL.¶ýÝÃx*Ëf›ÅË´L÷‡)÷FZáLx­ˆ$‘âèT’æqÑZXjëÐ'ƒGAV"å´Q.:¹WGRíb)M· lL ygpí{ AÚFhh2åý°±,Âí~cÕsêÎiºM SË<™+41Eábê^§˜JŒªÁ²,$–"ZG)Óñ$‰Òáååþáþ\K ?ÑÁ(ÎbH ÖÚžkðJ•ê^Û¶ƒÒþ¢)zíÞ|ù])“/E²¸H%²vŒ•ª×k³¨VD›‹˜T&ÿðfÿñ(î-¬pkj‚áí$Ñž²+ß,S$kuiBD3f£„h¨2£$ZUal8H!ACrÅCBtѧÿf5jvSØÌJ$”% ›ÉeŽp7[sqTEXÀQ)á¬âí Û†©F;²#¢Ùž£]÷°›\ZT³RêJg5ƒIK_¬Õ›èMÕšzõ﵂¥@}ålŠ5u‘†ˆHÂJXb@Û¸Bg*$¢¡cÁVÙl^¼ö”mœ¢Q š’†Ñh.,B6Ù ÁÚ4'vQTÇ“µX—€ƒ 4ÌÅÉ è ÒE[ÔF…ÒŽ¯í½i)]¬1Ь ­âš»M¦üwÑ2ŒÑ¥Í ]Kxm‡'¢BÊS‚¬]éD–ØVeí_Ùàím¯¶"2žºÔvÌm÷4±'rkãβ雉uú¥QšbFFrŠ‚.Ð6ô5´‚Š>Õ5‘Z&-dó¹aÅ©µ•Šá«ÁqÔ¬Ç]H5$6MÖw_kX„K3PTJ˜›Dr¶ï3@]‘€ m•#º´×]´J›â%iÌ­ï‹Ô…ºõIMD:IiåÆª`©À-4)" LÚ¶H.YÀª)ÔH¡lï£U²ë§ Qºz¶í°ÝÏó$„¸’!æMf¹údiµ¨Š%U$Ó`R¥¶ßuhVër¦{…p†ÉªUÁ6S¶¯˜Ð)& Ú“ZËÚ¾X³ŠšR©É’š¸Ì`0U3©P×Ä`HWÚ©`H—ûkpÉ SR·Ý²a©•áPÝhTô9u¢dVË)‰˜K5i0gçv;\ïÕ¶øý÷xÿ›ï¯Œ§»â`P¶]2èT'‘JH·Í¡Ò©S²§ÔͪÁ)$ž]_ŽÓñùõõãiôB§öâKˆ²t)wƒ]_^l¶Ûét .ÄXHQ."ö¿ýþKë7oïN¦²ï{›éßüø猨᜖ñp¡pÁ4§Ùã°³z§ÇslúDgÏ]ËM»Z:¦Ó|Ka c~u½ƒÇÍÕ…êp<†<œ‡Óü˜v‚(Çi™ã\—Ó›mz8ׯ®ó§ÈÖÙËÛ營ïîËœÞ}|üxx_ëìS§û‡ûó¡ì†M„n:ÏÃn‰ôëûÇãtqqùüÊÎËi§Ï=ÿøñ˜SúŸÿþ¿¾évYö—/û-ùùÝx:ÇûÏŸ~úøáîð¡œ?ÎÓòÍíõ  ,?ýö8ûñùîÙyäãœ÷ׯiÃç»w“ûó­Mó8.ž‡ü/¿}:Ÿ§B £×ÊþúÝ7·¯Á¥êy~8ÍÞç|ø|8Ïç‡Ãaù$¥ö ÷ãÃã蹿zñúÏoß}ø|ÿإ݋Û=dw®îËé<žÎã©Äxsu)wwã8ÏKí²î÷éúÓÿøíw¿|x·,‡sÅ4bÓm*ãôx‹7Ï.Æe9Ÿæ¾DðñSì®o ¥,#êaU T¬¨š¨‰À´ñZ±¿¬…±R)tI „ÙF' Âöª´îžPˆvšjvj4šØ:‹”FUZ#/ „Q\ŠH;Ÿ®¬Š–"jnB…± Pè¨îð€8Z1ì é¡ívÙnCM‡£€Š¥¼o3g›¸ÅL€&4tæzØ"TS#]­³u u4$DÙ4ϺÚd¤©*hðϧ8ZOm•ò4ò»QÌ`¶à  ‡tho¨, ¼AôEaâ’ ­~'ÞÒü͆ ¨¶ºoŒí< CÛ$J..A R}z¥iƒ²ž#¦¶šDׂðŠ©RH°K6tˆJb“†<xV\áÄ â£ÊJ"!NP•,-¦Ž b+©•)Úø NeD2À‘:U*áÚ´ Š õŒ*w¯ô¥]*QuÙ…ËRË<}`€(˜D<å|quã¬ÁªbBˆ©…±a S¶Œ`¨™ ™ÈD¨¯K>J»î·ÕpkQ«)=lý…mu¶ð˜ªXÐ¥œ“U Fˆ¦ÕB­Òª!U’¨Zu±j5I¢HŠ(3æ:+SÕ>gš&Ñôõ78}~¬uq!“©fïtƒN¥Ó$&TS3éúš#séÙŒ”Ð2¥óRî?èq®ÙS§HûŽSˆ/ÔIëòâÕ«>÷Åýz1À.Ÿ]Þn_Z·ëD2–q¾;ÞRæR-s›ýo^lÎRk?˜&9|*çÜk­š2sÊÎŃ+ïOÓyª¹öÄDÅù8ÿå+Ëùú¢‹XDÂâäS}wñbš–çÛËý0Ž%å¼é4Ê´D\_ìÿôç?ÔÓ]ê·"ê1)¼xUÓ÷ÇZpu±Y–Çd²ßç>Étªçù<•r:žîŽK¿ÝLÕ|œ-_}ûíw‡qú|<™&íS2ÞlÒßü0‡>>Ž.^4Ûv³»Ø×óñÓÝq†Ýö‹/¿Þt|óúâª[þúéX ,ñr³¿ÜÜ|ûåWÿõ§Ÿ£ÿúËÛ½ùÿñÿðýßÿùýñÎ#ºÜ¿ùêwŸß¾zÕï¶ïî§çÏ_÷Ñ_Ü\}º›ÇJ‡?>üüþÓy~úêÑ¿|ýìîþÑKíL&’Ä÷ß?/z˜¥zþôáS9Ÿz³¯¾x9ŽÇS™Nãr}y!ªËù|˜ËfÛOÇ©îÿöËç¥Êwß}oY~“ÿõíãýè)ïë|—0}›~ù|\–^†¬½.ÿøßÿzÆ…°Z£–±rÄÂ@+JT?•ðB(…D¡G@¢€Ñ>K7š4DRK‹ª:E\¥–`xÓ[`—:ÖVçgˆk³~"š~@ 5ˆ€Â£=aí¿°µ :o²9<(‰–ÌHqõj’.•¬©s‰æ’Ô¡æ"ƪh‰¢PGÄ‚LPµ¹€a…b)âZµÐÅ› P*ªKHõ jÃBÉ:‰b«ÀE‚W²Ë %k•“B× 4$‚!u%8®j½v¬B1Í·WϦez š€ˆ–ÕÖF£lç%D˪¯›%Ar¥Hs*K‚µÈé(J…Èâ"´0Äú5}ª´¼Ø 3 ¡5±HS¶Q¥ žØiínM¸èOD)¡ªøŠ¡"Ûã8è°@ªÆ†pª;ƒÁÚÖW Ñ`”Ú€WíÁÝ:bOëXÊû¶ƒZ’°'<«à)¢ßî˜eÝKµÑ­Eò[´í ¬M½ ºéDÕB!™*J¶2ÓJš$I­}©ÍzC†ÃÙvtkÔp]v5rç*Ð&\-Q™š=r]Î…HàÉŽR¡í6²“»°Eƒë¦í’Âa ”&צá²j÷êÓˆ¾¬×MS0U j¡ætÝí-馩â"ŠXñym 2ÄÁ¤¨9÷&\Ä´‹d¤f°6HÒFLÍ*nS±5oHFŠ2‘e4«–$-OÓL@…ªƒUº„;DCdF[Yz-âVƒ© Ä•`“ Äk”¥XR“ÜF"5䤦  ¬…`„Ge„À›ÀRÂ@’¶"b,àJP$œ ƒH˜*š­‚G5“ÎDÛ~&FcC]Ø©ª¤ÜF릒ÊÖ½Øî ñÊšTÒ»Z ˆØì/ß<{õx<>¾/cš3D䜇|é¤Îݦˆ€aêiN eîzÒØš¤Œ·šO±hI§ãìó‹/¡‹«C™çù<ä|¹½:}~8×ñfèÆséˆäu>×Ðl(ããRj™£8º§}ê7}:k‘ˇÓržKÎ]x“M7üíï~x8=¤.ŸÆrÿp1ÁKJ™žJ=yÍ]gÓt¸Þo¿yójÚ0Æ˼¸£NS`¾ÿ‡ïv¹ÎÓFd±;L1NÓ⳨nR®µ Ö]^\Ÿ—úññqèònã‡y6ù‡/¿èºýi9)ýpñðxðyú‡ÿø·Øg}¶K—/¾ùúÅýÚX—ëmê7ÃTÎÏž½ÌÂý Éðæ²¿éÒÛû“âêÙíur5¤Ãý‡¿ÞßÝ¿ûõþñ¾.3 Þþ¼ï¢ŒËüæfx÷þãçÏ¿ÜÝ#]ÝåñðaÈË/~Ø_<ÿú«çc™Ž§ûãt~~my¸vM?ÝKòÛËí‹Ýå峫ˮ¿¹¸õ”ÞßÝ•Ãc™NÕëXo?¾µ„RÜ¿~y+–>=œOÇež†Áþþ?<θØíþí§Ÿÿõ·‡?þøç·ï>ÜŸ‹è’û ¸„]ìsþtF·éj»Á—i~ûù³šôCê‡]Yê—_½yùòeýñxNÓ⥞¦©Æ¥„ÌKY{,ðºT´_æbu1¸Ëüüöf<žE) ^ —ä y¸26[%XÆ œIìz+suzhU„&‘˜‚BR"Ìi)T‘h)÷ˆ¨¥v©¿ìmö²DSÒ>ÛŠê¢@"1Ô†Â*”íy©í‰LC+V·ôä¿/ÑDĽ›óž6lr—7Õµ}XU˜ˆª;E©å‚V™r H xˆXi…-†§Ç„Wwá‚â!!âÁ…‚„€ é$Á%PAˆ!DÅÙ%ùñ÷?||ø¼,3UCÃ/¶Ö@㤶')ÚMIW¶Pc'ÎãŒæÃiû¤æx\ ƒ„j³™4(«®ÎWˆ´NaëÞ«HÈÊdTƒªpHsþ¶ ‘ # T]ï"bÂö׸l"ÂÔWÅŸ°MÜ¡A‰–r[ÑóT8Zôˆ¢ \)iÄ'´¥åêtnK­¢!øu¥7ZdC3¨5ŒHH€b0ZêvͳÝHP¯m•° Ú‘‹JÒ\×s`KQD` š (ƒo¨*'ÌTWïM˜ˆj(-QB7+‰=EÈ™ˆ5 ««ë #“ŠXS|¯.¤ HG ,–AWÇOF q ‚ªÑÄ5€º¡ÂM\ `[±¶—#ÄIjYÞ\ƒPMê‚%‚©OFrC[ìOC“Þn»¹¶)N¬«dkkº¶ bûöVõSÓNún³Ýã<‹‰0 ½%¡RBD3¼½Ž"š2{C ,…FVM@Ò„®_‘jÂ]Û–•ÎE")D\!‘]Ü‚Y%ì@“Z•hªC€È®·ˆFj?XDK¨Ã’Šj? I5ê,°öyÁ%«®K¾PÀNíˆP‹¬—‰„©ÀCbR5…R¤¦5•*–ÌÃZs˜b.€uIÒ·ª$ %©*e„«N-›ˆÔ¨¸9«Q=hv™òi\JT†¡ªy§6±½C6¹&“lEL)]ÑÚõÝV»º@•Ù†jÒ,BYÌ# >ËÂ"B 5Ë%"´zæ§Éï_Lw×/ÏQ¾zý¦ÆÃ|:!r·Dõ¹ÖJ§ÛaÇ0jÍ©¿Øâf¿äÇÙ%ÍÌtgꢽéfˆÝþ:«êñ|x<œŽ§ûRÏãTs×—ÀROáó~·}q¹q/ûͧÇéáôðøp,Ë<—E4%÷›Í.ýú0ÿúéaž‹õ’»Mîl»½¬+àf“ß쥿¸/ÓЧËË畾ï{úxwðýÕ¦Ó~¿{9ûY†Üç„* 9kÝDUOfš—ðe‰d]×m—hø¢&¾é·®Ð~“RÜ&2-*“/‚TªÌ¥ú²¨r·ÛÝÇé4KÎeZ|†G¥Óé/¦B‰e9y­U/"¥xƒ ÖÆïÖIŸPRD£hºZ£ˆ+¬O]ÙåÍþÙ^*ª{ˆ´rŒ€›ˆY‚Õ)* §ÈJIÝ"²‹µg•J¬ù¨ S\Ñr -5•¼°ÌhB}±hí$Tõ*¢ ‘Úæƒ¬„–TIÔ€Ú,ÔHD­ŒÒJ¸»Š¬$áÞÜѨ«¾:êâB§Ke+é€D‰ùÓÝ}]Øt¼R-c¿Úýdà)ö³¶ýšÜ Oµiqloø÷u( ÀDÔ*EÕÖâ›+MZ.!OUM¦´§âZjÒèÖÇkËöç¾É¹ «X8Ì[`—ÒˆHѦÑ@Ðs˜ ¸^‚5·…f×Õ*iWžPEpñµÃ§X1Û-¶¡@, ”Âÿ¨wë’ä:²ôö6;ÇÝã’÷º Øɾ°gzVk-©¥Gé÷ëEKK#Ít·fšM@Ufå%Âo瘙Ž'ôİ …Ȩp ³½¿¯-I¥QV›¹± ™Bµ;hSëAD(mlÒ&TñWŽy›àe£a4–To~@Ð7ÎZû—“tŠ ¨‘jÖ-‹Åg"PI¢)[e ¼dC˜[õ7‘ NûEô¸ÇZM!5ß@ Ûq cÓ'G„Ôö›h„ÁØ’Š¢­ z åvl2B€ÐVJøÿɤ ÐÑЭS°6Í PJP<”í=KØ: m)Xà.˜ÖRÝ„ÐH‚¦HI‘ÀF pd·;À$@¢9¹IÓ#Bº '%‰$ˆ$Õ`…!Ë"êÉi$T(¡B³*©¡_’‚ÆðV!eëö„¸¶Ô”8ܼ}|´4¼„CᢄSDÂR(RƒàÝÅa´5 ÒB"“RÙjºA&CTFç.äc ¦Dsïú`*©WÑ™’] rvAxM¢/‘¢Ê6;×uµÑµ& {r5½¹"§.u}‡¬jZû”v’©5ºO×ùv­T$Íž¨HuLðÚuryÝ­k „ɰÏqsq|±³/,¬í~QÆÅ€ŒóùWµ.uYg‡ª3m¨Û!ëqØÿ/ÿð·§‘@#ÊT‹sõ0J–º¨‡G0‹»áêxS §ù|zYÖ‚ZW«E„CNû~Gõu”ú7w×@}:ÍKY‹Ë4­fîkb×_ÜÞìö»«ÿþãÃ8ƒB•´šÍë̘û¡ÿü´NÓËRÖ¹v)óíÝ]*|š¦q­H’km&Zìwé|6uïû¾Ì<¯Kêƒ)lœÿýó×Ì+¤¸ûøÝéôÔ û2®??Œüñç§ùñ§ûÇQ|¯'£üÃo?½œÖ}ß}œžÇ—iþã—s}ñ”†77‡C¾°ó˜ö¼¼ýôñî¯æzzørßå,\#|´E$nŽ»Ÿ¾>ÕЀÔb×Çë—ÓÓÿû—qò@¤}â!E¾ì×9•*ï®oŽûc7®ß]MË2ÍsÚuËR$ï®//ŸžNçå¼;ì~÷»ÿí¿þßÿÇZÎoo¯n/¯ÊZ¿>ž¾>=Os}:ŸW+Ús]Vóµ¬Ó²®¥@²®¶¼<çóË<ùû·¿üë¿ü×aè@{øòSµÕ#¢V«Já®”»Û7ó2Yõt<쎻«y^ËjËËyBXßïÆå¤ž¨I¤».I?ø»w˲¬Sy|¼_¦‰Ô–¡2§È4ÒT¬ÔfQ CVaÍt‰©»Ù]õ\ûœŽ‡ zu+9]§¾RçnU*W‚HóË¢óÂlè”&QÆÐ×É‹Õj>箿»¸~:Ÿ¦zvã\–ÕÚ3¤Î“¯Õá¦qÑÅxšª­Õ"J‘éê.K $ÙZ NÀ&n…pC-f^V$ _÷‡#\Ê²Š PIdoB¸EÓ†ZñѤ6 #@¾¢'[ü¢å¾[d±C¤h€Ú1%óˆâí±lz=r󻜳ÍV×!\’¶Ç§†à©ª…&µŠ›…ûb»€^£J­á†vÛ“ÀZÛ1Ó¢¥ô½Ýq$ ^/¥ldÇ"O*Žpß$¯Û[Ÿ¿ŒYmU§ ~Ðjš ðŠ˜lˆÄVëc¼6®œò U¼=K«^ëÔ)kŒYm×ó¸FÀ]"æólÅë:w—WÁ!oß¼ŸÆÙMöw1~~|ªí"\DÍ­ZøçóºË{eŠº>§Zì×ßÜýûÏÝaoNÁ){{;äáñt~ýp¸ú»>íºôþýÇáÀ©†êa]Q«/‰ËÊi¼g­tùá»»sµþúv­i±òÍ»ïv]¾Úåÿöç§É &Þ=Nóóê¹;С»ËË‹\Ou8¾žŸéñqYWܼù@áŒq¿Oäð8­4<==–q1W…Ôüéý;•ÝK-å¼ÜÞ]ŽË¼X ¨ØÚ_õý›oƵüþ›ï$ññë¬ÃñÓMw>X&1»×ÿù>¼X)Œ²–z¼¸þßÿö_þù_ŸÇ{³XJüñÇ?/¥’É#ε,+˜pyq%ˆy™—µN‹vݾïæi®æˆTV¹ÿúµ–r<–µ*q}uw>O†:šðÝ»«s‰é+,¢"L$HCÄbI|Y¦yŠºŠÄ²¬¡Z§¥x±Õ¦(ã鹬‹“]äð@…¦øýߟÎc [jºnR8aˆÀذTôˆ —Z—²®µX­"\Ä’!É-h[‹0 ˆi8EÔÁ¢²Ùƒ±“¡„Uä »×PxBt²Šš¤ÝƼÍfª)°5äÚø"žy“šÐ.†¦ÁªF1šBT©Åa^ÚÆ¦/‹Á­1´Ö’2˜Ã·ÍÃÃÖE÷¾?v×¥¬ÍñôíÔeÁíê›Î&¶àQNɽõÜ‘d{àQ¨ª ´W©-Ž(­ã/µ%ºA’IÚ"IT —¿æzAlíDPƲa¬Úñ ŽF{VÓ‚í€Õ™[µÍ£%KÐÒNM=¤¶†\ã„+ÙJ„òÁ ÊM#lX6Ì'¯èPB"^UË[ƒ°ÍMS”¶Äj)b €Þš¦|}î6…Ï÷' -ú×–mˆ3Ò„¯iù 1”®!°¶ÀhˆP´¾t‹ƒ)›VÇØ Ñ-8nÍ^H%E_AÿmíÑ6±= ¹Ý9ƒ-ôèmˆpDvi¼Qc¥q0¶šSÚMŒh¥€ˆmŒotµ„)m ¦ªêÒnèˆ3€vŒl#»k :LÚj•ÑŒ†í;Vi‘) 4‡¨H˲iP5U¡‹«5mBÛ„!ÂiÁŽ&!É•ÑиI%sHhHHVq…DäH"Ö9˜há–»]'²¸©Š£Z"ÂkóP‡h›¾*Z=”"²RrÞý«?<Ýÿdeɲeæ AñDz ¥”(DV‚Üܴ¨¦+=v)«– K¦DçÒüJŠ3J2DÔ!žÚbŽ.4ThI¶§±DBà`,Õ«ÅêªìÁ‹¡ŠªCN’›ˆ¢™]|ý>ƒ:…QuèS‚‚V;C˜‹xѧ­Ã… ™J醜’†ýá&O‹Ìçâ¨e!vº+)Rqžg“ {¸y­f§iž“Še8˜ôYxsw|{÷öᥬË\]T)®]ãáx}óa>/fúöîx.Îë¹X-ëjΧ—%éÒ¥²x}”eµn8BÒI’o?ÜÕž3"ïüõoÿîþáËx ÏùðÿðOúù§2—e²üõ›ááìÏç³R4Ìj9ë²®A\îú.wÓ´Ìã8ìöû‹a (‘#©÷‡7W×â‰*Ãþ:A—Å¿¾LÓ¼Z5aÚå\[*€v±»ý"¾Kûé7È"áW‘R’:Ôq…WŒ†S$ÖA:.wå¸R²ײ,áC‡ýáp¡uûQXÑ%„ TX’EŸt «%¹K˜Ã¤¯°³»—Rk-eYêjeYÍV[=ûTÑ]–ˆçe~>=þôùÏ)ü²¿Üwy_~üúÔ%¹úÚs¦r>ÍçÕ¸Ïù\OŽÎl¶y†Ç:W ÛçË?Χ±J¿˜Ê:_¥Ý¸¼<¿<}è´ÞìAóðýÛ7?þé/"ò0Ö9:•ý\–ë¬ªëOËúöæîò­üŸÿòÅËòñW¿¾ÿòø<žÎ§uÅZ—‰1תß|øöþe,Ó„²ÒâyZ³øËcyšQ!=< ÌI†¾â°?<<}. ooßÑæÏŸ¿>= ‘kYþòÓgÈKŸ}^Jòù¹ž—Z½ä”þö‡ß<qG&Oý!´K}ì] šb'Ë~w)]¯´<ìWÑ}'0|y†Ü\£ìö¨›™ÊJ(Ë¢‹Kb—‡~®ëk +¥Øâî^RYW#S×ïcž­.Õl¬õññ±ÌÏ@D5‡‰Ô(54†4¸‡¯sX3 Ã^€JAÚ¢è†!©X[ËU -. ·'„šº)BZSª‰l± x[Ú* IDATéu“Z«ØW¯¡  v»~§]© $#‚ÞU:¯‹œLQJ€‚ÔF•¥=1H(J„à nLÅ‚ÁDe6P/ü•øåZPÕýáb,QÂ7{¦¢ I¤¾Ù#!‡x—û®Ûç®8D†î>¾¹Â0aÎÇݾɿš5Ùâ]Ž@ª€’j.Ú!aêž$5u…#µ×ÖëZ)AºDF."ÊßþþÝ8¯)ê„ÐypM%²ë!×y_\à Ýò!ì;M^Ìl>Ÿ¦Õ`¢É$,¨%Ç7‡Ãü‚¼Û%A:\¿ãúì(»>'Máu\–··;íúÓ¸ˆ$dI)åNx.‹Í¾ú<0v9wC¯P;îŽûANÏ㲚ŠDÞÝ~ùñóíÍÇ}ÖÛÛO©—ezz|y,+‡ãa<ÛÓét>Ÿé´æòáæð~Xÿ÷ÿüŸ_¦SsYìÃX×ãᄞׯï>}úæÝ‡‡þòô˜S꟞ŸÖeÚöo.¦Z3“ß¼ûø»ßüîx™žžþòù!Êãíq¿„üøåñ]'Ïó¼ºøöÓå1—õT=..öÿý‡G—©‘‹C7Üd}šk—R?tVçÅøþíMŠúùÇŸË2ïóÅx.}?D”iù í>¼ýôtÿÔŽ«ÅOÏó1Ç ²pRRÚõY¾><ŸJyx®‹ãöæºKWÊ|š×5Jÿ§ÿðÃ<¥óúÒwûÏ?ýÄ4éò0>ιwê>uCÖTËùñd_OOþõô2×y)N>}:O]ÊQ\¯®?\u-u­¼»þòùåñôä¾ö}Ðy™³@%Ï%˜¸ö×ûôrzAHu¹<^¸ÕDç—²,«S»@œÇå³ì˜c*ˆ‚¥‚+¦bæë.c/1§gC“açi©&³UV¨‚ºzZË.ËTÖ±F±,fe¡EÔ‚öµÒÄT"§¾Ì£­V¶­„( …B»ġë¸8\ìw;[‹»8›-¬ð*J‰ä[ÕL¢}F'”I”Òrœª IZøZ[­ªâ@¨±Â¬V_C³hÑ.bPe¤†@¢ˆm¯#x¤t¦8J¥·rŸ„5Î\Qݬ4š»µkV¸;ØF½@8ÝÍçˆ-œÓölîÞpkÏÖ0Î8îv¹Ó²®[¡-Z€†„‰æ+=S°á ÀÍ#ˆ d¨5¹Ž¨&*)HÊ$Š6œˆrk'¶‡‡ClKŸëkèžÑP¤S ÂÍ™ÒÔ;tÀ%¤5æÈb{AÙ|$h‡@l'°Úk·ˆ.`´}60D֣ůu{µ­\°U+Û-+.ÒÚVi£¨Ó©)íÚkIBacŠl{¾:ÁͨÚâTŠÐ¡ êÖRm­Îjp(¬11nŽƒ"þz“CˆTÍqa{g²¶%#\ z°¢Z o1îí ÜR‚DÐÅéA†¶WjûlâsÙòù‘Œ[ЇôF;kÇCAØžh&ëh_š Y“16(Z—¤inÂöǨ3L6Y¬r}y7çˆ@±¸CC^ºNÔÛ[°5)¨jâNÉÁí2Î ŠU"˜…Ú®œº¶Ùb¢RU*Tí ’Rj› ¨†RÔ(bÁN©YI¡t]zóî›q|¶—JÏb• ]+*ˆÕ–˜%ÂL‡‹‡ÔíÂí¢Z%Ô¦:rÚ2¸˜¹×PŠ$ 09ŒˆÖÖ¨b›G4b™QjáfÄɪ!¢†&B¥“H’Œ™^×âÖþ‘Xê:$IÃ0V_JU©5! Qæ¤†Þ ‘ä¢I’†ªŠ'Gb¢ “\ò Ñxñ  –ðøééüòdã8s³_f"½öêên”Bíºt<ä)+mAeX±ódÓ\jØåþîúvî³EÅyÈ B¦r~’D ©4 $Òñúøõáéå< %‹æÝnZ¦qqÕÞ}ä¨ÑïûÝn¿ë©2À—·o?>¿¼ûúôõéé^(j‹HŽ]·+‹Ýêk  ÊÍñøç/§Ÿžæ\£ÖºÎ‹|xúŠX×Âe%¼üÍ{}XÖ4ì¾¾<¸ïŠ/ï/nu·~YR°ïéþá/çùeYÎF¯“ü<Ž÷ã8¯_OÏ>¾¼¼ô²<­Ót>ãþñez¶DUéþðë_}ùòü|> óü›ß½»Þ?|}xž§ È.åœÓn<—«¡Û_/öC0~ÿáû¯Óé4—NãoÿþŸ.tMŸ×êèÆu,§é§ç—‡ó“t‡á°aB¾}sµ»Ø}<礿ùõ_~|<Í/ÏÓ¿ýñaœÆ° Kçy¢À¡;üþ‡<ŸïSº¬ŸÞ¾Ùk÷õi\|]ÝOÓ¼‡ +õ¼NÕìpÜ c]Ç—qùzzt÷‹ý=RëR..»@Y×"šR¿+óóÃãóbbÕJY–eµj7×ÇÓ¸–pˆrè®Æe<“ÈJdM»¡ë–b_F{z^fÓ@*´z¸ÚqèNótyù~|~^}P©šФ:t‡¬É{ô)}üð©xÉ$Uªw’4j8¬ç>kš]#:MiÓTðu-UФ)kJК³Í™fTÄ"¡q¼8.¯Ç鄨Եæ2°HZç²$¦!¦ÙÍ—R¬’e²`]*ñÚjQç§ZëR-+¯s×R×Õ±h‡]GñUæË<¿¸ƒ!L’Dëâ3Â"LaI¢‰É½%ÌW_á $8UÚ^C) ¡ƒN:h¤¢Ùka@[ŽáÂÌvË  A&O×r•«Fg«&z@”ªð =Y‰ä Q¸† "K8 ¨BZØ…á"a†²%£Læ^ÕÍÐõ¬E-ï ¼•6ÎQ"Ag„{K—„à&¤¹ªnµ0”´v:C‹ÈµÙµ¥ÓÔ”·Ø¸«ñ#'UR6€¶¯ËA@E@§n\©`KT9…pZ3YÓ‚pšhÐ¥6€DC’KD´°46m4,IƒÏ6ß¡oõµ¨Þ®Œp%t¸5· “ÚÌ@@´­.·¤vªB¶Ú`ƒW4L•oøºã— u³c#@m¬Ž;*Î×:) |Å]ÚÕÑ[³Ó•¨ÞŽnmóÙ^4ä=‰ä.L-Ÿ$ØF:D |ÆÔL‚® ÜÙZ±`h+PBL Aj \µÐ™P)íW¶ÿ¢ %ºq¸b3¢[Ù"Zc¿}L Øí-eF¶•Ÿ€Òp±)°c&µ¿¥ßœØÌmœÖê–š[ÔhG\BTØúšŒhó65†²õ3¨ÛNÐ=è"EƒÌBn©96*G!(}öœ„šTB5geD[Õ HerÊf•4Su‡N_ÇÓ’xVzm”0£³ê³„´*Tq·0ˆ>÷)§µ}9Ú¾‰±`õ•"Ys UQa&°êáŽâQÍÃÕ]Ô²0©&¤ç¾Œ–àL‘Eõu™Á訚Ì=Š’àÚP„Þþº»ºF¸öJ ‰žÜ|ªQ*¦ª<:ɪÚuý¥À+­'è0‘¨iªááóèFVÍ’K§Ú)$«z°2Tö;>ŸëùÄ2›—"P'Uµ.iU$ YÈA¨F‰"º}ÂU0¡vˆ‚yE)(L"²ÖðÓºÖ ,K]§É«y?×ÉÊB[¥–â¤EµR¬ÌÕQhE#¯õ¼Ö©e¤†®=²ÝÌÝéžàádx>ft‚ÑVHc “/> årqOQ£R"®­âLA@¥]RT)ÞΆ ]cF IƒÕ4ˆJ*BCkHPcã ·óBÐRˆ;‘é¤*"Ñ+” ££†¶Œwµ&ã{­¶QŠMhØÌ¶nˆ ,p ±ð°0±ÆŒt„ ¶@wÛÒÙ^·6”í¹ÙžãÍ8H†¸ƒ”_ÊáQ­T¯h"aHlÉoÙˆ¡ жñ@bÛµãf Bªªlv5W¦v@ŒÆWl¨ƒm¨i׆ZhŸ_NGxÀ\!bDb4“`xHÚÙ`S­Û½Ö„Ãa!Âb[Ê=!Úi¸=ÊBšÑŽ¢moÄíI/”æ "DÙŽshè+ '%”îV€[­µ¥ñÛë6;·‡mªmC¦›¹- ½Íj §æáhþÔú­Ût•ÈÆ†u Â$µøŽä¤Â·ù'§+¯‡:±ö£0£·µ$Ã^u5MíLt‰p…9·…/¨yk̲í™b»˜n™Ä–EÛ”<d31±ÑnÑpgŽh\{(Ù•L††ß` ©mLm›Æ Âi¬‰[wµ]¤^S`$%RÓ?k8\œÁ0­êÚòNŒˆvñ´ç+€³ •*•’B¤P ¥j$²ñ¬ˆk@“+IQªR#Gv bö*‹an€ke]LÃT\Û×pÔ¤tW9Ë:OšÄ*Áp¸“Û×Jä&Yr2P náî$ªE3F#Q»0ºHI"™r²¶Ý"³ô)¥¤‰*=U‚ÒüÖÊ׬¤[æ¾YvæLC¦Ä—ׯõ (8$"rh:ô)BŸ×ZÊ8޶®QÀ¢Z±ê9gT³¥ø°ï®oßô©Ÿ×ù|új¾Ý1e{ûæû?ýû?—:%‘µ¦j¡žËjËz""¼Åh$ I%€!ªÁ°º÷9÷š°·®Ë”b¥†ÜÀ@‚ÄZÍ’&vÞk¦ô‡i‘R—£6ÄÉ ‰ES&TëíQ^*(øæ¾Ü£9{źÈðÂεT Ì“,u]ª®kÔµ"¬fºs­³ù±w·q]¬Æ´–¥®KàK)kîS—dZ¦ÎÚÜFD]Œî›¢SÙåóêëRÂ7Ã]3éù&äh—0´ôl‰lˆf4“‘HˆgJBSÃW57`* 8Z5 ôW~£P½—¨hõž.T)"ÉÞªZLóÐ&€¹¢€ÕéÞ€Fô`x«:<< ­INœîî ÔoG÷J´’æö£6.t@B›ÕNm†–_Ú*kmÕ²<ÂT2SÁÖ¢jk1U‚€Š6ŸK•í4ƒ-³›Û))"5| ëáíIï4RáN©®NÏ©i@Ú…‘²ñ¦ÈH­$VÉðŠ5hõÃADrnw%¶YŠÈ‡‹KK„Aª²•¶®‹0µ!}kømÿòĶÛ4B¼5Áö~iÒœ6`m±ª–‡ß¼- Õâw±µšë±¹W-Þ  n¡ú–Ѧ±Á[ƒ" ¯"ÁD QBÛeOj4zX[ü´[›Î±5!!L&EË8!è„:‚‘š¬;¼QǼÑDO[_›Q¸ù°[ܼñr†´Ûe&€ÿÒº " yM'6¶¬á.ª±MO ¾Íhlø‰Øê¡aí­Úê~øNaDCøö{ÒZ|Ò±XTÚ[MDØ„sÔ4ÒŽE$d*¡¹±môFc¤P"Q’;·¬5\¢"±ÝGÑ"ùâ "ÀE<‘ ¬Jëï]…ž—Zj±(K¤”2Q·c6ƒ„§®WæV  XuÛlíŽ4¤žâî¦mû¬ J$æD&)9çœÚ`Â^sHF¦fŠ«'és°ÓHµQ;Ю—U"è<ˆ+µUd©!QaÂÌN¬"I§ýÛOïŸ?£Î)%w¡¯R¢I’o"¬Ža¢Ü÷b1ÕêŸÃÌrÊfnÞ‘UDºLKÜ¥(ÕJ Yh Õ$ž±LÒg¦œ&wÖå|*Á4àz¦•Š¥X™Æw7åüôóã™IßÞ\Šz·Û½yóëi)V¼5‡ yµŠÛÛK‹yžÎV¥¶5µÜc¿ëC­xú›¥Dx$É—Ç‹µÄºžçéyZ¦p‰Äêâ§¹Ô2w*W×wYõ0 ÃÐuÇ«Xûu™¿<üøÓÃýnÇ_ÒýÓ4;÷Çdï÷]ìö¿ûøi¬ˆß|zs8¿|yƲ¤ÝõñâðáîjÈÜ®îO§î°ûöÝ…-ój¦]·ëvû‡ó²ÔshNHcafy~>ó¹¬ÏC'Vì§óéß??ÔYŸ—yšÆózžæ§u|r§¤îæpÔþòb¨CÊ÷g:{Æòò8]^ô>O«17½’¾‡«ýÕϧ‰Y°ÿ‡ß|üÓýÓi_ž*âéôòR|©k}É«Ë_GŸúnÂÃïŸæ+—N³u–5¦¹wCˆº½üÝ7Çû¯ÓÏO/V"kº½8NKµ²•Ì#Ñ×íÓG%]:ó1ïòÍÝ›:Çyz.ói©¶¬"*^s8 ’Áe©ÅËI¤/«‡&ïzZ²0ËBcP™]üb7\]\˜Kê»·w‡ÓX_æ©syý<Ù851Tòªá‰=}@•”xèôI5§P¥wYA¤5š†®¤æ~f8—¥BLÔ¹¼`w”p³gA¹¢Þõ»±.ç%v¨ÃAëÄy|*æ«ÁݪkªÆºÎ°u±aX«ÙF+k)+±255x-k]׺vy'F[ÛÁ+h,*Rл®»t*Qƒ H.\› 7R$°¸¸ô}ß {/gp±ôÊk¤'z¸h]’‹»Jf(5ÙF6¶OiÄkÆÙ8jíC‡më&0ªÐ¨µ‹Ö˜²h X.f zS»E@M¼í‚aXaaQ£-ÊÆÝnP{/Ûcˆ ¨8<ÔÅèm-&:DU M> œÒjA¶g¢Çß™r3=·ÍESðAÔšàF@UHiAí F$÷@¢ÖVIÔ¦µe8Zo%"`›ˆ¥u×ÑX¦T¾éÖÚtBi;°W:Z9½å|Jë5~L±í®L„m†‚¢®ÒXTÒòóJma¬€¿v]HÍ`¤p‡¾²6!í–·àÕnߺ”Ò^;Äk”«ýÙùÊÿ ñ6‡kÀe»R*LT8[ñ”m‘×Úü[AO™• ""ˆf²†øÆŽÐ8›eÚÝ#ª¬©=^ÿ‡‘Ú£„  hã‘ZDèfêÙ–BâÔ%ZÚn“<ÒF©m\—öFr7G„’NÙD@--h"œlýRCà‚Ä€‰Ì$!Òœ  Ú÷#4îHN@ÁD!¤n½O‘ÿ©7k²äH²ôŽªš¹ß5¶\ÈL –î®®ši™rÈÿÿÎ7>¡HO»«€B!—ˆ¸‹»›™êáƒyäô DÄH׫zÎ÷^ÈéÞý}£“6ÿˆ~'5$¼)TT ª!šTÕ!2¬mFd¥ªP¡á¾6º K‚†ð¼¤DCÚÞ4jíã'D%:'¥K+ÅâdjAsK)W¸GQÁ¨Is®Å)ðSaXʶßì¦r‰æ-Á_†K´Æ&)éýíª—²ŠDé_šÁT¬ã}-’™‰QÄTÔ4YVQI)q#ƒE¶‘¤IChn)(i€Ws¥ÂB2= I)4#k³Ì´F 1I°´}>?{}:nLX\rªŽZQã¨:ŽLFƒšÕêU ­â­Y¸€ÕJÀ]Ò`ƒ(ûMÚnÓ\ZKûwon¼íç˲ßwLJZ—@mEwÃÖ†”M]’‰\ç¹Ì…I·;¼{ûî§/§Ó0>¼¹Í¢Êmmr¹>7w: ¸Ýmjñívôêç篭†™íwZ Â…l7cÀ–Òà[‡´ZEdÅòv¾žç©,ËQÕ›i@´¹å!ÿæí1§qž2n’k$Zä:¦Lq~~œž.×qwûñݧËå©Î^™÷¯¾<}*µfƒ»§íñpÿññùéÝû·ŸÿýßïããTçyAÂÌ4&½»}òaPýÓïþøöÝC_?Ûíæëe¾Öi Ë–þ÷ß¿OÛ‡dÛÏËÛﮗÓfvû»ã°«ê—eÿóþßæV¯×3›Ô|¼†ÿúóãxînò—SÑÑ,ù¼L¿ûá·ùòõõ«Ã«W·5ìíwoŽCûËß¾üãÇ×§¯Ói©Žùo§éîx7ã°?þòåçei?üðóóóåT—Sç6ãíëANS i¸Ùo;ìo¾{¼|q[ªO>Ãë\âo_õTæyñݘ‡ááö¾-îIúæön;nSRb¶”ÚjT÷lºL§æÏoïoOe CUKÎ,{衊œ6ôBê8ä÷ïßÏç+°˜í†ÕÊápÜä!eËiØm7ÇW½íe[Êðeª­Õ^[ibãfôÜ,a'›< TÓ„œŒÙ’ÂR£Šc—·HPd“Ý8ÄÈJ‘Œ —›©ðú|¹T²ÖÒª"Qôt½¿V¯-¼µ`xä¶\.®m®ÑȵHWo¡L^ @À½u#aa]ïy°í>ZP,T‰Qc.(ѳŠ?³Ž ¢åQ¢ÆHàj­ÎXKk ’’DÝHœT#««É$z©¼‹mÀÕÝ#;Ñù‚DcNkGƒÖþÍ4+覩C~:È9¬‡­ésDô·÷ý饨dkëâµà ªÉ[A‰FiÐø ’ î™§ÁTö-L~ [· +ÎAõ—ÍZlÅu‰¡“´ãNÙŸ¢©GlòÚÍ¡ª ­Û?,šjêG§¾S5„‹hýcª¦õf’Ø·p´ŠHo J¸r]2b]Èœk:zf}°ZôG.œéÀ'Fí@EN èë¼¾¯¡†½Ü€EÉ> K×ô­ë©Ù°V XÊ7ºþ‰iç¨ZïôíJça È—£éj€ì®&‰XUMº²L{í‚Y^ •Ú îJ Õ.î ut8„ý‡|œ®YH˜„=ÄWP$´· ½sbŠôÊ-DB) 5F!ÔM?<Œ×ÆÅ!ajÑ[¯iØEM­/´Ä‚ZJ·›È Dƒ½—#ÌUê—ªCF¡±Dër¬ht7¶J6›‰ÍÝz­µ€½ýl)å÷wç¹07%Â[óç$ IU“–†:_–jux´`0`¨Ú!µYêµÎEM‘BÌ,Rª“ ÉœTTMRJÀ°vttHÙ¤I#›†ÍÅ€f¡á¤ %jƒjN I ƒ+·¦.t$K*¹66¦qïß¿þò¼»ÙÝn­ø%¿ûîïŸ?·º T”ÉÙ¬Ò-©¤4æ¼­^H Âé1l7­jÒ9 iLiLʲÌË  ³OAm-L½Dm–"´÷‡Ýö¾Ì—¹A-ÂðAeYÚy*e9‰¤‡»›Óyy|<]ë¥xˆWHÞ –t¸¶³£I ¢€’G»s•ív£ÂÃáPJ«ÅaÑæ‚lɆÌ2NLðݸƒ7¨hÒáþnÜ?=ÇéùüðêÝï>¼ýr>érùUÈÃ6¥Q^ßÞN%®×r¸¹¿Ì§i)bZš)vS¹ŠÔË¥<_+‰¹M?¼Ýýëÿ×q“?~üýí›×[âá~üé§O9Ç´‘r9ÈòË¥<žŸ»áp|i“·%¶»íøÛïßN¡ÿ÷O¿¾~w©Ó/Ÿ¿>=>! ¤o÷‡ZOî*b r™®§yf£Ó‡ñ¶x{ýpãmº^/ùþÕÆÕkí'üiªóR’ªl³úü°Û4Áé´Dè)*Ë»¼ ÈÔê\4FŒ7ÓétŽ˜-4Á´C~ÛÞ¾y[‹Ø )òéR+ÓífW!-°ÝÜiIf9Ë&%’’ª¦‘%‡e ÍÝ“e&CjH»aP©Þ²˜·ý~Ü2y¸`¦NËq+ÒÊgPJ-sm¾”–ÆÓynó-¢¶æ±ÔS\š¶æujâ.ш‚ZhÊšE‡4²zAôþ+›!¨¦¢ÔIÝ$"Þy”Ñír½/”oý¯­Úýkt~¹yñêóº}Z YaÞÁJ7¦ŠH@3H'4ýd—<…1AìÛ-$Váš¹8­c³ÚéÙJÕˆˆˆžE±~¾ƒ˜Z¡µÞý“q÷Nx¨7¸S„ ïòAÐÃ5à +;*æØƒÞŒ¾ø YјÄÔ$YÝ5û·J<¾Æ;­]LUÁµÊÐ+î½’•LsçQgQ$2É ƒÁ¨Iu­t$;»r®O hˆPû;yz7ñ€Ú€”UŸY{"nå¹°7éº :H©èÛD’t‚¤m}¸b@{gݧӇ€•<ÙßÜ8ßžî cçÖ˜¹tE/â±óÁèŽZ –@Ëi÷âãIݳr¢ØAåvª"+ËE "wjhf¢ñ­ï%MUS/*X(:ÕÖ¤Ô„ëÅÍ0Pè2i‰c“~É ÓÔê–FDÀ FD·é@TÕ$(Ú„€¶>töPûú{µž9]hýŒ²Ï·]ÔÝgª ”ýG¾Ÿ?û•ДÊÕ/!ÖçËužëŸ$dß¼©½g$"¤æ èWQå7ð+i„ˆuÏ&û¥ó\—æ}(VÀH±Øîn¶]ÿQ¨­šõœ{žÏÆÃq¿m4¨Š&ˆƒÖ{N]¶bÖ_G †ÄB*Zˆ›'´ âÁ¥ãêÕ΀»°5Ušžæ¹ié óµI’*l"t„7¯$=ÑIÕP%¢44JD4X4%bHªaa0º$£¨B¼jØ`Y MBe­–š¢‰f¥÷hÍÑ` ¡ê ƒ&ªBF‚5³¿opS©:0©¶Zçå™DÞI)(¿>]¼4Í M’ æ!²Á†awû°}~žœÃÝíÁè‡a;=!PI×PKãïÐ~…eËhSióTexÜnM[´°Ì4ìĬj C6;õºp‰¥NKZñ¹ŲTßlŽ×ÓÉeÖ4À¤Êf«^Ë2µÒÎ)YiRæÔb.QULaq)’ÍšW:sò¼ÑMÖZ—R=ܳåû»}sRê0lr:4ç×çëãã¥K)ÏOçëÓ\[‹¥¹ƒ"ƒåRE…WÚ°=ìöû›ÛÝæ•_.‰”Ýf|Ø¥]?¾~ý—Ÿ~ ÷7ßýòù«¤øüõéiŠ<¾ò6½ÛÛ‡K«9ó¿üöÍ<Û£oÂ),†ñáõëÇÏ_Îç+y”óâò‡ßþá‡ûzNQ?<Üþüé"yö’-š7Ï&"ãá0ÔÚ¶‰ïnoK<}>‡ÿð÷¿ûòér<ÞXÚ|=}=Nçkûç?~xºÖÏçS+¾7_OnZv›×§ó¤ÚÔqŸ²ió–†Ý<Õl›æíëóé4χíð8Ÿç¥,Õ‡qûp{w™/07Ù„XÎÉòf´Õ°Aüå—Ÿk º7Çez¾œ/­-û1'ÍvØæ&º?Ü¡]Z ¥œ/•â"jiÌãÆTa²,3UM`yxûî])—ï_¿zœOÂvw»“$hA°Ô6/uGIy.>Õ(•ÐÜ`¡Kîo‡qÃn¡š­Aõ ¤nsV1VÁ6 cÞŒ8*0ŠJ©I®“"Aò`l9«Z¨— Eµ9RÆ0¦§ë5¦º„9ª—Ö._—eZ¢¢z¯‘¤¶—¥!ÈæÎ3}¶J“Ñ}uzêQ׈¨=\$ÞyÆ)ÊÆRœÆœ ¸RH("K?S˜‰ôœ¨´!ïïa5º ¬ï}¢¿—•Ä¢–™2D“ˆ !"šÅÃ|EP t‡ŒvÞ`Šî¶ºDRKÔŽ§ÅݤyÐÒ’¨Ž¢ó`ëÊaJ8Äáp,MèÑäÛv/£õ`6˜#D" k‡ ð¬’d+àS k)H<Ä{¤«é–z­gžs´ÔÇ!éý/… ·yÌÉÜ]aT7$˜¨© T;ê3ˆ~¦‚D@]TÄtßHá ø:ŠG¿ëR0‰FA¨)thß!¨  šHò:éÝ‘Gqz×?‹€4’T¼?|»°s¤?ÉUz¦‡0° BЀèd©o[ïÒ„(D:ųÃX£gŽJ¨¥a¯=AÕ—S* ë76Q]!XÐ Ž—Á·wm•evl("Þ鬶.ºLµåÒéBj‚jĄֿÑÔzB):ý…YT Ah  ® öu…´•©ÀhÂ5çpH–.î‘õ’Œ®¼é»6Bb]é!”^ûS€­«"µCSE:_­3¹ÒO÷/’Ÿ~‡ŽÎ—í¨Ð¤¶;Øá!L7;56ôÚåJ•­+­ƒ•Ñnð—ÏQ×»²'O¨Ñ´I„@¤E™Ô¤H»æá¥¶æ¦=;è*¡žº]ÈÐö`ŸÖ U‡ÉTit©ôTûa”"  àô 'ó"½´ax i"aAYC‚ˆH4# *LÚjXƒ, „Õ Í0Ë "›jS¯“@ÝáU)I¡@S!›5 uSt ¬&CÉþ‡¨‘HÐ(4,DAó°@«*QµiJ½n³ à‘­âº´(IÆ!´±¹A™ÎPÑДUdÓ\f¶â¬­ -±Ôy®¤U+L†ËÜ4ûîƒÍe¹©•Û´¹=ø<]K”ñv³·Ûñ8P0Öûûc™ŸJñÒ@ŸD TÏ’{Fئ´*)t€µ$6h skÞÈ@)m»Ù©nJkµÐ]"jV}È—I%Ã4«H¶›´ÝÚ2_¡–UsÚw›êP±ë‡]Þ^Û­ÚHŸ%.ó\]è­¾½Ýv¯B8 fyðÖ’jTV²„k:ŽÃƧ§k}Œ¦ å¸ß?ÜÞœæéëãé:ûijýòÓß~ý¼,˘·¯„ˆé–ÃPÊ$È!ÃÝñðtŽßüÃ?¼º½»œ/ÿúÿýO_>Ms³Ì‡q;ì¿Ï9ý×ÿò¿üŸÿÏ¿¸·^¥?ýæþéÓRßÿøûÛ??·òö»ïhº4yóæî×óõ/?= ê`þùëi3ÊïÿîO?~s-®hûÃxØ}úô凿yýêøoû•QË\Ër}ÿúÍß§¶7¯Þ6ÁÓ¹]¦"Eæ(Ï3g¼½¹ou±”öI~=_AÙyYüt]ê¢TÝmÞ"©„/-D^®µÖꕲ¤Èc"IšµK±öÇ%üôø¹¶:•8_ŠÓETQoMÀÖ½3wƒJª^[«Ûýáùñkm“ØpwóÆK]jK`–ŽGT´™": ’Ò6"{€:p‰Mî«‹ Í4«ùpܤœrظÙ'µA›qH›·1¨Æ âÚ()âˆT›Ü âóÚ”:Üåëy®óÒª°‰VÛT"P\4vÃæÝ›ïýzæ ¹m&­[&BK£ßf]è!€¦)ú8n,oD¡$EÑ—V;aØö«uÿíÒNé̃…y¢Y•PŠinÍ`"ì´`#!!p0‹–UÔ¨„“´©Ë Ó°€{ÂÐA7"U$ÜØ$3 ]¤›7z¡CÎ7ÇÑ‹ˆ,A¥Db"MP=1õüCäRZmE„ÈÕÃï^÷ë_Ÿæo™¢ãˆP&³*ŒùÚª‡sÉ-84Ö1Ä£¹’‰ùûÛóõZ[ÔÒLbÐm]b©áîâ…IûXp„ÔíX£·O‡ÝÖk”z}ÿÃkÃ$ºaQK»Ýq×¼¸{D>ÍlôZ|vyÌ\fN×ÅÉÒ«û›óe>Os ÛãqÚÝÍx»;[ñ‹ÑÆaø§øm!G›²™ø˜öoßýø|¾Ü?ÜÏóÅ^ìé2×8çtÓßÐíÛñæø|=!Z£²Õmm)›Žc¼ÙßäÝFSÙŽÇÃe™ïöÆ:}¾Èv¿ùxóý·ûôåëtyf2¨=ܽ®Íó¸ýúåñßÿüßÿò×SaKyø—?Ÿ¦ùôã1ÿÛ/Ÿ>OÓ\"\ÆÍ&J)Í“c ¹NÓvïŽÇâ›·ïno7{ÐÆáà…iLŸ¾^/§óqËv~þôøå2=Ÿ¯sm>S.ÏÏólbbÃíÃÝÃéñ\êhãá|9“u™ù<_Ï—Ë/_ž?[mj©²MÓÕ’$¡l÷2Àæå:O¥´ÆæÍ¯ 4Ê»»»á8°aŠ«Dón?L“ëp (—ƒªôf†úf§3Ã!Ê •!YxçRÚ\$JDhiS‹ê(ÓÔi²Úô! *¹‘Imc;I ÒD!l}k‰–4ÙV8ØvÈ£ŒILô‡ïNÉtX$*=¼¥ÈÕB”›!ù¨Û]z³Ç\´2”I%æ(J÷ÅÛõ¢R&µ†›×šîŽÍçÖ&J£Hüz>/Ó¥µh)˜Ã¯hXBš/,N:PØŸ•„&Û —æŒÁ†ãÝÃõÒ¢:SRE‚JJ´`wƒµnŽ;öTÃØƒHëƒ. ¥NêÃE{*RÇ‘‹vø€™JC5U¦@ UaR*ŒÂڂ𚨽ƒ}¸ÆC=GÀS„ˆ )5d-.õ¿åÖØ4£#PƒPl`ï„Á@D#H¯ „x×0‹PAêAa˜®!ã¡Jt±nÃúB‡ëPDmN¢ Ah¢Ú×6®‘Vå.ÈXKä€H"RGÁÔ$K0”Œoоâ0MQT_® t2"T„ˆ'ö7µ+j@ þÒa„„Ëš}êÏgíX‰ô_îèÓÀ:Ëj}ˆô,–t°øjI]§=BÖ¯wìlvBÀúò¬‡, ¢ŒYQèüÌœXÓÙì¾ëŽ4èËu2‚ dý XgT¾xû,¢}šUuUb%gö‚bÏËAÅ$)LÄH©‘Žj3¡ÿÏ”¼R$)⽤øb÷Ž% ÒBH8D;È©k D& í°Â{f.°Ú¡a|ÉüS=´Ÿª×ã LþÂÖ»ˆ°×, G´õfØ}9Pj4 ÒÑœ„KB¸ûh%ñ-Šg¶²WIÕ>j‰Q5´‹Øv¦ÿøöøù*áIÒ G µH+º 0Ñ­›IMU5kÊ&ƒ†U)>éõ\‘MÖ_PA4¨#H !Ø$¤1„F§h0Ä$¥4SQ“hHšzŠ MMïö‡R—æ$­†“ΰŽ®àâTJX用Ö,A¨ª‘†ÁÂIëü7! }­†,ª †AÒÆîv‡ê zÿPÑ‘£-$Öß`‘°dÊÒ±¿Iµ¢ e€Õ)@õc6L^2¤VfKI‹NMк۩ffI9Ѫ1šŒÇ]r‚nÇ;JúåÓçËRѤFÀm¿ÓÃF~÷jsš[ˆc©µ•LÃ~<ÌÆ¥¹q™¦êϧë|™A-Sìö»ÃîPŠ7Ÿ¼Ô° ¯A©¬ËLÁõõ!ín¾”p†´aÈ×ùÜ¢ÌË©-¾4°x©3­"–pÑALÒífÜfîwÇW×éÞàH‰‡]®! E=_§…>_–g)­€¡QJ yØ+ÍïŽ&BØm·áM³½Ú?¾?ÑÜå빈Åv·­e¦,㸫•—é,HN˜è¨¸,S°"Ê6ÅÜà¡&² )µ2!“HÐ[DihîQ¼º;ºËBäý‡ïwûÛóùœ÷·ûï>–êÛýƆ})T£&@w@KDÇà Hi‰ÒÄbãÃàº5¶÷·Ù6Çé’ÌF³£·ly¤eEÅÑËUÚ\=Їɭmγ»K¶:—Êhp¶˜æºp™kÌ-ÏÕÛ2E¿Ì,qõ¨Œ…˲4¶h-Š5D®ˆ1"šˆ‰™·FA ÷Fph÷¤@ÓÜßdeu "‰*h‘ SéD'†¬›Í6Š$U´?S9dS¥l0U¥äa3ø?Ö`éê„8Zs 7ª‹6É!JHD·³Ÿ•H(4©3!]EÔœ&%šXMÂÑØ5z$µC L€Âû.+„]B¸:VDU$YwÒ(ìÞ¸Î#ÝTÜ@êèð§•Hlj°KpúÃD$Dä²Új`}h° Ä»0$ú5È銥Jˆb}lÓÙLj@¸@¬YÝTC ¬âe4E¬¦œ®ìiÞUyâý<¥ ß‘ðcŠŽ^– v bÜѨk6«O“” °c®&€®ØÍMXÑUÅúG‹.¤\}ÂX·êÊhÆËéqVL­šöР’U`ÔÕä§ Q Uap-!~C=Áäe-§ÒŸäªT%E4©ÁÄÜ+þPíù(é)m‰Uu® #˜H°uÉ!M%¡“@{D^û¨äB X%T ºzˆ°'C™H8V«¶®ñ@€Œ´Eó%+‡ ™¼§É,:5¨=/BFw{¯oԾݔ­¯Wìªx÷R%í'5D>]}ñŽuè-c•uO¦YhnªTÕ”•Юƌ T¡ŽNúg¨¶ì}q †GÔnvŒÈÚÄÉˆÖ ò Q ¥õwI”Š–ˆ H¥„¢.åR[e@½Wa™d“þÛÔ—•Ì’4%‹”€Èª’€¬*zÜî¯óܪ¨ZDßÁI’&L&6Ž5|Ð!ÙP`cMCŒ7»|¿·V*ˆä‘«ÐL£93µCÙÔ¬‰pPüøÝÍqÎ¥…×*aA “4ÒÔd4iƒ &êbjúðêøñÍw¿|úìµ:2'$VÐY ƒ¼>7È_æ%©¦Æ˜[ó!ÄQ÷âo†|:ÕÆp¤VÒ†´À¨1_®Sõ‰Xš&Ô0UG156nmx½×ççëe¹†æVÛR‚µšá|)ËŒí¸ßÇËôi^j­žlØ û›Ã\ªør¢î·Zņ¤¬â"ûÝ¥a»Þ0Ìs<^§ëµi¾“a¬sñÙ,gZRÞWÍ©©7¢ª":¿>nÆ<,^n_½¯×éf¿÷êðúõ›Ý~“TŸN_œæ×ŶyóñÃï“Ê»ÛÛó²Í†‡ûãÃñæóù©•ë&åÔʗ糇mnoD\ü4ìþéÿü×Oîñý«7ïvþTb™ãîÍ~7õùú´à7?¼{š£…YªKYb–8ÜÞ_—SŠ|Ð⨬sÅ~»Rýôi. Oç)%9îÞÃçÇkáŒH×KÛï6·‡7—ëùñ©=Üo,~þéË\$ܧ&-LR8‹{›ªìÌóNK‹¤E¤ÝF6óuB.ã§ÿõÓcó¦_¦ç!EsºÇq»ŸË|ºw¾yõ Ùæëõt©ÓÌaÓRà9JJ*dÚˆ»Àðæf˜î!Ú’j„5U€Y“ˆ1TÈ$&ש\§9ÊÔ0lv¯ÆÃ†Qxûj—åêHÉ2WX¦š4MbÂÍÝQ[‡ýRM¢)1²`»Ä¾VoP±[9ƦVU-… ²´+$’j’¦PK)AUíRÈVéî"¾ lÁÒfY,œèÅ—º6Á¹Nºs97¸º&c)áK‹¨UÂ<\ªPÉkµ6‡KÓ 8 Z„X€¢J ŠzG÷NÀÖ²–U…I”&Í$[ƒàR#Ü;’Ú×É¢&‚€Zr¨£Šg¯Þ/ÚÑŠq[é«åÍH€êIà.ÑDˆÐ ÆDñ*‘:P‘¡!Ú(’“jrUemáÑ× ‚ˆ†kSK&áNKI¬ÙzH£B¨J %¨¡èë…BWr*¶Ú:ARVób/µw\%׎Woyõlw@ººÍ^ìÃ"ý¸$‰¢/ÈTí1ÑeÀBЬs³Ä%ø?£WŠŽ£ê·±H¥££|¥øK£I*=[Þm5«(¯Ù£Ï }üã·¯ßÖv]£Ó¯xÊ••Dƒ¦M_°X¢Ý¤Ô‡/«:fÝa‘X™VkV–†ƒtÒZ”•¾¯ìR—oX˜VVD 'DÑ¿³´—²ˆØº3"(Ž I""Ùè"’h+í[‘ЕBA®´w óî"xÙå‚‚ÑáyXáý…îÞ¬’2$bõ7¯ºf¡0Uíàé¹®.ÊQxße‰ƒ+j¬71,V,(4éñ¸ñëREˆ¬åO×u¥ïWú\h"Ú+£&­³t)`×ók “0©ÿ«‹µ& 13ºÁDš&˜æD@‰£ª23dô½¤ÍXEš©b+Ðspó¾j•PïõÕ¦AÒÝ…Ô(^ØK“ÒTÑaý.t¸C€¦¢¡%$ÆŒ­À[w^ ÑD¶¦ÑÏÉBë£wdMÃþà>‹CS‡õ‹(©mšÊ²,Š&UHöÜÿv0õÍÕ0Š·ŠðÄìÊP(©èÇh ª§Ä[Áì`g¿hh„Kj(Œ”úÂP]¥ë5ÔÓRæë’à"ð.dͺrV† B“ntw/år-¿|ú|æ©Ì×îçp·aCJ! ¯›3÷™¸12»ED íæe¾\Ï”dæÃ¡nšáõZ\ȼKû1Yóa°Í ËR{l)~.ËÒ,5P•oûWã«1?/×R$D£F´b‡|<èvØnÿÓ?þé2]æ2YJï?üýéü$ê·›a®Óõª‚hÍZqM^J@³Ë(QkKCÞŽƒS¥z3a­€x£Q‘(ûœS–i¹˜‡»¼=–ˆåzžÕ—¹þòéü—_?9Ùîbsè IDATKø¸É±Ýo6» l7øíØ>Í^uº\e™[Mš’5ÊRj)ËOþùë©þú|þõó§Óel¶R~þòuZ–<è÷ᆵ§ç¿=~9?M–n%0^Ë"Uk¤Ôe¿ß¶6•ņÃá§OŸžŸ§Ryèü|9Kà¯??‹µ÷o>~=?>=O.sãðõñ©Ô 6ž.ñt™=·ww·o P%Š×ðXDã|=ío˜¬,§ÛÛ×øÓÿúëç/K©%@Ôêçz]®0î·#`q`ZŠn³Òuö¶\ç ‚¨×Ò–+Ôª„ ÆA ÉÔRu˜´»ýþ»}Z$cè®Y¥¦Ih†2„·Š¤‚êmº\EüéyºÎçh*Ù*.H´ÍÃ^¬I¤¼ß¿N*y;ÀÇVR­œ] 7Ã&--ŒzÜK1шäÕ²hÍAQË.¼yu4!$AB¯óIé‹HͼÕTÕµi«6qFmK™ÊÕ—©ÒKT÷h yÿݧ/>Ïê½ÄgÔ(ÓÖëÝJˆW× —0š ÙÔ‘ WHH[ŸÅjXx$žUT|ˆ¨ o5DA G3Pl¡J¬#‘RÃMZÙŸ@4Hˆ¸º:&@Cm…84ñ°$4‰±‹E)®æà!ª$ªŒ£uЧKUö\ zÆ]šWËÒМ­õ\‹7ªt 4¤7»Õ¯¦¢ˆ5߬½Êö ±Ý©—¢è¬uô0›Åj›#i½0×C?Â¥j­>^ô¢ƒX ƒ"1wó0€`×ñrEMw¾@ã7%¤õf¿°Rº Xú½3^>Ö+›„@Ü…BíŸþËLØó@PÖoL‚Îyì׺—v§¯®èµ*Õ1æÚzÎÚz±w&@躣e'Žõ3«u#LOšõ¾[x@hi8¾ Ñ!5ëò–ž‚íÉû4Q¥)¡ê b"¶v:Âõœ-$T]££ó£çâ$ÄM)Æoºh "Ñ€„A”&BÙz`Nô<8W1­‚ I 4QBLùÂíÅè:šº°Ó1{/@©ì)1Šõ’¡¼àÿ]¢Cøû·²¿žFJ/Q~[ÏÛ¨txˆ¬¸®~|¨F¿~÷!hõoK¯šb¥ûv”ËeJªjzÂ}6ÇÛû¥Î „eé1‡F„¨4Z ¡‰´¬–Í0f£¤7゚Êܧ¹©QºŒ@ A 4kpR6G‹0š‘ K ěÕNÖ—¶÷0‚MØ:™~k˜]¼tX«(ÖÔ¡ëÛpÉ0gDg¨YÏ7hˆXb“%‘”I,Ì$A–=%s JƒÐÀq *F³Èb¶11ÉU Ö:wbzg‰„÷÷¯$Ã-µ´¬CêÎn ®ÍÄÅD&ÕœL-! f¸OðÙìŽC­î‹ÔXY£,IÓ°½­µ˜¥àÂê&6lvD½V&bwØ¿½KÁTÎË2¹%dÝ:ìÙÿ¢CUÍûíÑ‹©@ëµ2Ê7)§10i‚¶·¦¬‹üá7Åä—¯O_.> sÑI›ãn³ÎK\¦y™®¥”ˆåryЍ~)^½í’¾ºÙGW ³17»ñŸ~ûáõÃ÷ßOK½œ.Ç톂êžu³Û§‡Û‘Íæf“e.3hiàRYêòêæn.³—ê¢Ñš&Leö7·»qºÄ4ùí¸Ýn?|øðøÓÿñßþßK½µ?~·ûù×Çýf;¦ÔêöÃÛ×›œæòôõÓ¥¶VÇl¿ûýŸ¦óµœŸOóã~;üþã÷“·Ç“Ïs[¦åÃðË1ׂÅoîv›QéÃÿùïÿ~ªËö°ýúõétž¬õºQl²nn¿o¼4G¸_—vwK-çùÉÒæùô|=—ê…hÉü~´¥úÒâîþÕÝÝnº>6ÇÛÓù\ª/­žµŽˆ¥JÃÓÓOót9¼½;Œ»¬µõ.…cYJHÎ DâÍÁΗùt>¥t<"¼Î Î<švÌQÊDìnnŽ‡Û”r- ÈÃqx\ÄÓa£É¢:’Ô‰¥„PëÄç¬;g ÷H9‡3Ð* I-Ò(C¶,9°)4@èÃÃûNÂÉ–ÇûÛµì^Æ»üû´>3›»áͽÕ*áVE3k²\ÆujçrYàS%ÕP_t ¯p‹‰¤T8½T¡¶Zdm%ÐZc-ÑO¿”åÜBzY’¢U‚*…IéÕC qe‚–e;¤äJ^ñî§¡Õî¹U¡ªE‚ÁÂVd’«E²@‚L@RˆÂ¼ŸX4!(Úº!G’»Ï&‘N‰µÏ$\³6F FÕ$Ch4Ä@7& J“šÓ@³•'> Ô¥Fkát°ëoV>‘‡#»áÎÞìò~SëiqaoOö/AŒ/ù!tª"ITu½$ö»ƒˆ€„'tØ• •Öé =û#@Oátq'‚†Šª$¡HÂKÖÝ]¹þÖ«_P‚ì‚3vé®~%_ÖÒõ@/ÇètR„º¬Ë–Mê#et¨v»0ÖÖK°¬›•CÈè$†^˜öìNo󅊱óúùH_¤{‚žõ‘$NZ}™e}E”‹ Ü4ïz¨^;ðI¥³^š–½`Š5 Óak’Àº¿ZÒO›+óÝUMl½ €PQQ¦þ¡Øy¡š6j&:P½òμös•@$2½·ëO‰Ä ¢ß{!ñB#_ÇFÚ) ¬bæu¤€±þ—¢#Ã4Ä;0U!ÖÕýGQ(TÚºÜëîPíEöa5­Ôï6^uˆ„:‚)¬Sœ£)dŽW Z_6ŠH.Kkµ‰¸2©ôS¹†¸™5PÅhTËÙ š´Á¢±ºLsu Z(E44ÂDNˆG„1ÀæõÆa¨cx£yÇŸ@¸´©`S˜0Bhä%@g„:œnI˜]܈ÄÔßcêfÄ(­¹¦¡)”ÎjžH&(’"¹¥¾5dUqS±A{™×ÂJ „J€0d‘ôÿõnM²dÉuÞr÷½#òVUçÞÝÓƒÆC ‚’H‘&‘füüùzQf ‚ƒéîs©KfDì½Ý—äGJR©#‘“ÕŠéÇ‹PϧÉz“ªUŒVB%n·¶a-޹N‡9EŸWÇsàz+1SX±¤aIsn ø¶ùÚĤè4X¤ÀÊN5FÆn³ÎX74ÛÑB¬P½Çh+}í#œô¾y ŠØå$¥Tï7É"8G…}|w·Þ6€F°@jV ‚ôæzHØîÍE!ÌÄL @Œ6UŠqתL¨Dâ'%g À`ÂNqj!A‰"OzS"¿U¥"SGÐPUÑ Ì]ñ[bT̓iƒª"V¥¨Q=II ”ÞIüzcÈžÀJÚ:)D¸Rœ1 ƒêÜΫÞÞz—ÜÁ(Xf¬…H–“A!4ªÖ•×GÝPý“(0Ÿži_ÆžåB>Zòá«0…íFMyÜë N$ óa¨gÛ ‘ÁgÐ…ŠzV3°LOŒIâ)áIÀ?aE’J}'-D$(ˆPÃîªÑ\‚1ý)2åŸZ¶”/þSï2y—ÊBæ¶ÅvˆSì_‘ÚpìCaf—Ý…hS¥*$½× t‡Úëò&I[‰À5ˆŠÙ"(¢"T-н¬©&–÷Gìd2CžrKNu%+TK*­ÀÉ œá¡ác¡æ‘.Wœ9·Ùk¸òºoDò«ö6¥@,1û Hs“„ØÉ^’?6¤9›ª0'jáŸÆ°’‚äØ{û×N©}£–C¯ÀˆýBKǮԦdH_c% ˆB!_¹ZæTU*Bƒæ ‰ŠPgxOçeÉH‹*kiÛ,Rûx*ßž¿Dt±8N‡Ã݇çe5­×…tÎB9Ÿ"Š»Ãñ»÷Ösë©ôv ·Óå·ÑÖ¥mšÊÝeN¼ÆJEPîïîó¡ÎsïO=¹g¸Ü}ß[·¨oNÁøíÿ?ø:ŠMÕ; ϧ‡?ÿí‡> för»z…BdOf,>ÊÛ÷ÞßyyºŸ0ÑÎè6—ã©>¼ûñ_¿žî?¼<¾¼±íñe=¾9/w÷oïgø§ƒþã—µ9ƒËc.óÓ£ûümC‹ö":O·V‹}}ú¢ÅDÇmeÑ~:LëJ»޽o‹³¨OïÊ<--^^®oïï¼\¾~{îR×íëóxZ¨õÍõz+LTõÜùðåù©…^×뵞¦ñ´®åpþ«ñï¢mÝá#¦ãåpœ¬„xÙ<¢ëTÇað¼A×Õ×uÝ:=Ü›?/kkô†>—CLöômùòòôôø¹·µÞåÊîm¨Ä$úáÃû/¿þòíé+A+8f°y®æSqw äÿ¡ªe˜‰ÀkI1ßN•|ÏÕ&Òº1¶1«k¡×óáÊárïD7™çɦ:™ª¯©‚«¦,«N6SÄ;ÕêŒÉ0:ŠÉZ÷xÞÜnM«Sm;ŽÃåÝáþÛòÜýÆœâm×>Úæ7]±ÁW8¼E(é1O'¹µöAJDH0Èm[ºGpP÷‘™ $8<µ!ž 0’&ˆ•ýö£¡Zóc½áÈ 5”BÄ«'5‚%˜PÆâ™î-¹«P‰t3©‹ºPça8REf1¥Hh¨¢tQ¡•ÉÍRFÑ¡€„Ñ æ›j‡‡#œô ½ <4¡™ZòÔÃy&fšÜ|vËî¼6-bZ÷] ²DR‘L'Ä2´£9‡¸báiå vBœJ¨ì¦ZF¤fÓÚDU­¨Ñ²ÜÇÜgôÞãõŒBh’×;%  Rc¤‰©á‰¬ž)ÀÉ3cÀ¨*9+…²×.Ž0"à;è“ugR“ š%NQQfÆÙL%);ž û¨°ï²‡Ù^›„º!"^#x4+ìSšj‚?_—XÈN'ŽŸfÜ‚‚ר²’‰}ܱ¦¹O,B‘¡ 3(¤ì!ç(»è°¤¬H €íwâP/CUe$ÝßÁ %Oɺcá³Á ”<%ín©l4`g¾R\³ I(LÒb³—K-¿‡ÜO€Y-Tqèp'_c‚»Æ2ÿfîhPI\÷Êæ>›ãŸRT;EC·eº£Eò£CšCCTráÈØÑe‘íÉ’/ (© èŽ#6M¥L”š›Ü/l¢¨R­„ jQK½9€b¬S„LjÃmH7 ex§ˆ5¤¿&Ø ‹¨:ò*+Œ,D÷WòxmNôˆÐ×ú€ÄËè¡á¥¨$ŽjuxMF‰ýU‰ÂêZ"ª-Ô2‹ª°j%D‹Õ"ªTW˜ ÊþFšøäu‡‘"bêI†Ujx „^X !Zb.õÇ»¹áçÓ٭ݤ¬ŽI”1 #¦¡µ”y„z8E‘9ý¯m]ÖÑ|èØ #&Ÿ)® jœ¦´ÙDoŽØ®W‘zwwa¬:ñx2ïݧù¨ÆNÑTûççX–ëãÛÓX·¥õÛ¡×áO·[Ð( ÔZ ÄÝå‡Çç'‡çÃéd¦÷ç‰cU‹Ö¥¢aSŒ«Â_–etÓÚGRbª%:æ‚jè"8½·ð&0…©CÛÚ|ª:‹=|^Ÿ»o­_o›NÓlå7?üÐÛÚ6/RŽõҹϧûϧzü¾7e‘ÑY­÷RŸ¾ÿ^Í·Åß“ƒÊÇ—~]µƒÙh[x—S=èÌðVpÿt[ßëò¿|{¼.O›Ÿ«<¯ßzô¾öÞÛ–¯OT=¹Û­5â¥àñÖ<²®×_ÛöËós­¿½?{ôÖo·ÅáR‹ðÃ¥˜™OíE§s™m6Uo1MƒˆaeúøáýËË˺¬ÞºHÙºè~:Ý}pëm¹®OB0j÷I*‰8”ßÜŸÛæº¿mM4…DDŸëôÃýGÿÏþâñÛãâ^íî÷ß¿?ݸ—Ž®¡, öa½-ež.o. ae®˜Å&ÄQG©RQ”J›ªU•RP¥Þ@oXé 5|z‹‹â6°µŽvÝé1–méÞ–å[ë_^žÖÛFʶcè˜çòr QÏz—ÇÛ EæËÅÊáׯ_ÜÇ?ûéÇÏOÏÛ&÷—Ë·¯/‚ª“¬ Vµwçúõ¥/c¥7ÆöûïÞÄtw]^Öëck«Z­µQë€Ô uŽÏ×¶b¼¿»W`ô%œVºk©”ÂÑAY[ó¡ª!Tb:]ÄÇm\ó†2ŽTkM Å€›{D)èÍ &E¦’Yۡአfk߯ˆ—åz]é£uç¶tï‹"Xªiðªe²÷ï¾k˵›ÐÄÆ$2AK±Ãùtê% 69TÌBnXÈÀµàúÏ7x7ÛâÕ·uèXyÃêk÷¾. o¸õæcŒæAx½·ØÀ¥{ÃÖ[3—pÒ%ÜéΖ­}eÞ«"Ï;,u…ç´ FƒS=Ì€*b'5jDBwö ·‚ØÕȹÕR3q1î‘#”0qÅp˜[˜Iâ©¡®fr¬G1ÜIÌ5´•{;LHD(j=•ýh¨ ?$4´ˆÖ…Àb‚ÀL_ B"œÞ"Êé|ÓÒCÔdªö?ýøôôtU/¦‘ír-*IOB’‰$§!¨*D”¦,Êx@‚û*#@¤fa0¡B°ÈÉ`_%y4©Lâ* Lòê¾ÆV á{­+ºÒÃétÒÁ0ˆûë:ˆ{Ô*SB ͈¼N†„îÉuIºæ ¢tU‚L}c.6‚d˜©0QG ¡YÁÊÝPQõZ¡ŒLig°L,W›*Ô]‰4î)̱OJùE½¡ÀJ½ìù+)ÙTÊÅwÛsÞ °[,a¶çíá°¤xA48¹ÁÐý{ÍbÎËy½M*WêuÄCEÉl—¦i/äô”°D09a °œl#ÔEE#T$}ÍÜÑÌzFÞOwð{ª£Õ\Ô= 1òØÍ]V¥ zßÿ"ìá?ƒî‹›˜À˜\<í‘-HÞÖ„Q˜ÊA•ªªâÝc×:¨)‘Àv˜ÀBû&«•Àö*J~¼0Zê„(RQÒPni•Š[„b(fQ™!J¨†©wôp ‘ŒMCó¿;h`Q-¢ e÷á‘Å ":Ý1à>FèÞ‘jíÍÁ¾ÿ3YbäuX ‚"Ü/Õb¡*-5|q®QTÇD!,/å“D^·…ê“ „¦“Mt-¢š»i-©F’³ƒZ„-ˆèû'.m%ýñj¤jŒ€ˆ+zgÓX# usƒ†RÝs=Ôótùùå:ÕÉ{ïálÃ+õrüróàºÑŽUgSQ« ®ÇÕÛm[–ÆŸbÐTì°lí7ÿyk·":¦ßþö§v{þpwq«ÛÖæ¾ÿt_ëµ½€s:ºxs÷ðá|ÿî^xsùö\ưRÛ¶ùwß½ý÷÷×OëËÓÓ—Æ›ÒTæ;TPµb½ ”¢u>z4üáó/üõËóà?üòËÓsXQžÖgÃcôÑnKÄè±õZeº{^n[ô÷‡ãT1]êãË~ Íáˆé»‡w§Óô?¿^ÛÚ¦r<× j×eÛŠ6|È¿ü݇—%Ê¡P¥ûví+ZÏóÖý¶…)¥Ï—c;êôîPžnkX6ëmQßþìÓ›_žºÕòg¿ùKÎõñéqÙÆãã—p[¶m¸¿ýøýý›OÂñö|/rº®ãq[Ú¸Yñùø0×ùrxxºÝn·›M¸K-Ó Ó„A±e‹Zíáá²®K oë:8Þ9‡Þ¼ýøöûJ™§»uk¦‘C"@EDc· ˜…hŒ2ú!ó.¦‡*]«”2ME§"Ÿîæ¿ü8ÿúÔîÎó$ÚÓù™–eø@ëePE´;·›½,îcQ5;Ÿ(CL6—Ñ–­oÝ»jØÑd6J™KQ+êR´Ed¨² Y78q_ñܰå¥MxžåÍ;\_ÆvåÖÙ"¢­Þ ¸Ä¶l#dÛ®„»·mô¥µƒkT£m‚&J! ƒgòÙ‘Aä@ºq¨BÙ‡s×"„ÃP˜0vQXTOû]2 NQcúw¢ƒ1W îŸØ+B%B¡Š@{ÉþÄë ªxPÄ$ÍõaF­ž˜ëW­"ýõaª¦¡œ$'0ÜB]¨Å­ÏÈ—ËH7 ó Ž@žas×öø|£`&—»™z JHÍ pS } IDATîת2yT*ˆ¶ ùDýYçRI&rÜj¾ u‹]G¬ ÏJ8ªEU5#\ZDU"ŽL{D8îßX¤784“\»A%ÂS<' Tê  Ç΢Ì-’gŸ¯+Fìlt%\†2;j¡û˜Ndîé*åÎÚÊA84ÇQÒŸ¬°¹ˆ¨²xnÌ)ˆŽ]D(iPßoLLÂêtÍÜ–#ó`hi¤e:__1¡’MƒÜ$BCEK˜+^¿n¡PQ^‘#ÜPŠh$dA"û‚²‹9Ô´{¾ë¸GõÈH´_ÿ»…œ¹hÜ—ÄLà$vîÅEª%¨ƒ#B%mE¹DO§e¤q@ ;…Åħ‘]Ü× *™a2aZ† U_õÕPQTÁ޽ys ¥#ÒÕ_EI§c¢~3yŸŠ‚T³#Dµæ¶Ô*&š]Ù<„& OÆK$M3›‡„-\Q"öZŸ’35«"ƒìiÉßè‰Aô2‚Ôh4µÒ1 Ê\O•2´ÑÝ‹(äíaú›¿þÝç§kÑIâ°n±ÜF„¨LNmÛZ ¥”ÙËÃÝ|9œ¬XkŒâjwç»÷Ÿ>ž/Ÿ¾\…ËÊëv½-ý¬óTΓÞ½uõÇeô#z©ãáp¹¶¶-õtx áuÙn>:çëèÅuÛV_#Ì·äØ|ª“ ÆÝôÃî_œ¦öõz[—5Æ€qõx^Ÿ¿]{gÜ,œ[¿nKär:¾yóö¿ü—¿ýôÇ?>.Ë­ÔÃKkO„®v4µ§õZêaŒ®uš!+‚p  [àÛóÍéìÛµ  G+•ibLjúôüõ—ÇïŽ=Z2Mz,ðáÎŒùÔ6ªræIçï>^j•æ~<×÷w3‰~ü«?þúuq»¿»>< uæÊ­ƒ¢u*Z&‹¨”.DÅÙÓ=‰ªfs±ÃaòN‹¡h™aÁFŠQd«Ì‚°øR écƒË  Î÷0‘!¸=âe]¶¶ ŽXWïÖÇMê©î­]>ùË£½y¸_Ú*”¥Ý|ëèô>„#ÆÈ|ªƒ=8À„ââ½µNážø#ÛÝd>€ÂòƒÅ Ó4™XGQh‚AŠ µ+jCȶ»q½æ3@X  S MqI‡ìüNÐBºo%@#ó#h…]QU ”ÂRZɰp+®"´P˜LŒL± Hª M˜FeP=£-ù•ÐáÑD)pópgQ-º[óHMI9EÌ P™ ¡0ŠAÆðw£Š3àY“„ùñÍañ¥‡PöuHÂx2§“”€øÓúJ „ÓA¡#d0H*ž…2'ÿ„[OJ:LëtödàeÚ]ö'¥Jä’+ÐBAFÀs7EÄ«g9ë ™üIN@.tæÒ$¯‡»~R²Kt˜×$#Tè¥X„#¥Ì{±q'eî6ÃPA «õ”ÜÛ=pôº®Êæ&T)Ø•;’ RhÙþâžsPR —q3ý´<×@Üÿ¹.PaÕÌ¥+àAÍAIs¥—Ë+Õ PĘÈ*šH ÃÅ„ Ï—Üè;(C໤yû'•ð\SùkÚjßååuO¶k!Pƒ¨¼>öì®å–äKí¥C1ÙW¶Š%Td/ ðOHŠ\éåÕ„DÙkƒ))…H[ŽäŒ #IMà… «(Â$³x,ÒBDé:#¤˜RІ‡­=ÄCX @‰Éƒ€«i'Å e@Í©#Bdˆ@‡‹÷@óá6Òê#ª01ñ"CˆŠUÄÕà Í"•Ân¢“Tˆ¿.as=ŸçCk·ŒÈ©B¥¨†(M ªiÝœ8w…V±Ì@‰Êè‘Øe*0‚4áf-€b¤ aµØežF#ª¦†Ã4<ŠBAMZD@jµòòæj·Jîá]4‡ô PGX)n>´IÜTcmj+1j›«.óº^'‘ q™.?/ë†hë2Fo×—UTß|¸ûÍoŸÝ…£O}”ðá4¥×¶5~°âÎÞ|]P§ù¡<´QÆØNõPµæpšBó‘T™Ê¥ŠÖ—Cãî<—J¾{ÿÑe¢{Œ»Ž¸Þ^¾<‡ªN§ç—§ëºŠàátúüíÿó/Ý—ZÕŠÎÇcµK@æ£^`º­åÛ·íÖÙº•ÃôýÃÉê=f]‹<¼ÿôîÝFûqüå:œ2·U›÷IU%ÖÛòx}Ñ1Zåþç¾­½w«§û‡‹j¼ÜžûÐËñþã›÷}ˆú²lçóI‡eyVŒÃ4ÿÅOïΗ»·oß«À*¶æCpÿþÍÃé º.cíó·ÛýéÒ{}Þ®1Z-Q$®7¨³xÿý×Ï?ß^?TXB¬oýû7ÿËÝ}íMÿñç\×oÞŸ¯‹{@¤uÙÆµÝƒ5 ‰>xÿp÷Óo~3¨[*9 ZC ªÞGÈÒáNH ÁÖÖëí¥ûV8Ö6ØéÑDç¢&Úîe#Ó0Þ-1ôX¼+[_×€ŠGèp~þv».À$c庹S½¯a2EÑ£N£ C¡©_N§ÓÛ£‚RеR§yšŽµG Z-ªViJz)SQyw9˜ºÌb}4J™"ÃÁL@À 6Á Ø–>ÚÒÀXš¬ôxÚ<ÖÖýº ®[»mc¸n·«+ÞMß¾=ûDƒÇ* |8¦"‡ùì½5˜ÉJfÎ’:„dø‚JIÛœ¨Ta„„‘ÆRæò»ß|üz}f€®€¢ 4{àa…B,2ãR$˜õ~JË04¤«@,¯]AP:˜w•"„8*J³¢EŠî¦0ÍSUäc¢'n±¤©Ç†R#04(t÷ zÓ hÀ =(ЙçáÈb½{O>QICc‘‚Â|×…j¦½E#ƒé{å+§„pÅ™Xòˆðµ¼%å8DJhÙ«[9 òXHÝö‡N*øÊáÒA„@"]Í„rŸÈ! D¤úwçsEÈéJRÅräl8¦æ&Ôy}”t­ä0–ë4ì‹Á|1°_«{é½AÉ (GÆ?¯®! †G™x¶b‹{Àïß\¶ÞÀI¢¾Ü¶§ë"Ô é.Ž6š¾9[9œ£O×eMÃÛèRgUŽCÄ©º|úô Û²,­u4F¡`žO¨ÌwžÛÚZ>m• ñÚÇ*"ÑÇ&]Þ½ùQa¥¢ˆ^NçÑI+(ULß¾{ÿéûÛõ:¹MÇËݤ.åÃaº-eè$UQtÖ©H`ÄÛ·÷23H«R ßï–1ù6Â5BÑó õŠ£*®[Í·mm£m½hmôæWô±añvCÛnm½õ—Ço_ÛKöœT½I3Tï[~Lõж­Ý»逹Š™LUÅ ©ó|TJEP$D½@ƒÂ§ÛÖ÷Gì™qRÌH±y´üÀ­ªPЧ}ÞdÇgí}+HOLOö¼@‘°PX`ˆÌÀ)€ÁU`DX±Ý‰hTz‘i#Ž.,ª%KZ#¢ç×à4 VS÷ˆc' Ô s¯ºå¸±CŠˆ¢"¹“É!©f»TX_9ØYƒ¨’Æ]G:Ò‚“k‘¸’"ž7¦?ñ¨"`{JÙt·Ú©ÏÃYrÎ5 ª&3œ¦xõϤ€n'O¦Å:r— žï?æS8#ªð#únžÃ(Ç^NÈ+\:Ž‘mˆ€Â( I”Åš?“}cËpÐw"ªÓÑ,&ÚT3*D…2^‘­@€‚‘sªÕùB-ªj‰oØ[ŒÉtOºVîTVži¶¬I¼nµe0"“ÝÈ6D¾&™KR$z^U$*JÀ©’½.QE@eŠšéµ½Ë±ëÇEšJM 8(á0 ŒéŒŽ< jžºdIæ*Ò3œ'lD$ˆ!?]† AHNoØVÙ zº/“‚nÄŸ´ˆLàEªâ-×z îr&5hÑ"›("ŒLæ[Á×¼µgœ*C@‡*øoC÷Ä ÍbŸ%$²m ÏŽ¢ZZŸÃBS>QU«@£(½X¨õ Óª .A„ÄŒ7aŒL&€èÝU³eP‰a -*¡1TD ”b(E*ÊÑÈ Ri6tl2åOCŒR³s1EÎJù V5@#†J ©t@c*)Áa.a³„*c-„C™Ÿ'•„X ´e„³Ã†G"þ«hhÏ„X ©pÚ &ÒŒjŠbz˜ÞÚËÓÏËö’dÞ^nÑ–æô2ÉÇ;VžNU¦Cï/o–í¹ê|:È=¾]OÅß¾½_ûµXLÀuëìkDƒqë±t«òòÓIEøüÜûXŽÓÁl2 ¸~}^‚r(Óåîx˜ïŠÒ€6p÷îÏ®·a2ª–Ëû7Ï×õ0ϳžL  ç»óù0ð1ÕRO‡YY§IÝ…ÇÓåÖù»ï¿s¿×óA~ýõÛ·¯ÿS$îóçŸþÛÏׯ ¥W• “Ä᨟öí¹[)ÞØš—)惾;}üÝ_þðþþoK9ÌøÏÿî§ü¬£ž¼µO—wß}üíùxµLo~}ú áüüôÔG«“ýü+þÇ?<>>}Û0âþ|ø¶Æˆ¾.ñîržªm&‚ÙF_®O/CO×ÛK—mlâíñzõÞˆ‡¹½üñ×ëæòõù6÷÷“B¶Xt1a˜ÐûÖ¶ ؽm-„å§ûãTŽw—ûóÃÛeëÛÖ¶­Ó£c²©Œ6DÅQkx‡»÷ âz»µÍÇæD6Ì'«ŸÞ>¸ÉäÄ<é¤&@S˜AjHx¸êt(G;ÔÓt mL¥¾½?u3T˜NQ+'2F„؇¿».::l>¿ys.S] ³:†ÐņªŽúñRýv WöC©Ò]7§P¬À’V‚ïïp¾Ç—çËâ1¶ámñÕGßFïæhÏäpmÞv÷æðѼµæÍGôtJ#E¢#ëcôÑš 2(%-l™6ˆšM“Ä‘%¶Ö(¡¯¬ëÄ£}$Â% á‚ d'éníSš2JÊLDb0‘ Òˆª«¸ªˆ,—&†ì©(Í…9ÒhúdC¤$a6¨pñ ç¦GDHCîB„ÈÀbÞ=CF*Ò%¼’[•lG$ýLÔÑ!É¿€(¹›‡ú¾[ ¼.L¬ìû¬ü|’ÎGÙùóBË™=¡ ‰½ˆ\Þ[ÙÉZûT› ºô, ¤A4ÔP>Zu¿^+rg †H °œÏ$EWê¤ÕÔM)bE0©Åi ±¢4Ó¨‰”·0ÒÄ]É  18"Uª7¡QFÆYö@–YHÍ€07•] Åh 5ª³xÉw@5 Â"ÄÑ&TDŒ|ÅšDÑPw Þn½„J@j’G(Ecd–!hæ“£f¦.]# D3Ì}怨„º ¦Z'e8ŦRÑéÅ2¨°T5H­9×â ©.¥«nª&…Oöæþá;gŸÎo·Þg·ZŽOÁÇ/Û6Z_û ŽëhO@ 9­Z-¹›ä¶Œƒyp”ÙVÚµK›°óùx™êôüÒ×~+è¿ûáÍÚäeYKõÑËáNÇr{yþfÆ¿þÛŸZ?l·¢¢EÕÊ`±q8̇iÆqZ7UËT+d;Ëñpüñã›}šôy‘ÛíE°‚ÚÃ×µ¹ã8=üÙï¯×öîÞÔÖæî¼Üÿêû;”Ë×çX¼•Y2MÓq¶ƒÎëóµG­o=âo–¾þ¿ø²{¸;*úÛ)^¢ÝM÷Zl ï^S9ΧáÔINwßßÏ¥írÿn¬×ÓË1·Ö‡·§§«Û–—¹Úl:z#Z ³z:Nüúõùéúyáý¯ÿðù¹•ÂC-¿ýþÝ­s»mÝ7Œ--Q¬RCE§"÷ÇÓq.g·yÙn·ëµ[ƒ¯kNÛÖk„Ki.Ó¥V™ÂƒEMÛÆmíf¤”®;ïÿgêÍz-9Ž-͵ÌÜ#öpÆÌdry5•J…º]Ý( ÿÿÿè ÜÒE2óL{ˆpw3«‹£j@O"$ær‡…ÙZßÇÕÅÝÂ+t­!±/r®Ã(2è,“B†ïîoïï^.¯ÅÂKýáüŒQë`0 BÓ¦¥÷nÕZÍZë1æÝÞÔe-4“ÑtiF[ÝÌYæË¥µ5ÖüL8¯¯fÝ[³†¥{fÞÂânÞ蚥¸áÝì„eÍÞ‡«A_¡´p#͸9ãCÜß$Œ"b”’5n Ì£…#Èã~¯RÆè.™ÀvqqŒ\ôÐib©¼%`cf¦o“ýd¨€Ê|N¸„‹‡«AfA±b& 2”Bºf·Ü83€€eÖy6Ñ(Pb’BµHøAr¼ E•|nɻޥ´°%‚6¢µ6l#;Y^æò¥Ò6¬Q¨ (Y „AIUMU$!Ôˆ‚@Ò¨"Qé¶!v$»ÐšÁáA@†ä‘‡A¨3ÏtXX>R·È³dð–p8ÜÝ’.A݃J8‘W?VcÀ,<ß$¶¡1—,áÌ’V63ÿ'˜]RTÀ` lë-Ó žÜ9.9Ë9E%³r–(¤^175és¦R”éTK~}DΉï4 fäÈ4wœ™èC§ùv“nq!Ùœ5[¶‰Š÷™5ç^ºD!=Q÷ù×*a`É ‡+)&Yì$™{A sš 0̘Árô %«J7¢Dé¾ ¿ó °oÓ4%&è<-Rïû5n:dlL‹”ß¼‡¬èaY p!ÈÎÈ/$G½¬±2¿¹ïrM(Å)T‰AXþ&mµ¾íˆ‰}7+nU· ssn)ÀÜ£2q‰côͱv7G7¹s–EEŠ‘A@BglTWB‘eO'JDG¥Šˆ2  EBˆp8`nôêáôYRa5Ü=PKÑîJx)$Y*ÃKHqÂ…xPc$סd)â8×rðáC\9`ÀÒ²R:²ûb1‰P$mòH%èG·4fH0zFodDdh®ßHl€Òâ¡AwqªÙZQ„ÿéÃôÖ­›©e2–„ªj-5Fx˜°Ïs·i‡»ãÜÌ™¢†É.%?VL=ñ½¨­•ƒ!…hJhìŠÂ(±°_Ñd¬Å½¨þøù®P.m‘袪P­ !•æc˜Ž7×µ/Ãíë®Jçv]®q^ú¹÷€‰ùõõíu˜öÇÛéðµ¿ÂË7?ÁÝÑ`KÖÛ1p>ŸT§¹ Q»Oî×›Ý^w3PwSÌ:üe½ÚÚF¹¹½Y†L"»*}ø¥Y˜ÖÝþöá›âUŒóþ8OÇYír9M»ý‡B°K•ýþpµRË|]—:Q´FòÃúòõx+çemë:†od]¸^×SC+°åÏ?Î_/XÝ®ëÏïÿðýáþá›—×>šÜݕϟ>¿¼-÷~{~~}>}þæc=-v};-·óxÔøûo§s¿þÇßÞ~þòvº\óLykç§×ß<Ö˜Ì}h¨N¸4éÞËýÃá»OE§»›»ÞOÝâûo?ÿzz…_ºó¯øËiñ_¿|ÙÝ<ÚÿøC\ßþñåi‚8Œ¾|yy{x¼y¼ßýûß_.×F A«îƒEÂÔ­„jDìGsùz:ÿãô²\.î6ÄZ„̳¬×ëׯSÑãþæþæîôvÞÕãï~÷——·gË>Ü<-æ½Sª  ÄT,µN;NA¯ÓÌyÒafTwu‰¢ÂÞ¹Ž¸¶õåí…£<|~˜Š¼¾Úè£öeRHc¸hqÔ:Éͼ¿ôX„ê|ÿéöóu½Â z•1iµÁ@HHiê—õŠè£9óðÀyÂM`íÐæÏçˆÑmíô«tï}ıj78àÞàW7—X-ÐǾŒ>nm)áRHªÝ–ìOÙ0IxÙ,£ÒÅÉ.‚Z-"ûÃΆ·áæ!²•…雯C-S eÃÈ¡ §FuÝVÈK]<É—B£Þi¡Ë(˜(B±!ôpÇ$dJlöÊJ…ˆÒ4 ®˜ÅQèˆô$ lûMÉmLâJ%ÕxÑm貜Âò§ Ñ $ÁTâ*""”(•I訅 Ôpµºééè…`5(’àuÂ{F†Cy)…†:©”΢êáp£2ìd±WPŧÀˆá:¹ÓÃÒÓM¨ (PvjÏ;¥«äñ‘V¢ ÅË:%ƒ£–z({³¡VXèž{>WÕ>¨êé-‚…Ùi Oq3é QTsf Y}ŒUŒŒê£Ãb@ݽV­OóèPºGoƒRMÁ¡Ž(R«(ªS ŒhÖO°¶Û•›]]–ëR°:°˜L2At®]‡H…ËpNÁRçâuqwJ-Òú"½b¬Ì•3÷K³µ‰gÁ$ IDATÝÅ‹ÄÍäTÿòüÒ¯¨µ÷ˆ¥Ÿ|YÊNŽ‡Ë¹¯/µeô֢І5_¢Â=næ£('âémµÑK­÷7ûo½Åj¸ÝCCÎmíàÇcýË+ýf¾-¥¼]ý°¿¿½ß®ËyÃa¾9Ì+D¤^®×€h˺Î;ìw{DÓŠŠù|צÓ´¿{¼¿Y¿ÿôíOîÞ–nÑ-¼µ7ñë ËÀºÖÖ¥ÍEF¿¼¾OŽŠòŸüáÿûÛß^^_žžŸ‡‡µ×uØÇÉÖ6¾½™þøaÿýæ,»ËznÇû»ýn¾òÚFâ|w<öàa¶Ý„s﾿=Ü?¼¾==ìäo_ýùõíîÃ7ÿøå×µIçõ2~ùòõ×ççóÛeõËíáæñîø?~ùùë—ó:d´q^®}]Sy;Ëòd=Ú¸¸ §íwõánº»½ îÖ¥ßÜV¶(÷‡z]V‰¡¢#ì²´ó~_®—Õ¼ oö×Ë8¯º?ÌËy}¾ôÇã‡__ž‡™Ns•E{€©ƒ»Z>Üî׿!v˜(²ôkTAQ•Ð)|ô¨ýÚµTÙï¼£õ…“Ü4Hê0av7×Ny|¸Ÿ´ŽNhY[oÑ-v¤ÔPì÷;åÁúÐR†Áw7z¨»{þù;üéüã7\Þ®E¼‹ >ØÝÖX­RF‡¯ÅÇ“‹9ƒ½°»‹õu0F¸·¡R¾ÿüÓéú꾊Ûn>"ÌÌ\†GnOÞP~xÝÎ#±•aÈM–àªK[ú²æ™HÉVÕû¶ÅØÓn"YË©) Cœ5í³T¡{æÀ],ÏpP¯áêÂæìC-‚ª  Lò9b %jx¨°HK„z A¨Ê ‚ZŠJÝMåöñSëWÃâÃÄÃdÚ ¸• ô¶´üŸz5zˆDTŠ)"E$ø~ÓØv°I6"íY‰‹T_ê —0€àðlû…lVD’dFËÅ¢1ky•GÚa ÃcF©ßÍÇ "Ì6‚T¸#E?ÿÜeщ’“·mS ßБnJB6@éûDùXOzhnòqXcŽŒ†3HJÞ~!‰ÚYE 1"J¦¼F‚çáù‡GdùÑ3¶npÕ鸽$ëi+CÆ;9‹¤yŽ ÛÁpKø!¯ËA ø†ÉrI¡sˆÐ©ÎPe…¨˜2‹ YÜEiN2¶a'¥>¢Nlúz9Rï’ð0qÏã-2à.&Iã‚§[e³/çß#øvo g8i€!å†y{Œ\vIˆDšUSuùÿmó‰[n®lÛ,è¶·Ë…hòëÒÌ™V™‰Ëÿ‹-“—Ig°¸Š"{›±ò½‘-[¥€^¶œ½@B¥¨T‘È¿"˜ê¶÷‚’žòî7óÔ¹† £ÐÝ]DáîŒJ(¦Hj)¢RÓaSÄÁ,LüÀ· Ùµ¾Ž>BtÐÊû-]œ„åôÙ•d”H7enÄÒ³3RΈh,0Ð9"µL¢}Є”1P-uôjxh"Rº‹É‡µÄ̽NE/‘JRm4¡ÓéÑ}í]-užúh>"ªÒ,0‰Vˆ.J^$X4©Q4µìDd4´Ö1líê4{ìçR??ÜõXŒÃs™ÝÑÑŠ7F>P§éáN~zœFóoÿðÓùt]ƒà"‚?þôçËóSsûüÃO§õm9]Bú¼û@‘—×WrµîAè4×ÃîÖ)>lÞU‰ÉàmÄÛÒ¢ðáæî°?îjË0p?< ‚°]7[{xðëeY[k£ƒP­:‰ÖEBë U=ŽqM‡°[?LóãÃt: H-5º•¹Ö¾ÿöãÃbw9_›R÷Çéù¥‹Æ§ÇÇé8å›Ï?xÈùl½sÃù:–¥?¿žÊÎêñã¿ÿý×Rv/¯—0‡–Þš„¼µn«vÿŸ_žÏÝ'Œ‡Ç»·¥æýÇÏßýúüåÛ︭FÙ÷ÝÚùuF³Žr¼‹1=?µ×Ÿ{yy½¾ôeí íúÒÇøøx·\Ï—uØè½-Ýc\½Îýr¾üüOÍ0Ö6ÆúðñÃöí7‡——¯ƒÌÖW7P!7wßhð­­ {||Œ¨Ãìá~n×õz}é¾-¬Y7ûüpèaá&2U³(¢w/íº\Çb«5[®¯¶;Ì·>}ü4ÖëãÝ]öíã¾u4wú\(ÞÛ°©,º;–IæÃtÐuí„xÁ¤æ¬k_áÆÒ!a¦â.,ÃuŒX;Ö¢Q ÇÛÞ™ãÏ¿ÿóõíÔ%|*1Pˆ¹ÌôZi‹Qn(ªUŽíŒÛ=¾<ã´öÕÝ…n!Ò.o±ºGÄä7EF³D^wÑ€6Íûµ_»›õÑšÅp·~¾\|\Âìîc°± Q$‹äN¨ƒ!¢áÀ2LP4`–´ÈÝñà«÷|ÿ ]®„{À P 0,"½Å VBÂó`¥Ñ)ªå¸ŸÍ(Ö€I.Ò’ïs WÔí ÔRª$Ê; “ † Ò¾Æ2”ê!bFˆ×@¨­_V®æÑ#†À¶P>d dšI“.p¢êD*DUÉ„þ¨ $ ÝÜLÏž°‚lpùV0Ãob¡[Ò ’WÃ$fðý饱Å×R ssBä·x?hB„§ÜÎÃ"Ì‘Z47„&`¦¯˜Ex&@IãÝW …Bm[FÈ6)ç6#“Nš ¾üƒ 9hjl÷fæ¶'#UžA#dû€Gˆ (âݱÑàžw±ˆpƒ'­>9QÖ›É\šl« 5 ‚á„%ÞãXŒLj}«cæè·$#ü}Õ%Óq!‚ˆ nR>nħO0\rØ’Åeâ °Ø4d@.â(C DažµÃÁtMÈå5;£4=J‘‹£œãˆÆ0Hd´>ÂÃ5 "#蛕'¢‘£ö?ÅãF„¼“X¦†<î&ZƒÂŒOæ×–'ø¶uÎÍ—f—0°LqÇ`mnBÂ]\Þ¯¯ò†Ë@.šÇ5BÔU§BO£øa𥠙Z$‘¾Õ¶+ÂŽðá’)>PvûÃ!¢Ç^•®«DS‘ŠÊ—ÁQP¡,3ZJfHè>nw;ƒáTE‘ʃSW¸÷!A?ÉápZžËä!¢{»½Ù}}ng[^^[}h±Z1ìõùmÐû»§ó«¯=1F ~t=Ü~t†Æ$jÐi\–«Òˆ cê>¬àn'Ÿ>Þ|º™þñÖ¾>/}i€¤#ûDzB×öz½ŽwÃo.fÀ<íef7èáð­”u´Óî¶Ôº×¹Þî~}ke"¡Âù»o¿ûó§ùüúå:äºá^KŸÊñÔ/ûùþ¿ýoÿv:}1ïN—¸.¯FxÃc˜ÂýÇo¾}yùåãñãnÞ-ÖgÕ¥­ç(eWDãåÒö“>Üîß^Ûr^—ö²öS@Žsi6êtóóß¿ž×7Àk™ßN²bëùËÓë:šÊt{ÿ#wwoËúúö$ªªuž4B{ßýñOO_~ýút=_ÃLŽ{_úë.¾.Ëe½Ž¶[7ë„÷¥_¾ýøðç—‰ØïÚºÞÜ>þã·§çÓ‹Ç£u³ Žáëa§ß}~úåµÎÇÝnv¿öGg±ð·ËyY[Ñ µ¨Ôɯýu9½®K»ôuØÚ|MÓùðaᵊîÖB£'v#°øŠ˜Ë\Uœ²½·À<´F„‡Þe]tˆ©pW6Æ•.nâ±´EU¥ÕŽ‚2Àz·Û³î÷»¡ìÒÔ&«øÒ.m©gCo¶%úªµ³ÅÓëµÙj>Ú(—SbºcÄ%t ¸ôÕZ4Y/k‡7[ ƒv‚uÂ-¬RÊŸ~üîåéÍT1‘ G¦[9$`®ƒHaJáÜVÎMžëŽ-#!žw,Ž byÉRy ‘€ zJp8!µÎEgº;¼bKçn²ºHZDURóE³ ­2¥EY}ë>:’Ô8³Ôb 10F4 ÔX,{õt tî Dò #îž~‰xV‘Øœ½Rʶà hÞÈ”à?óV.jØf€Ü?¹Ó€<çÁ#sE598“ ;ó™– uÉÑ,Ï“ئ“ÛÕÌG¯›‡ww‡S-‚`hl½@u;,½w$D<ŠÑErˆ`Ð"(d(ÓWœ˜¤´A%$c=”4wwBgQ‘ÂpÏ‘( ´ümB¸f7ŒŽí\x`dtNÀ’W-‰j©Gd#1§¿|âe®>Þ‘Ÿ[uƒY‚â`8ÈBEÀ  $’•…ây>Ë…[ ÄÿyÌãHiÒÖ… $"Ñ5A$4‘¬¹û*ùgÉ),¶üŽ0(Œ'í*¤øæ{d¼÷pÞE;žÏœ±"Áp —qD)¹ÜJƒ$dã’%öaÛ¬eÿBÂØïRï·8XHP6¢%+,%„Ð…dUËUÊÐ :CsùF¤†g[ ¤h(skR•EÔ'1M<­D7…ù\ãáf¿ŒCH D¸ XHq„+À":Iq•Íd )1­3µTS""Ó~0FPJÐà¦à5Ä! "ŠÂtj D(Ä•ˆqxÉ_ùt TƒDG§:7¬XHš„jÞ—ó8íêŒ2*¨&˜#h9uº—À*œÂzÀ³,˜é­û |µ€FzÍ[@5$0tšæyoca` Á QJ@h4cæ ’°R¢TÎ1…N“þëŸþðr9‘TZÀ'Ùïp³Ÿ£YŒB9|øðÙF?ÓÑÌKV^Çšt|ëÙ q_ÚJeóÆ~vÃÃ.:â›Ï?í÷ûs[¤v¥¾ÞÂãöp»®-x™&ÿñãáéµ¼‚·ëÓ¬å8OÍùpóðøxãZµS$ò¶/ËÒ×,-Î;üp,×!Ö·Çc-åxû¸;Ü¿i¦Ýk):.ÿçǧ »÷Þ–©ØO?üp]åú׿üñË×õrwÇ»ýt”2îjy½Êþíu•¢¥¢Ûr:ùë¹9M²›÷ÏÏ/îÿí¿ÿå翟^Ïëašêþ^áÃõfž?üô¿ýßëòT‹këÁ}q>ìó~žª.]Ü.uw§ó¡·åf¿§öûÛ›ûÛïºÙ¹Ïo]ÜÌ/mNŽæç³ësëÓ,¤/Ë‹â€r³¬¯püõ¿ü7ï¾|y:½]ú¸\Ïϗ岟o(BLC§ùã§o—ëúvy±6´pxØ U/¬‡© ‹/¯kdÄör½öõt=/‚®Rj¥Ž¾¾µÖáÝÞ./£Ù¸œ×Ö[¸î÷“]ßV_—5Z«J-ÝÖ¶R£ˆ‘£–lg¥ôÀ æ>ÄuЊVî*†Æˆf#¬ŠL*ՏдÈRê’4=ìêt·»…•³µ¨³@wCjÀ¦*ÔZù(uÍh»ÑwR'Í—xŒ)cÚsa€ Þ†ôË*Ö®ctïζðiìY_"–ÅÝíf>ìvóùܼŸÃî×nÞ×1ÚÀpD¬t÷AÀd5t5§§S#ÄuVA¦Ò‘9’üÀ…PœÆÀÖµ¤ÓQ‰.!Cƒ(3: ¸{Òš*PÈ0z4Ý®QÆî(*z‘º ¹S¡†Ôt†ªWŠ„°PEE ÔZ¨¡Á‰&)²ÓVñ°ª"Õ¨3`d‰w©bvîF†ý"úfxC&U=47Md¨oÒ60$9Êù-  YusµD^Š‚i!…Ô"ûB£÷„3 QŒÙÑÏälÔIšÐ¨ ÇI@l ¢T2oè̼¤µ…!¢æîî1"â½[)n,Ð-¨¾]„°¹µ7ò~áòX¡Y}`§ÂYˆË5£D ýÈÙðdµ-|’¤°í—ˆmÉ7ÄS8!îˆDQ&#!7G‰gØÒZì#Àªõ6‘ã9[j~#²àøÝ6S"@Þpó >L_žÏ÷GÈîæ¼,¿ýÖ¿¼ž5FÅnRBânw\cŒö6bœÛX–«qz¼Wqw·ÿ/ýï¿=?79ìf7Ö~{9ív»?~Þɤ§µ³ìÝ/í² zÐnï7û§//RÇúý‡§ÓùíºV¥8°¬×ç·/×ë2äùéí·_þãõåüvén˺^îþú_ÿÖúˆëˆ±;ìS½.o§sGU· tšw7ÇÃO¿ûîõüzYV÷.Sé_/Kh 7yØ?ÁÐ#l,èê]­¯×«]‡E?{ôÎVݯ”r¿Ûk-ÞÝÃç©ÜÃ’"£L“zñû¡jùö,¬uw÷ð©Ú¾ÅˆP”Ðá0 ¡O*‰Gš)%LŽõøûïî~}{:­>¥O«TQXÝ©b'6e2,Œ¥O€T!Et_÷Ç#[QG,~]/ƒê$b„boÖþó¿~?ÚX^.C[_̇_Öeõ3Ý®æ­10Fs\›!:†‹ktñ¾"< Ã)áÚmD˜&!‹å‡Dˆ1t»(xž*†¤Œ„ï°«(!›!ȘQD`„YX0J6×·J–)hA†8Ó¨ HQ‰‘®³ÉÔ<·AR•B¡hQFaD%AÕls«K©* ò¿.!Œ>CÄ @¥rš#Ø¥ËP7–'%Ï(bÓFÙQ MÛëT$<ž”¤N*µÐU8bÛµ÷§~Õ-àƒéŠÙvâÌzöæ”c„Iݳ¹©K"îb9%‰¤-:e6„ á#© ؼ~á‚@ôdc¼÷óÐWžf„nzÃ4Òe Q‘QŸž¦±yKd¬GIÊ:S&’÷)#™ƒErÉ‘Í3f@œ!,dj ¼fÏaòk¡oÇÃ]µò·rKÓçúJ’?ŸÅ‰Èã[ ”Ü¿º{†æÞãjáH™R’¸iø6ð²Û€í ÊΆP…g›ÉyM´½À` ¦L*¶pú–ç¼CÃìøp#Å%Åž*$P5ïË΄XaOƒp…‡g =ˆB‘‰YöË™‘VÖ‘?£¡”›z3/’†Hð}0…HA(X„²ÕÓX>r£G‚ ý@ˆ3û¯°Ü^™BSwÍBQœ7ʼ-6U¶ë¸Ä:´©žÿ’×J©åà0êÑ«Rb¢°˜½ +Á£ (J„Š+Î˹ÐD¼ÙK†ƒ FWßÞª°1U Rñ )u»UŠN ÷ü¶#8wŠˆ2x˜ëÿ·ûååïc !%;—Âîƒâ‰˜â uq²'$^u¬Öª¦½•bEFEµ>¬j„AÇ È2®QÇTå8Ï ¡yÖƒüþÇ?Ÿ–·Ëˆ‰ÍK™nkýz: °?ÞÄ:}¾¡ ›±õùîøéÃÝþ­?ýð©Î7/çeøêÍ­-m´¿Ý¿žýáVCç¥ ±(§uõ¡¥‡7hÜÞܪÎëbs™µî¾>ŸÝ¬Lõõ´DYçÝìºÿ´,'³Ž°Ûϸ¿;þëçÏ¿½¹(ÎkŒÖ[wÎû»——¯kï”ö͇ûë²~óéãj—Ÿ¿,ÎÇûËz}{}úòåi·ÓÝ45“Ó²”ªcY?ûí§ïÒr¼¶k)C'y»œàüíëËÓËWc?é¼Ù—óõÜ/Þ¿¾¬§ëy´v~;/k÷À㇪__/§"øõéúz± ÝíÃÃaºýmyé×·Õ¢ÞÞ^/§çýAl¸ùRçÙ¼?}‚´ëùÅmøp³µ-£‡©A Uåîî>toá_Ÿ¾„Ô›ã^5z[ ø¤óÃãÿ—ùËÏÿøwk—Õljta›ã6t*Ň½¦²?ÎûLJ›‡»Ã—ç/£[­JŠûˆ©–‡»ã~W|ôÇ›ûuµ EŠJº4ÚêP pÉw>jV¥*5sƒaqé—5xóp',dSÜË¡ŒbŠ)hRK-Ç©L¢ƒœP«êDë1b„9ʇîd·»9^—ÓeYÜ丿Ñ^×õõº7_qëÙš»™ùèq]Gï}ñ º­îÃÝÃ; ¶­ÔjÁhRF>L(„ Ñ\u‘Ъc-¬õîn7˘Uon>tèåí9Ø…®ÓªuwûpxÜë×ë©êè.ÀŽÃn1Î ÷µN³ö±_[s³ÑÚÚc©¢ôöúz î#\Ké.¤öBçóëq®—¥Õ×3Öå<Æ8·µÌüævÿûŸ¾(?yE²ÿñ_þüôüÛÚ!±ìëîîXo÷»ÿö»ÇŸ__~yúu?ï><ß^®1âÿÃïÏ×_¾žß®£-ã ý~Ï»/ÃK}47-úoÿõ_ÿþó×Óå%Æ)¤y”±F_kïî³êý‡Ï—åõõå×Óééåô¥{?în¤Òû[ Æp´áo§—Þ._¿þ»-—uíÐ(E½·A× óZUU{k£aÀàå²\.Ë›yÆrZŽ9Lz¼=êts½,oÃ[¸DÀÝh½Ž6Bm>ìö‡]Ä`¸°B Ž9Cv𔡸ºïö»O÷ŸàW[—ZUÊî0©èÜ—Ú=ÈI¤’c=rr=Ô2ÏÓŽSøg);}^oƒc¦ÿáó§ç³±ÔS»\Ï/1ÚÝÍÌÆ¯¯_Æ«­p²Á#=7Ö}ø:–h­“±Œ«™¹[w:–o@8QK¥hs˜C:Èq')áȾž ¬æ†ÜÃÅéádʉ#,„f!·|™·ìÕXb™ÝòCZóQª5wî’úø›LlO(‹H-‰ƒqP‹Ñ@ªvGEP…:) D¥„HÍA PªpjHE5Xqš£wW7Æp üŽèDÉZw:<÷9=RÎY IDATšò£ß²¿ ‰2‰„¨(¶Þ¼§':£ 0AZhŽ©Á°-l¢’[ ßX·ú¦Î"sCø_-Á¬‹ñ“ §•™–s˜Ð™\j 4;g¢ïpñͬÂí–ç“«CRˆ’«ŸPˆÐ„švƒÍ{GæžcŽd‡O”Ûš1_¶·ÐлÙ˜5¿`âøÖÌK•dLÓõ³EŸ FxŽÚfêñl–ã& ¸B bñ¿@LÐÍ“–n(Q2Y¶"i¥áve…0Or¹£j®Ë „¢$‡UBá¹’mFÊc0*A1 æÞðÝûˆQDÀ ±- KÈü'D¸l}¼€Ñœ[®-QhˆÐˆT š7ë`´÷nl§Áí7ø}ã…æÔˆˆðÔÔÈ»â3ƒVù'Ã#ïZ9k&€ï`Ùw·„"9Öóý›×ÞÈú‰xº¢kæÎLLè ‘€‡)B©ûÉ#Ô4Á¤Ê©†¹Ë¤ªØ¹!êTTr8–}±º³„€ÅÕ*£¤P²l¹I—ZÁRCDlE¼ãÃÔdê Û–¢0„§O€+$¨,Yº 7É3¤FÑÂdi8#cf(9Ð'+Ÿ(³BšYOF8ŠZì ݆åÙî¨"Sqkn¬Œpá p4°ÐE54<)¨¿ºK IÖ>KH6H 5€œÁ9&×›ýá8íº;¼œÚĘ0U ‰Ð-¢J%UJm¡„Ô8×¹ª{üôéñôüÒ± ÍzŒÞ/á÷õc´™»¶ôÕ®,â-^ÎË(+k7/Qæz?Íë(—%fìAZøÚÔxÖÖÚhZµN¥ì¾ýxór]/o맺_¢"8ïQeZ»ÃéÖ®£OÜßoúÀ×ÆÇ‡OO_{ë˜J¹9ìtæç›ÛÓ2õÃKenŽ8î÷Œøíe}nк«“>Þß]z#ðñÓíÝÝÇý¡ÜÞæI´ÎÍú²^—f‡Zf­u'rYÊðµªj»×Ãa,£(cm½–Ú–7ïmÒJø¡‚ÕB ˜d’ßÿËOÿñËÏ—å<>•óùåº,ÞhÄ~’ã¤Ïëéù2þúÇßýöôuYÇ麚÷?}úÿöòrîÞìõ ûÓõªÊó2ÚÐf|½¬KëDy¾>÷~š ¦Zhºx—‰æ‰1¼µáîîf†Z éfcí&Så¡ÀZ_¬ùX–uúN‹BG_Woæòñvÿûß}XN—A)õ°\.Ýœ5‚KVp±QÄ<¦p„9[“ï¿ýp:]¢yÑi 7j%XT{™çB= Œu!%ªFzc†ÔDv[íµ¥ª‘ŠºûáÓ÷Ç(fÅbÚ•)¦0µy7Ýìn¶ðÐ"ó˜Ð7Ú¡d²€ò»-dÒŸ„–r ßÝJ9Rl„ra´’¶™;«\þˆ"€l¤ ‡…ÓSRä4@ƒôêI„ó,SŠ”íǘ!9fpKt%]6E²o¼uÙT‘ï œ.PË!áÑÙidˆ„ó‡¬ü§O°Ä˜€›lç ¬„HY±©}[ˆ¥­2GùôJ¢0 tÃÒc+…lXÉ<:7@•À5™X 1Ï[¿j¦¹RŠíáImMíQÂíÔU“¾Â„kÔw’W޼A§jûpÓ‚E©§‚Sj€!­€â .BuQW£‡º‹NŒB3²Ž¢{i÷ÆpÁ -†.æÿ“©7k’$I’ô˜ETÍÜ=޼êèîéž™Áìõ€‡]"üÿÐÒØÝ飲2#ÂÝÍTU„ñ 5xê®O0qæï£ I–%Q412KÙIpÃÉær=0µ—9å„EÓjæ Ÿ 650a:Òf ñ¤§QòCþ Ò!–žaûìØDvÁ™ÞÚHGqÏÈ„ S©YX‹*+NY ÈØê5 ³4AÊtgÂ’IK7JÁ\N×L}Ûö>Z &Î@Á¨Ã‚ÅëRâê5,ÌmÛ®ï¯mÏL(`8-'x:o±ÿøû¸½~•°ï·ØÂ·ëöÖÞ,;mú r 5Šj]ê??RXj}úðc<~øòøø’ëéTˆNåóâ©hIÈ!‰VQ«e×øþí:F.ër¼§½Njˆ[—KdôÖXK:—² ÌŒÄ)iY»<<ŸrS=»ˆEf¬''2Újܼ”§Ç2ævœ‹%vÜ öSv³s§×ŠÜFÚý6 m‰f£á糿¼{ŽJ߯ÞúF‘-{éƒ×a}o£ÅØ6åÖGôÐ}ô–£í#7S ô,Ý™šæ^½ßcD—Ï$YòátZ‹õœhš)†±Eo@TKz "Aòñô™¡”è÷j?\~÷óOoÛËè}’¨fž“:h‚‰Æ@ö¹™·hMbÞU=]©®§Pµ(a鳩_YYÌT²X¥ª±ÀJšYQ šäHš›B("}þÛ20ä«`îå‡Ç÷cŒãs`„Y€IÌ0Ÿˆ0+s‹ÇÂÊÆI# Ú"þ[e=†@*³‰4¥„íˆß²ä„çLtOx£Íšà¤—¹œ1õ#•OsœkWΞ¢û¼"Æo+–Y`›0«ljBÇ+>Ÿs(Ta@GIî˜Ìgo6 ñ^§›§GMÜê4÷ȈY^s"&^ÓX9GDGNƒ¤œ·ù”?È šµc<åÞˆड़™ñý8}F‚r2T¤wàéœ3Ž&¤,Ži7œ÷á§lyНmM0e‡$årÚP9Ê~˜¬4 Ÿ‹Ö4—H!!‡R(TÇmTîsÞIÌõ¨Ù˜ƒ¸NcNbÄ<¾K2½óõç8f`pÖ˜ãÌ~ŒÃyV' ÃI°/ÖDn‰šy½—!å$Ã^àQ¥°´9l»ÙgWFˆÓUäCNKøÜÚdô·2å Ó;•¡S¿®B!aN¯¦ÌÞÒÓhÅÅ´”¡Ô.”LÎBZÊ MÚ›!K3ÃÜ«dªÎÔ|º©@Ì"îèˆ Hæ`AÉîF’e»’®LÆ| gÙ”ä,¿¦Å+ÙIOYQ¸Y‰2-Ñéf†Œ™>«é^è”JŒt¤3§œt„`I‡…I1¤a«_Öõãå¼5M¹V*iš1B›÷2 Õ&ù­š'ƒ s…¦>[‡ ¡¸É\ÀŒÒX+¹FÊYЫ0ªûÃJ[¦bFÏjW½j¤Þ²ƒUcÊÀÕbË×hýåírkÃZ0µU$²—u]J©ÎÇr±žyqþý§žåµímßý—/^Ÿ>¼½½Fö~9]O/[|ý¶wmØGìm»¾.(ŽÜ÷qÛ·oÛm§á‡Ç/¿¾¼m¡´S].?Æ/ß´ÝëúPÎ=®£(ÅV[ÎÊó§õñaý׿ÅËëÛ–ËO­÷·ë/·LT–¯/˱~ùùO×ï/ŸžOÁ­½|<]F–‡‡õtzö¶ët>Ý·JœÄx(qõ®¿}ÛßbÛs¿ z­ÏÿîŸÿóßþòà 8ÆÙ£µ—^e¨<¯Ë}û¾ž–Ñî_ßRBÉRþõúvm½#1¶örßwR1Ô#hûåò´mû.†=Z×ÖL> ´òÓ)ŸN–í\uòSÍ5’Åé6Rfƒa­y½<*–—ý^T8bäº}¬-Òëz¾\~yý¯Ú ^*4"Qªƒ5\ÒU{­kÛ÷À(Ь7Œ=ˆ1:úè-ºö}àÖcï½ß€HŒê=Ñ|^¢$Ñ2{æeš­%†Q(rôãaÃ'óÙ‘CÀ²,_~øÃv{ËL¹!”Ò  Ò7"&Kfy?ÎÍpRÌàÜKÍ!áh¢9ÛÞ ‘cBEß>öÞÛ?ÂVšÒÐÜvòXÊDy÷DK:Äâ!ÏÈy+><Â\@꥜àó"x€¯ÞÑõ›%Z™³Ð)ÿ·eðýµŠ)TÎ ÀuòHà©Ì“5`ž¿Ýn*ó%K’Nc«=p’wÙ´æ@8Ö;(áGrJ€OŽshëBËiýcÖôi̹¤Å$ $3ßÛ’.çÑø°MOÔ´ãèjÉXÁ ú$lÑÊŒÄÛLHŸÿÌ ¦\ 2aæ“6’C6 cõE“©:ÎE™1IÊ´9µø¬oT+©Ùud‡˜97A* e¦‡™¹Y¬Ì!ÜPEÆê–ÕPhn“XåÈše7ÒÓú˜í[Ú*¨+››}ÑÉŸ˜ G7%)·÷ï}d- +=ÓDöI‰Ë Qg™së:¦H|ÒìÌë pÔá“/šÂB‹irá<¥Ìa”«ÐƤúÑ” äñ‘ƒ…X“<Éâ»m½çê,¼š;¨QÌçï“2Vn.¸YY. Zó©7ɇ#`¬"ªY±*'ù,Áâ¬î%bl¼T3ÿñaµe]O8³$CÊ—óC ÔR¯12 S]X Ke¥?=}¹Þ¶ˆ}ÄxZÎûuøù|ñg™™eýa}¸œÎ—s"#¥Å&µŒ«Š5³cÿéÃé—_^ZÞÔG-u9=~|*ÂÒ¤¶…•(¨é}»«Ý®÷è+ï·fkø¶½ŒvZ„õ”°²Ýs ,žÉâ§’#{WjôìmÇ ØnøóŸï×{_=§ÏOÛûM»lì¹ ´÷ÛíååÏ£õ¦[k/èýÞu߸oc}±ž—”ùýßýnßÞ¾žkùéú£\Ÿ·í꾜OžÞ¾µTûòüàIúúùé¼÷AÙï>>>>.ûõÞ÷X¾ß¯×Ûu!³ççß_ooÃ.½/·k á“[Ëûèé5Ó¤Œu}¼]Çuë[ϼßw¨KÙc)e¹Ôõñü jl·ÖdVêãi½ß<>ýøXa«hŠq¿£Ëúè­šzôì´ºJ¸ÞûÞ9r°”§óÃRòÞ2wì{ EbpÌ ifp:£ë–62YŒeY”´ß77` TÉLÆåøX}I[Z7a¹ŒUªÍ9xª§JkðËRÓ½XY°.%ê<—”„ /u1µ`"c[<Ó]#mw¶T[òô°>0v`Ø£k¯çK‘ݶ×1zuŠŠ*1·1´[cÏ6vÌït ’Bêéó#zO åLˆ™¢täo•ëpŠÙ{ ’e¥ýã§úzïGã dkoÁ>(ä8Ü}Š4¸ÁÁÔ4xmÑ›†ë0 b!èf‚`BÊ¥×2÷é·tç\hÅbIwKB‰d xºÓÌ¢ÈL¢q-°b2!ç€),}¸{ÊÌkY\¢30ÜW$Ò“9û¾I…K”åºK­¨È™I¡@T)„§"1ç/†3Gy'Q¸å ÛÖû¶·M=²ºÊ9.æÈP_†Øz4È{»Þ”a§ê4ë;l-dXÆçObØH¬ë§-±Çˆû´&±2[k¿Ü·h¹.—R$–mÇuÓ­íieÛ‚»k¹†º:¦Ó-³ûce&ÏÕŽ`ßZJ¥¬@¹ßÇÊ6âÖ[ô1P×õ‡?ÿþõ×·¾üËzË FžêhýœOÏm»k~ó:ö{‹qwÚˆõ¥µËiq—Ñ×õä,Ë©kuÛîmäyúßþî_ÿçÿmfŸ?~”NO—²ï‘±öá-ùãçü~»EjïzÅíu»·n­w1Ìêr²ûmS”6ÐûÊÓÙëòøº·S-ÅAÔOŸz½]³o!sçËÓãåihÄð­q]}Y¸ï=I·:Tzú÷ëµç–žÖ¹øI¹ÜÆ}룰¡ÐÄh9’ÂeŒ–™„ ?<[Äׯ÷¡ì#ÁfÌÊtZO&j)µäè‚ÌWó„ÅZKå–½OX„ײغr<ÌK—9×ê—ÓùbßcÀo™=6©Š<-Qp™•ôz^êiIT²Ê?Sµpõ“}þøüûŸ?}»ÞÑ;0Lf‰{Cdç¡¶ú?ýtúvc4ùØRÃì?ýðñáòõÚFî´î#,¢–*¹_3FìÙSŠ+B¶)[Uûh/õñaDo1€Ž†ír†T©y8a„Y.ó`Þ†FW#™Üåˆ(-€4˜B;–÷›ËqñY“‡P8ç¦RÁ¶[9¨æ!ͼ‹ƒó¬'ܽ¬F7 BMY ” uJ¸•šL«fÅá*uµyÈðÕh•D›Æ"HBqŒ˜’O¨§BÎá&)&›E°LÖý(£sçRæ‘ÅÈd’ŒàDBµ¹š"BF†ÓF2ºDÚü$6›lŽ:á,îfVdæÊæ|ÏpÈ"ç<$ø{Ì}ŒÇì13Xsó0À™2ƒÙ¤=Áf‡n’¼`1ÿA0iãIÍ„9§»¦2¹ÅC”qdzŒBâ@sÎ,Q™ç§w$Ó\|™ D@) ËùˆŸY+…4mÇ6¹ž¬áð;"é^NÇ÷5/¸F¾k˜.Ø—f]sþ-œÃ²”3Ø®©¥ÁûâL†Ùôr;ø©*L%P’–@¬UÌ+¯sºÏ=iɘ§ÇٛײCñgóþWìÐOQZ†Ù“„OÜçó&çDÐQ©$$Tă!;÷…¢OZ,DÖ¼%;#äNŸ/µè2ûí¨zÈ®mj ¤Ã9Ùª7³¥y¬¤ Míö 襣¤ÚÓn#sÌ×vsáÀr:=]>îc£x$úÌžùbzÿ4ºÄ"–B0­–QH¯H`ÍÌí¤褜 YV¹•¨¶”¹t„AnÞej”Ý€R,w‹¢E˜øY˜hé1 D¦°DÒŽ·‹µe©&Е,žfæ„ÌF ÔšBqnÝ[ë‘‹ŒBº¦V3Ó0cžª<•õéá¡©·°¢%K1Cñ¬*b)"UfÛQ¸.†˜ÚZN_>ÿÐÚF­Á”`ñu7?¯ ŸjDì±Goc‡XŽ¡´Â¾J,rs?Ã[ m)ç{(ºgšÒ‘ÈŒèÑ[«§ñáÃCÑÒòm߯òôÙ¼ÜÛÖwëm[½]N˵cÄ wÈŒ/õ|j=£Ûí[ŒË¥TÚ­S:?>:ýóûÛHº'F¸QQá´^’\¼’î¥ÊRŠ&!2Àm»¿í»¨q¿ïÛ®{ëËåñO?—ïwÞn£ïí~S×-’Nëï~÷ñå~³ó£Åµ^Næ§¥T‹¸Ž!Ù‡ÏÏûý¯ûvÝûˆÑþò×ïÆfBâ­µýÞöçõùòøz ³ø‡Müúr;—³ŸÏ%óúò²µqWâa-[oMãqy|»Æ}Ï>:ÂÒ;üôÏúüõå¾·Îdßo¯o÷}ï‰!Dj9_ÖÏzÏmSdÓþ€Ü÷VÜ?<>ýíÛÖ25ЉÉói­—mÛ2Ãü ”R.'¯wÁ `'ï£rÊü™Qßî×½5Ë/k±d/Ðúpv¯Ùùñùò¿ÿÇÿðëË÷{Ï“Ÿæ@ÄèAvYÍòüø|:=î½7ιЊ»!KfëF²Â•^eNXÊ©ZeY¸„ËKbUñµ,í™/·_‘–¬ë‚¾ÁG«¡šcþêK‰}|ûz¿Ýî#r`K¤e€¹ßö»Šm4Œ–Û>ö¶GÏÑï1¶P·È‚Lnc4`·1²l1zˆÌâÌÑ,u]—Õ¢‡$¹ÑÆ$Iµ>ëÞ÷¤’m>‚&,I9×1†´#ëT&¼ RQeÖº>;âæZhŽ=rø|äÛL*ÏÿUlÐÌe‹8ª#=SL.O¸›Ì–²¸{u÷‹¹.rÈÁá„ÍЬø™,]œ{’L"#adLÈxôš‡0ÄPÒ€0ôä0Ð, .Š(aóc†‚Ãp]$ Á»Tòˆj'Ž´Ž2É&Pˆr@†QTÎùj¶ÞB’Hœ&¥•ùŠå» Æ&¯G³ö5†‡œgl$NЫÍó!e ùäÈθ9~2gP{‚Íç˜=/T˜pÚ<ïðÝñš {ÍÐN„œ‚!¾7çL8Q¤Á1·%˜ q³db²~¨y; ¢úƒ5…Õˆãøæ¥œæefß'ÁŽë´%Î]™’å|³‰%e‡ÿ&›×;£s–äÈ©_LS3cM`±w‰#”R™F˜ –•ÉÅy§/9%Ä^!Q,Sr0ÇLÑŠbö…,}óÿm7Ói©qXŠD*„ÉN™f$?é“’B-Õé…9Þ–NL‘˜a˜Áœ‰q9øè4VZhŠ•u¶ü J'\éFÑÝ™*(‡×ʨ”²“ffšµZ¢–)×ÄzX÷A¹‡ˆ®‘¨ã¨WÒæO–äÞg&NÐ!7‡1‹;lLGhHgZØ$ù.ÅXX“RN·Å„ÌÞK{à–Å¥Ö¿íÙ³ÀË\D»×O¥Ï^Ð,Ù„¸Ô>~Úî×{›õ :í®Ÿê’@E0RJ§/õ?ÿ}¦b„ɺq»7±RAo.çj´®²ž D)u’vÁ¼>œX2¬GjQ&bÍRfÞKÎ?þô§·Û[6F˜µ–•Û+ZĨìýöºË-ãÝî×Ûõf¨9R6@­«/±7Vüø©¥9²ŒÐ¹>žÙ.¥>_H Ee/»Ù­õÛõ¶M-;œ¬Õˆ…£–óúùÓé÷¿óÞí_þì¥ÅuÛ °Ü{2˜ƒó´¤d©ÂÃBÿö²?Ô\küþ‡g[³÷qÛ_"ãûKo·øÃO?~ÍÙ×ÿí?þû—_¿7ôH©Ýn×ï»Ò))¨òt^¿œÊúÃóÿóÖv=>ÔËzÁâÑ0â~½¿¼ÞøúùãçþŸÿõ^ß¾gŽÛÝœ™òºš. IDAT=î {y½ï­^.ÛµŒ 'û¯o÷­ß¶k޼á}ït~üøáþvSåØ{ƒ_î½õÜOËéç¾ß^¿÷|½‡’fãtZVW=Ÿ³û½õûmÃ|j»™/ʽXÒÖŒQ}ý§÷/_oMí¼ ßoo½m›/˲”1:•1 Ãê‹/ÖZº/_ß¾¾Þ·¥\þþ¿ë;šP¦0¤²¦Q^=èõ( ©¤Ð{™sdÈr¤ûº|ùði© ¿¯•ëy1¡ú´*x‚¾¸-õäÊÚT2†2ÀRŠ'MvÊžóS4ÀG57†z g@º§À(°Þ_ÿé“þõz½·ž"bôm— ¼e‹Ž-²EÛˆ ÜTfï£%‚Y‘H*ûNIŽZüÃó—¦}D7P çUY#CL³î²Ü¼Ð‘‰dLꃬ”òøüa#:БîS‡,3™ÈpŸlJ›R£œÎ LCu?JïNÂJ•—y=” š¥u±x¹ÀÝ<î´Z–ÊÝÓ ³ÖBúBºl1%‹d§^QÀHÆÄ<¦i KŒÜ§H£-i`f¢'$kžqXų́€A%JšÙ<±M•Mâ0H–sq`óAj©C)1­j³M??Æ›”Ìc¦ `œXÌ4ãAâ ÄÌûߌÅÓã¬#^.ytßfx÷s)çÅqÎqÄ£¦¬f.Ò˜Swx´òÌ8ç'Ì`ŽŸ ­é2F9*dNæÁ$*gµt†ª$ ds·ó¾*Ä4Iv@Ç—”œƒÑÁ%¦Rs/õt¬ð>P¾_4wßܽÌôíðlÏ•`Î _¡ON)Í*f„ê ²é4›ly³PpP-¤9ImbcMÁé´Ya!™jfZP6¯šCLŒˆ'†ùdGÍ„Ö,:ë$ ˜ùA0 ­Ç¡2À4väñt4-ç ‘2²ÈRC(Êü#’25ãëi)hªÊ}þò:þ ÊìJBL%Ç3æ(5k¡³i0•D1?üÆt‹Lôùš¥`Q¢YÁÁ(KÑ`fV âjæ2’•¨QœîéÕ˜s_4q æ­¤VOœéæIˆr¦ŽùFgq²jMáÍ" Y @z‚V EÆ4¯3˜˜áSOeó—SºÁ$S æ´d›¦Ü(æ[|r:Á>ŠcR(¼¦‹×¬##Dz-ϧµ·L×&9¼ÈÑG¤Œ.…,dSä -½¶£ºeë½e ¦gX!d²-]-‡, ©@P¥.çšáÙ{î€9Ì`n(Fj­µðé\²äÂEn àYJ ±žUPt®Õ–‹xGÒ +,RX üTž—õo¿Þ÷­÷d]:çÏüõ&uŽÀíÎÖvæðµ˜ãþz]?}xþxÛþÂÈ›[R[¡ØÎçºòáo¯qßÔ¹LÌ=Z©¶ïíÚ7´ûwí¬©-¢zÅÃåÙýrí–*ûüÉï÷;Ú«ú[×é|ùéCýùáaÛ·îE°r¹ÈA”zº ××ݳ Ê/No÷åá±Þnûíööúò}`sÇuÛœbëû÷_ÿöÖº¢,•÷ùáûÛî#<³ÿî÷¿ÿç¿ÿÃûóëûsÛSC¾uûúý%³i(R×=zÜ;r{{}yý+Ì·-ªTjfÞ·8Ÿz.,ëëõ6r?¯8/ðZ¼>mûËÓ²ZyHŒe‰‡§'¨mmË!’·¾÷¶)GÆðÅ?.Åpkº8KM¸ÕÏ?ýžÌ—`kC§"ÖÖƒ9zïmôHYy/oßÛv=ŸqY¹Ô#†* m­§é´šÁ[¿ï÷ ­©ï÷¦QhÊJëÚLPV.¬¨3:jA_K!ZGxm Fƒ-#Ò¬ Ïëéï~÷©glmû¸¬ÿÇ?}Û·‘6‘LÅ«Ÿ¹ÿþùùóóòrî8|¼—êv:ÕçÇ/ÚBÈ Ö,—Ä2zÂÁçtK_N]ñð|¾”Óÿøúuß8"•™#24"2zöfqÛ#™¹+ß XÖ5tØ25B9Ô‰¡1ýw1Ú\§Ð¼gßö—ÐÝhö¾Ý ˜™cHéÔì8Q8_VsßÛ Lé¢ÏÕSöÊŒ}ZàåàBÂÒlVæ]( ‡DƒçŒJIÈ0ÑéI÷Õ´ÂâœåéÀ!ˆJ7§;KÁ0² ²¯KŪÅÍŠ™¬T·Š0«#9Ž˜J34AQS—hiÊT`çÐèÕ‰€ I¡– FúL1ÍOý“uL7Æ£¶žœ½{¨<Î9¿]k8”ûŒTϾ•i‡«ää’‰ùt;ºc$ì_óÿg×85Gµï®Ï›`‰X¦Ùˆ1Y zÏõÇ ·ž§çõû5¶m Ðü|)غeÊh™v2»¬ËóÃão_¿÷6z]Úõ¥£Š7+åë©7»˜Îî{x9…š^®÷D\-üñãÃù¯ÿüý÷¯ÿú×o]]§|xþÝoÑK9Õ}´‡O…ìÑ£ï×ë½°Ö·Þ¯×m †><¶RFÿÕÙ?}ü±µvÝÚuÛýáù³YÉÞþT ú~¿ªuÝ6Ô‘Ði}¼ß¯ÑÙ›×ólº½Ü¤eKìÑ|þâE9Ì·]™~9ŸëùáÁÏ÷Û­óÄOÊÛ#d,côÖ2‘±.õˇçmôÞ#GÉãØïsmïZO—š=˜¨«ÁK§Ïa!³#†«2» ^©9ûg²}ŽS& ›@ùù[Ú€AJ6&òÓ4×OS2˜AKO¢bâYâ½é™ô›IªÃ7w=™ÒS3Š5kuÜÍ ñ±Iä÷B8E7¿eÑyøƒç?7§êw“ަácÒþ &•š4!Î…RàxQ 2ç0’88¯@yr–$†[©óÞª£a(ð‰ÓÖolRÙí˜hŽ]‹‰2›bËyÔ#J‘w!›½Öã›™¢‡fâŒwÌ&ª¼#¼æê1§ <Žm‹Ž¸Ýü’s* ‚Ï úáÿd™‡7ÑLÉ0)àsy€(p@•ÂÞO¡šF8C¯ÍcîÑyå+è0hb秃I›#-l^õA‘>7_T†÷ 6¥Ãu ›&Fà¸íÏ…ÃeF¸à ÊœÉçw_ŒIGíû$¦šÌªÍ‹l©^çû¾ N/À„¥VeÑâ²\³x7¡¦’EFFx%1\«còËíl³UÚC‘sOç Z†·©O bÍЉ>)2”•eâS»iS–Åqºœ¡‘Súd˜¾ fèˆ<@@q§ŠÓÜ’¥ªCÐÈŒ4ôÈyZ×j†á3,—J{L‚S!æ²®«Ûˆ½H!ŠŠPíTÏCÅLé]€qIUz{5üT&œJ™¿~øÂû@ϵ\P)–,A¦Ñ1ÜMX—S­ç½¥„eÅÉìäÈ·}\>pÄëÞMä@d/uzøÝöëUò6C„‚PC¾Ý¯W»²gäÙ3}¨eîwÁq^BÿåïüÝ–Óú_ÿýxyù_§2¤Ù—zécÿë_ß¾]ol[¤%{b\#¸žýtF‹1ß™Õþúý—Ûë[o}DöÁåáñ z—â|>Ŷ߮¶-ÆÖ´íwjÔJ»¾]ק燳ó×·ÜÁd,Á>†"Øw-åü}ÛF»÷Ü{ŒoÛH“ÓGô¾ß·7”uÉhµduö/J„–ârDKËPG–LX†²Ó>ÿøá/¿¼";l)¢/O§åççOm»ÝûMZó² G°žž×jlMUÙ¬,_>ý¡åHD&3Œ2×£9»,:Gku©çÇ_ßÞFus&î[.ÆnõzoÃͰÈh1ˆÜâ cÀz±Ýš«Ç²#zX——3jDw|ŒÛ¾uõ&EÜ«T[n¡ÖºÆŽÔP‹lâ¶E„²©Gvdä@ÑLØZRIfj00rp`—¼ØD™9³yœjhR¦€ d0`Æ0ú÷pZåJ€­Ër¾|hjÈnÅ2µßzdª<ž~°‡Ìš¹Í²¶Ña¥„¿Ôõ_.Ûˆž«ÓY¼ØrBaV‚Þ“ÌY|r/X”#³Ñ)¥@ Ç ê™#‚Ñ„4COsŒ!xj:,¹Âd~RÈD¦*ÞÁDFa†±ÌiY“ À‰fW61é±ÔBR™Î`:ë0G²¹8À<¢NL‘ta½=á²Ãº;óÈIù¬xMÎר‘²á±õ˜e2 [‰B4Ep9}Ó¶2ÿÎ÷]ŽÍƒs^¥+Іò¨*¨œ’ËéV¢IÇxø¦¼ÏåÑ:RÈÌ 1?bÎÌ˰ÈÉ@ÂÍ–ã"v$àMÖ\VñýK?ä2³h7SE~ÜÌ ®bÓ)àÓL™G™n¹€˜y,[6›á)Ϭ8¸6o¢É˜HiòábáLþ¾Éñéb”,'0 ˜ÓmVà„3sÒn3Ñ\@Nf:²jÓ•yöœí ›ÑšK°b:DØÊã¼8˜=gTmújDŒÙk§_)duÖ/§„2JlÛ¯17¢¤è(fœ¡²3g©õŠô* ‰É%%Ì,“3énà´†L#kɲÍËQ<ý¶{=´Zæñv­#Xe\€•(DVÈÌ%ä†%} ›œ<ƒA³èâ¨1?ÊI$ÆãùáÇ/ç×ï÷}¿m‘yí·´oÛ·i·ml}P¼Ÿ×õõõÛãjòì{yXÈ>¶‘9Æ»ÖEÛvÿå—_³·ºì×}—šÁ»åFÕçLJýþ­0_·ÂÁš„ÇÐàˆ}ßZßúÐØ·ïûKŒYÑ¿¥:¹ö–Ëý~ß›ŒÃ¬›´±”Ëó§FïÑuZ#–Ñ’™ öq÷eõbeÛ·háðZ]‘#Ë KfÐÆÈRyzüìˆvï½µï/oŒaX£D-(ë%RöþjÆbçÁPuÒ±X) FŸ×€™¯û½ºúf½%ª– ËjŸ~úÁÞÒ¸÷ûý¾§E­fB¨üD½Y$Y’¥wŽÈUU3_""·ÊÌÚzfº»Ýè$þ’¯ä¯o bÀÁ fz*—XÜmÑ{¯Èშg…B¡énaá*&rÎ÷eã¶*öe9ýðÍw×ÞLjÖÄbæl8}ó|í¯Ú X‘¹,nIlÖÅ—ý~qå ›‘J²ý:òΙè×{ç>‘1†æÌ™sNô=R1Gd&æ8Vyq/òž­rÇRn óú^Åô¶,kbe‹ ¹!3Š„T§£¨tJO Ó 9…4—&Žž|FÖBˆ‘1²sW(ÃÔBðÊ‹s8¾Ps¸Ì­U¥±zn¹x‘‘€ot^{ŽHÇbw‡»—% k¹±¥ MæYÝ0 Cڜ晖3¤…fNJMʉ¨,ÀVFÂ4LEf°ä9ål9L9…7<Ž3ÃÌC̬…„ŽÛÍФ©Ñš¬Š‡s‚ ß}ýP¿÷ Â#A…1H”ÀìÈ[GµK‚“¬½”ŠÓxD°Ò€ÉVþL£Á`Uõ+H~i@¼UZ‚ŠzdÿfÌc­†XçÈê·2uôÏä­¦çÂxVŠo‚jé`Yéˆw]³F&8Ê Sè#NTÌDmEŠ– z ãíŸÑA>U=±’@ZÔªÊÍ–c,Á[Gð`e™ÔùGFÉŽL–p„µ¼Üƒ€™´ƒ¤3í­vçhéå`,+– ƒ Œ‚ñ8kºD)`YSêÁ°b–w戼 q–"­^\De)Y*™*nw5¶|@õ'•+o€ý:i o¹'Ê$¯qÜ Kõ“J)êˆi•©ªI»ÔæÀ –Þè:Ä>*ax)·)©ã<>›lž|õíÛïÿ´_oî>1y,¬j…hi4ƒ±Ñ,â,éM¹RÌf lÚjmÛ¶‘64,nfÏÖ¦‡µI-JB `­¼¶8#[£e@¦ÀÎP"ÈÌVÓ~§4aÁ}QÊr}k¤[ú¡'¨/K¦ÙNF˜Ñæˆ_~ý뽇dDT „-”D/a èôªk8˜! åÍ‚¢¢ñðZ%VOóK÷ Y;7;{ë3˜)N#fÊ©÷ÛºlëŸ~üÓåõKLx*Sä"éý†Èc¡ß–æËš‘K:…‹h¾-O§óùÌÀHçþããòšì1 jb{X¶eó„Ç„ãaŒvÏÙ9™sîcpo¶em„ïàh™ó¾Ç¼6ÈÈÑ<fFò1,𧯿ûãýöé~ß'bL]^û}¾ìcÔÍvk›¯§­Ùò¸ÝÇzv4úÓ’÷ˆÞÍdŸ>^î·û•ýòåÒ#‰åÝi…5ß–Ë}BcÛw)øý§]îƒ0>šßÆí²÷¾ylô‘ýéô¼œøë¯;³§íÑ/·}ï}DÆßþn´ÛU€¾ÆËçv^Ïëã»ï¦–ïÞßãv~øöví3å[‚ãݺžÏëëý²Χ-oŸæ3§4óéäöíúðùúÒìaÌÛÌ1˜#O®ÓfïóºÛ¸'”‰ÕüÜâ6Ó•¡>#uîg[O§Ó¸ÅM×Eb¦ÃÒeï?|s½Ýa!“ƒwc¶\·ÇÞ,çëèƒÄÜÝælDÜ’ ɰÄè½6 –PÛ(hÞLäÓòðþýÿÿóWïß]>Ü÷Î6iA7w“û»óû¿ùᇟ?þç® ‰æKDïêŠ}bN홑ƙ3”r'²>äY@Ž üöýõw…¼pÜ:„Ïim]|’œS&¥†§éw$‚ ˆíp°™ŠP® /Šf¾¦ÓÝZí Ý!oÇé*ÝÍEØ*ŠT ìoEàfQ›”ÍœvZZ[ªÇ'ÀSDš1½™„·9Ä\Ú’”Ò,f‘AŒH4gÖâdŠ6fÈ™™4Í øC¸QQ›FZW¾HGzžaL&óðô¥ÛØæ½gô¡cÅEk‚‰IxÖÑT&ÊÒìp{X!‡Þ¸€»¶ÃUÃc¦€™–Gk­²0QJC“ V âBH.”ýùíq z=#øÛ×W¼îºU˜E”RŽu«¸^¥Âx½&ªYi:ý›0ëˆVq÷rAV@,Œê¡…‰º¶;*–yÛÖc:<¬ˆvŒS"TU¾cíX©§³˜~ÐÇÍZ¡ìåH Óå~„æéHY1Žø JðD«qÍ[Q) P0«Ç\–j\Õ¡õú³Ô[è›a‰¨ÞGµv*TZV2«W͆YЈP]@ƒ3[ÝG¢„ñÊê™B½_ÓŽÕSʽÊdŸ‚ÿv¬©•¢³G:,UGn0ôׂ²tFGñí.-âz»nŽožO·ý(¼”­Y XŽ•F63 ‹Y1`Æé@K³äºË2dh­ˆ -ÜåÑ&æ`¨2õ¾iYÐ 4úQB0uZƒµ¼2O’!±ÒëàA±Yƒ§·ÈüÁ[ó5†dTCŒ M®Ç®Vo¥‡åd!ÚL¤e")kA˜B+åmš‰Ì oÕŒ¹gL¤½µ%, ˜¶g~ùø¹÷¤›kÊA'¬a†çb”Ìllæ™–Ó’@[Ô¶ušykM§­q¸F€>-\I3oe8mfí<+wm«™I±évÙ÷D[Ùö˜áz^×½ïÀö©×EDdº†0æx½Ž/û®ÐéÉÖ$^úMÑ÷ѳ1ÕnR®müÏþãO·ûˆ›˹í·Dï›'ðeïbÌi×~StO´¯×{{<Î>Ùo18‡ÔO§wçöòr%íñ„Óéi\sæ]mx¦[^{_üÃÿô¿ò_þy\ï·Í|;wÓ¹™Ÿ›¸«÷KûîÃ-ë§KÎýSë¶üá÷zÿtº]_.—Ñç¸õ¤ë›oþøzùä°ú§¿ýן?Þ®Ÿly¶–·Ûëç/·8y›] ÇÄ>Ƨëõv‹—ý9û=aÖïxxâóóÑîWθDâa]¶ ™ý²_Ûbç³iÐ,cŸÀöøðÁî¯ó«çGZæcÏÀœŠ¹ú ‹©˜çó»?ÿùo^/·s5üã?üÃÿò?þ·Ÿþz¹Ì1,1ÇNµåÍ4nÀœ²™9Ãpþøé—Ÿÿú¯÷±×žÅq{¤/Û—ÛëËõÞ Ãâ¶#imb1LÉ;C3¹¦s[CáhD& æŽ0¶®]ù²-¾½;7ÛdZàljdëÆ‘Á\ Îi9ÛP~óôüçÿüíÙ'úìÊÑLÙ!ìê-%÷ÙOÞ°OmmÑó63ÐÕí¶÷×iz7bޏ¡Ãòâ¹Ï9…Ù9G$FzÚßÿÍŸ~ýøóÞcÎÀÀŒÈ4•߉©4#³ ˱¸í=î…ü..i}±vÞžÆØ;X…Õ.È8Y ­GÍ ¢§"ìæopixýè¦y3‡É“h\ÂÌ-\åV9"pZ®Ù`ðœ^àä «Y³‹/«-Z0XÝ\Y)ÏÕ8 ®FKP9)“%K_#Õ„ðàMØYlª¢› Y^Àc0êÇiÒ³àQ’f)Aîá‚Èl%S~Û®[ÄŒ9s†™'Âò8ë”2W™!VnùX d݉dë—Uš[5WÔ.K¹ì&ÓIϲðC¾a Š¿€”e’+gŸ¸áHU×ïl‡g®8 Hüö€•¹”5~š@CH‰³K8òT%èËÚµ(QȪڃ!EE™,%u?Xÿ\…ÈŽ~ZíËÜly³êÔNçí>¨vD°hE‡Á`^h"+*?¦ Wkõ¸'Wn­bâÓi@óÆtÓÒÖ…<?vºÀÄLÓ4HVŠÒ µKµlÌ Î<öLÅÌ’£ç¤R–l†²UÊSǾ±–Qq|ioð{Ö|[$§ãZªš"ßП<°Â•ÇqÛU½ XX%å-*g—0µÔÄ1çRP‚HeJHËšbm¢@¤•œÅ 1’š qŸ×€VpkU§07”¿l\XŠf ˆ…Øœ%›¦ô4ÊD“«©âñ͉Åç*™Oqµui§ã«¶ü IDATœYø Z½:Q7ª—iP Œ˜ÜÀ&ÕÚº›}$%(’LiXÙ–ë/b-–é΢Œ¹±.V­W±ÓLé*KykX°š•ÈÂaþ–å Nˆð”Ú HKÆN:3ÄÔÈ®ÙIez4Kp]ØdJóEHÆ¥Ž“iúÈ'²9[X"›Ø2¯ûìÑ'²A9×¥J¥‹Ùyië9å«ëÝötß1h¹)Ï>»¿ÎYÊ/׼ǘûkd:®š8h1÷ìÍBˆìII–m[æÈÆ–Ú¯—/=®óÖ&††µ|~xDæ¾òÓÇX×åi#¯¯Ÿ%;ï÷x—•O™cúêÔ4-öÛŠx>?ߦÆ~7E9øŠûËK§·e¯œ÷Û­<µ¶,Û/¯_fä»÷ÏÿòÏý2^öÕ÷_].2n_=½;3ï?¼ÿžÔ_?¾¼¼¨ï·t·Ⱦ_?ö}|yýÂÈ…š›Û\¶Û¸_?þëëå6w#1òõ2f×™l}P꯷1ÆÝå†y»¾#Ìã¼Ùé´¾;½ÏõÃç//㾋|zÚÎÛö—¯O/#WŒ‰ñáéCïS°ç÷¶l×ëËÀÜÖ‘bâ|Ú̹-k™%Uãå˧>îítþþ«§ÿøùôùåÿý¯ÿº÷ið‡‡óy9MÍ>C÷ÖÈe™aqÛ÷HĜʾÇ~¹ß®ûuî}ïië{8úÞC+ýÝym í|Þw¼Û¶CZÛÙ//Ÿnã³í1—6ûyá/ŸïŸ¿ÜfXŒùÓOŸ§nÞæårýüúóþòùãçë_?¿\nû˜ÑŒZiç•—ëýËõ–9aþpÂåõub>>¯MºÜ÷9¦¨ûœ1÷“q €=•³‡ÇG±í÷ 0}½gb~üüùõµ÷›¡>áà:?î/·—Ë'ÍÝ÷™j®);¿z÷üt½¼Þe\ˆŽ1ÕÞ–¾ø—ÿôŸþŸùÏ{ô¹ÓNËæÛöíó¹ß:“¡‘ˆ‘,’õX4gHàÒÎùÿáÃ7ßþüñWͨD4‡-ËñƒW-™ iR´¼÷©HÚ†,@pzyzO_oxZüÃ÷.ÍNYvÌzŒŽh3hl¾¾{þæªîmûÝó×4î±lT4oÊñ2^FhîLöEÌ”â¶Ôˆ†yBö=Ssßu§mðÖy½¿Žý¾÷¾ßÇâãéye‹¹ÙÇu)±çiÉÈ9S}Îk ûh­AsjÏ–Ma³^$²žÞò4ÍC¨à©¬­¿dòHeÚñl-=œËáa•rÉY™æðÊÁ A³¬5£y+l Yt§Ö ÓI3f®I£K £'a0j5l„hŽ ¾Xƒ‡a˜tæ”Giæ4a6QæÐâ<œy)ezCF&•JÌ )3‰ŒÈŠ›-– ¶óö@.9Ãp÷¬Ð¯¥‹ƒ~|²ýML Ztl}tíPß0¤Œ˜:ÀQ¤-0#F‘ u@Ð+ïÃt ½P 7Ýí-Æ~ å½Qv¾#ê~¼ãe^?„í@hÑëÁ ’íˆ az 5\¤gKo¼‰¢Ïõ´.ƒy$©yL¤‰dÖÚHÇ~Ѝí H}T®ºh¨yD¯xVþ‡‚ç7ç1„t3?F«·õ•ÞN„´¢@±(øoi¤²Î€–GˆV;.dK£´  4’F–Í`jÎÊ(K"ÝŒ\Œ ÅÆ8tƒ–ŒUž´cW£ ÊÓ 7±Ly3ÁŒ k©#¸&…ˆ ¤Y©lª¬Z.hv³· Wñtca¦Š~½9ÆšHeé˜Këdq0ÎàVÔ SiQ‡ã·ñðxëARU8 µùÓŽ"€‘šs( Ìæ„§‰^tµfD^ 578¼öÍi‡¬jnVç©dÒÙ4[D˦4ƒ<ƒq=5›¼´Ðl ÖÒ¡dD?€te4•ÉsÁÜ1%ZƒÂá…a[ÌŒÈ$j… á²Ò¹»[;?½šÍ˜csûö‹6†5Ô4L"ÓÍ--á8'Ö°0›Ù²¢5¾&Â`k‚æÇÛ\”I«Øb4;„d$ØžÖg6æÜÕKTuµ¹¡¨ÇntWÌ©ÆfH¦¥˜r7À¨mÛ²ºzŒiÜ$™Ri¡8¯Û¶­r=¸¾þþ9côÓÃöÝÓÃì÷»udzüFûœq'2fÎÛízë34bæ¼Æm„G·ó2çmŽÅä} šnïÏæÉ_nÙeZ“‹­ïŸßwÖ§ÛŒ.­P§Ä1÷=;$L3G&z Íö‰éºí–ãäÜÇÞÑsÏDoÛé|Zé0[ˆeß?þxÙsïý5µöŒ|úðíã²sŸ} \:cDôq9ö{ßmÜ/·¹þr¹æŒWŒ±Ä­»lºkiç’]ÛÒ"Ç}¿Ìž¯¯¿¾îwõñôôð¼æìM<].1´C¶œ–Ñgb´e¾^nSóz™}Ü5÷ÑÇž{†ÿá÷×_~ޯ䶭Lvõ¹|}ý|ûüúkØúx~:}xz~øpZ­Íûèc¦ž¤‹h¶}Øõöz¹ßç2éKžWò´­üš×´ŸþûÇ®}u©.ëóšC×¾ÏyÿÓï¿ûôùrsÌÈ‘nÓÖPîÆ¹-' €?Ÿ×O/úë_çìÆi¤/¹®ë»•>„V^2/Ó[2ÕÁ…ö»§çkŸéXIå2cƒMXn™¿{—œkK)¼‘‹áÜ<¬q®ûìXîžÄØÇkËF´¶pXP¢KіLJÇÕ6 ÞRšs÷»|*Œ3ôm‹˜K†æíz½˜£Wðîrö1Çåvùt»]ö1„:Ìýt>ýø»ßÿòå ¦)´­§wÏß^~ùõÑ}—e¬ºßGhÄ­Mffó%rŽŒS¡Ðì „´÷ûˆ®´Tõûþùz¹~Þûm„‡·3NX÷Øïýú鋺¦çwïÞõAÄl‹™cFrr—¶b9Ÿûm¼{|ôeÁÞ—fß}õ0î·/Ÿ¯}ìç4÷‰ÙÇýÓåsŸ÷}°!Í´¶½e[¾zð¯Úëç‹óôãï–öþÓõxíÊF,îËr¾ÃZÎŒh#f[sm.×Ûl¶Ž *ÌGÎ}ê=Èœ™¹÷{Ílûú«ï{tÅî¾¾·è_./Ñy~°ÙçÌé®Ì±{Ï&õʱÃÐïÌH'÷îév»^÷[æ%ÇuúñÇÏ?~÷ãÓóÓ~{ÙG|úò2ͱ¸"éZÞ=œÓÏ1øœ€hi‘Ë:Þ??ÿôéöó¯¿î}7 ‰ûÐì–qïŸsÜ>½\÷Ci‚ÈPfS ´1•9îã~¹Þö]À”³Ù<5÷îÛËõvŸ9+á3S&ÕÔï7$æv^WUˆ¦¡ûØ_öwD[¡aÔ̶Ix>´Í÷‡߯—>[À·ÏöøáëïG^ÓZ5«ÄÁå{ܲï1'"‚ˆ¹$½÷Íjã:_8Â×·Ûuï{(µ÷3ñðØ>ß?õë92&ò¦ÈÈ{æÞcBŒ}FÞB¡˜Š˜cïyŸâ =4ÎìG¶¸žo†úžY0FÝ0 &²övª3™™ÉÍݼ®ZTáãJ2{ØJRÙ›Íó€Wð -e9Z2Í5‡—k|Œ‹·ÂT× XøDÈ‚nYÀ÷NµÊ‘dw! £73¹\ìD·Œ@Œdö¡-ÔOV¯«†îúÑQ=¹h0Jš2%2Wþ8W‡$…¡¸s¯òY½Úe¶¨àñnŠúà¦jJ’‹džP6ÖBØTEOÕ‚àØÅ˜ÉÒ]²:æ¹µZ\½iú²‰Bº”žõgx¨Sd¿3ÎúTqÝÊO×U±â$J! yTíS,mV27C¼Uúp˜“k¢2(huÁ˜ã@[­*Úq {.õ=+šÊEÊÍÞêtG «þ‡þ6™XúÛý’e°VùKzÉcƒ CCýG4–µ­R+,=é¿Ùd’€--ZíHè$šSé™`£¥þ½5u¤!j!{´[óÛ9YÇÒ²àáÈrà¤d,ÿ‹–Pôì¸=è´Tm’ë5õ7NÑI¶eêMimR*° eØÎ” ã|ìõËìí( ¼Á…·rD–VÈ ]זǯ#ÇXíŽ*µArjÜi¢­™Ý`Þh\j\“lkGGÕIãr¨G¬ ²Ù!FjÖˆFzƒµ†²íXÎ NæÖsôЕ­ˆi„å´ÅBšÉmDÂÖ]˜)Éf+`T‹ÔˆëÔL7¨•â}ÌŒeaaCÎëúð°ïû’M©9 ¤É5bŸîÙÐIÑ›ÉÒ"E4Ù–p™M“Ã}N_ðD:È1‚–jrS’Xꊨaœ˜ôœj¢Ú<»Y¶H.U¿AkÆD¸-C³ï{ä\Àdƒ=™w™ÈíîaÁë—ëý²_r´Ô¿ÿãÿ㯽]æ-‡ï}=²aß¿8’Ü Î%tÚ¬a˜.ìºþîý×_>_ö9b×Ì;Û>ÃrÌ…R{·6%î“xØž?|÷øüîykÛû¯þp¹~θÃ<Û¢!XÆê~ùüºkó%Ïû~»1f¼Ü÷¾_×u‹ÌÍ2cQ¦ž‘K³ÅO9gäe¿^#të¯ývÿrùõ>øç?þþç_ùtýLeµÛåvÛ±ïýrÛ7±õûŽæï?¼Ï™=C£>`äÿþ÷_º]/ãËÜw[‘Aaš²·‡u{½>3©1"bŽ9Û†o¿~/žrì¾ÿÝïï—/nûÃi½1™Æ4ŽŒ)8ܸ\sŽyÍ·ûõ2c„øõ×ßžÎçÿãÿüßþæßýîŸÿù¿|º¼|ùò‰«?–µ=¦YI8R}„õ¾û *­ÙúELrö½ßÇLx#z'<¶Å]ýöàÞ…¦mÕ¶žˆŒÈ1À Þf4;¸-§¶ªÑ"ƒ¢ÇmîcN…¨Í ¸[Éæ°T±}È+ë}š“pó¥Ít_×µ½ó™Bs_EoÜ2maØzjçÆû5-,¸nOÏß)ûéü¼¬±Ë]4›+³q}µí™šy7iÆ¸Ž—}EêªýzßgqéWH²èýÖ[7õÈži÷Á9ÑGŸûˆˆœsFÄÔˆ‰I·a°dΦUöSd˜¥ª7åQà‚™+ ?8 š»5ج…-R\C‘hÔ„ÕÔÊh kæÀñ…¾ÔƒË­½Uæxp–XŸ˜„ˆfD:ÐꆳÂ(iB8™T3›\ØèÆ0¸æëš ÞéÜZé I“sa&ÎÈœB0 3 £C L ÁP…Vz„``–¼¾²Q UUî6D=L(±!"á&"ó¸~eÖ3tg‚Å@‘6íPù•ߥújÕÛ+ì!ú­>ÿz\Æ Ú@7t‡¥½yPÓH×›òæÈŒS’A­‹Ç¶(yì³ žPìT« ™­ž¨¯/ÃÒÁÊ,òéñ«*¿T[©B¦ÖM„ÇüùF¬"˜ÿºB ·>dERþ–ÁÂmâ`Ù¿…»ù6dÍߦ&;õMÎ7ÚRïBXú1îÝÌ“pÏ@¯)WGnÖ¢ÁãÂHcBî°V˱…¢Ò™E%‰êî[M¦¬ ¨¡ríGþ)ìÐÝ)ô ÎМ ™éÍ¥Îä18Wvï ˆæQ´B©¼½,­þÌ$;Ê™ üh.Z]&ßHù‡¡olX:gÞÒ‚°ºeVN¿viƒ ËQܰƒ2ú7ZlréÕuéro‰ãð^-jÝÚÙÝDÙbhÆXà¢hŽ3f!úÁÖà\Ðaöï²'Ð]ÉÒ9 ½¥…ä0“29Yר÷ë’D„»\lžN[5Çêë»u}ʧɈh²ˆ³¦ ^ïƒTÄÌ0ÐM9zÈhÝ-l› &O±™/H=´¸XŸBÑ”X­Á±±zO¸-'ÔÀ`‹ÆD lX`¡l͉\dª¹©K™–™ÑaSæ>ESÞwYéa) •*…ÅÌO××yMfO„<Õú³+çïû=ÆmôkÜûÌFÌÞÇν_GŸ¹cÆÈ˜i°°ˆ… I\´¬Ëå3òááÃãÓ‡o¾y~÷ühiŒñúå×1Bé² Ò·Üû˜1{FÌ>Æý–·Ž9ðððaï· »Å7Û*Û¾ô[$––Óg¤kÌ=òsŸœc _wì/·Ëëkik¶µåz›È™qóÞ–ñ´>v|˜aÜs„žŸ¿:oþôðxÇNBs ÏûиöÕá÷‡?ýáÃù§Á©ž÷ÑGs~ÿÝûo¾ùá´=öΗ˗ûëíõþiÎû~»ÌÞ!¼µ>u†ÙF„¸=œ·eñ1÷OŸ_gÜg?h=ÿú_?þÿù§Ë—//_n·õýûÇwÏï¿ÿñÏ/—O·{ï1÷;Sœ4¸ª(ã§e#æ~ Sœ)AŒL·æ§íéÜ-&åtʰm[öÞôŒõÔ3æ\·óòÿáŸ>ÿüËDB63§9²=,4+K»­íát^_R¾¬«Ág´ía{8?œ—Bhr=µõ¼€|nïO§¥ÃAºoŒF_Ú"óæÛ²6ÿút~<¯ Œô¡Nçrnçóz1“æ¶Æ.Ùt,¾ŽØ³‡b à˜a.iÎëWDæÖæÝö©›¢»öÍû¾_û¼ŸLK1;ÆŒPæÜç˜@ôÌ=æPVì*%Dä£GQܬvøºÐ-ÆT TH˜1§?p2ZΨ|LJ%¨ÊaòÐk 6sk`ƒy=ä½R³F5Ø$` K`°™Xè`–iö$G]k Ç"™áÎ\šgÓj .«Œ´at²e ÉM \QgÁàdŽôœÕ¸‘% 914a(êØžq\Xò˜GÁ YÚAɬdZ{ëÐa2dõ5ÇÖ É2ÒŒ±TÂåp;ÈÞ2—SG—޲·R¢ëBU¯ùoµø‚Cv«}O±¥„¨§pý-U‰±Iªz¹(ƒ§Õ·Èß2W’TÄ-¡Bäx Ðë½£7Çp em¾ ± =+—g%«Å]îc– )HJ™V/˜»~SÊžÂ¼Ž Va,Žç¹µß„<•;bI<·"çúÑ%p³rúù Ðj2M^rBÃQ3W8ÂÕÈÍ`ÙŽ)MªàôÃi~PA­þ"•”Û³øq£ «#®(!&¹‚h ŠÚ#×°T.º:ç¬~ÀœÁŒ¶ki1†í´=ÊøÃû¯¾ùîÛ¿üíßÿå/?þüy¿½~Úû~Û¯C3æPWr†éô“˜0;™ï¼\/lã’ˆ§§ïöÌqßá»òááëoï·/ºw¶ÄÜæ`ÎÄÆ–ÀÌœùþé÷Ûœs.Z¾ùá×—Ï\§9ú~¹÷9ï{Ì9¢çˆóÃóy=~ûùËkfèý¼œ¾úðøáÃïþ¯ÿûožžÞ}ú¥úôé¿ýüsÆ%™†ÕÌú¸:½µíýi%–—×Ѹn–ϧmýüùã¸} ÝPΈù—¿û»ÿú_þ‡õ×ëíòßù‰™§u‰ËõååÓç1gFš°%è aÎ1zF`2¶æGÝÍ`V AÌ̶lOOïÞøqï—½ïû¸í·¨ýÆŒ)È&B‚2dsî1ñó/?íèŒÑ¤†Á¶5µõýæOïŸO§Ç­mO+ö€¸4Ó|Z¶Ç‡'PXæÒæâ«qµ¾Û},¼ÑÖ¦5Ò°l±¬‹oçµµÓy6·ÑÜIËüã¿{ßg¸É–6£‘z/¸³G2íF2æhm³kØžÎÛÃýõóß¼¿¼~‰Ûˆ˜>ÍÓ"& ·ûõvÜÔøöñür¹½cÆô„bÌœ\ù|~Š¡ÈŒPz°•^Ý- *ÌV­tºy¼ôTѪ=…g—)åX˜L¨Õ(4\C‰LÒ4 ¤5™QQÁl†q!“²4òi=›CSÆrò¶6N&Œ5$̺@ òÕètS«mƦ⓯(Ÿµ–œZN`ø(55'F¥Œb¤gF·b*'"“!4¨3v§#º±èõ妳>™'qŒ–-Ò(„æ‘$·42½¢.‚¹… Áèo¤¨„Z™ßPåÍ‚aVµ›°$@ʳƒRÅÃu†·Ã ``2(³Æ?Yþá:[åñP,ÐÍÛpt˜H½p ܲC<“õÐ=.PG5±6WV¿§Ž¡é EF¡A; IDAT0-Òò¸+ìèâ ®#ee¥w.nSÊT uàí™_7B§ùq³ÂJ¿F«ßàW<Ì‹oÛÀººÕ©|2ä‘cwÒ‹Åfôònp³LÂ]X( X9#G’&9ùŒãw¯N^M kÍaIÏ#¦V‡Âái³!בP·ßZ  ÑQDRáiVÞêl½–<ª‘G$­ÆØj!ŸN <°ïl¸4ƒj&à ™Y77Yk˲–Ѱ˜y`Ã<ѬI¢f(¢ƒHaï)ä”/Ïç À=G°÷Œ)çøÿ©zÓI²$Kï‘«jfî±dD.µtõ63ÝÍ{žˆA¾& üI€Àtwm™‹»™©Þ+"‡?®Zô 2Q€‡{¸»©ØY¾Sý¶«RÔžè‘Ju vžÚº6ë}Œ¥N«sÔÓ‡ËÛ§÷õ7?ý×ÿ†ÿëÿþôÇ?üür¿Çª]v~úøæÒ»T7¯äšÕ •a¾øˆm" Ï|þçÿùy½þ(÷ÈL«=TM±ðÔ3…»laçóÓéÜî÷­¬,k{í=+¹Efèšé[ô¬Ñ’§“gÊ›*’/×/}¿ßî¹o±ÝúëëËÛçõíºíÞû}ßFâÓ×—½vDyóÅO¾žJ~9-ÿöGüÛÿôÇ_~ùãŸÿuÜïäV¨ÕNß}|÷òåµwŒ£÷Ó2zVî½÷ø9.OOs¾/s»çþ¯¿ÿý×/¿|zyýó/Ÿ·Ès[þÓoÞ:Þ}¾Þz™ÖõŒ¤©.ï¾ÿíOc»Ý÷¡ÉÛUôÜR¤«qH¦V­îî(w$›²€<ÕètÑ|ê'p§,XéN•e¢<\HI#UÌ1*‹K¤ ‹,+i/$åÆJò‚¤”sº‘’¤zs>5- ¥ä(‚š3Г˜>Y;Ó-»Ëf%“irƒ00ÀRE°í i‹hr<2δ"‹,° Ÿ4/s>£f|}¶gVNVœAõ¹yLJ“6ȘªÅ|¤ÎâBJ˜Fça¸I~iáÈR¨Žøô>à£Ì71 œƒÕÓ”Ïl‘hÙðfÉ)”ÈA¬Ãü³CøJ"O æÕ¨œ‹%3-äó¡û¼¶ãÁ™ãt|Wéˆeœ5{Ðmb¦tep\ {™è lÓݤ›wÚ`>[;4Ïcê8«äl’M4û,Á£M VM¼÷L Ö<‰ åæÄ¢‡þ8ûŸš`ùÃJu¨Xn| üá3Dî(ƒÏHÈæ¬ó¬bNâ胗6qýs&r2ŸõXïáÜ~›?$Gu³€Ò#ú·Öb?Oå„‹9/B›¦âq€ORiƒv ¬Ž7XôZ5µD¸™]Öõr^sd¢’eh^6›"2'¥ffð„X²IÙksBôù#ç5ʺH/xe¢FY*YPÒ½•a)Ê$s.…P[€UEh#Âà¥b Tc°&óÙ°$ë÷쥴4#FʨÌr2)Ò-­J%Yh”Oa[._Ðh—o„™LA¸KÈF·Ö&¨wiŽ¤Ûª”ѧů6©üZÐQmžÁæ0 „©—šQ´F"*ze*àlgc±³Q°µÌaahM,ÒØÌQKAŒæ„/>܂ʉTž˜Ùbª:+k–L#¦aA×étY]{¿*2YŠ%ÔKŪ‰g…|Y/wõêêû˶ç~—·~û?þÏOùË_ö—þéËçå¼.§ó~ÛÖv>­ª~)8 n‰`$¡-kOÀJðüãŸÞFw[Ï>®ûµ›E ëÛ_^¿Dpbã?¼±¡{ÄݲžŸ¨é³"_Äeµ´e•ôáÍùû÷ï÷ÎÁµv"ÝXh½FöQ[ŒˆÜÆ­âúÒo÷Óº|üø»ÛË/+u×ÒT²K¼Þ^¿ÿñ?þÛþŸßÿëÇö5·Yéïűïq¿½ô.9øý«Ÿ>Ý^î{õ¾«îËÙ>\N½*cKÝ›ŸÈ‘¤o¢+³¬•cY–õý×/7#ZbÛósËÛØ¶Ï¥®œb³¥î ¥íã^ä©Q™sˆ×)EvÄj²ž0§¤Ért5W´Y²º,ëÇ¿ÚGX¢,³¸`½\.‘.3ãkͼÍÛª†ZoÛÞYKóÊ>´먋ª]Vy³2/§/íôáÍÄm3'Ïí|nË›·ÏYPðÓsœ{s-ö5n·—#gª¨“²óôör.î·¸5ÛÝ{Ô÷r[\øòõóvßs íii+ޜ߾Þo$Ýh§¼ÅÞÕ]{Œ{ee¥ìdèžÚsÄØ²:Ít믩^YŽœÃºá&+FZV…1ÙÇ‹áãŶ•Ç^+-àæ“Ó=ÓDD–¡ØD™WËùÌ"iîîj†ù&vfƒó{)¢IeÙDyFUÍ 7‘YÌ•@¸%åL,nèT–ƃÑhf&¹QÆäåpDqˆŒÂ TC•2±º) k>i$!`]Å’ €Ê&™'鉮,«f„œ–‡ÅF&(ìRiÒÒTöØØ›&M'*ÞüOÁîa˜{e˜„ƒòc1×!ƒ†Ž®¤–Ô0$M<0 ’ (NĸTè4Èú8Ì¿¹Î >"îÇá4S·4gˆçT4r²Nô”ÃA¼œ—Œt¸Ogfµi²cqÚ»Çʇ¾º¾iaö@0àáõ0ÛÑ <„¨c2‡[o®ÀÌmþ§™M ³løÈÅ7³¼f.šÁÒ‹f.Ÿ$ÎeÖgË góiýñ°J-]T¡2zµ2gB45ð(†1í]ÂѦ³K§kþf”5=LRdKð8ã°`¢Gæ“|"TŽXþ$Ìê±Öl¾*”Í?Ê‚5/°Éž<Èa5émGa6ædÁ¼ò ,Ö¬ 6Ø}Ì«œ“(/Šp?xò-˜»Ù|Ó`³¯IÉ5ÍAÓêr/†}“²P.OMD¬»Œ †·6ç*a¨‘àš4jÐÒNò@YÕl*y)D:$Òé BB¬m‰Qm± ”U3g]t®AÀ|Rß[Hp4¤La¤Á‡ÃÍÁÌT¡4k§óÓÖΧ'oŽnŹä 'Zã¨CwžÛfb+[V07kX°ÎÕ×2¬„/†¤SV `ÃD«¬å¨tÎ!ŹŠ.ém“] ä$ËIåŽ1¦Aˆç ¿{>÷Ü Ø²>ùéíº*Ô"J–l]N —§Ó©=o}+îY!U^险ÈÓ¢ï/¶Uÿ͇w/}ìÛ-ËJ~ÊðØ·Ó‰n–yì9¼¡Èíµe…}ì~âÈúôó×íõþéó'Œ{êqݰ]lAô÷D’‚5çÓÓ"L+M ê¨ðмízÖ= YŒ÷žñºýÃÇËëX¶1”Ⱦßc~ãüýeéÂß½m_o#JëÉšûû· „¿þéíÿûó_¨õÝóÛÛþØ×¶üêýÇkïÑÇ~ˆÈ}¿?}|Ó~þùë×ýK¤UÁ–å×oŸ>}½ŽÛøÓÿðûùã§Ï_+úy=¥óörUóÚcô¡ÌPP©úåó+ÓÖų$bìyß¶5íªó»§uõõþû÷Yê;NοÿõOï~øõúÇþrýúúõË¢ïªÜoùzûÚǽ¹üJ*5šéÍ©‘­œ®“¼kÖœˆ›25£E-+K\)_²-Y–¬ªÍÂú~»¥ÄÈ\Ú™%î‹á¥d™¡l.O  xyúT Û`agÃÉš–æD -?¾ö•Õv¶ßžj­eUS•¥ ÙRå!³Ahýðæ9p +³& ¼V&ö} ìÕ…*Ƀ³Òøáôîôî»íåzþtâ¯Þ-×íõzÛ‹ãÍó‡m{1ÏíËžòßþÍÿôõÓ§‰³Æ@¯ÈÜ£FªZunûKŒÎ¨jº6‹’Õr-ÀËÆRfݪG™¨æí|y“G£¨L5ÇëÞ9}§²Éº?ÒÙÕm&«Ù渗Ñ“Ñk™Q ° M A1J+oT(†›X(.~ÐkægRî«OÉXÒZKYŸó'–C¨*S¥™ 1¢,»2„…„ ¥(«y6E ¨¬JHTV™2T`ô˜v˜sØ&L*mˆy6es³™Å:ªƒe qÌÄ#ºÇÓ¾&'2áá†#*73ÛŽœc'^8NWéÛuѦ³“3BMúi¢ÏT¥fçèú¥ÍmA›2çS.Ñf|³}§:ÚP€râ*Ô&ä‹"kÆylr2´ G8X(N¨ã‘?›”‰|(i¤þÝõ{hióqî°€`9Ѿ m?þáÃ%¬£OwÆŽÄ& ëÈÒûRÅr0G\æfRs÷?¤Y•O~jÃ$¹Â0ã9t'ÑŠÐ<£…rHj3úVåâ"ˆ0Iž“â!›ô̠ٔAÙteåöm_‘³%Œ4úùÒ°†æ[4Ff˜£í­,Ì|‰œß2  J°¤sTªÂP"”åmÕi9ªt€î´ETà^ M~¶Ëù}¯Þ!"Ay;-'ôQ€›šYqë× $ªÉ¤½ [ß2î/¯¯/·MQú,`‘[©_Öç§§ÓõúÂ!³ í1ö-¢²3ZÁñ²½~þåz¯ÛëWTDvÖ«¯¹Ý÷1ń…OOo¬Ñ8Fg¬Õ"zt—_ÿê·_ùÜ÷QÞ˜êéÙ[«õ”–ËóõöIÙË­ç圱Ýk«×Y#Ø-ǵçècûèã~ßE«{¾\{y*«ùmlæ÷±^ûQJ©ðæM{:¿ÿùÓµUžéË5n[ÊÿðôÔ÷x¹¿ì±o/·}ô}¯¡Š,ªÂÙ ýÂP¤P^ÕUYãÆÒìrö¿þáãÏŸ~¹o¹ÝîH.×á~øíåíÇ?þþÏÛíjÌÅNUy×®ZòãÉ3N=†yùTÎå*¥ysö,¥O¼’MIŠù8ð¦”ËÝãùl?|øn2ÉÎî"F¢02+ =*»Áʲu ªèsM£5HfvñµysÊííßþݹ}½6—ÓšÃî© ==Ÿž>¾1ùwç7}´-ùßþéþôå/÷£¢,3u*Îå鋯»gî·H_ÒZži£šT)9è 1çÇ`•66帽Ýú-úˆØÄ¨V×—××};ÞÆ¾¿\¿^¯·­—v^?ÿyë=z®«{Õ1ú1Ì‹d%ªd™Ê Z½­ÞBB*E‹9“°p"Ø!•d3‰Ñj.­ÍD³Öü‡~·m÷¬áe6W0„f2 ”Á«ÙÉée6'!‚¡‘*¸1ò@ M„ ‘"aÇlÑ–ÁÚDäÃçö‰›9,i´Æ\Š=LÎVåh¡2¦¡Fë &2…Res„$Y°€ÄðñžLƒTQÊ©O8U¤‚ƒ!Œ…ÈšÀªRÌCF f–Dm÷ùæÞ.!„ÏvfF×1ã0in’qÞDâD˜NP |>ì™ë 7Y1EHV*Põ?ZÒ,›BÅ9ƒ¾Ï‘¤Òµqk3§>9` ¶)‚‰(úL ¡æÉup¦¦R'«´8gòA嬵‰tš|èãß ØÄ^á‘ë?ˆúæS54Ðä†r5•­ù¢c>ɳRȹ)`ÍüA’`ÑÊ›ÌÌÊ}¾M&ÿ"'þŒ°9èRtHf•3Ý>g&ÅÉ‹¬uåMÇP@Á7=3]Ž: Ý¬éé)øü>ƃ·Y—8®+”5¥ ÇÊá¼°eNiöXJzŒ–Œ3p a#¹Ä1I0?hŠMš€y]‰hkó§ói¢b Rœh\ÓpÛ<3ß½ÿÐ÷ެ©ÏÉŠuôT¹aòä"]î‡ ‚ÙèT)+Z"‘)#ö%{`‡‹Ã–f„“¹8ƒ&Ê)‹f…–=ÀZy‘ Õ‚.;Ve KæÉ#ËÆ"Â>z†¬¸À“‹Zº"MLå>6–IAM³œ°Ó%•MîÎô§çwfn9\F±‡Ò˜‡ÐsIeµŠYAkH5Ns—ÚAƒ,O”è[¶4§ÐÌ!e!Ëàš¯fC>„’Á­¬÷Üú0 ,+¼]Öå™ë>"©nà|q  =A  9 C2öØ%fXG¨Â-qW³}Ó(”˜#ÆkÏ/…pT¯ª²SÛe cE1«omíq…)»Eä>ZjO/MdÑÂËȺ~‡AcpÊ#úõëK €íÝÙOë2ªÔÚ2}ñrÿЍ v~:#k(Sн’ …ºtß#Çn¾Ú(žˆ¶,×Mˆ‘Œè·^°õ½•!‡ …ˆ´”«–ë­› po=2'ÖxÉúÍ÷ßýùÓ§ÛèÛ6r^ªµµ12-}«êÒPY›¡ÒñÝóéó_oå [q¹´¿{÷åëöñÇ¿ýÓ§O£ÒhV­xÝ·?þë§ßÿþ_„Ìr·vvîÙ­v_¢ÔG¡ÒŒYÞ*´kÙ‡*mŒìùò&µ+J+Ïç笑£ÊÜFa)1RãíóéééyÛǶïë lTY¥e÷9_æÒIUÄnÙde­5 æOÏo xûôîÔ–ý~ÇyaÔë§Þk1žJHêÙWÛ9ú—mßSòÚùôÒ÷}Y}9[ž-M)ÓÔŒèE2ÔÂÚ²²Þ?ÿºsÏ|¡ÕyåÓÛç­gE¦z%ÏŽËÉN'$U[Þ/+þËë>D?eÜö{Ïñr¨¬¨Ü·ž1ª:³Çªz*$š‰¢ª|î›<)½Y/ß}ÿñöúZ…b.|¶ÂeN–‰ r¶’D\9¡9.ƒ—d{ï•ÉLj ÅfÎÆ¶Ò¦µ sÏYl(~8G¦ˆ8ÐËŒ{N©ÖÒ\ÂpOKN\!ÒefUÅœM¿ ”* –…šT°TLÞýÎéGM”ªÃÔ›ßbŸ‡ :¿}Saµr¶ãm„ÐÍ©0çd(SRX„5Ñ›|Öqe)Ê"Bæšð¹‰zo€YCc›P8–¡eSÙ¨fÁ—™K„ŠjÆ$£Ð¤—¢%,¡7ëÙBæ¥=+@Ý † ŒÖŒ§Å¸Û¡:iÐP™K.,zd¯èjä UVµ®ëOßx½wU™‚È1 "«!3 6UgµËrYqäSjfªÚ ¨Q\OLPåuüªºÍªˆ9\öôîÝ6:2áù´žN¾Þ D™5©`‹”ÁÒ°Àª$9惌8Ú¢¾˜TU{/ª¦6õ¾Ñ{d\i‘Ií6i¬…Êm„)ɳ7UµdT"³ö¸2¢J?€Å¹.çK¥žO÷ž9fT="_•cYÇÓÓ³/‹ÆW[.4˽Kä-¾<|÷Lb¿£LÕeu'l¡²*ÒUßÎ#rd×j—uÙÇ£FI#*}õÑ·mY~úácí÷2®ëjÍêË=9ÚÞã6”y?_–õ ì[mÄØ¾ÿÎG×õ>‚:/'iÙ÷G1Y¶fù|:½ûá—Ï_þðéÓõzs#ÒÒ”¹/ç§¿ý«ß]û§'eÖPy'hÏä0?Ã[_{×—7¿ù«ÿú‡_~þÓï_…ÖÖ}®¡VQ¼½ôO¯×³R£wZ€¶ ³›ÆfUU*˜»÷5<³¡ŽàdŵGɰhùÏÿåŸ_?ý¼ïI•TeöʺÝ^£ÇØ)äi±uy÷‚máÂ%%SX,žÍÊlivú›ßüÎj<]Ú^èàÇ÷?ŽGÚêK9†z»X;ÁQ§‘§öŒ36t–%Œvªïߟ><=÷º‰Šj1Øh1Ÿ^Žw—Åx§‰Õ33,«£jio?Ü^¿Fï'ºÖeýÝÏ럿~Ý®cïãcÖþNÖjÏÛýk)2 #ØU¥‰Òµþ:j‰JZrn cÂgÈP©±Ý¯c°¤˜ë,F1{Us!V0Òò`nÄnÙ!@ØÃ˜’^Ôô]Üæ>‡—ib3›±a©&æA›!Ì,OÖ˜¨A1Ti)ÅÄ;Á­Ò’…AxC3YŠÂ 1ö\¥^Êb®N/¦ÑŒ¹r0ËͳR šò^˜2¡(‹5£ìDBåѬÓAŸû„ˆUëgJô‰Dˆ<˜ÚªùÒ<[YM3É#Ù‘"‡ÙÜìKÔ\á´ŒNᕊ»M¬vá°¾H³ù)Îuó‹œß åWÕLˆ´cÆÓ5|&A91G#ñøÓÀ²Ç¥†œ_4-=ROüœòߥ’žS×9RÎjßË9ž­ ZŽnœêß½Bヮõ-e„‰E÷ÿ”á€ØAÁ’`“E£“ÓŸš‚^#œ$°Ms‘e^‡:ä+>6ºç7š°Ls¶öØCj kö=Òça‰› ª5°ü¨ÞH6ýµ©ÇŒæfó7UÒÜaD•YYNwÆgæ™ý îÏü¹4/ج˜™Ñ`êÐ3Φ¹ô9ïÔCkúwÆþA5ÕQ„˜6ïÄ+û¦=pìó}„ãñ1Ì(Ú<ÅL ÝŒÚ-G éGmq‚ðbCJªYÜœ¿=Ó×2Ñkóæu‚¦ ¥• ªæf¬r1 TeΑTBC2CɪÎçË^%“±Jt±²ŠÖJd•ÊPVƒž.¶ƒàQÆÆ¾Ì›†–…éF“÷¦%N9“!I´ª@J‡B4EŽ×­rK;zÇe ”åje¥BM2×›eYNkTW‚´Ô¢r4BrsXáû÷=ï[fÒJ3,xÎ%¨ŒØ²ÇÄsKã+Is4ªÈò<"’I7g­~jYùBhm©xÌ:tÍQû©¾õXˆ‘2Sf¢.j\ZŽΙ†5—ŒŒÀ’s;Õ©T–Â(EUîýé´üÓ?þÓ/ùúåeì2ƒO¶ìV±¥¬R§õM‰÷ýºæ®REM£ VAo·­dK áy]W_vDõ> ¾ß[9¶þ2*[?¼{Zm¹uSE¥""‘£ƒ°ZžžO×{ï{dL=kï{úˆ#èMnööù‡{¿¦UFÊm`[ÎO5Fb¿o‘f¥Ð˜ê~'ãÒPP_FŽžß_ü^wpOÕêÅØ*ðö¢¾óµÈJԽȬ,h©·äuËëà§—?ì÷/húáÝÓý%ª–*?ŸO¡Q½£BL¥,—óraê‰Å,äâ •ahݲEfköë|½õªŽ‚ŸÿòõÖï}´)XûHÆè£÷Ž@ȳ°e]×Õ_ÇŽRª­§õÍÓ‰užîÐj Ñ(¥Âíº²Jû–ŽsW!ÙrñÓSõîéãå»wÕc„?¿ynÏKöZWšËAgk6jTÈC=˜h†šû÷ëS4¡ü®aU,-YÖ¯×mW¹ÿôݯ÷ÛH·ëUŸ¾~¹¾Þ£†2u¹´kßÇKH`ÓHµ÷.ÈRªýoÞêvQ¡VéUH«"GiîœÎЪ,3³z.33Z›*ƒéòxavQ>óo)XiöŸ˜¢0Ùèîæå6÷cœlÖ`‚ÓàH’LZ W+yÁq8ÅZ IDATŒª9ìȰ4W.©²á8Õn9-Ÿi&Ñ’ÊÕ¯í€ÊÎ\„–aæn°,œõ@¥´ÊRW6ˆ¢¡fʰæ [¡d%¢&‰‰sDž,bÔ‘1’ >¸¹~ÃÉ£ 8*›õ³bx™¤ÉÀ<ÂBq<ܼæS0ŒÙÓÇsÎþ5!; g«mb&åü˜¡©š@| &ò íköq4q¤ßXáÛà/SÕ„qW̓  *>8nó°IZã¡›LÌêt™ Hãä‚ÍŸyRä‘·>\(;Žº#su|u<ô)Jðàk¶_tŽáñ¸Al <´Ã.ld#šXPçĨÁH:ÍÀP›Ý÷ãH«`Eór,ZPS?ðYÄ´ÍIa8š™Ì§n3 E\`¡Ù,-ržŸÅ6‰GvìÌ fÀ³Ž¦š£BUó›Ð/rj‚ÄœBÏ€8>ê<Š¥‡{4 fLþ¡„bálÂ)°M’ÅAh¥9 ”O¶ ÊYG€“Üpü)‡øÒ&GÔ õ8·}&¼&` Þèlrpz‹œSî³pj2ŸJØÔ3¢ ¦…Æ|vÂÙ3-hj”ª*,UŽq¯˜ÓAˆ´Q •©&‹ÂR°-=©FíÞ`åŽ@£«@bµÅœ¢–²0˜‡Å‡AŒ}M£,€2µR²sª¡ ×ñ׃VÇù[–nhDÒÐj-LpҚا“¸‚Í–>x‹¾ï1ŽßÍ?Üêx I„—2P,×X{V³œ» \ga$d•‹XvZ–¿ùñ‡×-K|ûý»Â¨­¤¦´Š õDЧÍBÜQ&53Ye¾,ÆmßGDÁrÈÜÊ*šÅ– .¯bÈÂÕSU™}÷ƹޅ¨Ü0ÕrÒ÷o «e=Ïqîã¶Ývsiœ††ÌaUÏoÞØrùóç_®×hȰ¥nñüô6ß_ni;‰DV÷}lÃŒdå^q·´,Œóòv8ïû•bÑþËõ~½ß£.l)W†2"£Òo«òëõeThˆ¾dH¦¶,\°o›Ï­FŒD™¼<‘Ì{ÕbXõ¼/6ƨȒrµ±>) ¿¼î{Wʧ™Ü;ExSeï©®©ëÑë¾Ý ÊÀ½ªù2zoËy=¿‰^#FÙ¾À$…ÅØ·û6P¡–3«|jËi!Tˆâ¬°¤p{Õ¨`É ­Õ™ l=5@¡ž’éàåé÷¾¥´ßýð]ï7¦ŠE·gŠ¡“­/ôõâ¾Dõ,ìÝÅZ;½{nÖB­º°ªÉ½ÕŠòÖÎå£[Âl5ƒ/\ÌsUŒr®cd„ûê¶¶çu9H •\v“Ú‰òÊ‘¹ß€‚2«o²×¾YKÈn·mdÆ6ö{؆jgl#ó:^£êŽ^aû—-#[­‚Œ~8¨"Œsµ ¦äI—OäQdE "Ê$³â¼Âfˆ~TâMM é–hmfæ‹h¢YÕáIp}mbž’ŽbÒDŸd„¢é",8Ë=fbJd±DW+¦‡òÄâ4ÂÎí9Ši¢M‰TÄ‘í©$³‚…ÙIHA¨©”‰ý@*–èHUÍN˜(Š U!ég>:æ×r³Kä‚HÑ%È>KV3ŽI’ô7!‹RBHÕÄ?1Ëf\œE²””) &¯R¹Ž…eU63Uó}IèOå¾:¸K“J:­8͵ ö‘P~cMý œ®léHbOÌ1²« Jm’`Í0;Hƒ6DLZ§òi éšüqœ³Bv׿­“¬ÇÊ4ë‘sŸ’H>¸¢Âÿp`鞎xû#ÿXÑ1šùò˜z&Øà in:L˜’‘f¡ËgMP3¥DZé45£dºÏ(æš·™ì05±Om¾HG%wµ:(s“ÏG™Ô–Vx$‹8ÁìóøÃUdQt™,+³DÕ<ÁŽýfNªŽÕ‚).«À’he²£¥P(sáØ¿„šæ8(™dMnÊÔç߃Of¡Ù™œß_«cP·ÿaE; “Ì6A“*6a2›£ëÍO炉øjæó§y®øÌæ¤*¬à–S‰Wµa¾Z”jjΦ‰ AHT.JW³™6·4U:e±`rsÝ8gŸèa $Êš&Ãú´¬¶*³¤Rd8­÷»åôy³Ä¬ª¹þ4UMó¤·çeÛfëcµ*©‰4_L¶ÌYÌ’¯0Öš;žHZY™i8¬µ•ÕöÂ>ƨT ÃM „¬Ò -Šf6W¸*T”Óˆæ0³ð‡M,š,jq,̉›É>öÉL¥²2£`ÊÌ¥ªì|1?Û}·eùÍO¿¾nW+µb÷ !ûò’¹åf2¥Æ€{d‚ͱ‡›²Gª—¶Ö£‰¨4Vhñ÷Þüæã÷—ç ÍX;rÀkd­­e:ݶ¯}1¨1F4S[—ÆÖGl{šZŸž~Úû~»]û~gËb±nÎåéíeäU3°÷~ßµš¼Úò¼p\+öZh[eßv¨ÌÇwoŸ~øéWÿø÷ÿùOü—½ï‘¡5Áü¶õ1°À;‡)2Û÷Ò‚ýÍû§Þ£ÆÜg¨ñùìl6ºbÜ÷ñÚ±ýú|yûöôuë#;S£r1ûé7÷éõ†h €þöÍ{?þð›v~¾^¯cŒ>F 7yVŸåÖòʲ9;»c6.ÄùÕÖ5´w õ@‚ƒ•ÅiiÔà¶,RÎ@©Y¥¡Î¡ªF‘ÍžNO·1¢¹›Ûº^¶ëRëiFfŒLF€/¯Û¢@­ª’™s;š@HZY¡?_¾{÷«± ­V£ì´žØ $kÅ…èUãän^ ×ah‹QÍŸ–¶pf:ßþîwÿðú—/æ „ÄÒQZ’¢­²0D´ÊŠªJê¹=×è‰<¯}XœWÙ^±÷»gTíŒ,©¸ï¸EÜGÀª«Fv”FI™Ý!G«4X™ŒH1€„“6I0p;@3ç ÌSFóɦ†Èlª˜5·Yʶ‰vu3‹‰m„³Ü›@.SVR‘t–Of¸ŒN+Ž#Û3IáF!]†DN Áܳ­ ;ÀÜÿ°cZ×!+¥m=f|Í0`- <Cb VRUU"4Ê{¤‡2çžaˆÅ­ªLd*ÌV% ½ÔUE$EZL•Ì‚D—©d‰Ò|ýÂQœðír6Ÿ¹¡y9Pž'†eAs˜¡âƒ¬-§@‡¦y—óh*ш˜|ÌÔlx£ä¼©²ªÕL>9œƒQ„•=ž÷vL"A‡˜ô€Å²dSu:ø*{æÁš`ÆÙß"KMÒmó›ð |³9çœçdÔZ=¼½)‡pJ|y=ö §ã5“ײÒcñ¸"ö¹ó["ÿXlz䱌4k­-mZ\´Fsø1d˜¤V·!6Ï”eBB³šºÛ ÏOéqZVÙÌòÛq8›Q£4±hS”ªf=®ö8¿Ù’vŒEq®Êykf9g.ô¸¾l’‰ œšnút¦þèÊ€vüõB8Æ|R“Ö›u†›¦±h„•Íý燭;…¦÷‹9{,ZNs÷q–O&ƒ jÁÙ0—ˆ¾9›„ÓÛ&Ê(—›¦o+¦L³|øñuÛï·îÕ–u•­·Û¾°F¤í‘Ÿ>¿œyþÍO¿þüzE©~ÿ[æ= R†¬zïb•Ú ·jÅEUùÅÏKsÂסåôn½¼9ŸY·v:ÿÝø×ívvÛiO²¥¡7|¹θ1Êš¯~NXwÏÁµ™ÿïo_¶=˜1”Ü(êoÿ—Ï}¿ÆÀu¿Ýo*++öÞ¼jÏ»JÈ{F¯PE%0*£”V-å‰,eªXa¬Ì>†ð¨Ý'hŽÔ†"E“U¡ Z&Ȇ4s6o m¦³ÊØfD4Å qƒyBQ8aúfVò:Œ –ÌŠŠiÒ²LbPDUªˆ9‡!+ƒ)§ú!ÕyYå”a•s·͸4›J¼8 f—Ç´y êH¨ÔrBÍR(…ÌJ‰µ) ¤ä9ÁÓB¡¡#ƯI/(I«)¤IT%Hc“,´ã!5ŎDz ¬šøE—ÓHY¹Y-´PU¡ JªcUP9ŸÿsRgæ¼¾GÖ|Š…a~]G²¨Í³£¦ƒ rIü£|vħºÔfHê`¢(hŠzu0P§C8‹z³¨¥ƒj5³ÉCN²hNüÐCªüP”Ž :¢éƒfu t˜˜óϤâèÉÇ8ó£yXa‡9HÂìŸñ¸Up@1ÑŒlíñ27›»ló.)Vò(Þ?€–3ÙærbZÒZÈfhe^Œ¥f~Bu‰ÅÎaéÃv›ÎÝqôºJ˜ä ‹*Iˆ9°3°ƒçjY‚i:ÎÇMˆÿ©·[’åJŽõÜ#Öʬêîýƒ¿Áp8ñHFJç˜$Ó¥ôþÏ!³CŠ2r436º»*3׊p]DÖo`0C£wíª¬ÌXáîŸÃu?2#A—@n©7£(^¹˜“<1ôµñ: AösÜdmò(§ *fñ%©áÄqVNñä0fƒ-Xe1ÈÈ‹·ËÓ%DçœàÊ߬e&³r-"2K‡Zz¦Ò¤QL‘I%ì<ÆÌ1½­VÉŽ`¹AŠY¬- çQ HõN¤'@÷„4Ä&oCÙÉn`Ls™YÌ%@zÀl(;ÔÂèNiôCaíÄOX*M„Ô%ÏÆþت#/«!g¨)ŒF¡%23)eŠd6ÚPš·0¶DËyN Ì–æÃ$e/O…8ô£Ü²F[½š©fQ†çyHÈF´Óšgpæ4M†â¼ÜŽ·c¿E@Ca‡ÆàÌ"f(šGº~xúøñÓO}ÿïÿöÿüóÛ¯_¾}$zŽ9âØßÅ “qùøé£Ü¾}^§uj>5sÌu]3үˇ§o¿þ-f̘חåzñ·93w“Í8bNFÒe´ãLnŒ9gN|úôãûíõ˜ÛêÏ3Ò, ŸÛ¦=÷í>fìÆtt"åGNŽÙ¯½}þæúËë=cÀm*qÌ}Œ±ù~MúööÞÜîs[¼¾û>r›jIt·öåíNÇïÿ§ÕÛmÛÇ~<=¿üïß­ÿúë¯Ðöú¶ÝoñëÛ¯ûí”)i0)3Œ‘¸à¥·ã‰ÓÓGÐ'®öéÒGÌûñŽ 9¤) Á“Æ@¿Þ¦â™ÏÇįo¯ãv÷Ì9fðд}Ç‘ï¯Ðñ¶Í÷Ø<îsæ3-#†"g*ܪO$Y7rª`§uF‚’‰^œN7Ç’TmØ Ÿµø(7T- ‘‰³®¬69QT€€0hR‡ý±›ÄéŠIM¨^£Â‡â4Wó7GŒ„t; ”«O.™v±"uNLʼn\(æeyØS•{p‘ÊÕS@sVU Ad­INê”úÌdd²Ö"iÌÓ5%DÏü`Èj®æi(ËG±bÖD[ [7ë_-X¿q°ª¢®š„H«–ðJ&ÙšÈR­ÐÊ,e$œÎ“*{”ʲü×eåj2Ð `2ÁTs™ÌÝ@ªØ“†BéC&Š©ÀY»Sèãr¼'yÚ"Ūß4Ë,ž‹h<Ñfù˜8UU<ÂYcÉä4Ê 2T"¼ f”Ñ4­‡xè“£ìÿ΀ð¥=[.3Ã[ºúâY?âQ»ê@zˆ6ÀKFÄÜe&fÛ··÷_߯H­[T[íxÜ—25xÀÔâ>n1¶?ÿÇŸúåË_ÿòï~óÃ÷±Í×ýn­‘ËïF†Y×Á­øøcŒ¤Ï?þaõ½µå>3¦þáOüÛ_ÿ6sW²w®/ýöëMsD$£šVç§—§ož.ï·;Sr¼\ÚÔþöþe¾!»Zw*iOPþðñóÚŸßnosÏÁÀŒMwâÈÉ-êõöËû-¶Ã™sXLBýÀŽÈš™Ûû¯ë“}þô±s9"öí ã°î×ËSG|yÿuä¶úó·Ïßìó~{½K¶@ÿöö*]Ø”s;"ÄyFël‡€ËêÞ–çf1rÛ1Ç1«K3ÖæKÏyÞ­¦i¿m™GÝËâ,s­mÈâŸ?~D¾ßn²¾.öýï8n÷)\¯/¯¿¾Åœ‘l˜™/~ì·}×ÈÃÒ–~:"Ž‘>Cd\_>ÿá/¯os–ÄÿîÇ?¾ÞöÄ!îçë"Í—‹·uLÐ[³ÞÖÖÚ¶®¾|¸~’ÛÅ'ý’ȧæî}ß‘êjíÓÓ‹[¤õÞÑÚå˜E»úå›Ë¥|‰¸í_fìîÝ›…2Ç.qË Î9™æ¢æžÑæ±é¾ìÇímÄSóùùé¾Ý ùOøø×/÷˜óÒ–sŒ™š‘ó KR™š åc†Š ’Õé:¬Â÷4’ Í„‹Íën¦JKÓa–FC–Ü«¤‘æ9ãë#Y2£{«{‹£9Àe|nÍÑÖ›¯Ë:#d‰$SÌŒ¤’É!„)£ºíÂdé’…A·4ÁL¦Å lƒùù˜AI±& %/d³)ì ƒ¦E“èS‚Ñ"CaBh”D›‘1•ˆ„¦e‡s‘rͨèÆã‘%ל;+›6²ˆJ ’ÍKb¶LÀ [I0`” D •¬mbA0;-7UéV?eT 4eÕóAõ̳ú–õ˜µô?Ã|gR¿N%s>@Q§þd'î"PøÃìÄl¨ñˆðŠQµZS>2;F);ëwŒPž™»ÓêU4Ñsõs| &–ag…¬ÓÁU]Í'½«LWg²]F£[ÝxBÍ¡º€T 9ekç»{þj‡Õ¬ª¥ÊÔÉ`¬65“צ­Êã`ó¬|t·N•Ë*ÌèJÕºNè[16Q^xÊ‘„‡)kÿäCpUajVV‚ìçÌS=(OMí¦¯ë&=¶AN?¿Þö˜öª=‚Û67msÀ0Ö¾2õ~Ü­;ÔÙ©÷ûÛ1ŽëŶãxßkÃ9K9Ÿ%µQÔ¡)dö†ç×="æTÚ0B9E,«_¯O}ß¿ð/?íoÇ¢»Ãú¸Ï±ŠÚqÓ~ Ñ‚Öõý7ëИ!¶vÌ[K“’Š`˜„î×þ,çíý}汤?-‹L­B«1¹dG»|É9#™GK3k ƒ¶ÐûÛ}ʈoï·¹½ßÆöþúvìû@fÜ­­Ýûqÿu5ŸdÍsÄ‘ ÏîÊml OKhx¿]ž.­÷Öðý/úå¾­ëó>5ëR 8·…—ÖVs,‹ür½<}ü.3½ÛeY/K»®í~?F\¯—æظŒ®ÝÖ&÷Öü¸.¸4,Þˆûsk}ñ[ð1ãÐJ- Ì2¶c¡iÛîûÍÇü¼¬÷9Ç}Ì÷û— MJVs|õÈW•µå£èFÔ)핇gk ÊêHK"qîÒ¼âl¿©m|°Ó _U²›å¹…;QShe“ÂWIªF ?«Røµ¡ïÓ`%¶V ³üõ§NùuóZ£~«GDrZÓ }È‚V5Ì__·=‡l@Q¯¬…‚‘p%œI¡9zq0Œntzmy)ï¬y^†ÙȤ›-VˆË°yµèá’©íX5Ö”=,=í\Ó¬ršf‰TÒÎ2£B  )GÅ"dÅ`3©ªPêSrZ$EV»cÙ) ÂOª}3;õNñ¤ôVÔ¬«¯Þ}ó·aIP ¨ˆÀ”TÔˆˆ€¥ †0‚4‰ÇÇ™&N¦Á2-Ó8Æœ3!-­dlX3÷ÎD³,¸5Aë0¶j ,m˜ ®L”ÐVõ”dKö•Óx¡}ûée̘·í¬p̪,X‘RVÔ+ ‹ŽL6*^*1<½{_ò†¹Æh¨ uvµ:h$ „•ãfélÑ <æVYüû¾#8‘ÀPfÒ±ØSŸ‘Ùé•0µ„vY €;{¤œ”Ó4Ó# ”ËÙM°ß÷œaIÇÐ\™sÎcÏcŽ6‹¤œÙ&Fv÷ëåyLÍJéH5òÃÓ‡@Œ±ýõ=¶{ŽÌøöóçõzÕ¾ct»¸ÛÓ…ùÿøß~ùóß’j—eß!ÝŒ!m…Äpî30fHʹh ´nÇáo÷ãÐû ç¼Ýsd28"c¹¶c{Ÿ#æ´{ŒÈÌ9æ<žœmiÇhf©»G$Z¸lyZßÇŒ}›Ç/}þø4æ>µ)ùáº.ëÓíoïyýëO¿Î˲FrÎ1ǾÇ=wÈ,aü»ß}³¬Ï[Ü_·Û¶Å<6fîwôtOoSám¹üîóßÝÆ¼¿ß†p„uʾ¾,­ïó¦Lq$Ó-‚höjó8ÒrµKb ÈY†Ðy`„f±‡2q‹<æÁ Lj˜ËòüñÓçíýmŽ9‘PfGì7ðZ¬ŸÄ„ qäž"¢ôéú¹?½àØÞ·q{=îñ6BÈ‹áb¹LïæÖúìmqs¾¼t[÷ãuú½»÷§O‹¯?|úéKLÚÓúâþ²ïÍuY“K?Zo­“}Ž6¡5–°ã?\>zž7äNÍÌS˜¦íéüÇßß~ùÛû2ò–Ì=g»Ï-Æ1c?¸ë8îcÏcbN9˜{J™3ç&R1`?±– IDAT*5ÑÜ>>_g䌉¨ôõdQœk(Q~ñXöéJ -GƘØgêÈ-FY¬ùRíLÂZÌû‘ÉæUûôÒ–™vä´ä-±ß"ÖcÎm¾Þoƒ¼Ð}ém#'úå/ûÖð¼¼ìÚc?`œRd6»-¾H8F‡Ö#GÇ™jfÿô÷?,—g‘¯¯7ba¶}¿Ïe3¸¬mñÕЄ3Ì-¼}pÊyÄîÆ˜Òµu°Ý÷Ms íº^þøý‡ŸïûíýèˆìÚïÛ¯··Û¶Mr{ù滈Æ FÂ&6ÆÛ¾‡6æ.msÌeY‚~¿½›§¥7„dB#r÷q éóÇOÖBÛAO7·kßßç¡}*­é 5fÎùZÆËë²1Ó‰ 2 ¶š'ÆÔHí¡]ŽØs•t1ÏÆËÛû똷d0Äå²>½|û-Ó‘£™;-Ò”9•¡¨w>]>n±û¯¿ûÜ~z›o#4L-´f¾h]Ó–ÅâÃey¦#9WØÒ/÷¹3ßÞö÷C³­¹yÄjýÙ[‡¯í"gë¶ ¬™ý Z:Ò]G\éÛæ_&òp8ò°ƒ±/½ýÏÿô§ý×Ýó S¤tÜen—åvÆ}Fæ1BNé>tS955fæ”eŽ Ã &²Ú’)c3çæDKÖ9(å"p©*…i"¯ëS_Ÿæf§Ý’ÖRif˜0øW'³A€ŸÁL$ä´hfÝ÷w³–ë4¡ÓˆˆÔÀ ¨‘¢¦¦T†e:ÊOA°@š *FÔóÏ*úæ_[V0:›ô`"` †Àì™ ‚Qgö %#e9fj$1‚¬t¥bœHFæŒ &Ä„,ª\©ü :©Z±?(ãr‹ÚT¸Ur¢2i¬j)‘ÏÏOn6Ž@¹µavRª¡²/&_ËÊSÖ§Ñ! ^¿íÑAS}ÉY+¡’C€‚™Y»2¼ŠèD_}]ù×eÕcyUv|5LCȪ¾2Zý­~C|:N²f)Ê“]5ȧßÜ¿‚.%¶zJ _IY¾,IY+¼š©”øÚ¬wvÇÈi휮*ÃeVæCÚ$4ž b]ÃÞjËaVž2`Lƒ[6k̺¬A¹ê¡CZ«²~RÐÄlð€² Þe÷˜™2“Z5eT¼4d'g¢Šy`RExëC‡¬ÈH–{ˆg]¤*H̪ÝI…W :­ôÒ­¾!×e€³8Ñ’v†kiدžH3„ó·žÿód3-†x()r/÷RH8E HéA¾Xˆû= ,N~f ’ ”‹2XGÒŒ¢gfÄL‡Ê"¥‚rKäc? ÚT¹€I˜˜]:ÕŒ©ÆÄìêÆ”¥åÙgsb€hHCš"ƒå)R{ø|qbÇa©9Ó\Æèò3EW>R£Ä‰V(s¤™ƒH³YuÝSd¤l´LÈ"1²ôÁ%d 1”‘rœ%Õ È¢§Kóµ%Fq „™•?c^UdW(f¡î‹'vZÒ«hpìóÕ¾kUÍèiÞD*ã4òrfõÎdA˜š›Ð*ÆO(%…ÉO—”•«)³€ EÆFJ§oë1;eMZÂãzáŽGo- N®C-ÕS­GR¯PóÚ‚W‰ÎG¼Êß~²IA°Ùé³S;,gUõ«dUôœÐÓò‰•+¥þóL…Çòæü§¼–z$ÿOcXÍRªT1º¬ð¶_P";®>{„<`(œ”AlŽ:OÌQÐYä Ø—ijBuæÔ¥[À~ËtL¤Ò@J¦.­Û9€ÒTÖ¹´”™š f¤R2£%ÑH@ ûZym.‚l´úoÖ´–]Îðªq?‘ªUSÓªi5 “ÜèQùC=€ZfV§K—Ui(Ï’I•àËeéY´ËúùsI(÷Ó2_§ Yjòä#y"_Z¦Eêõ8&ç`nY¦ñ^CÀQ—ЧŠÂ䉾MÄ,,»48'Fñ7N}=t°9“nùh`Uç •.ª³™uv)ánK6x´Ž&'B°èÏ@†¾¤9ÃÖnËeŒΩö´.mmH£jÎã$°F’ÞZ‡ÏÜ"wxÔvh DÅ?Òh2ºkžarÂLÎ:ýyØ4¤[žn¸®u¢®Èp°3R™JobØ`áép7GóJZ˜4÷ŒrÉ4GË+˜†œîè³—˜ŒiJ?q?®°€%ó@hΈ18Ük̘U¥.73pš83 `¬l6Ãæˆ#S0¨:µÌ¹‚Z!˜s&-ãq^®ßÜ¨p˜²?=áåº:­-jK30cl½À¸ö…È}ìÊ4nxÌí›O׿þ|;ŽãÈñ¼|úö‡ßíÙ²+¯.¯Çs¶ž—ÕÕ.o¯û³7Â2b·L:—«kñÞ#^ǾqšgO÷#ÆØ‡¤§K“SÇ ûåùº£Ûa¦¬yú¼ø‚Œ¤q]VôxÝçñŽFoÖ(’6ã².Íqh›ûßÞ߯>˰7‡G¦[6t[†£+2fˆfËpïl:ŽÂM‘H¦FXÃ6d©†žU)‘²nóåƒ-Ïßoñ „#Bó¶ÝÙøÇ¿ÿ/RU½RÎ%•øIŽPH½y&}F_Ý^®ë‹9ÓSMô\ØÍ–Œ¥ùÒ½J´Ö{ôøÃ?üõooHæÓ3Ù^ž>þîÒÙûÿŸþa¹}yzYúúÑïÇØmŠÖ©uug¤HÛ/m!<¿Ymlã~ÏylÓfÁ¶zlGä!2ǘ—55¶8F숌I»OÌ‘ÉÌàÌbwæDLÄ2‘A3³nL ³èXDÐ&Ü„”ªH¤$Ÿ’fLŽrÏ0WàH#«ú­ Ó*FL/ËN=çiFw™;Ì`jÖ t’²ð)Nc5%7Á2 `’€ аPd•ŠÉ™ óNµ×<Ëfã¹ÔJ¤ÉfYû¹e¦[ªÞÙꆟ250ÐLRzœ°G !t˜œ8„ì×åû~»Un™–E…l3¸žçTZ–q¦òiYbkêÜ0œE匑‘ð*R„gT . ‘ê4cä ²¬&Ÿ&V)´ª¢©Qœ"ÒJgQµ4¢{ó å?±¥ Šgáò¹¸úMI|à h†šŽ‹îôتù ÍÂcèý¤~çOSñžjHYk 쀥5®¾Ð–i‘‚:8ERŒáùD!ÕCþŒ%Ì 3Û ´p0ÄÂF4C’8„…N'š³ã‡ ÖJpwÍËł޶¸)%†û‰^V ‚™Ô] Ô7jÆii„z.ÍzZe†(És.è§fîÀ§ëÅ×—c?ääeýÈÎ{³I·ÈåòyUÆi¿¿·”º6½,9×çï–kWzd¸Møòüí÷Ô>î#2¾»´#í6m¬ó˜ïÇ<âžc"mμßnKcCaGlÛ§?÷ÍŸŸ¾Y.x{½!ræ–9Æ`@¢¾ÿîû#0ǽ-—¹q„H󾯠4ÜÈ™tš™ãˆ™9V‹·™^^sßGŒôæÞ|?0#ƒ!-™¹HO—‹hq¤¹|áï¿ÿýëí äL<·çÞú13A¬—ë÷Ÿ—ûÆ}`ìr÷fr„»¹á8´ï‘tLÑ œ½_/—¸RsàÈèæyÜ!%.L ¶eí—Øo1RWF¾¼½í÷Û8ÞÙiæŒ4ôšA¨eÊxšt‰¤Œ\òéúá¿ý—?•øxän®Þ‹µYÛºYKÏ}ØO_Æ Ðv7=µvyz":—E·7ÌÆ{â82ŽðKn?|XÞ¡°#Ým<õ§mÜïïssçhÂË…ï÷û}Xt€e05f†å©¾Íi¡-ÀDJ^õ¢ÜÙiŒòJ×] žYï“Itþ›YÚ¬> æ ‰®¤3ä°ó,„ðpK/NAU(Â8kœu–·„šòÙÓZΔ²Nb†h !ä|f¦ àòÁ¦tŽzmô:3¬ÌÃHkY'’GøäUõQC: 2‘ž–9SGÎ X2S‰)¥bN)Õf ˜á93÷à(BäÉB@n¯ïsÅÕDí÷!Â-˜ð$Eƒ7{¤×’)e°ó$Ù÷~8zI§²Œ”MÛ4dଶ)-ï„­&5¥H©*>'©J7žaÇU‹ŨäþX ‘§ªç'Géôã—Œ|4>l@Mô" —NZ-‹'‹¡DÞSéªR•¯‹¬ü:te]cç 'd¥Xæ}ñ?U÷ÔZìôÃËÍÚC+Žˆ?²†55YÍUìBýœÏ0]¹æNÁõ6;ɬ‚”8ö%kÌ“iSF‡ÌÂ,ê{8§ † ŠŽµÌ,ÞÊr?L1«é&‰T2ÁVÙ€%1—ÙMVÛ\Cùܪ©ÆÙàžÖC5¡.½†Û$<|'°úM¥Ó Œ²û©ÉÓpTøT+¶ñ«zì(€•*ç`gd³~¬&¶ÂhÕ¸TøQÐÏ‹©S<>ǶÓWØZë"4Õ@Ëf:S)Xm€:Nñ¬–R€äï2Gyº E:h. ýÌXÑà ÑÝ&`&OÀ•ÉSÞf §õÄt“ÛB“qÍ€ákzd ÊrEkÒÜ'Ø °¶${ncê Œ™Öš„AK0,@)C4°5ÓuÎÙ™lÒŠ’œÙ«ãBÆf÷9›Ä‡kœPÆH ÁÙ—K›Çf#¦•23ùlgÀšš`im1c8œî’ÌY2¤ÑœÎ†Õ—d`šZlO¦jR$‘²bꆙ–BÚ w&g.h¤Â„‹¹3#’¶3XZ{òKWLµîž4ö¥{hTYƒ/Mœƒ‡ƒ‰¦–×¶\´deXÌ.Ýy?¶mz(iºÝöˆ[Œcs¡qü×?üðÿýüeß·áùáÃóûöEÜœøÝ“GØþøûåËž¸ö—õú1Žy¤|"sÎÆwßþþ¿þÓ?ýÿçÿöïÿý߯ý~ß÷DjŽd•ÆÙ<6ï|þøM_ÖÛ¡!óÔuu—‰KZ2gÄÑaGk}¹´_–-wÚ˜2–4A1rÎŒL¦ÆžÐ« fnqÜ¶Þøô´``¹~øvm¿îÕïΑ¹ÝGÈSÝûúrµÅú6öÞ8“÷ýNk×µI#%ë­Ÿ¯O·û¸o‡™5ïßÿáŸ~û`0gXàH…#sÌ÷ˆaîÍ×9sæ Æ&ŒËÚ®¶ÞGG˜¿ÏsŸ\õã§o"|Äf­¬±‡ñ}ß,»‰ƒùÔ›cþÔ¸ÐLôþ|élb•iµöaÕšÍvÛáÀ¸(Þ݈m2‘î’úÜÓ¢o6Œ­ùå˜#¶mDŽàó{‹Û± Ö^ž®­Ùöþ¾‘û®©Ìñå¶oû~@ÄñÔ’ÍŽýHÈgJ-13–s ‡aZU¦!‘Ê<IBÖ)o®Øòs“ò³è!++ž4?ÓDP:NéÎ.è¼y¡™Ù’&›¥cÀ˜t®mr²J¥U¢O%ì!01é0 C&f¦fJ¦ ù9OϬ¥B°ˆÕV(¨lF35GÆ0MVQ.s MHù$‹MEvB Rà+˜c$7ÅDîåÏv'²gj]Á0BÂTX’òòŸXªT®ótoÝ謒 ª á,aÆUß(=C‰4V¬ «»­# Ëz6QµÖIe9d¿²ˆB³ž)Þd$ò<ÿ?¢vÕ]*Ê£’î!©ýïzhygê¬ñl$&úS&:;êªÆª4“#™¦¯è*D³Z‹: ‘ùƒç„ (Lª¦âˆªà©5Ú¹w;¹^…ÅʇX=Æpóvþy8Ãw…ÑάD͹¢æ§öõ(Ó1–À PYU~+UI'º¶ •—T÷fyÝÎûŠe(îCµìy˜H„Ü'O£“‰I$вljÕcýÔûß~¸¶öº½„¤%,M.W™ˆtfÊ«dbÐpé>©•Œ1[¤Yµ$(+ 怪o=q¡Y_ ¹’3ÚxOä†Aƒ”†l°‘ûlÃ@6)cÊxfmÉ™ÌiAÙœµÅWeU[C·ž„R3u ¸Cð:°*'Ò8m*ìl»bÎ-¢ Í4ºÁé]”P€¾ 'Cfp9ñòüé±NgÂiAoF˜»Mƒäš²™á.:<‚4M&ég‡âÔá‚qö§çår=r³Œ¾t§&’æqVXغ>5A¾xÓšm:Žýð=/ÝîÛñ.«ŽØžA 7 _­ÃGT̶̆ _]`cŸ??­‹s7Ù>ö?ÿôË„%~Ó÷qw[ÜøË—¿Å<–Ö;[B×Åb„ŒA Ý…nžÄn“oûžÔb«à©tòº®S¦ÍæÚl½pŒ%£‡”Ââër±Œ˜½ûÒ|?8öñc Ü›q6Ln¬ ŒÐÌy2\®×¿ý¸íw¥}þæ›ã6_ßß·cŸš2‘_~úyH•ÿ 4sŒ¹åœ=bÎYž ÈL¾Ï1@g[ëªÍÔ>S²[k­5d »ë>aÍ!_,;íê­y[[÷ï>=éî:Úú|…³éâKçOëÙŒÖÜìðh‡h9KßÛcŸÙm.\ûe9xÇ[ E¾ã}?§Âíýµ:ý6"¼©÷ëv¿øŒ]Ò‘#g$gvæï>ømÓ)4Љƀ†àÉ™v±hš)§84K±‘Y±ok•‰g„™“e—È™¢U”(ÀJ'¦ÚÿŸzbxdðL@P –4—Éô“Op×%MZ 峚'İtÀPuO(³ögÃs9Ù«¦2׳†LrÚÉ/gMàÔ"³tÒ³lÄéJÇ!;k™a,ø¾b,YÞ&ˆ®²>ËOuæõD™Êïç^ë¬DUXŸ%Õ(±ó\`ÿ9IÁýlq~„ñ`‹Ö3»œË¼P¢Õ<à•? ª+°§s}wN›'澺Kã$Œa¨`fBÓŠË`$jÖ‰Pd³lUÖ”6Mªbãz+#Òv¤2”Ù3™lJÂŤìh ªV_¬ÎYÛ½@pUÆŸL·Ì¨Ï¡> Zµ‹É½ÆE£Ë™DziÌõ‚=-jÖÿßÔ¹-I’$ÇUÕÌ="3«ú2³³Ø @‚¤€¾ðÿÿ‡„.öRÓ]•án¦|0ÏÞFdDº«»ª#-ÌTÏ1•ŠÇ ™÷*± NŠlBƒ«-3£–‹4 x®½æýZëÕ#Å–G²Âc…ôP1ñш5Æ.xÊd¨ ÷VùÊ2©V´ ‘J{|¿ŸßÞ?"EÕCÆÊÕ@Ih ‡”JZÈÙØÐ¬ÛÖû¾a¦(U1” ·dû)ØtäÖ[C¤Èf v&ü´Èˆ>œ19àðdC B ®âq—…eønF¢G J¸¹­\ª!Dø–’{Y±eéš9禡 3y1ÿù²½ßç5ú§›[Î2u_¯6N¸†ê1-k€Ff™0c73YµÐI*›TÖƒ$œ>[:ZG6ƒSF !»]bÉZ¬)›_úõÓçËãqçôŸ¾þ.ÍA˜`!'zøgfL%µ#€MS&Á³mndŒ1#±›k |ä;© 6¶dÈìæá zﯟ¿°’=ò±í·WM¥$3Ïܸ«‹ýˆh˜|ûþöýí-nt“µ6Êñ~êÿþó?ÿí¯ßÿòö½y’Í4„].ä‰È¤3ã86v¸+mLáQ%,gîÅÎ&ž”†déim>’ŒfMÜFh„¨¡ËÐz¬÷/׋fú>?m혮és´{ ÒB 8Ìͭ?Ü:ûå¶ÁòÖ¯H{“ÍE¢íûK™Ì98Æ1ÆÜ.Ÿ{ë#î™ScÞsfHœÈ6,,´ ÃS–ÒT¢Ä*á}ßi*Ñ¥ÉzF;†ÌZ{ùtL9f¨³óÂÖmï·½ƒRãËÖ?íݵGlíúå‚ö²m]û»0gËŽÆ–Ö|»5Â6õ«ïÍm—‡º6d¨!sÙ{Sž9a3MsžÇxÏLÄ8ã×÷÷û°qCÓôÀ1Fÿùw_þüí;Êïǘ3 žNÌŒÌ) gÜÏó~"2b*5+­CIÝ£{ "ÒUF1ÄZŠ3£Áè*œR–ðÃêöL-OÊÁ™PV}Þ}œ›øÄX*hæFÔ:‘o÷·9’©ÌY} Œ”¬rùUàVZÖSu·Y$u£2áÏœÔéð4n#õD“åIDATHƒØj °q9ÍÒeÆnJcJY NÉž¡™L_û9‡hkÇ£3l@Ê´YŸ¬bϺ|=ÿñ¬ªUGOš?›WFs"è&É* :Ë (ÉЦ OCИF>ÕÈÍ UTÅŸ)j1s-," ¿´ÈVÿU!mIYüä'§3’¹x9µm* ,+¿® ÏLÕb%<£S…@²Ü”Xû RòBZ2m]ïH™ªQ¯‚h-DA©sëãõ‰¤€erh†žÉ‚Y,m5+^¶fÅÚdHt÷þŒ·ë™{NW,g hÆ„›©¶°õÛ–{p±³*èõc½å«D—¶3«æU¥´¬¶â9Yw €“*:ÙtzB ¹‰5¥ h˜gÌSÓX†jò&ÑIÉm+úìôR¦€<äŠ'¯uõ«ž Oƒ r&‹¨ÐÒ}<ë~ÇE² ÁÂK[]Ášÿ îZ—•*´ˆîa)À×÷¶àýDñÜXQ—£–¬Þc-Œˆ ×"%,ŽƒŠ/Z•öÖ‡uyÁ çרR{*ü0ó®œ{éY–†V ;ƒ×§±Áªå`zZgêÍœ«»·*nÏÐXUAcUñC}Sð«¬#á¢cq-·$"éÞÚs g?.™`cqÊχÚÎ8Aw«9ƒ+â¾OFÁ~1h)["Çzù‡µ¨aSeÚ­oE¹‚ˆ›ËÌ$uùªÖ8$ɪ,{0™¬qZÐ@À¨y~QXR_¯3aåùÑX¶­"Ö`zXÙDr:e…ªX̲ £5¦VæÐ…ÃÌŒN9˜N«|jEèÅU'DUÆQ[ÐÔ(Ž\Ùƒ\UeñúËŠ§DZܬ×ä¶8g(ÿú:²bmXQ†A$P—ø5º‚´žŠ@*1W–&!œ+òÀ–£ÈiVŽ6Au5ghÌ3zU6-½…ºØg°¡e+Ð’ \›:8fÉ×½¿ì×ó˜œ9“v5ë‘ç‘-ÂÌ$Ø ˜²i:Dј˜K™fÍë ’ëæ)Z’§¡9-£ø*Ö0³„í„N “&Æä©•2è¨k-鋵¯ 7Æii"ÙÞw¬[&­¨æ0`Nª'¤ÚÉy7xЏô‹BcòÊDÑhŒ¦ŒÑ¦”-Ìœ`ä¯oßubv wiZu2 -̈äôë…¯_®ÊÔÖ&ÚÚÞ×fÙžÒömzS÷,ºGîú6¦SÛæ”82»9qWœsü§¯í×#h}ßÚåÚ˜L‹væÙÃÆÇ1BóÇB„#[+"º(k›4ØRC‹Ù ¡kAà†‘Iš·fÓ- ú¡Y¦Q6²[?û#'ŽÑƒˆƒÉ´ Z³Yð®LY‚B!ôiÖå”a‘8H’ ½¶ñwKó™Õ»…œÓR™¶5'óaDsìû(ŽÌî­½^[µ1Y‚lS f#Œ!xÇpc3cš:w¸Ó’Í'ÕÒ¤NÂrÔv½)j§ÑLÛ¾+¥É>{chϾœR¶dc“#‡üëçOî˜#~´äéV)\Ù¤Á—íÆ1FLnWE&¤é›·Ï¯¡Q¡Ý®}÷Ÿ_oÀL*S²nô&gœ÷c†OfëÜ^ðùúé—mûöq¾‘f¯¯¯?EDÌ{öׯãœ/n4ÜsF†¸î×½ç1òäN=™ÊÐ$N’r{Ù÷­éu¿LÃTžYÆ3å æÞ†¡NÑÁ‡'’˜ŒaÑûåg×÷ˆ9 Áëî{÷ïÇ7OûþëßæL(#m»˜gäÕ’hó‚‹òëËír¹$c œœ0ì°nña8šÙíz5ë“ûñ8FœÀHšäÍÔ÷댣 TÔkðI¢]6Ü6ë=¦SP¤Å´ f uTofžqŽó~ ``BÖÁnDÚ%ЃÑhéî]¶É´Ín©³(Âq¶i·é=;Z‹×O×­ef}ßÝÑ`Øn¢µæŒÚM ®îÆãϲ÷¿œ ËHæsLOF<ÇSçùˆœÿï_ÿåí×÷>oç{Ì»fFçÈŒ$#TF">½xÆø8މŠùbF w”â Ì31æ*ìsÖ^ªæƤYfB9K­é•uxn8&åÚR½# Î4gZ3üþ§ÏsÌ‘Q\ÄdTŒõ,+Fv`±g’BEŸ, D ³öúƒt ! Ã(‰L3zcäÍ¹äÆ¯Fy÷«{¤¢ŒZtƒ{ÌTÌ åF0»WïrÒ ZÆ763ƒLµ8››¡/Ô_:Äh9-˜„Íò@*lÛˆY6nŸH‡\-1½±öæÇl½ÿÃ/¿Œyp:‡ØÜ{­F’©L!•¢FÃöÛ8¢>J” ¨h0…ïrÙåê¾A3fA×—×ß\®÷3á K¼Þ¶ëËõõåÓŸÞßc`JÔí—?üÓÛÛ¿ÇÇ·ñ1Ïêißöæ¢ínÍlz‚Ó%ÏfŸ÷íÓ§¯H|ùzù~Ü'M¡9÷LÆ©@ÌY5ŠÊ~i~›ÖÜOÚæ¾Ýöæ{¾^./ûöú?ÿû?}ÿ~žHvKvÄÖ¸£íÖÐ/Ý›ï—Þ·‹yë­í ô€Û´¾ekmß ´æˆŽó}Æ÷a¦‘CI¤`]·›_î1gڌȵ m¿ØôóÛß’‘qŽù uΘ爡ŒS§ÝçcÎyytënaã|ÌB§˜H!FrÖ§z} 'BÅTí3Z©8˜\øgŠÌ0bVÙHÅË& ]È󘙳& fÊ£ì/p!"5d˜€â.WâE¥§)µ¼Ò2IEL "ÎÖKËÀÒ£™;zµp,)·Í¼ AÒݺbԵﰌŒÚF-QKæ$£4c.,>»êZÍ¥PW¢t7 2!—¼Ñ'é½Já^Ý”ÓÑjþÏL'r"!S<—4Å&×"{®Î\hqb‹!©¨QãSƒ£0-ëÐFZÝàä’M“ ,ø}­·žÇ³¥’³äžÃƒQmYV¬¼)µ‰Xað•$·E‰eh*X=ÛŠ½¯Í^¡̽¢ûÕ™xJzöY¾rå+â…N”Jz² ƒ¢!ÄÝk›•nmÃ":Ux{i¥Q÷µâ5.Y¡“¢€]V“‡­+"(3aAX1QêçúÈ\¿|9Iš©Ð ¹XY–„7/w&lõDÌ""ᤃ¡B¥õ,šÕ±»aÁ(ïŒR½~ˆ ѽPçD# M/T¬­A›V8UÈQe6,”¨ V‡PÕ2eTad™ødæ‹&ª²À¨¤£’Wž©¢kCaµ¨}rDôdKO]t1Bn*“Ku;õ4tÏVßdRVas‹êFYIQôõ ðDË¢¢9“„3¯·«)ɪ˜ÔþÒk"èž28µUüìXvr 9›¬hÁ©Ù“3“py63Ò&sÊhÁrF9l.&ŽhVë¤"òô „‚r„I³¤»(zÅ̘¨%fTVÔ̓̀ Ü´1 ?­9cÇÛPîû†ˆGä¬% Y½q‘«ì¸3<ò¶Ûõ‚‘0%‚háÞ`é± ÆÖ"­•:*mÖ1]¤š¬™WF'¡¶Áö˜–Mæe‡Õ@¤?ßâÞÏcœûyJvÙãËO¼¿(c é º–6Ð1rp õý2g>D÷íúéÏ!NÛ¨à@z#åáégº·ëvu·}w"¾oçã¡fqúýŒïoo™ã—Ÿ^ïß‚z ÓAÚqö3ê¹rnÖ~ûåë·oºÏ÷yž™àD¿n1'ÏSÖÛ?þÝïþöëÃq&G ÆÐ†PšÆçTC~úüÕ ÿù—?<Žï/œâ3#y²Å#¦,LrW¶fñ˜Š“ÇŒ“¸’!·0ÂÍÀMÊ#æãœÇû‘g©XR> ^±†–ºÞ. òTΘçL¯îã¡9Txv#Ò‰|él]ìDlš©ðÜ#õýq?çãã1ΘM3C^­RùŽžn]ìÎënÛ~iˆæìf¾;±SI7“5mø··?K“ö)±3­µÍúk—Þ„Û¶m@Ï]ºpcæhíÌshÍŒx„Æ_&úÅÕbXd|$:Íwk¿ûãO߾ݯûå~Ïùqý|ií~Îøýo~ûýíí8Î1ÇÌñÈ‘˜Ô8ÆS‘:§NŽ©ó>æ#¦É;|¾½¿?I¥òÕL©ÌéÅíT$0P9*12 Y„¬}¨éƒæiI8A›¨º„¾Ôg^¤!gšÌÄ1s䌤&99`é $ƒâIb ™ª\F2…Z%G–º6eJQ.ϧ'νZZ‚»¹måYjÕœffÖÒœFuxof$‚æ B†d@6  I«r{rÝÛ2¦ ’:U_°²ˆRq–˜†1ið–t53YK[>fÉ}ùE¨zý·gœ<Åp«©·Æ˜ z©Rgµ¶UB ›µí’4!ž\($‹ß\¥J–PPá×Xš´BüÕRï9àŠkBü‡üÒR®â]½ßªzxnêÌQŸ›Èj…Wû‰\ægÃRb}Ê`¹\˜5Uæbv+‘Qá2•éò9N%r-gÖ0e‹§TÇ»Õ#Ìÿ((Ù\{ÖsñIEND®B`‚vispy-0.14.1/doc/_static/ViewFrustum.svg0000644000175000017510000024441314507104216017522 0ustar nileshnilesh image/svg+xml Far clip plane Near clip plane Camera Perspective projection (P) Far clip plane Near clip plane Orthographic projection (O) Camera Top Bottom Left Right Top Bottom Left Right vispy-0.14.1/doc/_static/ViewFrustum.png0000644000175000017510000045613214507104216017512 0ustar nileshnilesh‰PNG  IHDRèâ{§æ«sBIT|dˆ pHYsŠŠïÚnetEXtSoftwarewww.inkscape.org›î< IDATxœìÝwœ$U¹ÆñßSÝ=qg— $HÎqÉI¢QàŠŠ‚×p "WT0 rE¢€¢€¨`@r’Œ9‚¢¢Àf湜ªžžafw»'tÏÌûO:UWi†­zÏyÏ{d›B!„B!4WÖì„B!„B!ôB!„B¡%D€B!„B!´€ÐC!„B!„z!„B!„Ð"@!„B!„Z@è!„B!„B ˆ=„B!„Bh ‡B!„B- ôB!„B¡D€B!„ iÛf·!„0½E€B!„¦5IHúð=I‡6»=!„é+ô0­Iª4» !„BhIÝ’ÎîVž¾(iÏæ¶,„0]E€¦%%onhv[B!„0ñ$< ì ì \üXÓöù6;Kêj^+CÓMèaÚ‘´1p ðÀ.’ÞÐä&…Ba‚Hz…¤ë€/ç[ؾµxßöSùvðIà1I;7£­!„é'ô0mHê‘t.pðkà<àWÀ ùI8„BS”¤vI§ÏëÚþ”í¾šÍ\}`÷ÛçOLhcCÓV%aZtð)í•¶žþl›Ÿ„C!„0åsÊ^mû0ÛCƒn ýœí¶Ï±ýë|?ß’tF¤½‡ÆKèaJ“´–¤/‘FÌ7µýóšMl{¾¤NIÇKzÉÉ9„B““¤•$}¸ø °±íŽb—‡ï‹ö…ÂP ‡)IR‡¤2Æö Û½#|dià$à”‰jc!„0ÕIÚNÒ¥’.“´ô·,éݤì¹.`ÛgÚž·ˆz¡o¦à~#àÓùq¶—´úX´9„ ô0IÚxx=ðZÛÛþËp›ò÷_¬81­ !„¦.I™¤O¿öÞœ9AÇÞž´lÚqÀmïgû÷cµÛómÏÏŸ¾xXÒ[Æjÿ!„é-ô0¦òõDß$éhI¥ >ö*’®®.6´}ý">V[ æÇ¶ß6žm !„¦‰ÿvV–þ l6ž”´Œ¤/7×ëÛ¾¦ž]Ô{LÛï&Ô•ßC¡añH3’öþLšï}1ð :nEÒûIk™v‘óÛžÛàþÖ”ô5IícÚÐBaôf` àU¶ÿnÛÀ]@Û8O’ŽÖ6³}¢íçØÝBS܇ý€}í[ò§çHºJÒª ;„"@cCÒ±¤ ü¿ç€Ý&à¸;÷ÿe{oÛ¿ånÿl|f´í !„¦IÝÀ'€l?-i]I7½Àþãp¼Í€Û€ÇÛÞÅö#c}œ:|¨5mB *7»aò“´5p°—íûò×®6Çc.|8 ø,pší9õî†azÊm?'é@`ýQ74„B˜Â$µ)¼öf`>p§¤³€×’‚õÆø¸³H×o¾ìmû?cyŒFäsÝ÷—Ô i#`…QVŽ!L#1‚F%Ÿkõྲྀï“Ô.éT`àÄñ8ž¤·“æz­laû= ç eûÛßÊ9.)y!„Âd%iWIÏçÙl…×’2ÑîúHéæcœ¿žt° °­íwŒQp>lÇ}#jŠÈm \+é c±ßÂÔz­ý—WIÚ“T5µØÜö÷Çò@’¶îNÞ ìdûÁ±<Æ0ÇÜxh"—† !„Z™¤#€:ÊKÀ5o¯M r_mûÛsó%Ï.˯FsÜ"]þ|RJû6¶ïÍ>Ç›íK€ ïBu¾|¥¹­ !´²ÐÃbÉG®–t¦¤5o½‰” øpH^˜å…1<ö’’>Ü Ü¬cû’¼èÌh-j·#] „BÓ–¤¥$íNZ¾l]`vþÖr5›-°"0 Ø%¯×"àYŸOöt)(?¸Ìö ÷àÓ¤k=lÿl¿Æâ“÷…¸‚Ô¹q2i9¸B$FÐÃBå£å?žb ªùš5›¬ Ü\;¢-é}¤‘õÇGqÜ $ý,?æ™Àì1Λí'mÿ1OK‹Âq!„¦£Ù¤im'Ú~ ?ïϲÝÇHsÏ'çg6œKZMÒµÀWI•Ñ7€à¼îuÐëeûÛ'{@*z+é8I1hB"@#´‰¤Ãó€ólAZ2¤´Öxá÷À«$ i{I—’–WÛ£‘‚-’º%}¸4¶ÏÚîíï4îˆ =„Â4u—í»¶ío’:ï×#MI»´žHj“t2ð©üz¶?n»·ÑF·¢šôüÕ÷‘ªÞGBˆ=Œè"R/ø¿l_`ûYà_C¶»’Tîàgäëœ62]ÒA¤ª¬û‘–l;Üößÿ}HêHe³} p5©@]!„0åHZKÒ)’öæíÅ:gÚþ«íÇê­#i7àRÞA¶²ýçzö1Æ;Å}ðÁìÛI§’fNdB­%zêÂÂ,N]ضO‘ô°$pí?Õ{I¯ aÛ‰”Îþ Û hïD8†Ô B!L*ùÒ¨ÇWØ~z˜÷Î>HêÀ~AÒʶÿ=ízp.ðààlÛsÇû¸­"Ÿ2ð=I[×çKÖ^Ð"„!„ #è…¤T=Y³Žçˆl_aû õçùšé§‘ÒØÖ·}v çØž›Ž[VÒÍnO!„P‡wŸgHáÓ¼Þ̵ÀÎÀžÀ ¤élk/Æ>ÿ|¹‘ÆH*I:Ž”=·°‘íÓšœûôEÉëí¼ x#ÐÞäæ„š ô)NR§¤a+KZIÒeÀó’~”÷žOT»ö"æG‘–f;Àö'êø5MeÛ øº¤Æ²1!„Â8êúBÞIÿMR0¸§í7æo/Nªõö¶ë^>MÒ6Àݤù×o±½—íßÔ»Ÿq0¡)îÃ6Àþªí­m¿ ©CÒÙ’–iv»B#ô)LÒV¤åN–æ½H'ÆÕIK—쬵˜»®»ø[ÍqW’t%ð]ÒÁú¶¯mt£ÔpO¹í›Ž]sB!„ ÷Òõà~y@¸/pââ~ØöÐ*î %iiI_$Õ­¹‰T öÊzö1Í,ìüJÒÊÍnLaüE€>µíJ À‘´)}íí¶w -_°8ÿð¿”zUIeIï!¥±- llûdÛ/Ö»¯Vaû£¶?³è-C!„–u1p€í¹’ŽÞ|`„mž‚–/Uz4i Öõ€-l¿ÇöœF÷9Ø~‚T£ç0Û´4[s[BO O3ù¨úWHÿÐ_§¿ˆ-òïÁö¥õ,ÉGëïÞc{wÛ ¯‘>ÆFÊ&igIu¯ñB!4[^9|¾¤³€­IKŠ>;̦—7r I›·ÿKêèßÑöƒµx\ÕµºËDqr#€¤¥€ßHº0B˜b"@Ÿ~*ÀëmOÒÀ÷§“Q^Híàæü¶®í¯Ç±šì9à½ùºñ!„Âds!ÐeûÍy¡Ö†n`û3¶ÿYÏN%õH:¸‹TwfÛ—Ô»üZWßß X“TÈ/„0ÅÄ2kÓŒí[$½¸‚´ŒZ/íŸ |§‘cäÅæÞ œMJeÛÊöý¶yIµVÛ÷JzÐŒ"w!„BÃ$í¬’?)|øû(ö{(ié´¿“FÌï}k€íûHµƒ$à<à+ùë!„I.ôiHÒf¤Þò·Ø~ ?‰u€íº‹ÁIÚ¸€Ô³{"pq‹÷”IÛl£x,©œ§ †BNR7p8° pÑ"F¾O®µýŒ¤ƒIsÐw:lÿ¹c¯Mªm³p ðùXË{\µ“FÒï”´·í7»A!„щ}šÉç+Ý”ç+º]½Á¹¤YÀ™ÀÛ€K}ëM…› ò‘‡=%½¦Å;&B!LA’V'UH/ ¿n 2¶°9°¾¤ë€Õ€ýmÿ£ãv’‚ý÷ß&Mkû[Ý¿@sµäô…É׌?FÒ…À/$­aûwÍmY¡Q1}ŠTÊ×2]”3?ؾEÒLà:RÑ–Ï·5xìÃI©ì;;Û>z:ç¹ïÛoovCB!L/ù2\ßÎÌ_Þ}¤ím÷’ª³ÿØÉöo8e]^eûðIœOj¶ï±Ý›wºÜ éI6»]!„úÅú é]¤ë¥%½Ùö Ùüդꟳs€kl_Üàq×>lœ|v’¥vyO¹í?KÚ ¨û'„Bh„¤C€3€wÙþyþúcÀº’*yñ·áìÌk$ë+¯gói`OÒÀ'mÏoäwcÃv¿¤í3šÝžBýb}’“ti½Ò¿“æ ¹mÌ"Í-» øŽí4pÌ.Ig“R©ž"¥±7É‚óqcûÞ|._G¬UBa<åS×®Èo§Áyî.à_5Áy„W§¶Ùž[op.©"éDàQÒµäú¶ÏžÁù¤KqŽíäùot–¤×5»]!„Å#蓤Ð  ¬¬J:©< ¬&©4\AÛÎGÛg?´ýhÇ>ø 04Ïü‡ÿ&SÞéÀn’vÈ爅Bãå"ÛWymà75Ï‹µÇW[Ĩúˆ$íBÊžë³}m# ê àIËÙþt³BX¸AŸdò",÷W’RÊŽ²Ý›ŸdÆð=¿Ël9_Ë´®à\Òê’®¾\ l4E‚óñì)ÿ()cáˆqÅ}8¶ÿV³BÏ™À£’ökf›BË÷ÉëjÛÏ ymGàæšç7——4PKÚ´–i'pø0©saŠjö’ÖzlßÝä&…B˜âò¥Ö.>dûéQìg6pð2à¿m}ŒšØªÆµã¾…¼øÒ O¡ÅÄú‘€Ë’N¤Øþƒí7Ú¾©Î}½LÒ×IK°]¬7…ƒó‰ê)#p¤å&èx!„&9IH:CÒkëøÌ ÀõÀÀy wII·ä·u§Ap>mäS#ϵý-IÇç…亚ݶBèSÉ{€Ûmÿ¤Ñäk©<|ø¶¤mñ‘#$ýø=p+ppÕÙ%é(R:û¦ÀlÛÇåS¶¦‹)™â¾÷’²&CM)î-DRØ ø•í?×ñ¹“€Ã€ÍFqìmHc{§íËÝ×$2a©ly]€}'êx!„Z›¤ã²íO óÞqÀ.ÀÀ“‚§W·/d—~ œdûÎÚ³!é:`}Òò­7²6z˜|lÿDÒÆ¤Â¶HZè°ý›…2„0b½EHÚ ø#ð#àaI«.â#+KÚAÒyÀ‰ÀAõ~É»´¤/’*ÀÿXgšç… ¿øt¤¤WOôqC!´I›“ u]_óÚ2’> é­¤ÝýmÿøR¾É ‹ØíͶϬ78—4CÒ'€{_‘®¾8Mƒóé2ý%ò‚ÿÈŸ <”…&XèDÒ ’Öá½íIkŠ~‰”fÔ¼ˆ]¾ø iôíë]ò,Oc{ ilÛÚ~GM…Ï0~:Ë$­Ý솄B’zòêB/)ýáš×NÎŽ!uºË ÷‹8L©v IšSŽí€$uY®¬LZBµv•– òûº³äÒ¦5óíH5R>k»w¬ö&?Û7%½88Îöïš×ª¦‡AŸÿ>U¼ éDà{À×€Cl=ã?ÉïU¥îÎI3%} ¸ø5)í‚iœšÒSnû#LI!„0éÌVª}AÒR’®†[‡ú_CžïHêÌ¿q´ ‘Ô!éÃÀƒÀ3¤êìçEpá^ øV³Âtú“”I:!ŸC€í¯ÛÞyÈ °èÞlû±š×—Íï¼Õ5ú+é0ÒèýîÀn¶´ýT=ûcOÒEy¡ BÓǺ œgàRÛÏ,d›Ç€ï/l'’ö&à¿xµíCkB""Åý%lÿÉöÀnP²±{“›”úØ[ 8—€WIÚJÒVÃlßç;’z):‘´¶¤“ÖCÝÌöÏÆbßaL|8GÒ–ÍnH!„Ö"é`-àô…lö °‰í{GØÇÊ’¾\ |ب޺5!ØþwþpàZIß–ÓeCc O€|Î÷íÀꋱùŽÀ÷lÿaÛ=·°7%uJ:“”Æö,°ží× -P­ÕöwIËçü²Ùm !„Ð:òL¼o\XÆ›í>Ûó‡ù|YÒ{IEà: lŸa{Þ¸5:L yφÀÏ‹ìPIM¿¦ aªˆ^¯‰ÑÅbt†äégû’‚ô‘,ŽXØRh’ö>CJÓzíëGÚ64?•Íö÷ -y<(!„0­]#iE`E`oÛ7×»I;’V‡™Eºf¸zŒÛ8•5ýº`2Èëè| ªQ—K:!_0„0 1‚Þ"ò5Q¯N)M Àöœ‘‚sI/—t ©ˆÇe¤ÞòÎ'«€7»!„šê6à³À+ê Î%-+éËÀ€ëHÙsœ/¾nÌãÀ5¤%d{µqaá"@o¾wK:¸ ø˜ísëÝ¤Š¤0ƶ‘íÓlÏ㶆ñõnà­’6Xä–!„ZޤY’>,é·’ÞÖànβ}aÍ|ßÅ9n–ïq`5R½™m?ß`BXl¶çÚ>…Ô©ôœ¤’¤£b~z‰ÿqšo7àIÒº§7ÕûaI»ŸfGÙ¾rŒÛ7•µTµVÛ÷HZ+ªê†Âä#im໤Z$-£ÚˆRÇݸX•´NõˆSàÂbi™ë‚ÉÆö ùÃÕ3€÷IÚ#®kB¨OŒ ’¤]$ýŸ¤ÿjp{Ú~]½Á¹¤å%]NZõzÒZ¦œOrÅILÒ’–hv{B!,š¤U_ß±½Ší}l?;ÎÇœ•gàÝܬÁyh¶¬\H„"FÓCX| 7(¯’þ5àfàPàžwU×?XyÛ;HcÛÜö{lÏiðø¡Åä•PÏ!\‰ùp!„ÐÂ$u–0û®íóc}¤à¥¡åÎ$½t°5°íwØþO#û ƒÄ9wŒä5“>cÛ’–~'é„ÔCX´Ð ©“˜/¬ Ì´ý»FwWÇqg“zÉONv´ý`ƒÇ IË¥²Ù6©ÓgüB¡u ,³ˆí6”ôrÛwØ~Û ºŸTy}Äú1’Ö—t3©ˆÜéÀÖ¶ïUëÃP-w]0ÙÙþp"éÚuí&7'„–zc."|¿Æöom÷/Ægæ ÍÁ‘´¤¤/·w“ÒØ.ɹи–í)·ýg`Ã|“B-HR 8ø‘í¾6û'08 X!»;+ É5…¤nIÿ Üü‰tpÁb^„Ðt¶¿¬aûIoËGÖCCD€^'I¯!­Uþ*ÛÏ-lÓšÇÖ·}Ëmæ/äX’t)m6°½ícm?ÝPãäb»?ÿ8YÒêÍnO!LW’–“tø0omNšnö’µŸ‹TÞ¼pÖ‘ÀlÛ7Œt Û/Žpì×û{Ø>ÒöSuÿaq´lÇýT`»ª™¨¯~%iëæ¶*„ÖzýÞü8O×No~|~RÅö¶?d»Ë-l/n'’6~|šT s+ÛwŒºõa¨–ÎBȳ$6®ÊOh!„&¤U€[Hó½‡Z/¿xÈgÞEZaÛWØ®«V¤Õ%}t½p>ié´ŸÕ³Z‘ímï ¼´â±~z"@¤öa^[ØøñHŸ³ý$p?p/©²êHÛ=Vô$9Æ IŸÈ?_¤±}v!©saê{p'ð’¿ÉBã#Ÿ^V¾|x÷0›-ŸßoTó¹c7’:ô9n»¤‘‚þyÀz¶?>R‡~s-Ýq?•Øþºí$eÀÝ’.‘´L³ÛB³E%ÅaH:Ø šÎ¶a~ëí·#-—öaÛ›5x܃OÏ{5².z¨Ë¤He˧RÛìv„Ât!iR…õÿ.·}î›#矔´°%i èÝm?ÓÀq÷ –—ƒl__ï>B˜lò)}‡Š$ήkr“BhªABÒûw_æí¢¢ö 5ÛoEJ?»jÇ\SÒ ù1?lÁù„™4=å’Ú$ýPÒ–ÍnK!Lq«ËoÎ[Èv?#eÍ­œIêøÝÓö¿ë9˜¤%}¸ø°AçM1):î§"Û¿v°}T‹ÈmÓäf…РSM';YÒz¤å­¶±}ã0›þ!¿ÿ°¤mòõÈ¿Aêå~ ãvHú0ð)m}ÛgÛ±p\˜¾ò¿‹ûoKšÕìö„Â4pÁÂRËmÏv"eÝ­fû ÛÿZÜK*åY{KÙ>ÍöˆK­…q7i:î§š!«­ü\ÒÑÍjOÍ)îÉÞ¤žï=€7,¤Ü/€§IóÐoþìoû¾z(ioR[ ø/Û×6Òð0í|¸·‘ÔÉBƒIj#u̯|*¯¸^ë—‹ÚG> ©îµÈóéqKGÛ¾²Þ}„0UÙ>AÒåÀßò¹éOÇÒ‚a:ˆôÁþiûё޴ý°+ð>àõÀª¶ë:)KZYÒ·€ïWÒØ"8o1ÉzÊm÷åk‰"i‰f·'„&Iå㵯m < \ |”‹º5Жe$ýðÒòlë68—´¼¤IzÛX´1„V`ûÛÉŸ~¸]ÒæÍlSaÚèùºÒ#ýÞÿ·¨ÏÛ~Àö'òÊ“‹Š.©,é½ ¤±mbûäazëCX$I3€Ç%Ðì¶„B«ªYå`FÍë’äχä/7Tൎ¶HÒ[ÇIé»[Ú~O>?šý®B*^ûàIãú{Lq“®ã~y iI¶íšÝÆÛ”LqÏ׋^Ýö#C^?š”"¼²¤WÙþɾ8NíÙ‘4¶·# !4ÊöIgJºÑö¸üí†ÂdU³2Ê€­më-ï|8Ôöõ’*¤ÕSfŽc[6%]¬ ¼øòù¶ìse`Õ|[‘–â|‚ü×=õ.„V–×v¨ÎG—t(©ÓíK£ý)„V3åFÐ%í<ìYóZ‡¤"…íŸ@iméñnËr’¾ÜDZ;}±Î%í é“’¶u#äýGÝö§IO#8!„œ¤Wä+£\|ر&8o'­i~Pœ÷וqjËLIŸî ]\2FŤކwÙ~Úö“À_Xú-Œ@ÒŽ’ŽË·7»=¡!mÀ'HK³…0¥L™tI/>ì |¸(½“´–鋤eÒJÀ¿€•DZ-iÝê¿f7RHn„}EJÉx%㜒7ÅMúåTlÿ@Ò®¶onv{B¡™$ýiíò+HÁð?kß·=OÒ&yÒŠÀ7¯a—#Vp_Œ¶¼ø$© ì޶ïht_#¸°˜£+ii@EgDX¨=•$]BšêP»´î¤í¸ŸNl_&éF` ¨ÖåQ½K†ÐŠ&ýº¤Š¤I£æs€umÁv(x–TmýRXãô»ç£Ú·“ªÂŸl;Á¹¤ótž5lwçû_g´û “_^Ùô*Iïkv[B¡ÉþhûMCƒóBœo@9?a„B­OûÙ¾µÞHZGÒH#õg[ÕœK* yþ I”´ÿMkƒÉHE碭 ìüÀvmp>é;î§Û·ýxþô8à1I‡7³M!Œ…I KÚ‰´.ô›WÛ~ƒí¿×l’‘ üÆö\IoN&¥¹u[–ô¹üx’zî¿8il’–%¥ÉïlûÔüå{ˆ4¶Å"élIkå9CO¾“¾§<¿=82_2(„BNÒ,IûÔ<߸Š4ý.Ik/«ýŒí;l¿ÎãtJú()•ý)ÒuÀgm÷Õ¹Ÿ½€jž@êøðI› ó™IƒgÔs¬©,Ÿ£?Üëv.µý‰mUGg’:Ä6lvCB­I !ˆØØöMCß´Ýkûc¶çK:ØÔcúü0ûºøu#ôFRuöHAôÑ#õÜBœRó|càÆ1>ÆTui:ÀÞ¿Øã IDATí žg8ezÊm_lZÏÊ!„0ÕIz=éü¼Kþ¼ \\fû÷’–#-{úgàw£8Îþ¤L¾WÙ~½í¿Õ¹%]AZîí]ùk¯Ž"u Ì&u*×ÖŸY_ÒñÀ•À1Å´§éNÒ'cFxû£¤©#™ô÷ÓQ¾ í§lª™§ŸÌëL„0©LöàÛój_È{ËÇ%I³€×åÛ-s3°í¿Ös`IëKú iNø9À¶Qï/P›Ê–/ò¤S$­:dÓÚ“ÆÎD€¾Hùœ¼%ƒH£%S6€µÝ›àœ”W%!„iIÒÚyšùyÀ‰¶‹5Î_l \–/Wù}à\R¦ÝÑÃïm¡ÇYUÒw€oÖiÞd¸ƒE죔,{ ¸Öön¶“´i>ýò‡'Hum~Yóñ .Rpþ@½íŸÂV(H:ZÒ£JËíaû?ÍkV˜@/{‘2PC˜T¦B€^%i I×3x¤ùT`eÛï·mI§«Õ~Îö³õ¤¢Kê–ô1RzýSÀz¶ÏµÝ[g{;$}„´DJ¨•t’/ðí>wiI˜Ÿ×s¼©JÒR’º†y½(Ø üÏ# S­§¼´BÁyÍnH!4ɪ¤óóã¤4óKkÞÛ2¿¿x¸:¯ª^×ȳ¤6I$š ØÀöYõvKÚš45n ‡Áçý}l?ŸoÛtÚ¾§f›oäÇ)o/Õ¯´Þý¤¿…ý†¼ŸAúoYóڔɬ›îlßE*¤|$TÿŸ]·¹­ añL‰=ÿŸîC¤¹ßO’ÒÊ‘ôª¨"é-Àý´õZàQ൤2‡Ô;òžïgoÒ°çó½I#Š]’Þl7¤ÜbɃýÏ’æ2Ÿ |lhjýbîg%àSÀúÀ5Ëd½øSÍ:é7çiËÅÇ·î>b»á¥_¦˜Œô7€¤Òš°»þ4g!Ÿ’=åù;oov;B¡Iž2Ê\eûÛ’f櫺ÔEÒ ¤ÿƒóû3ÙÏÛçÙ}kóÞ®äSÙò÷OÈF i£šeæ:€bu“¡×ßÖž°ý©‰j_hÛßÍk:üW½¯!L´©ÐóÚ \l>Ìüï%óûï’Ò~w¬78—Ôž§Å?Ì@ÛGê Îó9f'Ô\^ó|ó¼ÝµÞcûBÛ/Ösìi">CJ,“:g½Ÿÿ7˜ ó‹EÒË%Ý ´6h! Þ :?w¼‹”­µižùIcœ³ˆéuŽ“t1é:çPÛOÕ¼ÿ Q‹¨Nu»˜´ÔVáç#b¶¯±½cçÓ‹í›lÿ7T—.¼v„αšj*+°}Þÿ™T`eR/õõìXÒ¤à[Ãmïc»Ñ*¯‡’æÅmÉà”µbµUkÖr„` ÐK„‘œEªÊ{p+©l½¤ìŠw’¾ÏU†|n*§²ýÔIuY³B“Q>Gü.à$àíE· :ü ¤:7óH× OÖ¾iûƒ5£ÅÓB>½`èk~KšŸÿ–Ñ‚©}]Ì%M…¼3*½‡V3ôÙþk~û^=£Î5K|¸†TîªQ6çë¶·ýì0ïí ,UŒtJzðx^à" CiMóuj^ºÑvQ4¯ºŒ^¾þì×IY §Úþã6³©òbE‘:ªB!,¦|DöBR1Ö[HÅæ¾6‘m°}ƒíò¹ôu 0LE’ö£f¾x¾êͧIÑï&UìÉÈ ò†Õá@Rîs–fkr³B¦Æô1“xp:p°ÙXUF]Œ4¶vàË’æDÕ¦i™TÙ}¸à~Ú‘´=iY»È_q ÛG´«1mX ²ýà/ªÚÇôˆB™RÑ—£Hç˜ß[Û¾¯© …¿0xmóã€_Û¾XÒÀGú`>àƒá%ŠÌؼÂûõùòÉGÚþGS¦µ)=‚^IÛ‘ªªx§í'pÙ’ÛI…ã–"`ޝ}Óö?m3ŠZä£ymEIß'hœjûº18Ô´He“´ðI/ov[Ba=CZ×¼n’6"_>œ lÁyKyØ¡æù"»ñHÒJ8µæ6xœiq]˧®l@øy¦¹­ ÓÝ´A—´ iI³#‹€“†Y–e\å£ë'Lä1'/’–EyRÁ3R'ÆG~Òý‘zËAZÿ<älß'é6ÒßøÐz!„0UÒ@×àä ºËרþç8´maæoˆ,§$¯Ís(ppºí[H×i¯®–´4©˜îaùuÜý¶P³‹O3°”m]‡]Ë[×2ß[f™þ¶þ%*¥Ê,•5«œ•gfYÖ]ÊJó€ç„žC<ƒùÏüýÏÛþyÚ lÿž|I3IËži{èò†!Œ«© ­Ü¾Xò4¶·ŠÈýØÆöÝcÙ°0*Ú®­N{°¿íJúð£‘>hûüqoÝäô&V6!„)§àü¿Hk‘? ì:Ìj0"ŸcþÕf»•Hj'e2¾¸š´¼ìA¤:ÕàÙö¿òŒº×§?t°„í¯Ø¾sÂßbt¥Ê³ºf­U²õ*•Êm³ÚºJ¥Y–QÊJ”J¥t¯ü5•È”‘¥Ånú6»³'ç÷ͼonß/Ûþ±çšýû4Á|ÒµæU’¶¨YO=„q7Ùô·5RM5O÷ý°æ¯Öüù7ÿë›ÓâºÉö\à4IŸ+–7Ì×Q¿©ÞŽÀê5©ôzƒó<]å RúÊ׆¬):Qî.iÂq[R^ÜåpàÀÁ¶—_ €÷6’´?©·üðš ©^Ò<Á¿N|Ë'])­°ì ³JåR©ó¹Îÿ;7HÚÙöÍn_!LIÀIKo®ç–q4ðSÛÔìDšz0œcIiï÷’®¶F[geÒvÜw_Ùý²¬”íU®”W-UJd•,ãåår™R)åÕ=+Wïê*‘‘FÒe!¤®R×Ò´³ëÓ?½Õ–÷mùÓ»7»{ÚdœÖç]¤N½^IO·%ÃÄšÔz=$½ŽT8æi`wÛ?mV[l_Ó¬c·’|ãóÀ àÀ«IY 2xØ ¤åQöÞ,ô>à;¶LûT6]¤l‰U—X«”•Ö-—ËKf•¬»\*w¯öòÕ:J¥’JY‰¬;c½{×›÷Çcþx{ß¿û>»Á½œÛ××÷›y•yý~Óß7ZP'„Z^>úu>мzÈ4ªÐBòUu>O*ûä›]\cû;ùÌëH©ñÓJçeK¨¬=Jí¥õJå’J•Y9KÁx¥T ÌËårº/•_ —4¨—UN:ªùÁÐ^jŸ1£mƾ{>¶ç¶ÏÌ}æ‡wlzGÝY¬“•íòB’'OBš.»ˆUšBhÈ”Ðóe>l |8/¡ M$éÃÀ>À»mß’§®ïüz˜Í%ˆßMš’p0‹ô÷{ö(›2©ÿa]âkKtôw÷¯_ª”Ö]z¥W-—Ëm¥R:9)l¥¬fÎYV"SÖ¾ÎEëü´¤åöò:’ÖQ¿^µÕƒ[ý}Aï‚_÷½ØwÿƒÛ=øïfÿn!„0$­B*2ºéœñ±HQm]’V ¥©Ûö[FØ&ºóà|5RÙwØžVç®ö‹Û×+u•^[ª”*¥r:÷g¥<8/æE^)W(gåt+•«K*QÉ*ÕÑó²ÊUƒtLõjÉý¦½³}©™í3ÝõÁ]yóF7O›A'Û/jƒ7Kº87b‹0–¦l€ž§¢œLyý>)íÏÍmU¨q°‰íòç{7çs~†Ú x-°;©˜Ïº¤¥UF;M`ÒVkÕ•jëV÷Nå™åÙ•r¥2(•­TN½çÙT¶R©ÚK®ŠúK*1ïóºŸüê“ë®ý¡µïé¤óeB/c;nûà¶¿Û7÷û÷mzß„®hBcER…´BÊ©ÀÏ€ Š5CËÚt=ð`GI‡ØþfþÞ ¤‚±äÛì#éÒµìá¶Çji¬IÑqßþ¥öKí¥]³r¦"0¯Î5/‚óâ–žWJ)@¯½¯¨’®T¦¬¬—(USܳbEæWù3¤âa¯£õ²ÃØ3TSÝa»;€ÇWÙ~ZÒ.ÀomÿmBZÙBôeOtn5£cÆN¥¶RW©œæ™•ËåꉹÚc^3‚^ô–gÊ÷üäûìcÏ.ñ×ËÿúªÎ%:ç®}üÚç=åêèîX³ßýïÜáá|¶ÿÙ<°Ñ‘þB˜4$íDJ‘ž aûªE|$´†¿’ª¶÷?.“ôoÛ?´}YÍv–ȽÚöCcxü–ï¸×•*µ=Ýv@Ö‘mTæY–U ÀÕŽ˜·J©’‚òâqV¡­Ô–‚ò¬L%Ëu•ª)îEÁ¸Ú ½ßý¸ßô»Ÿ>õÑ«^–ë^n¥ÎrçÛwx`‡KoÙø–5ùë™P¶¯”t=ð÷ѯ~zÕK¯zÉ:²™•¬rÌv÷l÷·nqk3Š/7í95Ov“tTt †Ñ˜z^é󽤔ö_Ùn.sh-Ï‘z¬?*é'5Uõ&œ¼xEœÍö©ßÔæê¸¸cˬ3Û;+e¥¬”Uç™U{ËkÓÙ†¤²ÕÎ3«d•j [qž}Öì{J ô”E€ŽmÚ²¶¶Î%;÷Øí‘ÝVZú᥿5]–X !Lù|з‘æ"?lfûáæ¶*Œ†í§$mÔ„"\-{Ž«,YÙ?+e«)ÊD¤×Ö™)²çŠs1b^Œš×çÅ­¢Jõú ¬<Ë.ÒžÛ)0ïï­èó5I¨Mm+ÌZá[ß¹õçï˜}Ç‹MþªšÂökókÕÇ ;ŒåC#²f7`´$½’´Fæ;€7ÙÞ3‚óIãÛ¯V!-ý5ÀöÑ5Ëè] \ìiûScxüÖOe;]길c߬=Û7+g¥Aë™– Áy¥”N•R…¶rí¥vÚKít”:ª÷µ·®R¥þtåŸ^þãC~¼og©“ÎrguûÎR'íY;/›ñ²õçl<çØ-ïÙ²«ÙßK!$mIêÐ= 8ÎöΜO Q!{@åÜÊv*iÓAùôöj€^¤­gs͋༭4p]P{}БuЙuÒ•uÑ]êfFi]¥®tËò[©+mSêª÷íY;íY;•¬BO{ÏŒef.sä!WÒò×WãÅöå¶ÉŸ~EÒå’–oj£Â¤3itI+HúðR…ïuk ‰„ÉáNÛ·G‘2:Vº‘íïÙ¾Ðöx,kÙ“¿.R{eùÊ*kK•T=צ±4Ϭz2. Ћ[g)à]å.:Kt–:Yaíž}êž§6üÅ»±mñZg©“ör;íåvÚJm,ÓµÌò«ÌZåí³ïŸ½B³¿ŸÂô&i IŸnîÖ±}i“›7¸§ÉmhÉÀ²|vym•´»Jiä\™Òõ€òë‚l P/jÏÔNw«dƒGÌ‹ º3ë˜ Ƴ.fd3ª»KÝte]ÕNûÎRgõú¢è ¨d–îZzù§×{úfg-âl`5àíMnG˜d&]Š»¤i´ü à!`‹šJàa’²ý IWDoy¢Ó¥Ê2•C³ö”Ê6\p^¤¯ = A¯Ie+Nе…`ŠT¶%ÖØfÿ¼òŒW^Yéª,èÈ:èWªÔÚן ÁôÒKFÆÌö™ÝÙÛܱÍçnßúö˜ÛB˜p’Ž>ü ØÖöÝMnR½ãl·BjtK]‡ètÍ(w•T&Uóüz öymŠ{)+ JY¯½hÏÚiSYGzœµÑ®v:²ÚÈ礫R½6Rõvúés½ý½”³2óûç£LÕÊîv*ןõ³ÜŒåÖÝþ¾ígÿb³_ÜÙÜo¯¹l?$iGòx+/r<ÏömMmXhy“*@—´ ©*ë*¤¥S.‰€nêhÂË–ì)(Í*í­L«×¦°Áymoyu~y©\ ΋^ì¢W»šÖVÌ3S^†š¹fÊÈœ±Ã1;üÒ\³ysæ•J¥¾^õÒÛßˤyf=í=Ë.±ìQ[Þ³åwoqw³¿¯Âô iÒuÀƤº3_°ÿM-œ·œR¹´«2µK£çÊD¦”gYª¸^éEeöbYÕ"P¯E/‚óuЮöêã6µÑ¦6*Tªuj2gÕû^zY- ä|nz?83}ôÑï~Ê.Óë^JY‰eº—ÙyÃG6¼ç¡õêköwØLùµm±FúÖÀ™’ΞŽõ”Ââ›)î’–’tp p7)íKœOj¿%­sÚl-÷7Tþxyó¬”ÍÔ;^œÂ6t µJV©éEj{%«TÓØÚ³Ô;ÞYê¬Î!ë, Ì5ëκé*u¥9gY7Ÿzó¦_Øî G•ç—KYš‹>t®Ù’K.³TÇR‡6ûû !L}’º%Üü‘tðùÎÃT¦S´´²4ï¼Ê‹Ôö"`/^+©T Ø‹ ½z}•uÒW² mj«¦¹wª“ά“nuÓ“õ0C3èQÝY73”® Š”øvµWƒýbĽ¶™2:*]KÎ[r×f‡­ÄöÇHAú$µç.C¤¥ÿ(”¼™´öVÀ¶±ýt“›FÉöšÓqóE©œQYE%í“eÅ\³AóËŠôµ!Å`jOƵóÌÚJ©§|è­+ëJAy~?#›QkÖuóÊc^ùø¼gæõ\ýßWïXô´yµèLVa¹îåÖÞé—;íÒìï-„0uI:xØØÃö5+~„0–D uÜ—Ê¥Ý%eEöb 0/æ ç·’J+´äÏ‹)mU(“ÎÛíjÉ­¤«“Ù º•‚òê-›A—ºèTguÏÚ«yqMP¿è<ȲŒ%º—ØjË{¶loö÷ØJlßkûçùÓ;%Ínf›BëiÙwI“ÒØ6N>g{Z§É„©M§KåŽò«•©¤LÕ\5•­HoÏÓÚŠÞòbz‘¾VÏmYJU«Mg+RÙ†¦±•)#òõÏeVYc•½ìØË—Zk©9íY;êd`ÒÒk嬜ÒÝJ½,ß³ü³ïš}ï[Ýùl³¿ÃÂÔ!i à|`'à#Ày¶,üS!L :Q+—ºKë"ªy–åÁ¯^œz^\#ÁyÞ‰_<.®:ÔQ½u©‹u¤@N*TªK¬õÒ;hɵ>ú¨¨B¯z))-ÓZÊJȪ¶ \*·µgí{ßmÒרê> ¬¼8¬Ém -¤åFÐ%õH:—TÉóO¤ê쟉à<Œƒ–ê)Ï”m®LË šc–œWÓØ† γ‚oålàÄ[¤¯=äê¨ö’il3²Ô;^í-ÏfTGÖ7ßcó®øòçú9—ÿrÇ_–,‚ÿbľÚC_ª”ftÌØ¯Ùß_ajÈÓ>OæëÙ>'‚ó0dY¶UuÞy>‚.ß×Õ Á£éµ·rþ34HoS¤ ½[ÝtÑE—ºèQOu½“tíPŒÈ—(Us1gdÕ}òŽ…3ÖoÚ—ØâlÿÛöÛ€×HÚHÒ[%µl¤01Z*@—tð°°—í×Û~²ÉÍ aÜé}ªHÚePúZ1ÇLC–S©™çUVM!˜!½äµ#èÕ9èù<³"HŸ¡ôd=ôd=Õ ½K]Õ[GÖÁ7Oøæ¶_:ìKoœóÄœŽâxE'AÑi°T÷RkξköŠÍþC“›¤=#ƒlhûÏMnV˜^šÞq¯Ó%J¬Y»ƒÒÛ°£óq gdÕïŒl`Õ–š`½È k#ÍMo§RÞC=êa–fÑEJm¯P¡¶Â²ù±jËíö«¿˜JY©}ã»7^sB¿ÀI¦¦–ÆêÀÇ€›ØœÐZ"@—´–¤/Ÿ6¶}Ss[ÂÄÉ:³íUÒŒêè¹nCSÙj{Ç3es¿²T¶¢Lu$½f®YmÞ¥®j!˜™šIOÖS}½])þèÏ}k{wû‹7sÓ&Cç˜ÕÜÔÓÙ³o³¿ÇÂä$i%Iß$¥Â~ØÀöuMnV˜~Zcäò?¬*««Æ—Œ gdÕÇ@u$»ö§zÎ.õü§:²Î@°^àí¤Œ»z˜Á ÚÕN™òKƒñüǘ~ú«ïÏÿôÓO;íMôW8Ùþ.°i É¢×ÍmUh†¦è’:$}„Ô[>XßöY¶ç7³]aZi~Où‰jÚö%ékµâ40ï¬kO¼ÕõÌkŠÂ\ª:mƒæš -Ó£ºH¯ÁyEºgt÷òóS¾rØg»£¤Ò ‹…ÚÞýîöî7¿kóÕ›øU†&IeIï&eÏ-ldûTÛs›Ü´š&˲u• ÞBÈù(zmÐÎ@°ž)ôxh°ž1ð~±Æy¬W÷|Ô½x/#«âCòÚÜÊ—Z«yݸokk‹kƒÅdû¶‹ôÿ~%é¨H{Ÿ^š KÚ‡4¿ìàÛ¯¶ý‡fµ'LK-ñ]É¥µmÀ “ï°óÍxi˜Úʭź¥Åɶš¾Fe`$v:é¬ã=ô0‹YôГ‚sÚ«_‹^óYËÏšŸ)ã×7ýz™»/¹{õA=éJ=æÚ*m›6ï› !L&’¶îÞm{OÛ¿nr³BhzÇ=btZ­=¯IiRA¶Úk¼_û¸ØOíO¬ƒ~Û"ïss˜Ã³<˼âÇóXà?½ôÒWüxð­Ÿ´nzq_*—zÖºm­&úkœ¾M*y$-’õ&Æ„ÿÇ–´Š¤«€«+H£æQÝ1L[–×}I@¾¨à¼x\3߬¸¯êʪ£êÕåPò@½¢´ÔJ'kª›:hS[50/ŽQ¸ÿÊû×þÁI?8äO·ÿiI`P‡1åŽè%!,”¤e$} ¸ø©ì7›Ü¬Z‚ŽU&¥5Á³©Ú˜ÁÁøŸªšÏ ÷žmð@P^Ü XÀæðœŸ«Þžçù@¨Ï÷|x½î¥—ÞØç?½î¥×½ºûi£m‰ú§ Û}¶Ï·½«í>IËJ:GÒÌf·-Œ¯ Ð%U$½Ÿ´–éÀ&¶O²ýÂDµ!„V£C$Ä+‚a`®Ôœ\‹€|ÈOm!˜bNYm ÛПb¾Y‘òÞI'3™ImÕJ¬…ÚT¶~ú9ø ߺÂF+üú/·ÿeùAénJ÷år¹gý[×_nB¿Ä¤ϧ<xœ4ÏrKÛï¶ý\“›B¡ù™uefhW30²Žþšmòྲྀc½x½ÈmÊÝ—Fƽ€ý"Ïûyž÷ó<Çs<˳)@÷ó¼àxÑ/2Ïó˜Ï|zée>óÓhz÷¹¯zßOõqŸûpæžqþ榃e½‡$u5»1aüLÈ:è’v&­i¾pŒí¯MÄqCX ÍMe{« uJ[+zÈ5†V¤Û aç¥Ûs6ôC¯zy˜Ë\æ{~õÄÛK~­ýqGþðÈ«^è{¹}s1ù¼³šT¶J©² ðÃ&}£!„Öu.ðR¤Ùn~*q/ÕÜ¿Ë~f ¸kRÚ‡Ž¨ Ü‹û~÷æ;ÝŠÑì¾þ>ú”§¤+ès™[/QÊ›Ò_MkŸë¹¼è™ë¹ÌíŸË\Ï­Ž¢Ï÷ü—Œ¨×çý©HùŒñÿò¦6ÛHÚعà”´’í¿6¹iaŒëº¤å%] ü8¿­Áyh!Mï)ÏÈÖ^r9PéÅã¡üÐyfÕàÜ5óÍ0 ±ÿgïÌã£*¯ÿÿ9÷Ζ°…% b@‰È¢ì;²…ÄÝ`k¿ýYZk¿ÕÖõ[­µ÷Vmµn¨•¯ßÚ‚ŠÖ°¸²+`A!€,²/aÉ&3÷>ç÷Ç]r'ë$L2Û6vs‡ÜYÆÚ½R*T»è¤ÃMn{2†Ù•ÁF98s¶Dùq¦r§ÜíVÎYPB’®NÚúýï¾ÿÙñßÛæº6{IP[G¢rc%ɹÌBO8 àÃ(EÒúˆúƽ Q¬è HP¥(gëæÜ¯;¾6#éB­Œ˜Ûöv3r­( 4¡]_ÈßÎö©.Å…Í6­¤Úw€©2MÎjÉfÙ׃\™suó{ÝÌAšýlEÑ5¡•Fõƶ"˜y3˜…ãVÑ&¿bæýÑ™$\šM ›íî"¢˜ù@îêHZ9ASŒÛ»âÎqG^™ÓæVu¶'c½Jž«¨Æ„k¾O%š¢ÙÑsv]‡qG dwÜÎ33¿¶wÉÙ˜€Ûe¶;Ô³¼ç¼øÉñ‡5¡AWõ¨ÝV‰DrnbŠ{ˆæÃ´¼Ñx›ÍÈDÒD×¹¡¡€ŽJ1îäöÃz]5Dº®b\Wt£M+vv»«YøÍn»fæ—kl¬TEµ1ÉTcEÐížéæüïçA½rãÞÚ¼‰¢kZIônjë„™ËͶlOD{,’†Ñœt€Cœ·°ˆ–ø 3ç7÷X$’hÂÿàrõתÕ²²Y‘qg4ݲ­9óÊBª³šmS4¡AUT¨B ™˜¡˜ç1­l*©†å” ÛY­Õ™kæÜ%·ó̬ ÙÌ9ÓYGâ5‰‚Á üûý1å[ËÅ›+‘HÎYªˆñ?èGD?eæÏ¢5&‰¤Ù C76åI'Ãên¹ètklDÌuÇC1êÐXw;j®Seä\ƒQ«ÆüO@ (‚F]QŒ4¸*Ýê΢±f?[óV„¼ª­Ýèº#]×Á‚‹£z_[)Ì|ÀYßÑër™ù£èJRÍ.Ð-˜¹ÀÜÕy -ÀR$i•˜qå("PÊ‚íBqÎ]sS«B êV´\ÕU£B«#j® ÝæVtœ™í¾§nÅm䢙â\A"d"v³ŠÁØùf¢¢š8ׄ2¹ïÈ5ÁýÁT<„ì¨Þ[‰Dr>0 À/""™›.9Ÿáy\¦ÎT‹ !‘]f´\cÀ…P›»µ&0º®UFÎ-Q9W+…¹`M16õ­"²¶‡aoŒtëx+’ä Ýç< *źºjŽˆ*…=€QoGÜY3Ö ¢R¤ëºn uç:@×t«r»%ÒwEçNJj´½[¼ ;Ídæ•Ñ“$”#Й¹ÀÕD4 ÀV ¢¹C.ib¢ºSNLE¬›yfUìíÕª´êBFô\d…qØÙÿªÍü5¡VŠs» {{•<3k"ÖaV€‡ÁÂÎ3³vÊ­"0ÖDÐдJK›®·¦i%–8'¢ v2³¬è.‘HÎf^CD—p P&7õ%çDDÙÙÙʈâG¿îòutxY39»*óÏI3µ®™Qss  “^}Þ!´UÕØ¤×U +vô\Q̪ñUºÝxv8ìt¡Ûuoœ}ÎmA*Ì!t@ ½»*©‡©þ†WCÒBh1Ý‚™? q`7ý@3˞ʒHõÚB»M7*wÇ]ûšjLŠªØ­TtÝœ…a}³Ú¦Ø öN¹ .PX1ìí¤V6¥fë›]­•5ÛÊf õ*¹æ!}NõKÛǯ8 @"ÇÌÁf¿Á‰ä¼Ã´iV˜ß^ àˆèAf~:ŠÃ’œ?4ûƽ%̯½öZOYYYB{Wûމ‰Å…jaidtgô\¶]Wôj–vçÜnµRUY5ÒâTŠP ')•¿êº€B#èv.ºA·Ÿ-Qnƒ³šùêgÊ®.“©«-ÓÚn»‘ˆèm.ÿĄ̈ ¬•Óâº3—Ñ ^ð¹;.9ÿˆÃ ‚uö’N¶H‡Šê¹fDFž™c‡¼ª­0 Á¸TWH!˜+›c§Üš„CŠÁX"]Tæš™}L« s]3wÊÍ Yèb«ã7ü)€%úØÖ|7V"‘´˜ùA"Zs=CD.fÖ¢<,ɹK³nÜn½õV%33Óã÷ûtÐ @×´ò´Ø ž •k‚@eýRÈ~¶Ä¹F\pU tÓÖÎÌPX±Å¹Êæz@(€‚ÚºS¤se¡8!‘tg§0×uÍ(\§ôo¹¤õKÂf%€Äx0Êciµ´XÌ¼ŠˆX[‰èVï3ó©(MrþµYƒˆ““Ãî~t/k|1«•UZ…nXØœ»ä¶­›qBã·,àbCœ[66•U(B ±ÂY“°³JˆÍ*# Ë»ÝËÔj¢U±´uèš^xzúi»e¢Ù.iDóß]‰DÒZ`æÕŽoo¦Öü†™å¦ ¤ÅbFÍÕ£GÆ !:2sw¹ÀÄvÔ;º’Î$á„z¤Qt4óÎ5aˆë*Qs F­Àèl¸òVl7.tÛ™g skmPÓºÀvرÒÚÕæ¶H×u;Ï!ÐýêuE”n±¤0óÇDô9*Óˆú(fæCÑYë¢E tpˆó?ðe0ó¦¨Lr>‹;aüøñÊ”)SÔ7úÚùÚåŸvŸ¾˜Uid[ØXuØÙtGÎ9ìlŽßÀ¶µ›âÜ媪† èä4¸sH]çÊ<³jV6óYaç™éºŽ` ¸§Žßû!3óæf¸Í‰¤uò€~0"?7Ey,’s“&߸Ÿ1cegg»KJJˆ¨‹¢(=˜¹;µà ÀEþ‹pÒ{¬2 Pš‘m³¸+Èx0D¹õ¬°bÔ¢!Ìa>ÅN“³¾¶× ª¥¾9×ζ­Î3ºn uM©™£µµ%7•ÈêàçfZ±•Züs¿"¢û˜ù•(«UÑâº3Ÿ!¢ÑfÈŒÖlf.šDÒâ!"dggÓ”)S\.—+VQ”öBˆ®ƒüƒ:/ó-c¸@¶•Í,cÙϪ rËÎfÛÔMqnMž*«Æn¹¢@c­2Ï̱K^5OÍÊ1sV~·&aËÞ^-ÇÌÜ17óÌÀÁ: 5%xŸˆ.g悦½Û‰¤5Â̇üˆˆ,Ë{™y~tG&‘Ìž=›¸ý~UU“¤0s7"J„µT¬cãE<’Ï$ã°ë°!Ùcc]P Ø_;·ìè9³!Ê] H7£æªùl¶V³×¨¼²µy˜-×L¡/DåÁZ]Töc·žƒB%þcþµÍpK%M3ßKDkàø\JšžsF va˜7€ˆ<¾"¢7¼$…º¤‘4‹Åˆ(##C)++‹QUµ€ds·¼“‡= =ÏôÄ>×>@5&J;zNŽ|3s¶sÍÈ ZHëk2¶&NEUlk»¢ýMkl¥BR†Áv¥V»Œî°³YÂÜÊ;×ô€¾¿äº’ýu܆{`T U›ú~K$’Ö#}€7‰èFf¾>šc’œ4©³ÎtЩ±±±1:HÐF«áqnѧ¢ŽzÚk&¶[©Z#fâœsf†ŽÈ9›Â\˜Â\¯´¶‡¬ ¬ó£ŠÃŽ+óÐ_ ]Tvš±Ä¹.Œú9A^οeYü<€™Z_Ñ,CÌbfÙÛ¾‰8§ºfѳž‡Qÿ"ÊC’HjdÆŒ4~üx·ªªñÌÜÀDÔ@;>"RÓ*Òè°ï04—fXÙ‚0ºù°líöÔM€FT¨`°-έ Y°0ìkN;[M±)ÎÁ¨&Ò퇵Kî|6óˬè¹Ð M|R×}0«¸ß VúŠD"‘4̼€ˆÖ¸ÄzˆbdgI4ÉÜDD¸ì²Ë”„„¢ 3'èhv-ò –¥}¸´ôRlV6Eâ  ±á.`nÜ3ó¸#rN.²Û­*¢2rƳ•îԲ.@•6mq΢rÀæ,Œ‚¶¬3è åMxo·ô_9ÕŸw,0À\™QËyË9+Ѐ™ÿˆþmõJ'¢ñÖʶ’0iòô &(111î„„„6Bˆd"êÉÌÉaNÈÌ \H-KÅu‡-Ä­j­ @!‡Í93‡LÈÖ.¹U­Õ¶°YÑs3Íiq·,lLbcc6"è æÎr»Œ. ü"¯üGåaí¤‘às"z–™Gü¦K$‰f>à Ñõž#¢»™ùýèŽLÒÚèÔ©Áˆ”ÇQÅp3söá®zW”––b7í6æePL=/X€˜BòÎYk R B˜m[Uc= «F¡8§H¯A7‹ÅY®:§øgQis·"謯»Kݧ†|1dY||¼•L'úy3ç˜dn,Á 8õfæ•ÑÙùÅ9-У`[Þ_ ™V¶­u¿S"Є‡™gæ"¢x]×»˜âÜÊ3ó ÊAJ0û+öãŒrÆÎ3sîWÛN0Źon taäšY±ÕK=$—] „­Tl{*#è!=ÔÆ&4¡±àϽ/Ì,̶HïQº´HI$’fäß.p-”éq’*´ÓDç&—Ë¥‘*„ð‘†XW‰ê4§ÓPV^†£8joª“ã?Û=gŠuf66é]FA8K  EØét–0·ÖÖºÀi›j¯AgasÖ®2WpðŠÁ›bÊbÔ W…’--îç)–þ0À\"šÇÌ·DsLçç¼@·0-ïƒü€,@%‰*D„ŒŒ %>>ÞËÌmMk{G}%ݨ!zO ¤ŸIÇF÷F#zN›#znçš9vË¡€]ÆÄ©è¦(7£çV!ÛÂVe§ÜŽ¢×dcsXØXT±³™ýÙq«ý?÷5¤^23?CD»¥8—H$͉™jó=ÍÌLDíÜàOÒöÞz!¢‹].×ß].×f¦¸¸¸=åååc©zl4Ó³.õ_Š3ÊRa¥ g¢2’Nl䘳nØÜI7Eºb<Û–v%T¤Ûî=„FÐmqŽÊè¹I7£æ,ª_åAkŒ/FéöH¢3Ï#¢M0òÒÈ"Þ‘à¼èÀÌå~9®þÀgq‰¤9ÈÎΦÒÒR—"†ˆÚš‹Àx"òÔeeKғз´/vÆï4ijR¹° Â(d‚$&[<“NvµVk·œôllŽJ­ÖsÈlZÛís=T¤[½ÚÝ¥î=ãþ=ñe ‰†¬[¬—D”ÄÌ'x‹%‰¤Ñ8DV7?0“ˆú›ëÉyÙ ÄGDÑ]©©©4uêTÖ¬YÓ»gÏž›6nÜøÓ™3gÚ5Ž~øav¼@ƒ;kš&\.—®(J@èÌÖz\‚¡åC±ElÁ±Øc¶8·Ö¤Èm>›¸`‹tè¦(¯AéUÒßÀ¡t0*× ¦8-ŽåA›Æ—Ä—BÁ`P‹‹‹ ,óÖHÎe˜y€m@DÓÌ!¢ÿf毢;²s—óJ WlðÓòþïhHÒr ¢lUUÿKUU½ `3`L´V³Æ23 ÁPrr²ªišWQ”föÁÈ3«7ï½w°7JËKq˜C±#®™k&ŒâovAÁÆN¹‹m ›kfŠtk—ÜY¦¦V*!]OU`mQ.*'c¡ xK¼%£¾µŽ™]IIIÚ17SS6Ñ£Ì<·1çH$’ÆÂÌ[‰èR-qND˜9?ÊC“D˜*s;TUÍVåµøøø6Ó§O§ÔÔTûØŒŒ èÜ·oß%Ï?ÿ|iqqñÝ×_ý¼3fPNNW9¯ýu}k†üü|NNN(gæf.³òÐf‡*ûc§Ø‰=q{ ‘n u[¬ Ã=VEœ+èUĹ-Эgv]×OÄÅÅY°`ÞØ¿'D”ÌÌÕrý$ÉùCK[ÔeÀ¨g“ÁÌÅѤñåääüõ¯+((˜ÍÌw¥¥¥aÚ´i”˜˜ÖyÊÊʘ˜ˆ¼¼¼ãŸ~úéóiii8pà™ääd€mÛ¶1¤§§[¢k[+Ìž=›¶mÛæöûým„ÉR¸²€l½Ðë•a‡oŽÇÜ0Ö*ìõ\¨Ü°W+#è!kçÚÀÄ.T‹3Åy|I<úîí‹Î…Ì\ÊÌLjèMÓ»\®‚²²²ÀòåË¥“€ˆ.°À&È4¢ðh5ˆ¨+€<ÃÌ¢=IÓáØÅ†ªª×+Š2·M›6m233)%%¥Æ÷ìØ±.—Kœ›UÜUUUc™¹3÷2ÿ uàCwÈ÷¸÷àû¸ïI×mˆt[œ›»åä¢IØÚ1·#è5Tpw tó÷¨|vÚÙt ùp2÷Ïë_ALÌ|@UÕt]?V^^^¶bÅ -â<À÷þ™ï;«“I$’ǹ°.¨ µð6€+ fæQ’¤œÂæÌ™s3ÿ™/3f 9².;{Xhš†uëÖ¡W¯^g¶mÛöV‡èܹs !!;ÆíÚµckÍžž^«P'"ÊÎÎVý~œ¦iÉè'݈:ˆ…!­´V€ã®ãøÁ÷ |`•mW¨½=äQµ> *×` áLºì†”)¬°¢ð8 £ÊýA":^PPPšœœœ?¾Ì;—Ô©f»Ýîß«ªº×ï÷§5¦ÖSk Õ tóø_ü À™ùý(IÒHªä‰£S§Nq§OŸÎaæY]t,;{8lß¾íÛ·GçÎár¹°}ûöc6løõôéÓ—8'Þ+V ))‰ë²³Íž=›V¬XáŽMLD½`T ®±z}WckìVTx*l›;\†Í¶¯™"ݶ²™‘sûÙ¹[nݳ:llÌ 5¨"mo÷:Ô+HDŦ}€Ãªªz½ÞˆMÈD4ÀËF0sãÛ%I‹¤¥¯ ꂈÆ2óJóë^2šÞòpÚÙÿò—¿Äæ¸ë /l½!¸\.¼òÊ+………\rÉ%¿íҥ˙:ˆ””qäÈ6-ð¨y@D¸õÖ[•Ç{EIÐI×õn¦ã³Å8jØ4ˆ ‘ïÎÇ1ï1œŒ9 Ý¥W··;€½N "´-o‹ÎÅÑ¥¸ b± @#"¿¢ˆˆŽ3ó!":.„(P±dÉ’F§¹IΈhxLLÌ›>Ÿ/uðàÁžuëÖùËËË{°ìâS#çm‘¸ú0ÿHþ–ˆþ`+˜öã’èŽL.N»™ÓEn·û:"z=11±N;{m\|ñÅö×[¶lA¿~ýºôíÛ÷½µk×nÎËË›1yòä#§NbSœ‹~ýú!;;›ñwÉ~£ã"ø IDATøa6#Ë¥.—ë˜B!"!ØÌ5ó “ng½3:–tÄž°7n/4]3þë»Øåzež+\}"vì;'d+ݺŸ€qÞ Ž]€>úOÐP"„8 à€Çã9 ‹‹‹‹µåË—GlFfæÕD4@‘H$- ‡8…ÑyâK¿eæÃÑ™¤;û Š¢¼Ú¦M›6tá…6Ùµ5Møqã¼K—.ýqQQÑÈüüü)cÇŽ=\TTD^¯Wà#GŽÐ¤I“DNNª®Ì'ADÙÙÙAMÓJu]?àueæÎÚˆ%£M«µÝ^/nv£[ ººA”»ŠáWü¨P+àWý¨pU àÀÅ.ø„^Ýk? ðè Ñf.BäÑQf>ár¹ŠÚ·o_‘’’¢[Eò$’ªQ{Çó¬¢(7Ïœ9“çÌ™£¾òÊ+øê«¯¾eææ¿ß{¼Î²P°M«è̼Îñí "Úànf>­1IêǬ̘1c†òá‡öð€¡ãÆ«×Î^¥¥¥øä“O°}ûv\{íµêðáÃ/KOOÿnÍš5óâââ4hPÅ©S§hÁ‚üùçŸsrr2gggszzzˆí}ùòåbÆŒ'NÅÇÇëÌ€a ëcÒõ‘Êa´_Œö*.DÏ@OìòíÂØº0þ%»`TiuÁç lƒ\U‹»5·n›¯›‘󮧺¢ï¡¾ˆ­ˆÕT(€ae;@DÇ***JΜ9lŠ"0fÕO€‡<.7Í$IK™Ë‰h€§aä K¢DUa>gΜ¾Ìü =ztDììá––†^½zAUÕžÅÅÅÿY¼xñç]yå•{‹ŠŠô@ Àyyy@Ô´Np<ëDäÏÎÎy<žÃ.—«®ëtbæŽÚÀ(6kzèj*ïV Újmë:ÄÙ¬Mà7çßÓf¤ü¤¢Èår•3spðàÁB sIm˜ÿ>gz<žçúõëç;w®rÙe—9±>;aTy¿—ˆF3ó®ækK¤ÕZÜk‚ˆ.0À Ìün´Ç#©NÕ yÙ²e±………9Ì|wß¾}iêÔ©×Î^………X¶l²²²àñxªªbçÎùß|óÍo®¸âŠ%š¦‰ääd N;QFF†bÚÕÚQ²™oÖ @ŒhzƒWTcîc8î=ŽS1§ anå›9z5k{Õ†o$–'¢sQgt-ìŠø@¼ ÈÌ¥DtÀa‡u]?]XXX¾nÝ:½)ó†L¾ÀÌ|CS]G"‘4ç⺠ˆè%2ógÑKkÁigûí·öïßÿGf¾³Gâúë¯wÇÅÅEe\Ÿ}ö¯^½ñññ/^yå•PE÷ûý¢K—.Â*&‡:lï€a1ÏÉÉ¡¯¾úJ‰w•––z‰(–ˆˆ¨3·Ð@Œ"çÌ_Sé7 T„;:§CÀØÞQ3 !N»\®]׋”QEYY™–””¤/X°@¦ Kꄈ.‰‰ySUÕKŸyæ÷­·Þ4{òÉ'1gΜ¯JJJ†›Ç+f˜oiZ½£¹ÕGÐ0óV"iÙk‰èZ™ù`”‡Öê©ZøeÛ¶m´páBÛÎ>}útêÕ«WD¯Ù¶m[\wÝu€`0ˆŠŠ ÄÇÇã /ìÔ§OŸw×­[÷m^^ÞMW]uÕ!MÓD °„zv6"Ò³³³ËŠŠŠ*\.W1å !º’Ñb¬#€x"òÀøwVDÝË^ô ôDÏ@Oh¥òÝùÈ÷æ£Ü]¿Û°° U„ØÜ‰ ^6ll1Z ’ʒй´3¼š×²³\àÕuý€Sn·»¬´´T[·nÝYWk¯föÑuî#£|°I/(‘H$À\Xžð1ýDÖ³iZœk~ýúÑM7Ýt½®ëóxföð2³Ë¼† ‚™+Eñ3³Ÿ™ýÖkÌTE [ÏÇׯ¼òJ–‘rI8Q‚Ûí~LQ”ßÜpà âOú“+))©ÚqUצîú§yŽXßÑ¿<ÔZ…ºŒ ×½` €Ÿ0óÇÑOkŹS¾mÛ6úðÃÓ4M{MQ”ÑãǧáÇŸ•=V®\‰-[¶à¦›nB‡ì×O:U¼|ùò·RSSëÑ£‡ßëõ꜒’"ˆšª¸Æ¯x<Õãñøt]O$¢ŽDÔ™™“`ìŠÇÑÞÇYÞ­Q)ˆ ¥L ¯ðÂ#<Ö¬ÝtÆîùÅN8®ëú ·Û]äóùΜ8qB‹D5‰DÒz9ÖµAD©2s€ˆ.°[n,FŽªî¹Å‹÷)// ÀÈÉ“'.\è‹ yÏÞ½{±hÑ"œ8Ñ´5¨’’’™™‰Þ½{WûÙÑ£G1jÔ¨²ãÇéØ±ã “&MúÁåré@@ÔµN纖°ÏÎΦ½{÷RBB‚âñxHUUE¡(ŠBD¤èº¯×K𦱦iLDB×u¡i}úô¼`Ág^¼DÒ ˆh†×ëý[Ïž=ãçÎë3fL­Ç>ñÄxüñÇ×—””Œ¨å\#aò¾…™77Ñ[4R ×Ý`3ï5óžå_®f¢&;{AAÁCÌ<«{÷îÜÜܘ²²2,[¶ M:!-Z„„„Œ?^¯'NİaðvíÚà}÷Ý·÷äÉ“6lØREQôŽ; MÓê´³9-l^¯×­ëºOÓ´EQÚQGfn Ñ´Å[ùfµu-­IÀ[ŸU€‰H˜ê3ëD„!ÊKœp’ˆ ‚Á`1€3Á–`g#¢.æÃè¶ 2I$ç(ç˺ >ˆh ŒÖ[ªÔ¹‘4ç&ý;_wïÞ?2óÝ]»v |üñǾÁƒ×ú^]×±~ýz,_¾@ ¢ãòx<˜0a†^g®û®]»ð£ýˆ:T”˜˜xëÀ?…oˆí]"i‰QZLLÌëÌ<êÑGuÝu×]p¹ê6h×'Ðk¸ÆÿÈeæ¼HŒù\@ZÜë™ÿåøv¡YDîQÓ$ij±³_«ëúË^¯·Í³Ï>‹Ûo¿=Æ:¾ÿþøä“O°eË–&“¢(¸òÊ+ ÀرcaÙvFí^½zuß?ÿùϯ½óÎ;›‹‹‹o›8qâAs—œà°³egg1ŒMr;‚mÚß+””––æ»Ýn— £j¼ùˆƒÑC=†™½0òμV¤ˆ³Ø\ÕB/…Þü[[¹¢(¥DTJDeŠ¢hmÚ´ÑåU8£Hݳ€ˆ¬ð.‘HZ2“üFmI#©ÁÎ~igOœ3gî¹ç_}çPU£FÂ¥—^Š¥K—â»ï¾‹ÈØú÷ïiÓ¦…Uó&-- ›6m¢òòò¶/¼ðÂÜ7Þxc×Ñ£G_¹öÚkçêáÚÞ%’–ùTU}PUÕû§NÊýë_]Ý»woŠë(.ð(]Ï̹¿H DFÐ]à5aæ¢=žó‘Zì쯕™™|ï½÷|^oÍ…s÷ïßÜÜ\?~¼IÆÖ¹sgdee¡S§NHOOÇC=„™3g†“ŸŸ;î¸ãð·ß~ûAjjê#=zôð[ùé ±³YÑõ#GŽÐÑ£GUMӟϧPt]Wü~?P|>Ÿ\.—Ëm u…™3kÌt¹\A]×…Ë岊º UUEII‰ˆ‰‰>ŸO¤§§ sǾIî[$ £-Ý8fþw´Ç"‘HÇù¸."z @€ç¥í½~ê²³O™2%¸páBoLLL=g©™~ø¹¹¹¶½'%%!++ gSóæ©§žÂ<Àñññߎ=zr×®]ý‘²½K$ÍM÷ù|¯%%%u|íµ×¼Ó¦MkÐûüq<ñÄaGÐÍkްÙ,^܉™ó:îs )ЈY¼@3óÌÀ¨2]íqëÔgg_¼xqÌ%—\Rïy„øú믱lÙ2øý‘19ø|>Lœ8C‡µsÝçÏŸ™3gbË–-¨©Çêš5k´{ï½wï‰'r¼€Þ½{w½>Û{C°vØsrrhÅŠ”””D¥¥¥€üü|îÝ»÷y™SFDmYöÊ”HÎ9Î×uA}Ñ žð¿Ìüûh§%Sƒý!fžÕµk×@nn®oРAg} !Ö­[‡+V„çõz1~üxŒ1""5oòòò°xñbý½÷ÞÛ{êÔ©œnݺ}Ú©S'¤× I$!¢>Ÿïoš¦M{ðÁÕûï¿>_½F–j˜}mIIɨFŽã+5“ngæïsŽ–ŽègåàgÌüy´Çs.†Ýuûí·7x6,--ŧŸ~Š-[¶4Zœ €)S¦ >>¾ÚÏ;†.]ºÔú~]×ñüóÏÍ›7oKQQÑí£G> (Š®ªªž’’"Ž9“&Myyyr—ŸW\q† ¶mïÞ½˜:u*FŒyóæÕzÜÚµk-ÛûìaÆå!íl ‡Œ^ñ]˜ù@´Ç"‘H§5® jƒˆ~ @T)DÛ"!¢¾Fø'3Ÿ‰àykµ³×W=Ò!ìjïìêìMÝÂÕ 3cõêÕúwÞy`×®]k† vWûöí+ä:A ˆ¨³×ë}^Ó´wÞy'fÏž­Ôä&m sæÌÁ“O>¹¦¤¤dt$ÎGDw˜àf~$çŒ6²Š{dÙày<å±´8j±³_£ëú+^¯·ÍŸþô'ºí¶Û"¾U®( † †K.¹Ÿ~ú)6oÞ\ÍöND8p ¦L™‚¸¸¸¿wïÞX»vm½EgFŽéZ½zuŸçŸþoo¿ýöÝ–°? YÅ5|˜9à€ùy.ÛI$’s f~×úšˆ~`€ßµ4Û; °@§|sVµ³_KDscccãgÏžM³fÍjxRëY¢( FŽi¯ FŽÙÜCa̘1ê³Ï>Ûëšk®Iùú믯k×®Ýdzϲ½C®$MŒY5ý×空çΫD2hÖ0ó_ˆÈ¶º›íy‹"¹¡ØÜHA˜ù‰è­­@DÝä˪­µÚÙ_#¢QW^y¥6þ|OmÕÙ#E\\®½öZ\vÙeX´hŽ= èÚµ+233ò³×FRR’Ýví‰'ž@vvvÅãEÁ¬Y³úÓŸŽ¹ë®»–®_¿þß=zôx¸wïÞgŽ9"€ÈËË#";;›ÓÓÓYNÀµ2À "ÊbæÏ¢=‰DÒü˜bïAÛø|ËÌ£;ª³À¼ `z”ÇbCD=ü @gµ?†ã,zU÷Ü3Ï<“Þfæ!£F¢‘#GÂåraÏž=HMMį6ùùùÈÍÍž}û;wî´»¶4'{öìÁÖ­[qÛm·Ñ¡C‡b;uê´~ݺuÿ:uêÔ“'O>"× ’¦„ˆ†ÄÄļéõzû<÷Üsž›o¾ÙÞjé0ó!Ç·˜@D·3óâhél÷&„ˆè à̼1Úã©s×ì îH} k²³<¬ëú¬ .¸ "ÒvöpB`Õ*£lÀ˜1c"fcB`æÌ™Xºt)–-[†ôôô:_·nþàƒÈÏÏdàÀHÛ{à ¢û cæ³OŠ’H$MJS¬ ˆè%'<`#€[ÜàÌ|:R×iˆ¨3˜z0óú(Œ¡=€ÿp Q~‹ÙÖè]˜ùw<¯½Iÿü#nÏž=9Ì|Wjjª2mÚ4´mÛ6äø~ýú!##‰‰‰g÷ ÕCEE–-[†¯¾ú BˆŸY'¢©EEEX²d òòòªýìСCxçwØçóýcäÈ‘w´iÓ& × ’HBDíÜn÷Sº®ÿâ–[ná'Ÿ|Rm×®]“]Ï´¸¯.))Óç'"/ŒaeÌü|S\£©‘ô¦åfOcáÐÒyÀ/œ!¢.Ì,ê{CmÔbg¿šˆÞLHHh“••E7ß|sLß¾}#3òÀÌØ²e Ö¯7Ö> 4hPDv EÁßÿþw¼øâ‹õ–Ñ4 ê„ z !ÞX¿~ý=Û¶mûÉØ±c¥-L˜ù"R£=‰DÒüÑd0óíæ÷IÞ0@A4ÇÖ˜Ùó$s‰èmfþes]ß\Ô>`ŒHþÔ^#ÏYÕÎ~ ½ß&##ƒÒÒÒj|_^^víÚ…qãÆaÔ¨QPÕÈÿ™ß²e >ùäXíI«bµcûî»ï0uêT 0 âcÐukÖ¬Á—_~‰`°f³e÷îÝ‘™™I›7o¾I×õ_ýõ=ϹND"ºÙëõ>Ÿ––óÆo(C‡6}ýݦþh2sŒœtÍÐÀSfšd‹çœŠ ›v«gü ÀÕÌü÷H»9 ¢›œh‰v\"º@>€lЇ™w5ò\Uíì©æ !†Ž;–†nO¶mÛ¶EFF.¾øâÈü"õpôèQäææâàÁPä\€¬¬,tíÚ5¢×+++Ã矎«¯¾:äõ;wbñâÅ((]C–——ŸY»ví111÷¤¤¤”Ë*®áADü?f~&Úc‘H$5éu=€Ìð7ã\ÎÌ›"qþhBD`¸ƒ^3¿§¦ú{OD±Â(rûg;`¸n±ª"Ñ? 7‚^Cuö ÀÛBˆ¡£G¶íìáСCdffÖ˜6ÖŽ?ŽÜÜ\ìß¿¿AïëÙ³'²²²Ð¹s爌c÷îÝX´hN:Õ ÷œy÷Ýw÷µiÓæ† &”ëIc ¢KbbbÞ ¢AO?ý´û׿þu³E|ì±ÇðÔSO5Y½*D4ÀË63óõÍqͳ圉 ›ùÜÿจvÈ`懣:¸ð¹À[Dô3?íÁQ&ŒÉø€eÌüYÅYü¯´6;»bVß¾}•)S¦ M›6!ï),,Ļヒ´´4LŸ>:t8Ëߪfü~?¾øâ lذ¡š <ˆW^yC† ÁW\HµuY³f ²³³1{öl<ðÀ(((ÀâÅ‹±sçΙ:uêOvïÞ=uíÚµ÷÷ïßÿ=zQQ‘®iµk×NàœœEeää hà!":ÃÌ/F{0‰¤YHÐFÄùN+a䡟ó0ó[€ˆ¦x”ˆ~ÛD¶÷xS\ËÌÛÍkÕp\X3j°³?ÌÌw×fg¯S§NaÞ¼ygm{÷ûýX¾|yvöpØ¿?^~ùe 6 &L€Ï׸ZvuÙÙÃAQ”˜ØØØ‹:´uÅŠw]vÙeoC®$aBDñ.—k¶¢(w^sÍ5üÜsϹ"µéÔ@ší3ÉÌŸQ?É@D‰Ú2sÃvéš‘s&‚næ?Ígæ…Dt5€Xà7̼-×hˆèb:3OD1Ѫ0hVf½@€Á0>¨l ô3.bæšUdõsÕdg¿JQ”·âââÚLŸ>z÷î]ïy\.F…±cÇÂív7ò7 …™±yóf|úé§(++ ë=±±±˜2eJÄlïK—.ÅgŸ}†¬¬,¬Zµ š¦…õ>UUÅúõë·›¶÷,;[JJŠ8räOš4IäååÉ]r"º@:3?í±H$’êDr]@D#¼àK?efˆ4Î¥5A8Q[˜cno©ûœvDžˆ:8 ½e³'¢½îqDлÔWa¾;ûÕDôVlllB]vö†àv»l{·ÒÚ>ýôÓZíì %>>S¦LÁ€Â^'„cgo@¯^½Äºuëò¾þúëYÓ§OÿJ®$uAD×û|¾—»uëÖfîܹÞñãÇGef}UIIÉØh\Ÿˆnð4€‡™ùÙhŒ¡>Î%~À8#`ÜÔ£Ì<0çŽDô6€.þ›™h¦kNðc¶ÀŸ¸ À[ÌÜÎüyƒzCììá’˜˜ˆŒŒ ôëׯAï«JmvöpéÞ½;²²²œœ|VãpÚÙ ˜˜Ø Qyy¹íÚµx<žY©©©Òö.‘HÎI",Яp€•ÌüùÚy)Ð-ˆ(–™Ë‰(Àõþ¯¡ïÍ`ÔÆy˜™G ×sΈÙÙÃ¥C‡˜>}:êýÇŽâE‹lg—ž={"33]ºt©ó¸]»vañâÅ ¶³‡CYY^xá$$$ìîØ±cÖØ±cÈu‚Ä ¥ú|¾×„cyä׬Y³"k >ú(ž~úé¨ t ¢«\ÖR]Øç„@'¢Å0ì^_ÈðfnúJMˆiÛÿŒœˆfù€Ñýn0˜™ ‰h<€…UúQ‘È#uœ§&;û…÷ôéÓG™:uj5;{C¹ð ‘™™Ù`Û{}vö†@D¸üòË1iÒ¤ÛÞ °hÑ"|ÿý÷ökóæÍƒªªÈÎΆÇã û\ªªb÷îݧ¾ùæ›ûÓÓÓÐeך!¢!îpc$"M‰$2œÍº€ˆ\0 —˜y©ùÚ{¶;z€Ûtð/Ë®}¾AD—X`7€+˜¹<Ì÷ ƒQ@ï}Ù0Ökèð(3­çœ!vö½{÷>,„˜•ššJS§NESV‚€‹/¾Õló~¿Ë–-Ã×_}Öë€úPC‡Åĉ«ÙÞ ±dÉlßÞ´Å£GbÅŠ¸ôÒKOïØ±ãÞôôô × ­"ò*Šòý>##/½ô’ûlZ G S ¯,))í±¶«ø³˜ùp´Çœ;9è÷™Ï¿gæÅDôÓ¨Ž&B˜‚«Ého" ˆ™¿Ô5j),³…™ kyK@_fίí|@Ívö„„„°íìá°{÷n¼øâ‹5jÆWïn_cììõÁÌØ°a¶mۆɓ'cðàÁõÚÙ‚Á V­Z…Õ«WW³³_ýõø×¿þ…C‡¡!÷I×uôêÕ«Ã…^øêúõëïÞºuëOÆ·WVq­‘}†Àè pot‡"‘HÎ"а@Gýˆè2fþO ‡®ƒQH6ç|çÀ̉¨/€–8'"³:1 0óØíïaÔœÉ47çóÌ£Zº`0ómu¡;û›qqquVg4Û·oÇîÝ»1vìXÛönUgÔ: >„X¿~}HµwËξråʈØÙë£k×®øñ íû÷ï?÷µ×^{¶¤¤ä7W_}õ"¹NhÑTŸÏ÷z‡:½úê«îÌÌÌh©%s@"€•D”ÆgÑÅ*Rœ+ÝJÚÕQ4ŽÂ4÷Ñ}ÌüÂÙž—ˆ0‹ˆt»ìõÚþ̱Ô*ΫØÙ{Ãè—:tôèѲ³×‡®ëX¹r%¶lÙR§íýlíìõQ^^Ž>ú›6mBff&ºuëVãq;vìÀâÅ‹QXXóþG\\fΜiOŒš¦5Èú§ëº2dÈ~éééëÖ¬Yó¡×ë½+55µüÈ‘#z yyy@dggszz:·Æ ˜™O˜ö×èù·$ID "€·`Ø­7ÑV…ájèW(n ïØèñþ Q_Ѽ `(€‰hŒ¹ÿ›•³›ó:€¥Ì¬›ó„f¾V'UÝsÏ<óLjyyù\"=zôh5jTÄíìõ ñÅ_àØ±cèܹ3–-[֬׷(++Ã|€ÂÂB?~Û¶E'Ó"(Ý»wo¿qãÆ¬\¹òã´´´_¦¤¤ÈuB+ˆºù|¾—\.WÖ=÷Ü£<øàƒ©ÂÇ‘¢¥}Ô˜y7€iDÔ™…¹q9œ™×DkL-R “Ñ2)À*fÞ£4þSŽC€žDt «û¬( 3â0óƒD´aL’õADOÃøÿ{€×aôdÿŸ=ƒÐ{[Ó¹B&ä•ôÊG IDAT+WúN:•#„˜Õ§OeÊ”)®ª.EEEøç?ÿ‰ÔÔTdff¢cÇŽ"kg‡C‡áµ×^«f{?}ú4/^bg¯ k2ŸW\q† EQÐ¥K—:7Ê›Š¶mÛbúôé¸è¢‹ „@JJ ¾øâ øýÍÛX W¯^øè£pòäI”••M¿çž{Ö,]ºtîСC_q»Ýš\'œŸ˜i@wºÝîdž®¼úê«jŸ>}¢=¬ºhqŸ5f>d~9Àr"šà—ÑXC¶¸t" £B»à43vä §2ó^"ê `€oü3ÙÑ· ˆèQUÖï¯Í’NDýaä}éx-€‹™ÿ`~ÿ!Œþ¥¿5sЇ1óµa\¿&;û•DôV|||b$íì AUUŒ5 :uÂ’%KšÍÆV•ØØXL›6  ªÎîä³Ï>Ã÷ßÛn»­Ñã].—XµjÕöíÛ·ÿ¿ñãÇï–U\+1#é³aì„F¦|¯D"iµ­ ˆèn±Ì<§†÷|à¿`Ð/¼l¹ÎˆèM2ó¿«¾¯5aÙÙÍz€w`ÂÌ{ÌÂpï1s7óø |`rénÎóWµ³_%„xÝív'>ýôÓêwÜaO^M‘jVǸ0`ÀL:qqq!?«+Õ,Ò¸\.Œ=cÆŒ©–ŠWVV†O>ù[¶liò¨a\\¦L™‚†¬'4MC×®]EYYY™ËåúùUW]õ©\'œ_Ñ蘘˜7ãââz¾øâ‹žo¼1ÚCª“GyÏ<óÌ—%%%ã£=–Ú £-Ûÿcæû£qýA'¢Aî àRk©ÕbŠôváL,ç8ï˜ #§î–Z޹À&³hÌßÀh9“ê8Fëø¾¢¾ ×dgƒ¯sõÕWk¿øÅ/hãÆÍµ®J§Nзo_´iÓ)))Q³‘õìÙ)))HJJÂ÷ßÇ^WbòäÉ5jT£Å¹YœF¹õÖ[ÓxàE_ýõ’:ü>--­LÚÙy«¿‡Ñ¢è®(E"‘ÔÌX˜=¿Ñ%.p7€ß2óq3÷Úê0ò«Z¢î­"j#o2 Fá·c 5Fïßd"à$€¿ÀnÔ¹†ªÍÎ`Tff¦>þ|WUë,aРA¸øâ‹›ÔÝÖ¥Kdee¡¶‚Wn·'NÄÀÃv·5†>}ú`úôéhß¾}?‹‹Ãu×]‡Ë/¿¹¹¹8v¬ÎNuBQ 2W\qE½Ù].vîÜ©Ü}÷Ý §OŸ~yË–-vèÐá2=î܇ˆ’<ÏŸE¹éÖ[oÅ£>ª$$$D{XçÌœ£à0ˆ(ÀßLm®ÍA7íë¥Ì\Våu7Œ¾æc`äŽ>@1£¾!ô&p Å̃ˆcæb"JàwV$¢`,n^BeŸÔlfþ‰ùs°Fá·] `OÕûï8_5;û‰'bæ{{ôè\¼x±ÏÊÏÏÏGnn.öíÛ×T¿~111¶Í)h÷ìÙƒE‹áäÉ“Í2ŽöíÛ#333¤½ 3cÓ¦MøüóÏQ^VAÝj,[¶ 1111bDXǧ¤¤ ++ :u²_Û´i“¸óÎ;8qâÄS ˜YÅDÔÆßœó}CO"iÑÔ±.X£xiN•×ï„aËîÇÌLDYnð f>ëT°óÓQ¸À6o3óóDt €_ÁØÜ_ “yL"€ƒ2ê«IãܤŸ?~ìŽ;bæ{ºuë¦åææz ÖøŽ;†ÜÜ\8p ±¿bUíìá²cÇ,Y²G»ví‘‘‹.º(ì÷!°aƈÚÞ{ô謬¬zÛ¼9Ù°aƒÈÊÊ*©¨¨X8a„»ÝÿŸ½óªLßÿýž2“I!‰‚@B‡€ô"½×ÔAѯ¬®»–U±¡ðSX×uE@w×]± «XÀ‚"ˆ]œB ¤)5ô’P$!™L?ïïSæÌd&™I&r>×u®I&3“wÍûÜïs?ÏÃóΦ'\oHe+Ót:Ý}ûöå/^ðÿ“ùóçcÁ‚ÊËËÇ5ôZÒ À@Ji½5¸¨WN‰„x²Û‡Ršçõøñ¦@¬-»b3“½ÒÏ¢ ZÛÆ7å ›òD«ßtJé焹æ˜M)](=f1€‹*{û4ˆŸ÷3Õ¼v%;ûO?ýdt¹\Kt:]ôÂ… ¹ÇÜgšwÿþýX½z5ÊÊÊBõV½×†þýûcÒ¤I•ll2.— Û¶mÃÆa·Û}>¦¶ð<¯tŠõ×§¢¢k׮Ş={‚¶³8qß}÷’’’п¿‹ŠŠBRRúôéãóç”R|øá‡e}ôÑᢢ¢éãÆ;Î0ŒËjµ ={öt5U;!DñïpC¯EC£)R~/€3·@œÐðLcè°Ûbª fÎ{È Y SJI߇Cüì6C¬§¼ZÅkzÛÙ'Kvöo;{ PJ±oß>dgg£¼¼f•FUÙÙÅétbóæÍ5.IÄlô¨Q£0jÔ¨7à …í=22‰‰‰èÛ·ox}ôž}öY}V¯×ÿßÈ‘#µò¸ëBȃÁð)Ïó=Þzë-þᇮ± ³¡¸ÞzCPß=b+ûö>ú›!f‚?†Ø< RzLÚ0˜¦~b.ÕOpÀg,ð%¥ôé1éÚI›qnìU}v>ììÇ'FÞyçÎ+V誛Ùm³Ù°aÃlß¾=¤v¶Ö­[Ãh4¢mÛ¶=¾´´YYY!·½û›µê‚‚˜L¦ mï.\ÀM7ÝäsF:Ã06lÆŽ ½^_íkã¹çž»°sçά-Z¼Ð¹sç Žã\v»]èСƒ@8tèíÙ³g“Ø€ !O@¬c½6’9—M‰ô0‹Äø‚RúU=-õº€ÒbBã)Ji3ÕýB,íéÑQ·RªCÀròÃëµ<Üs™™™Ìfó'FFç7ß|£óeŸ†šÎ&¯ÎÎ,ÅÅÅÈÈÈÀÑ£G«°Šnݺ!555d³Ýóòò‚¶½W5s=XŠŠŠ`³Ù0gΜ 6lØÁqÜßF}¾)Ç BH4Ïó¯¹\®Çzè!úú믳7ß|sC/«Fh½z“@ÿÀ Jé¥F'‹dRJ¬“^‡Bþ `:Ä&zƒhgÿ7€×)¥ïKyâgéq÷SJ}Wcgwfffê»wïÔ‹ŠŠ`2™púôéš¼Evö@ •íýæ›oFZZºtéôskc{w8ÈÉÉÁرc¡ÓéбcGF´hÑ"èuüöÛoÂŒ3N_¼xñõ~ýú­Dµ½K–°tˆÓ!þÝÐëÑÐhj#Ð !Í)¥õS·t!5.b忏Ryº—@ 1[~ Àó”Ò¥>^g6Ä1kCeg”‹/Âd2áìÙ³U>®¦vö@9zô(222ªµ½ÇÆÆ"55ݺu ù‚±½·oßF£-[¶ ù:’’’hNNŽõÑ„ ^D+„?éõúÿuîÜ9ü“O>Ñ:´¡—T+$¾¾¼¼|BC¯¥±Ò˜z6ÄÆpß`¥tM,î:„òD+Û—ògJIð3€ß(¥CUq¬ÏîöÕÙÙßyçî±Ç«•_æÀÈÊÊ ÚöNÁ€0iÒ$„‡‡×f µ²½ó<1cÆ`Ĉµží^Û{EE¾úê+p‡ï¿ÿƒ®Õ(¥øøãË?øàƒÃW¯^>zôècMÑöN £”ÖïÌ tBHsˆÎ°bzƒŒP ’›`%€Û¥»¦RJ¿ó#Г줔ž÷ñZ Äyé0uÞ¼y&•} ÏóÑ ,`Ÿyæ™:õÍîÛ·«W¯®d{…=Pª²½‡ÂÎ(UÙÞ###‘””„º¬1ü18`Ù¶mÛþ«W¯N5jÔqÍöÞ°Bº †O!_{í5~úô鵎IóæÍÃo¼¡ ô*hLý6¯CœqúFu Lš„!³çŔҙªû·Al—±¹Þ÷´ŠqR¡°³ŠÝnGNNvìØ—«úê„øøx¤¥¥¡M›6!ùý2ÁÚÞ{ôè”””ÏvÆöβ,úô郼¼<Ìœ9³ÚÇJII fÍšuaÇŽÙ111/Üzë­åMÑÎ&e¡ºi ú£¾b³ 67û¸©ÖšBõ{'„è|à+ˆÍtwXO)½_èë!6€»ƒRº"€×'À²ìðððð\JéÍf³ù CŒF£såÊ•¼wwöºÂjµ"''¿þú+A¹=P¼mï¡¶³ŠÚöÎ0 † ‚qãÆÕÚÎ ÅÅÅ;v¬ùÌ™3g[µj5eäÈ‘šbœÐBÂ9Ž›K)õÿ÷táÂ…\ëÖ­zY!CèÕÓà]êLîðìM"Ž—™` ¥tƒtߧʼúëføâø”ûÿüsôìÙ³îÞ¤Ξ=‹ßÿ“'O®;{ ìرÐbAðË/¿ _¿~hß¾}ƒ¬aË–-xä‘GPQQQ=»GËÑÄ℆‚rGXXØG­[·ŽY¼x±~„OÃj½zlºÔQùyˆ]ÉSj©óúkæB:ø²©IÌðJ©ƒˆ£êÌGÕeH¯åËΞær¹>‘ììÌc=V}DZZТE <ôÐC8xð ²²²píÚ5ù}†ÌÎ;wÆôéÓ+ÙÞu:ÆŒƒáÇ׹uˆ‚¢G•lïÍš5Crr2zõêUéyßÿ=,X€Ÿ~ú ãÆÕ¾§F¿~ý˜M›6uZ²dÉ‚÷ßÿá+W®L3fÌQÙöç*,,$'NæÍ›§œÜ(0¥t)!¤/Ä&Jš@×ÐhXC—z! !¤ÄÙäÍ ¶´—~`•F§J´ð&Pq6¼ÀQZÅZïîìúÓŸŒ„¥áááÍ’““ɰaðmÛ6äææÖ«@§”â÷ßGvv6Ìf3œN'’’’Yokľ/›7oÆ–-[ˆef£FÏóõºŽòòrÅê~äÈ$&&¢_¿~õÞ¥»k×®xþùçqìØ±ðˆˆˆ÷Ö¬Y3ûÀ/ÆÕ²í€p#Ç õ !¤£Á`øP§ÓM˜3g3{öl*Gk#Eûï¤ *ƒþ€ç 6lz…ú™ÉÝðae{ÀhJé#„ö@œYšEYqc~À­”ÒwªyíQ Ãt|ùå——©íì„‘wÞy§ó«¯¾ÒÒ <”ØívlذyyyHIIA|||½þ~ÙöÉÉÉ!·³JAA233Ñ®];¥!œ?Þxã téÒS¦L éJJJ0{öì‹Û·oÏnÖ¬Ù =zô(Óìl¡Æ_½)C¹À«ÇÇ­•„ô1(¥y„{¤RJ”Ï8 kUÓYT¯ïݽ£ÃáXæt:‡ >#GŽT꫆ÁСC1nÜ8|þùçˆŽŽÆ½÷Þ[7oþg¤ëõzŒ?C† ©—lú‘#G‘‘’Ï)¾111HMM jÖyM¿þú+Ö¯_›Íæñ³šÌ:¯)6›M)MTwÚß¶mrrrмyóÃݺu›`Öâ„Ð@Ñ1 ó<Ã0/Oš4 ‹-â;tèÐÐ˪SæÎ‹7ß|s]yyùĆ^Kc¥¡2踧©Ö™q&éŸLKéJ)¥ÒÈ”GŒJé^BH)ÄÚ<Øà#NTñú†‰á½ùóç'±,[@)•@,X@n¿ýö9–s:°Z­°Z­•6¡úÄáp(SGƒ­Ãf³Áb±ÀjµÂétV)ÐgÏž­|››‹`;ìû#&&3gÎlÙ­[·û8¶cÇŽù={ö\ ÀUZZêr:$66V@çÍ›'¢àFÙ|¥¸³ÖÙ]CC£ `7¥t­ôýÝþ§*ûK…覓y À»Šs/;ûpÄÚôÑÎWw˜èÃΞFù2,,,*99™:.,::'NDß¾}qéÒ%DDDÔª«º·=PäŽî¡²½;lÚ´ [·n­Ô¹ÝÇaĈ=ztÈlïj;{ DDD„Üö~éÒ%˜L&œ9s&àç©¡×ÒX©WÞÔ!„| €xšRz2bCœdéç»L“›VBž†(ÞÿÀk{wgï@Yæt:‡Œ5Š >»ví¡C‡”9š Ã`ذa;v,êÒê^]÷rN‡±cÇbذauZ~øðadff¢´´ÔçÏ«ª.— Û·odž üŽ~‹‡Ñh¬Òþ¿}ûvL›6 ëÖ­«ÑLT›Í† 6`ûöí>3`µZ­›7oÎðôn{'âÃ_)¥Gz-7*Z\PI ÷p¢;î#á»Ø·#âØÔ5ûËü‹Rº°Š× ØÎ(r'ñŸ~ú ™™™x÷ÝwkTfuþüy˜L&œ;w.è硳½çææ"33³’=Pbbb’’R+[Uvö@iÛ¶-ŒF#jÓÙÛf³aýúõJý`(++ÃÚµkqùòe¡S§N?x¦©N… B àžç_2d³xñb]}”P464^=š@¯G!W Î.]Oá,0ŸRz†  bï'i¬Ú?!vr­rlàÞ·lÙ¢/**šG)Õ¥K&))I©¯–meeeeueQQQHNNFïÞ½Cú~- Ö®]‹Ý»w4ÿ»E‹HKKC§NBºŽ+W® =='Nœèñ:u‚ÑhDóæÍCºŽS§NÁd2ùí"¯Fn&7qâDøw#‚¨X­Ö€moĄ́ç8'Nœ¸ºgÏžW{öìùš@WBO)m¸º -.¨Œ$Ðïp¥t—tßÃûÌ}r6mÚ´Úår=5hРÒÂÂBb·Û…ÇÂÔ©SiÏž=©|˜pmÀ ÄB’™9¬¡¡¡Qò¤ÿþûïS|Êó|Ì›o¾ÉNŸ>ÝÃ:Çó<&L˜€þýû×IÕºpÛm·aâĉ>»·GFFâ®»îB\\¶oߎ¥K—â™gž©Õìr_ð<1cÆ`Ĉ>]„-[¶Ä#Þ¥K<ýôÓØºu+6n܇£n&x¶jÕ F£íÚµ«ô3ù ¿gÏžX»v-öìÙSgñ[BBÒÒÒ*õAèÔ©Ž;†•+Wr.—ëÅwÞy箋/>?~üøÝ²í=..ÎUXXxÝÄ „˲/±,û·””áÝwßåê31r½C)­_‹Ëu†–A¯G!_xÙ_™/;»Ãáø„R:jêÔ©®/¾ø‚×ëõõÖ™Ggäççãü#ŒF#Ú´i ~;“ª¹õÖ[‘’’‚ØØXbs´ 6`ûöíõfgó5‚­¬¬ YYY8p Ú©8!ÃWÛüü|˜L&åà"??+V¬@÷îÝa4ëdÞlËÊÊð·¿ý­hÓ¦M9áááÏ 4¨T¯×»Š‹‹éõÚÅU*I¹D)=ÒÐkÑи^Ñâ‚êñæRð)¥tÔ”)SœË–-ã} coŽ9‚ŒŒ Û;#++CXY f3kd$*"#aŠ‚PˆŽ‡Ñh Ø%222°ÿ~X,œ9s&à†¯UÑ£G¤¤¤ ÐQUV«ëׯÇÎ;C'ÈvöñãÇ#,,, ç”––"333¤¶÷°°0L˜0ƒ ø¤  &“)¤¶÷˜˜¤¦¦âÖ[o èñR·wq¨U«Vw 6¬èzêöN1†……}Ô²eË›?þøc}b¢Öë,^zé%¼ýöÛYf³9¥¡×ÒXÑz#À—ýâÅ‹s(¥íر£3##CïÝüÅáp`ãÆØºuk6CQÛØöìÙƒ/¾øÿþ÷¿ûT]ÎöTsÓM7!55]»võùóÚÌø†êº¹Ÿ>}&“ EEEuºŽªfÀzÛÙŠŠŠP\\ì÷³«),ËbĈ3fŒÏ°¹¹¹ôé§Ÿ>›ŸŸÿ^ß¾}—à:¶³B>0À`e)U£ÅUãÝýàÁƒ/QJÿêmg—ÙŒÂU«ptÙ28Nœ@˜ÝŽ0§ ËŠÀ0 ˆô5Nž‡M§CiD Z·FaÛ¶prÂÃÃ1iÒ$ 0 F¨Íf3-Z„_|;wÆ”)Sü6Z­ vö@ •íÝŸ=PBa{'„ _¿~HLLDDDDÐϧ”bïÞ½X³fM­ür‡‘#GbÔ¨QÕ΂÷&333fÌ@óæÍÏ•””¼Ù»wïÏÑÈãBHû°°°\.Wâœ9s˜çŸžÔd$qSGèÕ£ ôÄ=Y„Ïu:]ô{ï½Ç?üðÃU¾Æ•+W‘‘r;! À¤I“ÛÞ½{qÿý÷C¯×cÏž=u]áy£Gƈ#ªé9pಲ²Bn9fºËåÂŽ;““ò†qÞãdª¢¢¢kÖ¬ÁÞ½{;ÛÉ“'WiL[°$$$ 55U'SË—/¯X°`ÁÑË—/Ï3fÌþ뱋+!$À*ÏSJ÷6ôz44®G´¸À7>º³§‚ð9ÇqÑo½õ÷ÔSOô:–óçQøÃ¸¼z5Ì{ö€8à,Ça° ãè, Â0`8„é{0Œ¸·3 \„ÀÒ¼9Z&&¢å]wAתU­Þç–-[ðÊ+¯`èСA=¯:;{0PJ±oß>dggm{ŒŒDbb"úöí[ë‘YrÓÙšØÞ[·n £Ñˆ¶mÛÖj @íFävíÚ©©©µëG)!Ë–-«˜5kV©Óé|&%%eMc‹!ø@שS§ú\ …$Ð3ÍfsjC¯¥±¢ ô‡½½ÃáøŒR:òøƒkéÒ¥|0§r¹¹¹ÈÌÌ I×øøx¤¥¥)vv5v»‡F¿~ý Š‹‹‘jÛ{÷îÝ‘’’tƒ™PÚÞ­æPÚÞ}ÙÙ%??_éâºlÙ2”••áOúš5kô:bbb’’‚îÝ»õ<³ÙŒ¿ÿýï—Ö¯_¿Q¯×Ïêׯ_ÉõdgÓÐШ=Z\à‰·{.++KéÎ>eÊײe˸@ìì– pxî\\øþ{°”‚cp, –eÁI¢œU s–eAT·²8Wºt B”‹2 týú¡õsÏAWÃÌ1 Æ »víÂ?þˆ¥K—b„ ¨Jèkg”`lï5±³J0¶÷šØÙ¥  éééMå‰EJJJÀvö@¨¨¨ÀÝwßììl¿®U«VõêÕëZcˆ!ã Ã'Íš5‹ÿàƒtwÞyg]ÿÊžüãX¸p¡&Ы@èõŒ÷†¼}ûv]aaáË’Ý•žž®«É=—ÙŒòÓ§±;+ §v킾¬ a Ja‡-<ÖÈHXÂÃQ »MÆ_WV|óÍ7˜>}:Þ~ûmüùÏVî¯­í½¶66™ÚÚÞ;vì£ÑXëQe§NBzzzmïUÙÙE¶½geeá§Ÿ~ÂðáÕ:þ@ðÕ•µ&9rD¶½¿ß§OŸÅhäv6_BæØN)]ÛÐkÑиžÐâ7¡°³;Ífzí5œ]¼ŒÍŽeÁ3Œ(Ì%®¾ŽsgÐ¥¯‰$àe¡®tB–ʏ`PJAYúáÃÑúé§ÁÖÀZ-sùòe<ù䓸ùçŸñÔSOU:0UP.\€ÉdB^^žÏŸ·k×F£­j騎ªlï„ôïß“&Mª‘=Pª³½ËvöÑ£G×Y—òÌÌLÄÅÅÑY³f9yòägƒ z 'BZéõúwœNçÝÏ=÷™;w.©ËÏ¿)¡ ôêÑz=Q…}©N§kˆÝ›’]»pá‡P¼~=yy¢…MU_¦Ô™©nAÃÀÎq¸üøxœoÓý òÛ•Õ”R,^¼™™™øñÇ+ýùd@¯säý÷qäõ×!ƒgðR¶œ“Å9Ç•ìí,lj"]è*¡N8ÎEWÙÜ+eÐHŒA¸8‘ÉÉhdÜâÍÁƒ±yófœ?¹¹¹èÙ³'Æò8 *dÛûêÕ«•I(JwöÚÚÙÅ—í=”vö@±X,•FévëÖ ©©©Aì×Aкukj±XJu:ÝÃÉÉÉëê+N „°žäyþ¿ƒ â/^¬“›àj„I g˜Íæ´†^KcEèõ@Uvö{î¹Gøì³Ï¸@mSE7¢`åJçäÀuù²¸ËÂ\-Ðeûš$Ô¡º%êr†åyèºtAÔ˜1h~Ï=¢¯X¹r%fΜ©l¬ÚÞëÊÆ&ˆía 6¬Fvö@¹ví²²²pðàA¿©=P ðÖ[oáÝwßÅ”)S|ZÖƒíÊ,f³ÿøÇ?ŠÖ­[·‘ã¸Y (n v¶êì9>¤”^jèõhh\/4å¸À‡½½ÅbYêr¹FÜyç®/¿ü2 ;»ËjÅæ‡Æ…ŸÇ0ÐI‚œ“E:Ë‚“9' qŽãÀHBQ‹tYœK·JŒ ݪÅ9TD¡N)(¥¤}{´ý׿ÀÔbϤ”bݺuøãÿˆ›o¾¿üò jüz5ÅjµbíZÑ5qâÄÛÙ¥´´èܹ3 ToÞœ?ÙÙÙ:t(¼×§NÂŒ3Àó|ñÙ³g×±,ûlŸ>}Jë2N „ 6 Ÿ†……%¼óÎ;ºûï¿?dïGÃ&ЫGèuˆ;ûJéó;vteddèý£WüÛo8ð⋸¶}»‡M®1“OÆYֳƌQßVWcQQˆ¹÷^4Ÿ:5è÷»eËüá@ûöí±uëVqéÏöÞ¼ys¤¥¥¡sçÎAÿ¾šàÏö*;{ ø³½‡ÂÎ(”RÌ›7_ý5î¹çåþÚte­ ÇŽ£Ó§O?›——÷A=>æyÞy½ØÞ !Œ6ËSC#0šj\ >¤_µjUØæ ‚0«C‡ÌÌ™3ñÈ#ä^3bÝ]w¡|ÿ~ðRÀ±¬øµJœËtNΞ{ tùây1>à8ŽcùÖÛâ¸Å¹ (]p:!DF"þÕWkÜDîÊ•+HOOÇÙ³gQTT„ÿüç?‡ uvH틼¼<˜L&ð;S¼:ìö˰ZÏÁj͇Ý~vû8E`Ù0èt·€ç[A¯o ½¾-ôú¶`YÏwJ)~ûí7dgg#..iii5c 5N§›7oÆ–-[лwo$&&å° ÈÎÎÆþýû±jÕ*Kaaáò‘#GÎBˆmï„XN·ÀétþåñÇÇ¿ÿýo&ØþGóâ‹/âwÞÑzh½ðcgO’ìì1ï½÷¨½üìYìé%\úåp@Õ5f²H÷²±É£T¼-l²å„€J'´Š-&7ýå/ˆ7.¨÷^RR‚ 6`Ê”)Ä?òêZ%Ùö^\\Œ1cÆ`øðáõfcS#ÛÞ„ÜÎ(jÛ{lllÈíìRQQuëÖaûöíèÑ£G­»²Ö”•+WZþóŸÿ»zõêÌQ£Fýv=ØÞ !YVPJ—6ôZ44;M-.ðagOfYv¹N§k–””¤¸“ ƒÒüË_¶ôÒ®]X÷‡?Àyñ"x)s.ÛÚyŽ/ż—0ç8,Ï»Å9Ï{Üõ¥®C—Źä´ sJ)— T ¸\\.¸A‹gŸEdÿþ>þÆÅ¶mÛ‰‰‰˜7ÝdDYYk˜L&œ;wNyýêÆ™ÖGEFFŠ‹‹•ûùo4”PJ±k×.¬[·‹°iÓ&lݺ]»v-2›Íÿ7räÈ}µ¤ÿGÔëõ »uëöÉ'ŸèXçﯩ# ôt³Ùllèµ4V4b|ØÙÛ1 óµÝn4eÊaÙ²el ¶)êraÏ?þÓ}Öåò_cæmcSY×”­²P÷j#[ܕ͗¥¾Œ JV­÷ÒKÐÇÅýY¬]»Ó§OÇÇŒ1cÆ(÷ïß¿'Nœ@ZZj~¤ ظq#`̘1!8¥§°Ù.Àj=Ž ‡^ßUí³l6ÒÓÓÑ¥KôéÓ§–k¨9ÅÅŘ0a°|ùò94Ä1pK–,±nÞ¼ycYYÙã¼Ú˜mï„?X  ¥ôDC­CCãz ©Ä¾ìì‡c¹Ýn2lØ0¿î¤¸¸8ÆJTò֮Ś»ïk³gYè¤x@¶·{dÏ9Ï»Eº$ÎYžËó¢0W‰t"eÑÕB £dÏ•øb\éVp¹@%q.8œN¸\.ÄL›†Õ~ïÇ#33¥¥¥>Î0 ìv;¾üòK,]ºãÇêß AÀÎ;±~ýzX­VŸ Ãøñã1xð`%N;Î[Ž‹M¸víW0Œ ÀãX°¬(ÊE‘®çî[Y¤DêÅ¢°0§NÅãÒ¥[<Ö””Ôe-tqq1222pôèQ¿ñ÷ßh(ÉÏχÉdBaa¡Ï5êõzèt:[vvöÖŠŠŠiÆ +ªIœ@ém0>a¦ß‚ øÇ¼^MM W&ÐC„/;ûÅ‹ç ‚0«sçÎLRRbcc+Í÷…­¸9÷܃’­[ª1S7axÞE—ëʼNÇ.ÙØ¨$Ö›d]“­lË¢ùsÏ!*ÈSE‹Å‚yóæá—_~ÁþýûQRR“É„S§NgŠÊMXê“Ó§OÃd2)öò-ZÀh4¢cÇŽ=¿¸x.]Z‹å\®+p¹ŠAi¡Ê‰¹¸‡ã¢Á0±àù›Þ7Ýt;ÂÂÄf/rSyk§N––Vo6{@t8lÙ²›7oFaa!–/_ŽiÓ¦á•W^©W;›÷Ìv–eqîܹ’_ýõÍ.]º¼×˜mï„$Ù!£¯¡Ñ˜i q?;{ûöí™äääjíÊ„8¡øøqü0r$píš(ÌUâ\±¶sx)à%AÎI‚\-ÎYÎ-ÐU$±9Nº|P+wp‡”EQœ; .\¨Ó —×  åܹ0ø)[“íì'Nvž©×둚šŠ~ýúá›o¾Á¨Q£Wƒ„7Á6±•›¶ Â&;ö\®óà8' sùR tNа,ç‘=û1ŠP—E:P œ?ÏaϞθzÕ³)[—.]Bn{—ã€M›6ÁétVûxïÿFC…wPß}÷ „ˆˆˆOGŽ9ÚÞ !‘ǽ"ÂŒûùæ›Ü-·ÜRÕ¯Ò1š@¯M ×?vöD–e¿ ‹JNN&ÞÍNª² çæbÝ]wÁ~ölÐ5f¬,ÎåÓqŽ‘î#,+ž”«jÌ”rùÜ›0¥â,Êy÷Ýh~×]AFåå娶m6mÚ„'N k×®?oß¾=ŒF#ZÖb¶j T7—¼wïÞHNNFT”gæ[´°­ÆÅ‹&”–n‚ ƒãÔ›°Xr@ˆTûOeSVoÄîMøfäç7áCѸzÕs“eYÆ ÃØ±cë¬Q̱cÇ‘‘«W¯*÷Y, †z³³ù²±©ÑéttÇŽg÷ïßÿ—Ñ£Gïm̶wBH…ZMº††onä¸À‡=‰eÙ<ÏG%%%_8«Â`0`ìС8>}:,§N)â\ç%Îy)६9§çŠHW‹sNüšã@ôzw]A—ŹÜHVZ“bqw¹@¥¬¹|+Øíp98Ym.§ÚK6mÚ„-[¶xØÙ¥mÛ¶X±bvíÚ…¯¿þii5+]­é؈ˆÓˆßŒ¨¨bð<£ˆs%QÂÊ}8%{.‹rñV΢³*qÎBÌ¢‹·¥n‘îrQœ=ËbÏžÎ(/w÷bYVwV[Û»¯8 P‚Íëy ìÚµk}Æþp8زe Z¶lI­Vëéƒ>€íýn½^¿¨]»vQK–,Ñ=ºÆëÖ¨9ÿûßñÎ;ï˜***&7ôZ+š@¯UØÙ9ǯ²nÊ{|Æ™ôtlzè!³Ùã”\]c&‹s¿5f’8gu:ÑÚ. u/ûá89§<2èTåŠHw:!¸\àúõCüìÙFDVV®]»†¼¼<,_¾;vÄÝwßía£fƒÆøñãCÞ9ÕåraÇŽÈÉÉ©vÄ™N§Ã¸qã0tèP‚GŽüùù_€›ê¤œºö_¶±É"ÝÛÆ¦®7#„‘6`ñ*/'Ø·¯5Nê౎fÍš!))©Njã‹‹‹‘™™‰#GŽTù˜_~ù?þ8þüç?×Ɉ—sçÎÁd2áüùóÕ>ÖårÙ7lذ¡¼¼ü±Æh{'â?ð!ßPJçÖçïÖи^¸ãoaþÈ#dg¯Jñõ׈È˃ža SÕëTâ\É Ë]æœJ˜³:xp/‹sD•M':"Ò!ߪKá j'YÛ©ËÁá•D¹Ëá€`·‹BÝn‡+:ß} ¹¹¹ÈÌÌDIII­>k†a`µZñÄO C‡°Ùl—ÉÉerrrüÚÙ}ÁqehÕ*±±Š0çywæ\<cQ¨s^tN‰‘³èœG\ >À—r¼R­?@©§S@n.]»<'ªÄÄÄ %%Åç–ê())AFFF•q@ ÄÇÇ#--­F¶÷üü|¤§§£   ÖëØ³gsß¾}'š5k–æm{_°`AGA>a䫯¾Ê=ûì³uÚ×@£j4^=š@¯¾ìì—.]šçr¹fwêÔI±³øZèß¿?ºZ­ØúðÃà(OÈ%®¾Ôâ¼Ê3ét\¾%ªL:TvwïY§Ùs/›,Ї$!m_~¹Ê÷uùòe;»Lii)rss1tèPŸÏ‹ŒŒDbbbÈfúë–^5Ú·ÏÅ-·ì!eÒ†¬êšÏ±^ݽ‹§dÑݧäj+›|ZiŠ‹)vïn‹ÂBOA(»Ë«íìòŒÕªûÃ? ¨¨O>ù$ˆI“&!""¢Êç‚ÙlÆš5kðÛo¿dc“QÙÞß8pà»f³¹QÙÞ !c¬ÐRZý©ƒ†FãF‹ |ØÙç ‚0;P;{Uè²³¹w¯[œ{ tÉÖ.7…S tV§s‹t½^Œ ôzEœ+—œA—;Ñé›»‡ÅR ºJœS)kNeQîp@°Ùà’¾wÚípuê„m 8~üx­?k5ryÜÌ™3Ѷm[¼þúëhݺµßÇkg—Ñë ÿ3 +xžQ„9Ï‹"e9E¤»³ç¢(—…º[¤s*î¾ÜâÜ3.oUå†T@a¡k×vƒÃáyà“€´´´€š»C°¶÷`íìpôèQdee¡E‹vƒÁ0¯{÷î‹Nž<É ë‰%.µ½Òë_¸€ÈÏ?‡žqîaoW t^Šxµ8—²æœ^/Šsžæ)ÎõzÑæî-ÐyÞ#ƒ.ý!•jÎq.‰rÁno%î’îsØlXÕ¢®˜°†a••…xO=õT¥Ÿ×ÔÎвåèõTäna.Ú«ú)·¼—@W_¼"ʆ Çj‘.Ç ÏšŹ(Ô(-µ#;»JK=;Ës‡#FTi{¯=Pª³½×ÔÎ(‡‹Í›7§ß~ûí¥ãÇsÍ›7úè£t)))!ÿ}5CèÕ£ ôñagoË0ÌJ»Ý>xĈ1bDì2ÄbAÌçŸÃPVVi#–Ź.Ð3ù¤\UkFd ›Z ó¼{Œ ËŠ›/ÈâªSrAE¹$ΩÓ)žŽ;p:ˆyäÄŒ«¼µ=, ²³³qâÄ ŸcTjb{W.«ÎÎîõL´l™‰›o>ª:)gUµfž±lg­l¼*‹.7…áà¶±yfÒÝ1@©ÚÊF;Ûöí‘8vÌskTTTÐ#á±³ÂÀó1—ºËfƒÓåBëùóQÞ¬™O;{è…ÃÒ IDAT ”••)Ù.]ºT)›ÄÄDôëׯÊÌí©S§`2™pùrp L–5£U«ïuÉã¤Ü]kæ®3-mê:3ÞK¤{[ÙÜ[=OÊ%ׂ²»ml‚ Öý:üúk×JëíØ±#ÒÒÒ*}NjBmcÛ»w/2331yòdôéÓ„ôë׉‰‰UÚÞÍf3²³³ñûï¿‡ÌÆ¦Fe{gàÀ }ÙÞÕB½>D:!DàwÛ)¥ÔõïÓи^¸žãvö—Aøk(ììœ:ÃÊ•JL Ä½Ÿì9¯Êš+·^â\èJÝ`EzXˆZ¤ëõîø€ãgAç²0§’8WDºôµKì.› N› Ù7Ý„Â:n‰'NàÓO?Uº¾×„èètDG’„¹ èt¬WLÀItù–WDºû–!¼ð „Snå¸oe‹;$gõèrÃ^§r9¬]‰sç|® HJJBnn.6mÚR;{ BpÛm·aĈغu+öìÙS'q€ŒÜg`Ë–-9r$>øàtéÒ¥Î~ŸFÍÑzõhüPUwvƒÁ•œœL¼»‘ ¿v-tùùRS1¢\Œ|˸ëžéRF¥©š¾)ãR䌹ÊÂF¤Ú3E K‘ëËd‘®XØdît‚:ŠÍM DÀHa8¬ àô?þïºv…£teq~îÜ9|ñÅ8p &Nœ¨4‘3›ÍøñDZgÏŸ¶÷k×®!33‡ úwsÜy´jõ# † ɺÎ(Ù½kͽëË*‹sy#fUv6·P÷<%w[Ùd{»¼"€a\èÝÛ…˜˜\¬_Ÿ‡Ãý¿êéÓ§±hÑ" :ãÆ«d{¯ Û€®ÔÂSJñÛo¿!77W±½«ç‡ ‚ ØÙƒiÈ,.— qqq1÷Þ{ïËÛ·oðÀÓ¢¢¢~u8‚Õjâââ\±±±:u*%„Òúë,J ”Ú !wXJ‰¡”Ö®3’††FƒáËÎ.ÂRžçcž{î9&222H·VÕ0ë׋±¤]‚qúŠÔ+†0Œ4ÎS¼ä±h·Þ—ª áyQëtžâ\¾d.;íÔé\.±4Nú;O¤Œ:áy§Ós êõ‚ÛΟ¯sNÁÓO?;ï¼›7o Ú9Ž ¸$+,l'""*1™ƒy~ÞêçbwvÎCœ»³æºJ_ËÙs†ÑA.}“­î¢0wÜ‚do:àT5ßÏØ±×°jU8JK£|¿¡&D^^²³³A)Å—_~‰»ï¾»¡—¤¡Q+´ ºêÊÎîAQ">ýa€û”<€3ïSrÅÚ._²8—OÉåXº%:»K«Ü©UB¡UÖ\n#X­Jæ\NÆåk7Ã`¿Ÿ™§ÁRXXˆÝ»wcòäÉ>7U†a0hÐ L˜0<ÏcÛ¶mظqc$½þš7ÿz½ŽUjÎ=OËݶvÑÎÆù<)wÛØÜ§åž—œAWS×áΠËuf.é”\¼-.¶ =½¬ÖÊõçQQQHJJBŸ>}Bfg„}ûö)™thÕªŒF#Úµk‡¼¼<˜L&\¸p¡Î×á öœœœMeeeÓ›í]C£©s=žììf³ùsAFÝ}÷ÝÂgŸ}ÆFDD ´´YYY5: öF8p:“ zBÜövU\ “JÞÔ®:ùRâ½^ÉžsêØÀ`pgÐ 1ƒ.ßÊâ\úR¼ ÞKÖvØí V+¨ÅÁj­¨³ç ‹.‹EÌ¢KÙt§œE·Z±9&§Û·¯õgä Ã0•«åLê³Ï>‹¼¼<$''WÛaœãN!6ö;èõ€NÇH1«ØÛu:9³æî˜€WÙÛuRL S s÷ׄð’ÃN1{.gÐ帀Qâ1‹®Žœ Ô Ap@ìp¹JJ*ðóÏa·‹¥111HMMÅ­·º;¾—”” 33¹¹¹!ÿüýѦM¤¥¥U²¸§§§#???d¿Çl6cýúõ8xð f̘ùóç#22²ú'j4(Rý犊Š) ½–ÆŠ–AWáËÎ~îܹ9þÖ¹sçÚÙÙ½àsrÀR*ž†«2çò×êSrõ©8#ewålº)ç¤X®5S tu7cqd¶,Î ëÌĬ»„ÄÆ“&Bzú­ÏC‹²²2üôÓO€íÛ·×ym 6ÞÉÉÉÁÑ£Gq×]wã8\¸p_ý5î¸ãüüóÏ(//¯óuø‚aÝ=÷Ü31;;{ÓÖ­[— 6ì]³Ùì,--u9N+ óæÍ1ëR—"¢ð€×)¥õb¡¡¡4ÞvöW^yå%JéóíÛ·~ùåÒ«W/åd;::÷ÜsNž<‰ôôô K¬ÔÐßW²æröÙã’š¶)Yué‚:N¾f¼²ç¾†N§\Š«N¯Åyx8ˆÁI¥ŠÏ‘2å°Z±‡„eA9°Û•I0îî*Ò稺º\¾rÞ¡CÆJå^ Ã`ذaøá‡ðè£âðáÃU tB®"2òg°,õÊ–3`âñ½z´ªºïŒçþïç,«!:Õý<ÑIÙs^%ÐYPªLµSYÛ]`a=ö¹‘\tt&N<Ž5kzcĈQ>û ÄÄÄàü#Ž?ŽŒŒ \¹r%$Ÿ¿/ÂÃÃ1iÒ$ 0 R’%>>Ó¦MÃÞ½{±fÍTTTÔø÷PJ±wï^lܸ}úôÁÞ½{ëd,­FÝ åFªGèðogáK½^½hÑ"öPfiפéˆçÎ=yÒC”ËÂ\ma#¾.•µ]ÝôM}1²0—¹º¾Lma“mî€{ã•6_0 ˆ3«ˆË%^âp("Jkã8ô;y;o½µÊ·,‡ÅÅÅøðÃñÌ3Ï@§Ó!::ÉÉÉèÙ³'±;Øæc„T :ú;pœ³Òìè¬êÖÝN¶¹»³æ¢8OÌuÒé¸ç&,nIJ•ÍmsWR­í.Jð¶Ã·l Œu 7vóx/]»vEZZbccÑ«W¯Ðý7Z‘‘‘xøá‡±iÓ&Ũnæ7cÆ ¬[·»ví‚ u¶oôz½2Ã~ÆŒmþùç¿¿òÊ+SKKKgétºr·w½^/Øl62qâDaÞ¼yJQGBÝ`€o!ã©ø«¡¡ÑñagOáKN³páBöñÇgý=·sçΘ>}zÍ]]V+PPà!jeá o‘. u1Æ="MîÄ®ŽÔÂ]:¬‡N'Šr½09“Î0bs8«UêrÙ›Ü\ž"ªû¤?«Í].ðv;:]pŸ‹Ôαªˆ‹‹CFF†2fm×®]¸|ù2ú÷ï¯. ß‚emª}Ÿ€aÜ¥†¢å]žÌ¢Ž *‹s÷­,Êu`=F/Úë$‹»,ÒÝÜÝkR[Û ”… 8À0î&r #ºîF@«Va¸ï>':w_åç‘€éÓ§cëÖ­!¯K'„(剃¡ÊÇÝvÛmèÑ£Ö®]‹Ý»w-ÖΟ?ììl”——ã½÷ÞÃ>pƒ†ÆõB“è¾ììN§s© £î»ï>aÉ’%¬ÜýqøðáèÓ§OÇvÈ0ëÖ¹kËÄExdͽOÌåús†eÅûTÙt¿5fr&]%Ì™°0ñÔÜ`po̲U_§SÜ€ QšÂ(Â\õ»| V+Z,¨¨âs°DGGãþûïGaa! FŒîÝ»{X¦Ú¶m‹'žx"ˆñ]""~Ï—ydÎÝ›°¼ùz^¾F¦¨­kž›±Z ó>NÊÝV6@€ºŒ¼»ʉža(ºt¡(.>ýû;"66©©©èÖÍ-؆ÁðáÃÑ»wo¬^½û÷ïÙ¿…7Íš5S,í£GFdd¤Ò%5,, iii¸í¶Û`2™——WgëéÛ·/’’’<¬mwÜqGXRRR¯¹sç.ÏÌÌÜîr¹f:´Èf³¹ìv;9|ø0 L:•öìÙ“Ö…P§”: !Süâß[M kh42|ØÙÛšÍæ¥Þvöê`Y£FBŸ>}‚¶½;+nñíU‡•Q„ºøòÈYtùk•ûÎC´ËåmR·vuÜžÅyD„(ì­V±]ÿêÏ Pú×ÈBœHßwT€R°”¢m~>Nuêðgâ/;{ ´oßO>ù$ÊÊÊðꫯâøñã¸çž{,û+8®X娔1¯|¹G¨ÊðîƒyN‰d!îçò­Nu‰±z¿?>âÙ® ÂRA  DŒ—bg”ž@EÅQ„‡w«ôþÕp‡1cÆ _¿~ÈÌÌÄáǃüW¨L›6m`4•‘¬`00yòd%NÄönµZ±qãFìÙ³?ü0þûßÿ†ÌÕªQÿh%†UÓdkÐýØÙ_ð|§N“ÉÄ«Å7ò‰ìÅ‹ƒú½ôøqpß~«Ô—ywhUº¸ó¼GçvNçÙ¡U§æYc&‰p&,L¬7ó®1 WêÌ 5Ž“7\*ŽS› Ôbë̬Vbý¹Tc&X­J™KÕ­Õiµâ$Ã`K¯^5ÿGñC—.]––†‚‚ :óæÍìY³”&r2ÈÎÎÆo¿ýæ÷DV¯Ï@DÄ~èõŒTgÆ@§ã”Z3¹ÎŒçå:3ÞãkÍtR­™N•9W_z•@W[Ùd‹»Ü­ðè©Î̡ԙ ‚ ‚à€Ëe‡ËeƒÓiGaá` òtµ}Μ9“É„K—.ÕúßÀ›ÈÈH$&&¢oß¾x饗°lÙ2deeÁ{þ/¥¿ÿþ;²³³a6›C¾Ž–-[Âh4¢}5ÖÉÓ§Oãé§ŸÎ;yòäç·ÝvÛB‡Ãᨪۻ´öÿq$„mSÒhª4Ƹ@}HŸ••¥ß¹sçÙÎn2™xÙ­U‚±½Û¿ýü‰Ðƒjª‹èÕqª{;Ïóbwù’kÐå[ƒA‰ Øðp¥þœ "ßFF‚DD QQ@d¤”—ƒšÍ€Ù ZVZQj6C0›Åt‹‚Ù,Ö K1ƒGw‹N»N› ×nº ™=zÀé þœÒŸ=XòóóñÕW_Ál6Ãå*Ç}„èh‡*&`=.9/^ŠÄ8@Œ Ä8€ãô`Yñ­íz0L˜"ÐÝ"=LªAðÕ‡÷²m]Œ Ę€RÁJåxÀ —Ë}ërÙ!V86Pz :v|'¨ÏãĉHOO¯‘í=<<‰‰‰^Ž„à‘›Îfggûµ½ïß¿999èС/^ŒÁƒ×ø÷i4<ûÛßðî»ïþd±Xîlèµ4Všœ@¯ÊήÓé¢-ZÄ=ôÐC½–\̬M×?@ô(t„ Œe=ºº ŒŽóÜ x¼ÔŽW5aõzpªæ/ŠH7ÀDDx4ƒ!ÒfL$ ›GÝáµZYœ[,**ܰܦ¢.Y¨KÝ) t§ `ï½÷"7DMÊ¢££‘’’‚=z(÷}÷Ýwxýõ×±nÝ:¥û»7ùùù0™L(,,ô¸Ÿ3ˆŒ\½žQ6c½Þ-ÌÅ ˜U6bß]ÞŒå Xï%ÒÕ±îf0:ÈÝZÅì¹|Z®Þˆ]’(—7cÁ&mÂ6©1Œ N'E‡Ÿ€aªÏ ‚RÛ»¯ÙôN§O<ñ:t耗^zÉçó¬VkHmïz½ãÇÇ!CÀxeuªbÕªUÖùóçŸ,))™5lذí²í½U«V‚Íf&Nœ(>|¸ND:!$Àz¦”Ö>m¡¡qјâvvžçe;{H~ËåªÖöN].Xßz aN'ÂTãÕÔâœ÷Š xž‡NjëW K1«>¸W‹ôÈH·@ÅyD„øµ^/:él6Њ Q KâœVT@(/ã960›Ý£Ö¤ƒ|§jÔšÓfƒ‹R´Z±™kÖàèÑ£}~ÚÙƒåìÙ³xî¹$¬^}“'s5J<¨×ëÝ£Õt:·8×éÄx@ŒËDaÎójq®S tY¬‡I±ÀÿgïË㣪Îþ¿çÞÙ²²…=²JX%¨ ¬"aËk­UÄWkµ+"mí¢¾µ ¸ðZûkߺ¨µ¶V­XK¾ƒì«ì²g¶{Ïùýqι÷Ìd’L’«Î÷ó9Ÿ{ïdrgrçfžçû<ßçy„_à±¶¼=‰$é¨3Ðyvœ“sIÌ9Q÷ ’î~O,¿ÄOKû%RS×ëZ˜¦‰/¿ük×®JöN±šõÖ&g¯/¼^¯å'HÓ[TT„eË–¡¨¨³fÍ”)Sª%gâøæáÉ'ŸÄ믿'èµà;%q gO7 ãJéûýöÛz}¾ld#))®SöN)ŸoKÖø›²¥lJ:SêÑ­Ú2µþLJ×ÄØKƦÊÜ…|­Ö3§“wh5fL©Ax ¡¾Œ1þx €1-[bà 7 77·Ác¾t]Ç-·Ü‚áÇWkvr×]wYã3^}õU<þøã!F"==?ýéO±}ûv¬X±Â’½;«„¤Ý^²ÆŒKÛÕz3MHØl™»”²©]Úmi›Ë"æ¶”ÍceÑí :7Äv#®zvð[d“R€Bxmº¦`LÖ¿pîÜ[hßþçu^ËXÊÞ;vìˆììl´—ãp8ðî»ïZÇÕº¨ÆRöIÎ-n¿ývϘ1czÏœ9óÃ… n6Mó烾.{Ÿ7o;pà@LeBÈ.ŸB2c±›ÍGqD…Úäìwß}7}ï½÷¢’³G‹hdïÆ©S ¢¼Lý¢aêVõ äãò1åçÖ±”½+çQeòìùæ”r;.ÊܘÏÇ÷) xàÞïç áÄ,tȦ²òX6“3M>Fœ[>Îä¹öíÃ}÷݇ÇcÑ¢E(..ŽxÝ*g))&®¿þÇŽ1 &í2Q|é#á ¨¾WÃI?AÚ¢*èd ^’r¾õ€p‚Îý[âN¡ÊÞùtPSy-Ýzoöâï… ïÔ› ëºŽaÆ!33³NÙû5×\ƒììl´mÛ¶^¯ à³Ï>Ã?ÿùOlÙ²wß}7þô§?U±Ç7W¯IÑ7ß ‚^[wö.]ºÐÜÜ\’‘‘Ñà\rr2~ðƒ`àÀµÊÞÍ'ø˜¥Î\¼AËpª_¸_JsuŸÈF0ÄHÒ.‰;ÂkÌ\.ÞœFÖ˜©× Ša—†°ëÊÄ1QêÏJV¬@·ßÿ?þxƒ‘H9{‹-ê|îÙ³gñ—¿üsçÎŦM›¬Ýò:Þpà èÝ»7–/_Ž;?€ÃqÞ"âü«FXS–BÔe—йæNkÙX•²©ÆØ–²Éfqâ¢BÖ™Z}Â¨ËÆq†¨AÓÇ€ÀçÛˆ`ð8ÑÕ_¥¤¤à®»î²îÑúÈÞ“““1fÌdffÖùÜŠŠ ôèÑÓ§OÇäÉ“«ý¼M›6xøá‡${VÎ^Ün7fÍšÕâ‘G™0uêÔë6nÜø·þýûÿ_0 Ênï+V¬ íÚµcW >}*€Ûâä<Ž8®>ÂåìÏ?ÿü³Œ±';uêDÿýï‡tg5jëö²šSX¤\%ÓaD›)>ƒ$çLì[ä\=–¿ÇXè¢ÔîÒðæo‚œ3M³zÓ0¿ßV×ùý<«ðß1 þ9šUKâÏ9—Ǿ'Ý»wG×®]±~ýz¬_¿>Dö+9{mX¾üw`,ˆ›nÒ0lï STİr¥÷Ýç@Ó¦¶_Àí?ïJ’ùq8Q—#Ô¸½WkÎ=$9ç[žI×8ÄSØ{é $¨¼O“„ú,Äzœ±‹¸ti1Z´WïkÒ¤ILš4)â=š””„Ñ£G7ZÎ 6oÞŒW^yIIIX¶lFŽyE_/Ž8þñ­&è5ÈÙG3Æþ!»³?øàƒ13ȲÉÖ­[±jÕ*ø|¾Ÿ*B&ë=†4[ca¤œ¨ä\î«Úä˜4• Gx^‰— bDö€MÀåŒqã-H8Sö-’®ìûEv@6"‘Ùºæo6mÚãÆ«VÃ\:wîŒ={öàÓO?µÈ¹a!µÙ‰‰‰ø¯ÿš€üü_Âï0¶&dÉLzxöÜîÚ*£ãv#¸ð)R.ål¡R6Õø2&£÷¼ c†â„a¾DAÁkèØqzÔ× àŽÏc=•ì]Ó4ÜtÓM¸õÖ[£Î`$''ãµ×^Ã>ˆ¬¬¬ˆdš‚믿={öŒJöîñxpë­·âÆo¬—œ½.têÔ ¹¹¹rrržœ>}úeee¿q¹\_ªÝÞ›5kF{õêEg̘3f°ÆÖ3Æ|r€Ò’1V³?(Ž8∈ں³¿úê«ú#û,~õ«_USQÆñí@¼OÝøÖôHrvÓ4?0MsÈý÷ßÏæÎ«]‰/)ËêÓ§–-[†={öX7"£ÕTD’°U#çµ-•‡IàÅâ$^^À2–D6ãy$\È× äk0MNÔå¾iÚÑvÓ 1ò’¨³²2”íØÔ„Îߌ${w8¸å–[0lذ}»ÝnÜÿýxS´áÇcÖ¬Y¸ï¾û¬çlÝ:§àvåR…’s[ºÆG›Õd€Õ™¥¡ÆØÎ¢Û$ÝB-²„KÙ ð®íÒØJ®£z”>œ¤»àó‚ÇÓ¡^×,Ù{§N0a„jröhpçwbøðáuª ê’½B™™‰¬¬¬ÉÙ£Evv¶{ôèѽ_xá…|ñÅ[ ÃøÙÍ7ß|þJÊÞ !m&„ÜÎ[›¿$Ž8âPNÌ'Ožœ 癦yã˜1cØ?ÿùO’ššzÕß—®ëèÔ©Ž=ÊÍ99 ¨N¨êD<"ñ[”Rh”VË–Cî `Ð&ã€mÛÕÀ½|^ ÀIºh k| RÎ ƒŸß4AÅëP)q7M"4ÌkÒ¤ :uêJi­óÊc…¼¼e0Œ*8ª½ÒÓu¼øb*Ξt_ïÓ§ƒèÚÕ™¡¶G Êì¹½/måÓZ\Eg—Á2‹žÀ-Îc>…¨;À˜]X¨Äg"Äeø§O`ù"Èßp?ü½’äÜï÷ã•W^Á¬Y³0fÌ8p ÑJ¹8¾ˆ³ôZð­#èµÉÙ»víJsrrHFFÆ•Õç€gï¼óNKR\°?j9G\="Ô›R-z²¯>¡w¼%aSjÁ UU`2ÓpÉš®É3+2.ëÊãkÕ•‰}¦¬’µk-‚.Ñ­[·j²÷nݺa„ hÞ¼yL®yÇŽñë_ÿO<ñFŒacÛ¼ùÍRnd›ˆ‡>fÞêD]e)u·ål2‹nKÚd&=Ò0s’®ƒÏ9 X‘pYwÆXõתù㦸páïèÐ!rc¶ºIö«†<’œÏŸ?}ô>üðóð‘dïmÚ´±Æ¸] ¸Ýn¼øâ‹Í'Ož<îñÇ_¹qãÆú÷ïÿÇ+%{gŒ#„¼ `6!¤ãsuâˆ#Ž!\ÎþÒK/MgŒýæšk®ÑÆŽ‹´´4òþûï#;;]1ú«¾¨¬¬Ä²e˰{÷n0Æpüøq˜[·†ÚîÈ8E˜}GÙºT»I…›Ì^ ÂL)gb¦9“뤜=´U~ŠÏÀd]%êr_ñ¨²oÕ©+=FÐçäççcÈ!:tèËžîÛ÷TÒ À"šFÐ¥ ·¿yy~õ«ó¸ãŽ&xüñvp:¥¯Àm3ÿ;°oOi‘þƒÔw†ísÂÎW²øÝ*„Oâ$„Æ| ð»@š ¹/Fܹ@qñ*4ožÕ ëéíÚµ+&L˜€´´´³&,_¾S§N…aøôÓO‘ÓóÇÇ7ß‚^“œÝáp|âñx’Þ|ó͘ÊÙ£E‡ðÓŸþo?÷Ž…KÓ!|‰h‡ÉÞÔŸ[ç 7Èb-'ÒÈŠÈ8‘5çj™ éá5f–±•„R;J®Ö™E°©²÷:`ÿþý1ÿ"&„`Ú´iøÉO~bE{?ø`.]ʃۭE$é*1ŽKâ. ®Z‹æPH¼» «-cãQs·µd”œgÒ5QkÆÇªb@vvçF¿¶¿QÞüI^oãgwêÔ >ú(æÍ›‡ììlÄ2£4`Àüö·¿ÅäÉ“ñ÷¿ÿ½ÆçIÙ{çαxñbLœ8ñkéÔÚ±cG,\¸°ÃçŸþ›¿üå/?úô:SùÀ IDAT郱cÇÆô»/”RlÛ¶ +W®¬VþV~á°I¸J»,r®p*–uŒÈõåV&]ÚhÃÓuÐ`—½ 4pA4÷ÉáoÚzÜÊ¢‹Eý~PIÎÐ0É;5MÐ`TɬSJa]yyyD—aX³f öìÙƒqãÆ¡G1ýc8|x±°£ÄZv ·ô€Œ þô§vøüó2+Û.ŸÃËÒ4ÅwÐCΧ«™vî'¨ûòX~êBï3l_Þ꾜‹n‡rJJ–×› ×v;v o¾ù&Œ#FÀårÕëÜá(((ÀO< à7¿ù žyæ™+.¡#Žo¾=\ξpáÂöº®F)8pà@Œ1:uªVŸ|5pêÔ)äääàäþýÉxM5È-©ÞÕUrp’_Í@Ë ·4°Âè2JyÖœÿ’-a“=¼ÆLn¥¡ k#_CÊ܃5to7 #$ƒ^\\Ó º„ü’?qâ¦Mû5RS)î»OƒÇCù­NÒU#‰¬‡’t5J)—dÝ)“™s7I'ìâ49rzNØU"NälöÂ%ñ%ðz"!áÚ_/5ƒëÀŽ;bÆ ())©õyáóÒçÎ{U3è†a`Æ Ø·oŸ«ÿþú÷ïÿùªU«6]ºtéáXËÞÅs ¡Ã[reþª8âøö£69û 7Ü€¡C‡ÖH*öíÛ‡¼¼<Œ1ƒŽypPú………N}¾P€1›”Õˆ:•[…¨[Ǧ *”s¥úè#ddd`üøñ1óNœØ„ÊÊ"¸Ýu )¹/л·ýú¥B×5”—S¼øb¦M낞=Ýàd]õ´ˆ‹ÛuIêÕ×àen}úÔë5nc_ýuLŸ>7ÝtöîÝ‹ŒŒŒzŸ'Žo6O‰KÜkÁ7š Kb>cÆ H9{aaáó¦iþ*==]3fŒõ¥¾jÕ*ìÞ½ãÇ¿*_!5è´¬ @Í5cáYò¨¹ú<(\DÉ«íKƒ*#âºnw„•ä\Ö©«6IÐkÌ`„Ö˜YQyåØŒ@Ð#Õ 9ro¼ñF£jÐkC§NðôÓ½›»20+kÍÄB‰¹md«wwW3ê6Q'ìÜGT;¸ê°£äN‰âµÃ›x«âÅð ß·åkê>Ã¥K ‘žþD½¯O¤ Fyy9þõ¯aûöí ®AGZZš%‹{öÙgqÿý÷£{÷îÖÏ «Õ â/ùËU©A—ÈËËâE‹Âû$8G=ìܹs;6mÚôN÷îÝgÄXöÞÀ|BÈ£Œ±šeqÄGD„ËÙgÍš5ƒRúkEÎ^ç9–-[†;wÆLö^YYi`­í+%$À,)‰œ=—ö݆-*2åTfÎMTnb¡}m4— TŒW“£Z­>9R‘§”¸AJØB®.f|ß0BȹºJ¼^¬[º4êk˜——SÙ{^Þj«¯­ Õ¢HNÖqÏ=;±lÙ´oïQ~nƒgáa½Ž­zcà‚)@¼ù[ì º$ë~ð9è6Qçd^’sŒñL;'íTwu[¯÷ºÖz=¢½GU”••aÞ¼y–Ÿ NЩ _~ù%{ì1áí·ßƤI“¢ú½8âø.âIÐk‘³Ïóx<)cÆŒ !—/_Æ?þñôèÑãÆC³fѨª(¥»¸›åå‘3äŠ×HÆÅ–2M­3 “—[µfÁ 4Q_F á‘rÆ,ÂÒ¦ [¤3µ¾,¼ ÑzªdKKJJjíânÖ®]kÉÙêÓŽ.”•¢¸x7FŽÔ ë>ðÎ;~|ï{nôê%o}Õ‡Ël»}lGÊÕŽªÕ#çPSAøÄÖnp¥Ñ•×FRb* º½•ûÜ1£¨¨ØR¯ëB)ŦM›jÍ`œ8qsæÌ©w÷Ú`š&Ξ=‹!C†`ݺuèܹs­]ÜeVýСCW¤‹»Dqq1/^ŒC‡Õø¾[µj•:qâÄ_nÙ²eâÁƒ§º\®µ±’½3ÆŽB¦˜ NÐãˆ#J„÷œ™?þh—Ëõ‰Ûí®&g±½×äÔ-9Ù¶õB5geÊå Ëœ3e_JÙ©’5§ºB)ˆ¨;'šªŽŸMÈUY› êTJÙº·š¥¥Õäìu¡¤¤üq½æ ×…½{ÿ-®7ÀºNž®áÿþ¯ ³f9‘ž^”×´ä9£åÜk!ÆÙnê" ³Bà5æAñ˜€W_¿b|egwÕøšˆDØå±aœE0XÕLôãÇ#777ª9è’ÈïÝ»7ê9èµA×u¼÷Þ{xûí·QVV†W_}5ª9è>Ÿ‹-ÂŽ;b2]BÊÙׯ_Õ=ê÷ûI¿~ý®éׯßg«W¯ÞráÂ…2¤0²wÆØß!4úŠ#Žï‰ùË/¿Ü^Ó´Oc7öëׯ ktlCeï'OžDnnn­Rápädå^ùvW¤ë&c0)…®iü˜RèÂ0…/ ‰¹¦4„³šŸªßqJ¿âp€†ý<5x¯JÜU;/ ºÌ¢«¤\É ËeLñžüö6Töîõz­`pEE!ìM¾åe 'ê‘о}JJøÑÖ£W¯fxíµ‘¸æE’9I—Ë·ëRQ'}IÖÙÜû~0æ~B@t¹‚bbÙ²wy,ï"¿¿ âûoÈ=Zj“½3Æðî»ïâé§ŸF÷îݱmÛ¶Fûq|«—¸×‚o A—³ïÚµËqúôé"ÉÙ£A0ĪU«°k×.Œ?>bÆ=ZTTTX¡š@h1+òˆuf‘jÌ×»+«BΩ:#B8E)ˆÓ Í4y”œ«=¤»$èj·V52.#媖r‘½§¦ #Äo¼Qm¤Z48zô¨%{>|xƒålŒ1ìÝ»,ÉžÓ Üq‡ YY¤¥qR}ꔉk¯u*‘rÕ`‡oõ8ÔpÓ})§"#n‚Ï9×@ˆ)2ïÜèÊH¹\ò.%mÔ¨xhý™Œ²«¡UUGФÉ5^òòr,Y²{÷Ö¯. à÷÷gŸ}†íÛ·#;;»Q²÷ÂÂBhš†ÜÜ\ø|>?~^ŸGÕ¡éß}÷]÷ùóç§3Æ~“žž®7.¦]¦ë#{—em»wï®÷ë¤$˜Œ‰¸´÷€u¬³êµç¦’175 š¦Á4MM³É¹ ß”‰.Ì4¡¾ô è£Bâ™l&+Usauæ’”›ÂO0ƒANÊåÏ…Üžš&|O½¯O8¢•½3ưk×.,[¶ Ub¬,¥åдð,yÝ‹Ç+XˆíoÒĉe˲ðÆ‡‘ ‡si³%1—[`°É9 Ã26AX$±$yçuu¾ÐdQcîѺ eïÛ¶mCvv6Μ9ƒ)S¦àèÑ£xå•Wðãÿ8ô>Œã;¸{S7þã z röÛœNç§µÉÙ£Eqq1>üðCtïÞãǯ—ìRŠ-[¶`ÕªUµ6;’’RQ\cöJæv­™Ì˜[ÝY¥„Mtad!:‘29…œS]çZ áÍâ€êM`¤ÒvªFÉÕH¹’=W/5Møi9—0MëÖ­³älõ•)ž9s9998rdeˆíΫ@Ó¦ÜH”–Rüö·Å0ÀƒŸý, -Z¨F^Êß8ª“òð¨»4ŠLq1çä‚°K ¼ÌŠËì¹Ì¤K#,—$é†r~ µYŒ”º×)7M›7oÆêÕ«„×½×'OžÄœ9spã7âÖ[o…§Ž–Ïç«&g/,,Ä矎K—.aÈ!QŸkÏž=–ìý¦›nª—콸¸‹-ÂáÇ£þH ”¢eË–©'N|bëÖ­÷ìß¿ÿ1—˵&²÷>~ î#ÏhÔ›Œ#Žo"ugSZRGÞ½{_±×®Mö.ý€Õ«W7X*¬ ‰»êD’µËLºF)t™9—ÙsqLL“}>J€—Åé:¨;ЄâN%è…R:D %s­R:°·È¹Ìœƒ

¥öjÑÂ…^]wÀ4M<ðÀ¸ë®Þ¸ë®~"8¯ Ò­ /KØœ-…“ø ØJ’γé”òcJƒÊVõ ø>¥¢Üš.ˆ¿¹ñ÷h´8tèÞyçlݺ=ôrssc¢ˆŒ#Žïþ£ z9{;‡Ãñ9¥tÀ€b*O?|ø°%{:thç­MÎ Zrrµáá†WfÈ™°…ŒS会„-<ú- ¬ü}Í4mr. ±|Žj•X0È#åj}¹$ç¢KkD›0ЖQ6MøbP¯ ¥¥¥øç?ÿµì½ªª Ë–-î]»„ ”KÈã” /¿ÜŸ|R‰ðìxøâ¿§JÖx¦œRSòPẩ9]Úeóéúp)7Ķ–‘r[Æ&°Ì¬Ëh¼¬YW#åÕ¥jùùùÈÍÍEQQQã>”RlÞ¼ûöíCVV233kЇwgWÑ©S'Ü{ï½Õ¨hà÷û±xñb+³U—ìÝ0 ¬_¿ëׯ‡aµ>·žïƒdff¶ïׯ߿V­ZµõÂ… 5DöNIv8ÏkšösB5MӌٛŒ#Žo0ÂåìþóŸÛƒÁ™¦yÃÀc"gá²w ŽÖ¨ z³f–¤ÝYtKÖ®fÒ)….:°[Ùs™A'öÕ08„!#Œñ79*Õá!šÒÎ*æþ“5èÊxUªn¥æ ˜r_’tѧ¦ªI“F]«p„ËÞ°bÅ lß¾=b–Î0* ë5ÙøP²Îã¡„<² ·<¸-~ùË¥hÖ, £G÷¥Üökš&žgŠ´&·|*‹-…"TÖ΃ùœ¤ËŸ-ÿ€RCY6Y.ÔÛWm öíÛ‡U«V!==ëׯÇàÁƒ¯økÆñÍE|Älíø$èµÉÙÛ·o¯;6棹îį^½Úêö)3œ=ôÔTø…„-„¡26ØÄ]5ÀÕŒ±¦ñ̹܃ܰòh^[&#ߢC«j„aˆM“t)qW3é’ ‡×š©I¹›RcæA—Pe1Ƙ5»ÓëõZSZ1:®®víxòÉp¹4˜&0gN!î¹§5:ur"´†Ì6Æê±lcKσ°ëÕ¹qçu窔Á®5—Æ7 °Oy,TÂÆ±©DÉÕH¹m|ËÊʰdÉìÛ·/¦ŸƒŠŠŠ ÌŸ?ß’½·iÓ¦Ús"ugGçÎѹsg]ïl\,’’‚1cÆ oß¾èСCĈl´p4o¦i!‘q)aÓÃ"äáZÃelDÓ@ £Z¤Œ9c ‚œË±k‘F¨¨uèÕ$lJt :t¨VïŸfëÖ0΃@YtMY&¥0áÁzÀòªÙvþÜ/>¦ëÐM“ï‹FsVö\‘·Wk§t&$ï’”[e ºø» ÓäïÙ4q¹];Ш ÃåìuÁáp`øðá–Ÿ°qcª¬êS÷ªœgÕM“A×ù¾¦1˜&—´¢‰¬¹ ÓÔBÈ9´¾¿¹RŽ÷²áãVu0fà?ÈÀÿüÏ*4i¢ãg?î3Èçóeûö²É8_6QÂ4 ˜¦íð}nwuÛYŸ{T…œ³aÃŒ5 ¹¹¹Vÿ˜88 ¢´t=¼Þ<ð1ºüóq»;!5uDÄÏ#Ž8jÂ×BÐk’³†qËøC6gÎ-<*Ú¦M<üðÃ5v„Ž:wîŒ & U«V.#²6lÀúõëED6:xºwGåéÓ\ª!rVäk&¥\Ú&À˜ŒqC,êÍd­™*N²È¹bˆ™˜‘nEÈ…A`r—‰INÐùZµÆ/¤l—ÓÒ`ĸI\RR²²²Ð¯_¿ˆÃSSSqÏ=÷ ??999¸xñ¢øI2L³¼Z¤\gû˜A×í,º=ÏÌLÁÇ÷‡Óé„iøä“ÓÈÎîˆæÍIØ{‘YIÐ QcFq%¢AFÊ¥œM¥¦FÄUR*e“µïj×V—˾_{ôè®]»býúõذaÑyBЯ_?dee!))©ÚÏ¥sÛ§Okîj}2[íÛ·G¿~ýðÅ_à§?ýiÄÏœN'†ZãD·Û±cÇ¢ÿþÈÍÍÅñãÇ£ÿ#ëšîQÃ0ðÚk¯aúôéŽAƒ%¿ùæ›!A„š I‹ìöNà=BÈÆØÕ¯cˆ#Ž#‚œ½]EEŇ‘äìÑ¢}ûöxä‘G°sçN,_¾¼Á²÷šäìÑ¢.I1¥†a  Âëõ¢ªª >ŸÁöí¡Ÿ;gù`ùAAÒÃÉ9 rLÓ¬€=£š$ç§ÐÚéä\2W«]ÉÂ3!y·z8IäÜ”äœò&·ýŸ|~ÃÀ‰'tMk’³G‹¦M›âÞ{ïEYÙZìÚµ”‹ˆsÿ€Yþ€TÂñ ¹¦dÒ4ÿ}œ˜›â<²_¾ì»ËÀÕsvYBxYÁÈ‘m±~ý½ 0Í*ìØQˆ-<èÜ9²§Œô ì,º »¼Í&çÕ‰¹ü L˜&…ÇSs£>²÷'N`Ù²ep8˜7þò¢ IDATo¾÷½ïÕûú[á÷ŸÁÅ‹ÿ‚×»”¿Û Ms€' UU{QUµ”ºáñ܈¶mEäÿÚïâ5èuƒ\©‹Dy c*…s!gÀS×^{-û÷¿ÿíˆf¦¹ÏçêU«°uëÖ˜eÅRRR0vìX\wÝuQÿNII /^ŒƒFõü`I ß|nBà&.Mƒ[Ó¬}—®Ã¥ëpŠ}‡¦ÁépÀ©ëpè:œbß!²äº®[K“[¥ÆŒÔ%`KØÔú2±O%I ^˜ÁåÔ!3Me„\l ÓÄéaÃp"Fóh5M«÷ÌmµQÚ¹s±<¸Ý·›/—KƒÛ­Ãå²—ÓéK‡Ëå„Ãá€Ãá€Óé‚Ãီ;á÷kxä‘]8w΋÷߆^½Z@×Ðu'4Í Msˆ/eMcNÐùµ·.:`ÍA5-ãm×›©’µ€0º\ÖÆ?6 n˜ #Ã0ѹóËhÛöžj×ãòåË1—½·mÛÙÙÙ¸æšk¢þS§N5Höîóùjüì{öì‰qãÆYröh°wï^,Y²$f²÷ÚîÑ/¿üS¦LÁÅ‹ñ§?ý “&Mªó|«W¯FVVVàûßÿ~bïÞ½ÙŒ3ä´`)€ÆØ1yóqÄq•!ýSÔîì§Nú€§;wîÌ,XàèÓ§O£_ËëõÖ:n+¢‘³×—.]Bnn.Ž9€“s¿ßŸÏ‡ªª*x½^øý~‹Ü&}ù%KKmAlÒOÐ48ÄÖ)ü‡¦q¿@luñ˜®i–O ‹eÉãÃêÏå4îuâ#X$]UYn*D]vk—û2ƒž2`†¯Z þßÁÑÊ٣ņ Çûï?‡ÀíÖàñhp¹ˆð 4áhŠÀ}‡ÃötÝ!|§ðø±¦9¡ëqÍÐ4ÝZÜ'PGáñ+>}úF¼÷ÞA¼ðÂxè¡ `m‚n7³‰xPp›¤s€ûf†aÀ0† 9‡£î —¼G=òxEEV®\‰ƒâ—¿ü%ž{|ßTUBAÁkðûwŠ{À ]wCÓ\ÖâAîR5cÀ4ÝHK{ ))×Â׊'žx³gÏþØï÷ÿ÷×ý^þSqUz-rö\.W“7ß|SðÁëýÑŒqª º®cРA1bD½;GK9r¹¹¹eïòúʨò…¿þúÅ‹pk\„X[—®Ã­iœœKÃ+—0ºNAÈ-‚.¶’˜ë²®LÙ—Æ $ƒ.Þ“W:¥©òjM`D¤<B‹Ck¼±5–`ÉÙ’Á ”6Xö¾hÑ"4iÒ·Ür š7oŽ & [·nõ~o&·fÍlÞ¼ùŠÜ£/^Ä“O>‰>øS§NÅóÏ?ÔÔÔ¨Î)ºÿÇ?þqÊm·ÝF8 ’ôæÌ¸Ì=Žo*!oB0}úôÇöïßOæÏŸ!äC‡Ãaug5 ““ƒ3gÎÔøœ†ÈÙëJ)öîÝ‹ÜÜ\\ºt •••9 È@¿|M¶l±ùNB¸ |§JÒ%)'„û*1~ƒFˆM΂.ý®ïB5‰»Ú´E%çr\3å˜OJÐ#¬!Ë—#mÐ ëzÈïàM›6Õšd©¯œ=TT\Â/~ÑN'µ‚÷4¡„«WŸEBÁ A-Å´¦tIÎíŃ AáƒÉ­𷉉ýqà 9õº6ÀâÅ‹QRR‚mÛ¶aݺu¸á†ðÖ[o¡gÏž1û ¾É(-ÝŠsç^C °N§‡ºîËMsƒ—©ç€±ËÇòQ˯3Í Z´øRSoúZÿž¯ q‚^7®¸Ä=‚œ½­išÿ4 ãæx³gÏ& ÈIÙûž={°lÙ²zwŽîܹ3²³³Ñ²e˽¾D·nÝðøã‡5"etƒÁ ”·m ÷Å‹\Ú!q_xE3BG¨@© “D/ŒXëRž.ɹ¨='Š–ç*qkRc¦vS%è2B.šÀH)[BÏžHíÜY;£ÿþÈÉÉA~~~½®err2²²²™™Ù (‘ššŠo¼ ,…X‹Z[C¯1³»µó5£íøõ×uàŽ;Ú@×)*+}¸ûîu˜4©+z¨'cÃx#9>V­º!V3è¶”Í !èê\S•œËÚ2Y_fסS$&Ö^ Ö½{wKö¾~ýúzÉÞ !¸þúë1zôèˆröhÑÙ{Ïž=1oÞ< 0?þxD9{´p»Ý3fŒuÖWö^Ó=Êûヒ§Ÿ~ضmúõëW¯sËëôtÞ¼yLõË@qèÃÛQ¯“ÇÇפ—Û—_~¹½aRJ‡Lš4‰Í;WKII¹"¯Ý®];Lž<¹FÙ{cåìuRŠ`0ˆôôtdeeaùòå8þ<¼^/‚Á Õ÷DÂlÞ¾V­ _¸`‘g àuèàöŸRK ¾3dóWµé©¨qwh˜h§itEÖ.É:”בç ñÔì¹ØW‰9eŒòe©žÌ SŠÖãÆ…s îïàÆÊÙkCrr té2'Nl> [¦I,9»aBEö[iGLË/€øtÛw>Ý9Ž•1 ¼‰¬¦øD4‡† k€Â0|xå•8p 3göÄ5׸ ŽN ·ývöÜT¶|µh1¦ÞצW¯^¸|ù2~øaãwÞÁ}÷ݳkÿM¥~;ö;”—/„Óé‚ËåÃÁ—®'@×=BÚî!nðný:“÷ï[D)ÿ£.üÏ«!%ŠqÄ!qE3è„2}úô)árönݺ± 8b9Ó¼>²÷ÔÔTŒ3¦^röhQ\\ŒE‹áàÁƒƒðûýðz½Ö VT yåJxL![“2w‡¼;åR"â.)[Yu¹¯fÎ÷҇“ójcÖÔt…¤K‚nÕ˜…t‘=—R¶¾o½…÷Þr=öíÛ‡%K–Ô9³!röºpêÔW˜9óz%JnGËC³è\.)a³el2“®ë|¸Îç6DÎ-ê#{ïÙ³':tè€þýû#RsµÆ`ïÞ½Xºti£îÑÝ»wcÊ”)8rä^~ùe<üðà r,W­Z…±cÇúï¸ãŽ´fÍšQ·Ûmúý~Ú®];¶ÿ~&dïwxÀ Œ±º?Ä8âø¡égΜ9‡1v›¦ic)g^¯+W®Ä¶mÛ””s9{8c0 •••8wîòòò°ÿ~;v —.]ª1PªUV"eÝ:¸®´“™s‘Q—ûá+8äYwQk®JÛå¾ô Ô­T×…ŽZ³2è2 uµ£¹)“ê:ÆìØÔ:ÔNRö^QQS9{$PJ‘›û,XðÛß <‹.eîÜ'àÇ2{îpè"sÎ÷eÖ\×eI]nH,Ô' b Ø>_gÏV`úôƒHMÕñ‡?t·²è²c¼MÎy­¹aØÇÜ'0aš C†ìBBBû¨¯ËåË—ñôÓOã¯ý+}ôQ¼øâ‹hÒ¤Éù ¾i()ÙŠüü'ÁéôX‹ôDèz4-At¨=w ‚ËÇ㾦éƒaø`š>˜f\{í»_÷ŸxÕñ‹_ü³gÏþ0Üÿu¿—ÿTÄ<ƒ)röQŒ±=Oµ;{,áñx0~üx«‹ëÉ“'«=G×u <#FŒ¸"c!(¥HNNFvv6Ú¶m‹Å‹£¸¸>Ÿ¯fºoçÎp=j5‘Máˆh#éÍ`‚P2¦iv´œhºÎ·b„Š®DÈU¢.?|Î)€P’®FÇ)Ùr™5—Qò¤Þ½«‘sèÓ§222,9[$Iñ•Ê`tèÐÍ›wBYÙI«Lõh9_†!çèÄêÖªi†Ái$GÊ0ÆŽm‰[o‰ÄDLÓuëŠ0xp¸ÝµeÐ9Q—#UBel¶œ`Û Kbε"æ-ZŒ¬×uiÖ¬î»ï¾ZeGƒhº½·hÑ&LÀµÊxž²²2<õÔSøýïµt¼6\wÝuèÞ½{ƒîѲ²2<û쳘={6xà,\¸iiiz?„×Î;Ç 4h©×ë%>ŸºÝnÚ¬Y3Ú«W/:cÆŒ͘1c €ø]£^,Ž8®ÂG¨É„ÎwÜqýì³Ï®z“Ú„„Œ?Œ1ôìÙ3ä{%Ö]Úý~?JKKQPP€“'Oâܹs¨¬¬„œØ)IB“’àíÚÚ±c¼1?¡mÃ)Màäk1á„h‘9§ÂÇÐÞ½°åíb ˆ z$r®ìË ~È\p± !èŒ!ã׿FJ×øºë®Crr2öìÙƒììlèºÞ¨kyM ÀÏçCzúÍ0 Àáàv\ÓtÁ0˜ØòŒ9÷l2Í3è@¸I´Çµê"{Îíº”¶3¦”jvÉ›í0¡¨chÝZÃ[oõ„ßϥЇWâüyNcT ڛʖŠr7Ù®GÔäœ1†¿þõ¯xê©§Ð¥KlÙ²ýû÷áÕÿæ‚1GŽÌÀåËÂétÀåòÀét‹•]O=Qtñ€ø¨_ ÒÇ£TJÝyG[yQˆŠŠ¯œÜ÷küKãøODÌ ¤Òu3gÎ$3gÎc¬Õ‚ Ö†qKcåìÑ¢uëÖ–ì}éÒ¥–ì½K—.˜0aB£åì5AÊØÊËËQPP€ââb4mÚçÏŸ·ÀXòÕ.]8u Z `ub éΪiÖ>ÖëQ‘°YÆQÓ K‰»ˆŽÓä<¤ÆL1º!2wixÃIºŒ+ `ÔÕû¥—j¼6.— YYYÕdïW¢!O8úöý/¬]ûº%Y3 M³Ç§˜&QAȹœMF¹„T&äl<ûítê0M?ü~‚çžÛ ‡CÃo D÷îM@ˆü}ft~Èq*6Içä}&L@—.]ûXïÛ4MTVVâÒ¥K8{ö,Ž?MKƒa\„®Krè:C0hUSýÐoâ3°‚îÒFQœ¤Kr®iJ½¿F@) #ùLHÝÕ /R^obÿþRLŸ~ ãÆ5ÁŒm«‘s› ›â=›HK‹NÞþÕW_aÊ”)8tèfÍš…É“'[ãR¿ë0ÍJìÞ}/üþ]p¹Üp:]‚˜{àt&Àáä< š–T Ëæp2ùøÀ'öð{@~†¢¢Oâ=Žjˆ A'„XRù™3g’5kÖh\îhß¾½¹dÉMwöX"33=zô@nn.®½öZôí{ån~IÎËÊÊpúôiäååáСC8}ú4¼^o5CÌt•×^ ýÀkŒJ°$f„ÒŽªA¨ÚPCiF‘9·À(Ka¢¼€ê#T”-UÈzø !猡ÅÈ‘h=²î,nZZ|ðA|õÕW8zô(&L˜pEò¨Ñòž=³°jÕ난sÌÉ:…iÂ"ç¼Ö\­3S£åÒ’J#ê¯cGÊy'} Ÿ~æÌ9ŽÔTÓ €÷ÏžsCl×¢ÛYtªÔ˜ÑbnïKclBÓš¡E‹[|­FŒÌÌL,Z´ÇiCžh‘””„ïÿû0`6lØ€ &Ô(¯kÙ²%Ö¬YƒyóæÅ\îÞ²eË:ïÑÇcêԩغu+fΜ‰iÓ¦5ª&>„<ýôÓM|ðÁ'N\¿xñâWîºë®¹^¯×,--%†a:PšÓéÌ0Mó&BÈû@œ¨ÇñõB ÔOœ8‘‘ŒŒ izô‰'®lÚ´éšÅ‹¿vùòe:nܸ?÷ë×oÐܹsSbUÚ X²d ¾úê+ë1Ævî܉ƒ^µ9 ªª eee())AYYYÔãY}™™Ð7n©¨àuç„€ ’B¬QjTd♲¥BÞî ¼Ç¬=7MÓöPsƒ8UîžIH?D,“R°pÍŒƒ5–¬ÉëÞ ¨¨ï¿ÿ>®»î:Œ3¦Á*)éx½^ãôéÓ8vìŽ?Ž‚‚TUuDBÂE‘5çeüºÎɹ$év°Þ>/!Òǰ_ǧ¦ó ˆ®E 耚—çâÙsX$]ÎCçþøqÉèÝ» òó}0Œ ‚AÁ !|e¤Ia@ºt™Rëµ)//ÇôéÓñÆoàþûïÇ‚ ®Xò꛿ÿvíú #n·JÎÝBÚn“s]O†¦%@דAHx‹'8A—Òö€ð'ƒJÉ£]êèõnc²³ÿwq¥4Ú«$ŠE›9s&éÕ«)(( ëׯ?O)={âĉöÿú׿Ï<óLì5åµÀ4MlÛ¶ À¹sçФItìØ1æ¯#£³UUU¸páNž<‰cÇŽáìÙ³(--µ:´†ÃèÐþãÇ¡{½¡™r!]#”‚“hµ L¸qd²Œ0ÒRƦ6€Q›¿¨ÿþÕšÀ(ò5UÆf-UÆF) Æ@\WKö<'OžÄúõëQ\\ŒV­ZaðàÁ1“³É÷ QUU…‹/¢´Ô MK†aT@׉"a4Ínc“s%W£Ürɼ)7ÆRÎætüâAC0èï½ÿõ_­1jTšr)q—†X®ðZ3»Ö_–µ2é­Zhôuóz½Ø°aòòòÀÄ ЬY³FŸ·¾¸téÖ¬Yƒüü|4mÚ´Ö~ÉÉÉøñ صkc1“åÕtz½^¼ôÒKøÃþ€;•ÊþUTTàÅ_¼XTT´%==}¡ßïwéºnøý~Ã0 Ò®];~#u£”ÎÕuý̳Ï>»RHfãF/ޝ 3fÌ 3gÎDïÞ½IAAq»ÝZii©@§”jUUUŽÛn»í‚a®S§NýêÿøÇºO?ýÔ?{ölíÁtÆò½PJ±yóf¬^½~¿?âs¼^/rrr°cÇŽ˜öÛÿ†²ýرc(,,´”JQý›ê:ª„¶q#Ø™mk$²èrQB8IÄœiÏéS!åÖv&]fÏåbJ6=${.ˆxøV.b"~ö3šV㔌³gÏ"''gÏž­ñOß»w/>Œ‘#GbРAõòd`¤¬¬ 8zô(Ž9‚3gÎàòåËðz½0ÍVдdhZ… èÄ"è¼)\(‘l5üì8¹ÖD`Ý&猩äœXû<ÆZn+ëì5¶OÀW›6­[»aAäæ–ãý÷ËðóŸ§`À§ éÔ"é]º< ·»æ2«yóæá‰'ž@‹-°jÕ* 2$êëú]@EÅQìÜù9wŠq»n«!œ”µ;É"{ž$î¥pr®0A©Oø•ö¸µÐ^DŒU¡²ò’“¯^?ޝq7¥n4ªIœ*e“26Ú‰'´fÍš¿ß¯úé§×§¤¤Ü~íµ×Þ3lذk._¾ŒŸüä'WtdC~~>rssQTTòxff&ÆŒƒääºçBFI}>.^¼ˆ£Gbÿþý8rä.\¸¯×kg¼#@+)AÊ–-pQ2NE6‘sOâØAxƒÙFk"R.›ÁȦ/j„Üj§ÊäD”œv=\Â._ «1ëñÌ3Èüíoë”EUTTXf-[¶l´œM^ã`0ˆÊÊJáôéÓ8zô(Nž<‰³gs ëĨ5§“]³›Åib” ßç_4Ñ F³Á8ZÈÜy.ÓÃ"å¶ Ž1‚¿ýí fÏ>…ÿû¿>¼¤œM’|› ›â1Ó"æ¶Ì]’Û‘rÓ¤¸é¦\¤¥5,ƒ^SÃáp`È!6lXL3Ã5! bíÚµøòË/C:µ‰Sm™­ÿýßÿŬY³ðÙgŸ!++«Á¢K—.Åž={BoÙ²%ðÒK/Áétâ7ÞhÔëÔ†•+WâöÛoG×®]˜~óÍ7ïôûýAMÓ.—Ë „ËÊÊLMÓLŸÏG˜Ÿ}öÙ“Ž?÷Üs€Ë'êq\-HÝÌ™3‰¬7ÏÈÈ ÅÅÅ$==]÷ûýºaºÏçshšæLLLt†á ƒî .´Ø°aÔôôt–‘‘1êõ×_oݾ}ôÍ­j‰'““ƒ .Ôçì]ú7nÄÒ¥KQTT„ªªª¨äíáÐ._FÒöíp™&\÷ÄrÜGP2ærir X~‚JÐ%·$îò1%{ÎÂö«‘tÀ"ç‡'MB×ÛnC¯^½pÍ5× %%EÌ×PUU…+V`ÇŽõúû£õä{óù|(..Æ©S§pøða9rgÏžEEEEHÒD×/ I“Šo@¬†q.â4áh¢Qœí#pŸ€/u_%åºnמ«ä\%é¡Kú6Qç÷ ßz½&þþ÷rìÞÀÿ˜*ü¾t= cÇWW–9rS§NŦM›0sæLüìg?»*öý›„’’ÝØ¾ýÐõJx<.¸Ý¸\¸\ p¹áp$ÁéL‚Ñ ‡#š– ]Oä<„$‚‹‡àŸ£”zAi%(­€i–Ã0ø +`•*T¡M›ç–6îk¾W?ÿùÏ1gΜ~Ýïå? þï Ÿ–ŸŸ¯ IIIšËåÒœN§îóùÙÙÙ‡œNçÑ={ö|Ÿ/bæ<äw›6EEŸ>Hýê+‹8‡7Œ3ªip0» %:åcT¨ˆž›B_—„ÍŠ˜+XŽm‹(a 'èŒ¡ÉØ±è2m(µ³Î‘®ÏÖ­[±jÕªˆMÀ¤œ­OŸ>;vl½ål2»/ƒ$§N‘#GŸŸÂÂB!'LGrrt݆SÍ K›¬7×§Z#ÙF5ššˆ˜ó®U3Ä<Ð YYMж­†ÀÉ“^tèà!2‹Î,i»}^• sÃË3èT‘·S4o>ªÁä¼¶ÙÀ†a`Íš5سgÆ+YžrðàAkîj8*++1þ|lß¾=â¼q‰ÿùŸÿÇãipD–RŠ-[¶`õêÕÕîÑÒÒR|öÙg8zô(žyæ<õÔSW¤,CBÈ2Aƒý”RêÕxH^ †@<OP×uÒºukãäÉ“˜:uêü~?]³fÖ²eK&‰¹ Mq’ÇUGFFi×® ÚÅ‹µ¦M›j~¿_w¹\Ó4•••.MÓÜ<íÚµ3ïºë®¿H¼xñâºîÝ»ÿ¿aÆ9?ÿüswCþ×ÊË˱téÒ9{´ˆ¥ìýüùóX¸p!òóó,’ª Å[}@›7GÅÍ7#iÛ6Àëµéœðx¨pÈc‘1×+s®6A"ö¦‘MâXÁ{5‹ÎÄc‘zeb"Žee¡]×®HLLDBB‚õ7À¶mÛ°bÅ x½Þz_Ëheï²)ßåË—qüøq:tyyy(,,Dee¥èxn_{ÓlŸ¯9tý²•1×4éð 9·Õ¶? JÛ¹ÍÖ¬E©Ó$‚¨‹¦½"k.åívOBйtÞö $Q§4”¨ë:Åxpß}n†‰Ë—M,_îÇøñ ðT5rîóù0kÖ,¼òÊ+¸ýöÛqèÐ!Ä"ömCEÅqlÝz4­LÌ7wŠå³ÎÝBÚž]—á’ i)ÖÁ;¸k`,€‚€øœížCö©š4|}üׄ¸R;êMÐÃkÌ Y³fZ—.]HRR’fš¦n†væÌ‡ÛívBœ@ÀÕ·oߊÌÌÌû·mÛvsAAÁS ,h߯_?Wÿþýqï½÷âÙgŸ­Ñ¯ ”RlÚ´ kÖ¬©QÆ&áóù°xñbìܹÙÙÙ–½KIµ×ëEii)Î;‡²²²¨ °Ù¾=*+*”ŸoG­í“ó&0RÂØd]dÑ¥¤M'„7€ “±„b)cS·|Ú0NÍžK¢nŠÇMÆ`vîŒ6O?@ €„„„ˆò³Œš° IDATúŒÑÚ·oòòò0bĈ¨eï2³\^^ŽÂÂB;v ‡ÆÉ“'QTTŸÏ'šó騪ê ]?b5ˆ“õf¼[«&¶€, •¶ë°¦4Ä ºNľ4Äj­™-ekÙ’À0ü ”áÑGóЦÏ=×ééD’³©ä\Ž«‹R‚Þ½£/-PÇ ÕõÝX\\Œ?üÝ»wÇøñãc*{¿|ù2rssqäÈ‘:Ÿ{êÔ)¼õÖ[µŽá{â‰'¬ý]»váúë¯ê}œ .ŒIÓ¢(A !É„Rªëº®»Ýnbq8$Àét1Ît8(,,d6l¸Ã4Í‘3gÎ|\œ‡ÅIzWáÍa•†p$h¦iê”RÝ0 =8’’’N§ÓÉs‰åÑ4-Rš”––fvêÔiÁòåËïÌÌÌ,yõÕWSÆŒ•¶99{´hŒìÝçóYß±¼OˆÏå„ÓéD0Œ^⮀%'£rȘ;w"áÒ%E ‹$³0‰»$åQªKÜÅùÕ¾7ª ä8ü=ˆc•œ3Æp±E :ÍÛµC³fÍдiS$&&Âår¡  ‹/®UÎ-j“½Ëšóòòrœ;wùùù8~ü8.\¸€ŠŠ «™Z8|¾žp:¿´3_²˜Š+#»å[¯&ºœiͬ ½,yãsÏ leß ¼®]rÀîOÃ`gÏå>¯;çÝÜùñ¹s- bÍ‚ÇùÛ-Z„iÓ¦AÓ4üûßÿ¾bʯo:|¾‹Ø¸ñ{`¬XÈÙCÉ9_rÖ¹l — ]O!)дT’úÿÙ{ó0©Ê3mü~Ï~ªª—꽚­Ù1¸ Hc5‰“8™/j¢¨ã•Ï,šM‰çGò%WbÇ%j&3™Ì$ŽšIXA$(Q±Y¤ÙšÞ»Ö³½ïïsÞS§šî¦«»cú¾®sUuÓ]uª¨>ï{?÷ýܧ K`Ìö .i ¹KÊm0f]ã#vÓéçõ=8×Þ–œVÐðƒà‹1WÎmÛTU•cR8–mÛVLÓTc*¥T»è¢‹öš¦yÏÃ?|KIIÉ5<òHéêÕ«}«Í¾}ûPUU…¼¼¼~Ç@ll€[Ý~öÙg1cÆ ,\¸pÀ¶wÞƒ¾ÿ~¼ýöÛèèèÈùƒg^pÄxðH¯ŽóÇg‚à÷›Q¸cR¤1÷ »Ï Ù ­=‘tð[®¤Õsþœ,ÃŒHÒwÀ²Òé9‰DV"w¿ßÓĺuëðî»ïžÑÎÆÇŠÄb1?~{÷îž}ûpäÈtttø66þü¦9–u¢hxrX3a0@v¥Ü­Œwïýç î‚· a~?óXÁÅø¹çFãç?oB"aÀ²2aqÙyõHÒm›bäÈ/   ÿm"Œ1¼ûî»X·n]–½?Ø·o8€Ë/¿—_~ù lq–eaóæÍxã7zÜøæûý÷ßÇÂ… {µ½·´´`þüù¸õÖ[ñÓŸþ´Wõ«·– À%íëÖ­ƒ øío‹n¸¡ÿ/pàmÞó(¥"¥T „¶mBqGí9H$$#BE²,7ضý«‡~øío¼ñ×Ë—/>Þðj8Œ³¾€ÆÆFÇÉÈ‘#‰a$™L b^^žhY–$I’Ä“)¥Š *cL£”ê‚ h„mîܹ»g̘qB×uõ‘G™÷ðÃOyâ‰'ô¾‚^:„U«Vå¼8Nœ8gžy¦_¶wÆvìØuëÖ!‘HàÊ©Y–¡( TU…eY>QÌUIg²ŒÔœ9 õõ9’EÎ)!p˜›Þ.q’ô¨ž‹Ü1ô¾?ðîûY8Èôàí‘1cpdÆ D‹ŠPQQ#F ¼¼’$aÍš5عsçnÆMÓÄÚµk±}ûvŸÀ÷aétmmm8vìŽ;†ææfÄãqwÄm/ç@i>’Éñ„ÏQ—!érì=ç}ç™[Þ{.ŠÜYÇsmEÐ3…{ÞÃî¿Ñ8xž`P6Içß=øÑBØ·ï³p÷ó´k×.<ôÐCX³f ¾ýíoŸuç×_3l;‰×_ÿ$Ló(t]ñæÛK$¢¨Bˆ¢æÁ‘j äC äƒî^HÈ9'å6 ŒYYÄ<“7”»³doô{§ ƒãÖvSQQA ÃlÛ)¥¢ã8¢,ËcLvG¡”*„ͳk†dYÍž=ûåÖÖÖO?ýô—®¾úê1áp8<ðÖ¯_|ßúÖ·z=ŸÁØØ‚رcöîÝ‹ `Μ99ÛÞ›››ñ‡?ü °mÛ§‘+RÓ§ƒ½ý6ÐÖvš}+é¼*N=¢žEνŷ§3~Ÿ/¼Á…دŽsrŽ íãHjZn¸ã‹‹!ËrÖøÆ¶mÛÖëLë\ÞSng[¼xñiž' œÛŽK/ÕqÁnÏRp!.ÂA‚è˜6íý~?Oœ8•+WâèÑ£¹þWø°m¯½övìØ1`ÛûÞ½{±zõêíìýE<Ç‹/¾è+[Ýç’—””àÕW_Åwܶ¶6gý{_-‰D6lÀû￯}íkX¾|9Âáð€Ïu0 ”FA ¿-n[‡¦i,NÃ0 Æc†a°Ñ£GãóŸÿüžÿüÏÿü¢,˾-÷£+éÃ8[ðÔóà¬sR^^N‰±m›èºN!DAQ!NKŒ1I nܱ,‚B)U !*!DÕu] „¨“&MÚûâ‹/Žž={¶úÙÏ~–þÛ¿ý›¤(™¼Ù3µµ Õë;“í½¯k,!nV‰¢(~Žc ¦iú¤1§?MB`L §¸ÚÞ½P=Ë»¯ó•“sïVô ùcî^¤~w‹{€˜û·|€ )‡W¼O†B84ib£G#??3f ª««qüøq¼ð 0 cÀ-gBpŸ°páBhš†T*…ŽŽ´´´ µµÕ/ÖŸéýµ¬ H¥â ä$2òÀtnQæ„=›œ”óû|Œ'ä™û=µÑeI2ÈÞsIz†¨óûî¨X†ÆÆ…¤*<ú裨®®Æm·Ý†ÒÒRìܹ'NÂwýãÆ(6mº Éd=t]†$IEÙ#箊î’tÍ#åªOÎ!äYÛóAH€¸qiïહ—˜› Ô ¨ç&(µà8(µ!I½û}Œ‘[…òo 9KaœœoܸQà!0áp˜8ŽC(¥DÓ4R**Š"¥ÓiYYUE…1¦Ðcº ¡¢¢"§¸¸øù;wNºì²Ë®ýÎw¾Sð /ˆ/¿ü2ø"ÜÚÚŠßþö·¸å–[F‡ÔÆÆ‘N§±zõjßö>jÔ¨~ýΫ¯¾ŠmÛ¶Á²,?8,Øc–Ó¢+IH_r èûï#ä-òÁœGÙ«Œó¤Vðƒ`¸µÛÜ»[غ÷™«äA‚ÎÉ9¿Ÿ•Ð  £ .\ˆªQ£‰D ë:dY!GŪU«pâÄÐÙtz²³ñj²aèèèÀ±cÇpøða466f)ç=Á¶G">B:ü3Ë:½ZîZ“¸é/³P’yä<8×òô…8Só öš¹ÙÚjã®»NâÁ£¸ôR9ËÊ–QσaĈ; ignÉÅÎÞ_ ÄöÞÞÞŽU«Váƒ>’s\•û‰'žèÑö>{öl¼ýöÛ\‡G*•B$éµå‚1†wÞy›6mÂŒ3°cÇÔÖÖÙ¹æÎÃEQ QJcŒB(cŒ2ƨ(ŠN*•¢’$1J©Ã£±XŒ& ¦( »óÎ;_6 ƒVUU‘Ç\_¾|yr˜¤ãl€W‚êùUW]…úúz.i---…iš$N ¢(BE‘8Ž#0ÆDƘDc¢ "Éûžw"~úÓŸÞºk×® 'Nœ,XPýío[[¼x1áûÓ4ÏÉëíÉöÞŸk,WÑùž¦;aÍ)Õ=ø{••ˆWTÀøðC„ [–K Óî´ºg÷ óÛî zƵçî)á[ÛùqäÈ ´·×@UR©öïßñãÇ£­­í¬Mù¸`ÇŽB[ÛfŸœ»‡ Q”!Š Áí=w‰¹ê‘rWA'$ B" $@q÷BŒ%á’r@Œ™`ÌðÓ'ê”ZžznÃq,Èryg:Œ¿Eô‹ “®²wÝu{å•W‚ê¹@)DQ *é’$I’ã8²ã8*…¢P½¾3 €>vìØÖQ£F­üùÏ>ë—¿üeÍã?®×ÔÔpÙþóŸãÀŸÿüg¼ñƧõ‹Nž<‰gŸ}ÖO{ïM=Û¹s'Ö®]‹x<ÆÁM÷æ=f܆šó¢KÌiÓÀòóÁvïö+Ö¼·Œß‰›ØÊ+åAû¿%ÈTʃ‹o÷>3œ„¢›Í#çUU84w.ÊËÊP\\Œ¢¢"äååÁq¬\¹2g;{´³-]ºcÆŒñIWkk+Nž<‰S§N¡£££Á|©ÔlÂ’QO3Ÿî •-ã à=眠»‹2 óŒz޽׌?~°RNü0àž{"X°@ÆèÑ,ËA2é@Uyø]¦bÎIzKËÔ× hiù-ZÔ£å’ÛÙׯ_ï[-‡ý±½Û¶Í›7cóæÍ9ÙÙû‹ í§½wÇO~òüîw¿Ã½÷Þ‹C‡ömllô-©ÿú¯ÿŠø‡òóÌÞuC'„ØŒ1Ëq›b°ز,Ûš¦ Žãˆ¦i:Œ1!NS˲ˆiš$‘HgžyæJé¼7.eŒ9çb£<ŒaQYYéÿÝ0~ŸRšõa$¼ï{b.#Œ1þOä /lbŒ%o½õÖÉåååêâÅ‹{Ãx6qâÄ <û쳸馛ðÚk¯6-¦'p’®ªª_Ä—$ ÉdétÚ/Ëy %ÖØ±è¬®†vàB‡A¤Ô%ç€_°—¼[€à‘ï> øüáLÁ^’pbÌœ¨©“$ß Ë2’É$"‘6nÜè»Ïå5Ç4M¼úꫨ®®†iš8xð`¯#Þz‡ˆTê"ˆâr:±g~¸KÐ9çäÜê’)ÖóÑmŽƒÈ9ßÉ9¿Í&èîÞƒe‘r~46ŽÀ‘#Ó‰Â~ƒ®ëøô§?H$Ó4ñÀà›ßü&*++øüpêÔ[Ø»÷§Ð4wJ+²IÞ!{Öv%@Î5‚wÖ¹BB^Ïy„äÈЗ˜»w÷HÒT€œÞaÂqL ÛPÕ¿­ÿ›a­àÌÈIAZÙà]»c±á•a]× cLA0MS$‰8Ž#c¢(м*.c \Ò. ‚ _pÁ‡’Édû¾ð…Úyóæå?òÈ#ÒÅ_Œ;vàÉ'ŸÄ /¼X»v-òòò0}úô!· ò>²žlïMMMX¹r%>ìÿ<_„$I‚ªª>9ç=OùZ£G##¼cÓõˆ¾Oνó¹} =÷™ÙÈ^€³È9à‡Ò±@µÓ[€&ËPUš¦¡ººK–,Áĉýv·Ñ£GcÙ²eøË_þ‚W_}u@#Õr½¦ª*ÚÛÛ …0bÄÄb19rét:§÷‘Ò$ó@éÛ`¬ EzÀFëîx±^99Ïs—¤Ùq§»ë‚mo.xQ ¸7@9?th œŽpXöÿo!þg4–XVV†W^y7nôÉù±cÇ0räÈ!x÷ÿ:ñÆ÷"•: ]—½ÖÔ Aw‰9'çîAˆâ‘t „(p5F.uKÀí;ïc]`,á”&AiÚ#ëiPjÀqLØv†¤3¦"™pþÞa|$1(‚ÞDQd^e óŒBGýÞJÞfíM¡åååñÒÒÒƒëÖ­QSS“§ëºOoæÌ™ƒ9sæøÄwï޽ذafΜ‰¹sçæôÖÊËËQZZŠ®®.èºÞ«e˜“ô åŽâ“Ú,¶TQž:ÆØ±Ð÷î…ÖÔ”EÔ¸—?8­×,‹¤3Ö£} ÈPFÂâùù8|Áè(-õ­ûº®£°°µµµ˜cÆ Lš4 6lÀ¶mÛúTÒC¡DQ„$IYöµ¾F}ô#9{6ÒÐ÷íƒÖÖ›1WR@ƺÆûÌ|õœõƒÓ z:AcM ÚFŒp_‹(ú‹ò”)SpÝuסººáp7Üp.¹ä¬\¹rHƒáú‚¢(¸òÊ+1sæLtvv‚1†iÓ¦aÇŽhnnι¥À².@"¡Ø ³(ò ¹»(»‡$0æø}gÙ6¶î½g+·°ñïÏ7Øk¬–¿õÁsÏ9¸ì²4&O³eEE®¿þzFïìÙ³1eʼòÊ+xûí·Ï™’Þ=0nÁ‚˜1cV¯^=¤Áp}A„Óãn¼ñF\tÑExñÅñ?ÿó?ضm>ûÙÏâg?ûÙiéï5‚`°c6¥Ô¦”Ú²,;–e9„‡âPJi$¡ñxœ¦Ói¦iE‘sï”Rx_C¿ ,ä#Pä@/¾øâSÕÕÕI˲”Œ3FêÉY2íìg!³fÍÂäÉ“s¶½óUU‡aYLÓô÷œ$†áý{RÙûÕæ%˰e¶7Z–ï‘„€RÎIŸ¢(Ð4ÍWÍgÏžk¯½ùùùþy÷†P(„OúÓ¾»`¨ö ªªúÁ±üùyÁC×ucĈèêêB<÷Ûárw3Š0Œ™ 4‚PhÀê¯Pϼ@8æ‘ræuNÐ3ÖöÓCc³³i¥¢gt÷Ö0ìÚ5 ±X TUöÿ_FމO}êS¨©©A>÷Ñh[·nÅý÷ß·±wßE׆ HíÚvò¤ëeQ‘$´ X(iÔ(Do¸‘ÀxFJlÝú-ïÿHðÕs~"z]:œ»¤œï¸·ûÆ€kþq<Å<J;AiŽ‡ã¸ ºKÎ3wÛ6½Ã%è……—û7ò<ƒñ~ŽaôŠ~t¾ï7Û½{7€ââböá‡2Çq˜®ë,;±XL0 ÃÁÁ’eYrÇ$„HŒ1™’æ=•„!÷*çÞD`ŒAUU(ŠrlïÞ½y………E•••Y¬[UUÌž=³gÏæç‹ßþö· „ ®®€­xá|¼ a|üìEÿàƒØÄ‰)tttY–©išŽaDQžäŽÀÄ&w…PcÌanÀ¡ÀfŒÙ„›¸á‰!Ħ”Ú„»²²Ò&®÷4ñâ‹/–uvvjŸúÔ§PZZ: ×Ò“=—ßÍÕödNÆ5MCUUæÎ‹’’´´´ ¹¹ÍÍÍèèè@WWR©”_ü͉»ã8Y rpŸ´ÚK’ä÷ÅóÀ[É ƒ5j®¿þz?<—÷¤ººwÞyçØÞ/¼ðB,Z´è´Ñ«üœ$IB^^ªªª`šf–0’H$ÔrhYÐÕUŠPh/(mƒãÀ?l›+èÌows‰:Ÿ,ÜUt÷œùíéE–L±žàøñQ8rd AUÝ¢I~~>,X€ùóç# åä›6mV¯^í}Ï=÷àæ›oÆe—}4‰"³m4ýÇ ý¿ÿ¤« ¢ªBTUª A–]‚.Ë ‚Fˆ;šøÐ!´þ¿ÿ‡ŽÉ“Qõÿ/]ÇÎÿŠx¼¡ä£ÜC!݉¹ä)朔E)ÜÑi|4¯Æ’A{G”&à8IضKÒm;}9·,ee ÏÏ;Œ4ú­ I:Ô×׳ææf´··“ŠŠ &Iíìì¤ííí„BeY¶ÇŽãˆŒ1Á³« ¢(ÆXV_%¯”{Lß°xÒ‚‚ÊKíÚµ«hüøñ!]×{V…'꺎T*…T*Õ#Qω°+ Ìêj˜ÕÕˆ9ä–(mmÒi(†Ñ0 $˂״ GUáh¨¢ÀÑ4yyHTTÀVÿ|e/¸†ÛÄ.»ì2Ì›7ÑhŠ¢@’¤ÓfApÉ%—øv¶¡Ns/--E]]Æ—õœ¡P%%%¨ªªBGGb±,ËB*•Ê™¤SZ‚XlB¡·ÁXÌë3çý^.1çI­‚À«æ [÷´Öž¬lÁJ¹‹` L¶•R†£GGaÿþ©$ãÇ— ‰@UU¼òÊ+¸úê«1cÆŒ>䪪*|å+_Á»ï¾‹uëÖ ©í]’¤>ÓÛƒ˜4ijjj°yóf¼ñÆCj{D"X¸p!¦OŸ~ÚçòàÁƒøÇüGlܸßÿþ÷ñõ¯rÀÂùE1A)M ‚! í¹ Ó4-Y–mAI’N<§”RÚÞÞ΢Ñ(êëëà’¦åË—ÇÌaîü—a cHÁ[&¼µœ-_¾p/vBcc#-..&’$ù…U$‰Y–ÅDQd¶m3UU¹ÛŽ2/NE›1fÃu’X¢(Z”RÞ`aƘI±¸îtuÖ¬Y§Þ|óÍ’U«V)7ß|³¨iZ¿ÙdììýE®¶÷ dYƼyópÅW@E?x–“NÓ4ýÂ<G"‘@2™„iš0 #«¿®¦¦&?~Œ1¿Ø9èÿiš „`þüùx饗°téÒ=ÎÙ5 4>õÚŸB2 IÓ é:DMó :Q—œ»ª ¯e“RÛ†³g>¼óN”­XŽ·ßþgŸ”»Ÿ{! ž DÌ:ºûRݽšƸó@€«ž§¼ÃUÎ)Áq89Ou#ç†OÐ-Ë‚ !?êùx{‡ñGÎ=èÞE€-_¾õõõ¨­­¥$IrlÛF:&à8Ž …ˆiš$8œ¿àvxA|ó™Ïࢋ.;›¢(YóÏ»Ÿ»,Ë(((Àˆ#²6'<œ/מÆt$Ÿ€mï® sYÎÜwôlK[Ïw~®§ÛØÜçëÙÊf*˜ˆææQ~o¢®ë¨ªªÂ'?ùI\{íµ¸ûî»1zôhÜ}÷Ý}¾¦X.Ï„\æŸsȲ<¤¶w^š?þic– ÃÀücüó?ÿ³œÇÛTþÀm_Œ±8!$ 鹎Ò^Ï® À2MÓN$N~~¾SRRB;F£Ñ(ûàƒØUW]ÅIܤ÷õåÒŒ±¿œ—8Œ-xÿ¡‡bð¹Ï}ŽÕÖÖ¢±±ÑQU•8qš¦1EQX<gŠ¢0J)#„0J)µm›J’Ä º À¢”ɹÀdŒYLBˆA)5c–×g°C¡}õÕWŽãÈ'OžT[[[Ë4Mkkkû<ÿ\ììýÅ@®Á555Xºt)Š‹‹ýïñB( æT€Rš55†)øfTsA‹Å°aÃìß¿ÿ4%=ApñÅãꫯÒQv¹ÚÞ{²³÷… ƒ±¸¸Øw†Ãa„Ãa?~mmmH§Ór2:Nb±R¤ÓÇ ë¡( Hƒm»$ï2û~°ÓÔóîAq”tv–âøñ‰H§ @¢¸{´ÒÒR\sÍ5˜9s&¢Ñ(t]9ܽÖc=†;ï¼ÓwÃ}ðÁ?~üyÍhi^µ Ç–/‡˜JAÖ4ˆšætAU!z·DQ@$É%è„dÂŽm›÷ é4Ö=öi˜#Ú¡ë™bTwBž!æÙ³Ül"ê‘s ”¸…ÁUÒù8µ¸§šgÈ9?,Ëðȹ嵰X°,ן·÷ø|ƒñyÃÃ蹎YËRÑy¿Yss3JKKY8¦Š¢`äÈ‘ˆÅbœ€2EQà8y}e”Rêˆ¢È ¸#‚oacŒYޢ˫ã6\‹›À&„8ªª:ªª6îÝ»7F£}^¡¦M›†)S¦ µµ€»¸ýïÿþ/>÷¹ÏáË_þ2zSãÏMÓpÝu×ù¶÷à6^Éå}隦!‰À²,†BŠŠŠü^©D"Ërÿˆ¹%+×™žŒ10ApûrËÏ#HÌUUŨQ£PWW‡Ñ£Gûjy®þ¡°½O›6Í·³÷„`¿YYYY–J Š"ZZZL&Ì'Â0¦Â4ÇB×÷AÓNz½f™*yо † ÚÙ‚ªy÷~3æ‡À°,ÇEcc “üPž‚‚\qŘ?>¢Ñ(¦NŠ]»vùjð[o½…I“&¡   ×W5PËeÑhô´Qj¹b(lï£GÎ¥Äúõëq÷Ýwò,<ÿüó)% WPJc„¥4EIK’d0Æ I’LI’¬x|˜VTTÐh4Êx@à«ç¾Âéa €/Bf3ÆÎ<ÀyÃÈ|ÀËÚLœ8‘jšÆÂá°ŸŸÏ‰“e™J’D‰uÇ‘eÙ¡”ò}€å8Ž$Ç3!cÌ€Ûø™fŒ…=bn2Æt®¤{^AUUÕN¥RíëÖ­+~÷ÝwÉ-·ÜrÚ9ÆÎÞ_ôç\PP€Å‹ãL…~Ž|OÑ“3¨·u¯¨¨_þò—±ÿ~¬^½ÚßÑWËÐP¡?¶÷ /¼‹/>££±'ð™óÑh𦡰°•••hhhÀ¡C‡pâÄ tvvÂ0 ŸÐÿ½eUòª!q(ÊI¨jTµÃ/Üó½@¨w/Ü™}㈈ÅJÑÕU†X¬”*žáþÿæççcöìÙ¸òÊ+Q^^ŽP(Ås?Õg–îc¸ñÆ!I~ùË_⢋.’Çï/ìx ßúâë×»íºž!èºÑSÐ…n¿ÑŒR0JýñÄp$‡£û  ™œ®š»{c®žwo%½cáªæÄ»5Á˜áô¤×wž€m'<õ<ËrtË2`Y¦¿··,Ššš»ÎéûûQÁP:m?®ÈYAï¶ÙãsÑQ[[KàÃ?DGGlÛFAAëììäæ8“e™ ¢(:Žã8‚ Ø„Ÿªá¦·0[Ìõ“„!Á…X :”Òäûï¿_TSS£ÓÞ»CE”••‚É“'#Nã‰'žÀœ9s°hÑ"ožõÀÂeÊËË}ÛûÚµkO³½sí5š¦áÚk¯ÅÌ™3aš&ºººÐÞÞŽ––´··£½½±X,+hŽW̓‹ ¿@sNñÃdx_Wò{ê/ ‡ÃX´hfÍš5èj,±½SYûó€ks®ªªò­_ápÀ©S§‹ÅÔsÆXÉä,¤Óž­ ŽãVÉE¬¤¾¬l™ôöî·DÀ©SÕhlœÛV½…Ûc6qâDÔÕÕa̘1ˆD"$·o*XLz衇°k×.üú׿Æ5×\Óçëâ–Ë;v`ݺuý²\ʲŒË/¿—]vÙíìýÅ@lï‘H‹-ÊrÅp466â¾ûîÃK/½„o~ó›øîw¿;à‚ÛGL„¸(Š)ƘÁ3Ç1$I2MÓ´cVAA­ªªã8ÇãÔ0 ÚÞÞN«ªªXmm-–/_Þ9€ïíÃ}CŽ@!ß·»××דææfa ‡ÃŒBc± ‡Ã4‘HPêö¼9”RG’$^˜·(¥– þÞ€Rjð » Aò î'¨ãÇ·+**mmm¡Ý»w‡Ç§‹¢HE2;{ÑÓ5XE|âŸÀ•W^9à©$Ýq¦5|„ ¸ûî»±e˼þúë°,«Ï–¡³Þlïåååþº7ØÇ—$ ‘H$‹¤;‡ÂáÇÑÔÔäõLÛ¡4‚tz<Òéñ Ä€ª6A’b$¢˜†,Þ}‚0&ƒR Ž£Âq4ضŠTª©T)Ñs͹ë,_ë'Nœˆ… ¢ººyyyY9@g„¼þúëøþ÷¿Ã0ÎÊsô†Öqðßé耢ª5 ²ªºÊy(äªçItDUÝÃÛ”yãN@M\›;³mì7óößÄ#äü6Hγc•™?ÑÇ!®rîNâá&·;LPÊôÇ%é¶íöž»äœsÓWÏMÓFAÁø æÌ™ƒÇ¼ÇÀº¿&¼üòËøÌg>“¼å–[®àÄ\E“1f)Šb[–e§Ói['NÓŠŠ j­ªªâáZX¾|9ë˜cç|Ÿ°|ùrÂIúĉ‰ªª‚$IBKK‹@)5M“B¡˜H$dÉ­D©„YUQ•1¦9ŽÃ×þ€¥4$‚¿g~ŸÂ÷üPø>aß¾}…ï¼óNÁ¢E‹ð_ÿõ_äl®}}!N㥗^Â5×\3à0»¡@{{;Ö¬Yƒn¸á¬î΄††ìÙ³uuuCn­î±R©ºººÐÔÔ„cÇŽáèÑ£8qâZ[[Ç‘L&>Ú®¸vhŒeRçøë¾¢(‡ÃˆF£(//Lj#ÐÜÜŒ /¼^x!B¡dYî71¿óÎ;O>ùä Î›ãÀ˜1cðÕ¯~õ¬ÙÞüøÇ8ùØcPdЦAÑ4— ‡BB!ˆÞ­ ¹êy(ä’sEÉ($ŒZ¨a€œdN*…ßUüXA¡]—½Cª*PU Š¢BUu(ŠYA’tïAuˆ¢ wºAàÖwn÷­éRsL(\ –Å4LÓ€i0 é´ Ã°0}ú¯1zôgdÚ3þVp÷Ýwã‰'žx†Rú•ó}.U X Ã0æÏ@Ess3F£¤ªªŠuvvŠÐÑÑA5Mc²,Ód2IApDQ´Çql—9Ù’$Ù„“xÉÅTÈ‘™e`zj:?t¯?Ý`«ªj«ªjíÛ·/RPP ¦½kšæÛØ‚˜ *øµ¯} ùùùxÿý÷ýï½ÿþû˜:5·UU}ÛûªU«ðá‡úÿFqÝu×aÒ¤Iþ÷ø&'Ùº®g-ÁdÖ zÞXmìü˜3g°zõê,Kñ¹°±qôe{ï+•µ?à…UUQRR‚p8ŒÒÒRŒ9‡‡~ˆãÇ£½½ÝçËÍÎ8N!§éô$ˆbŠršÖYŽA’ì¬ôŒ’ÎÏϽ¥TA2Y„x¼ñx9ËØÓEôSéçÌ™ƒË/¿eee‡Ãgl5E÷ÝwŸÿõ·¾õ-ÔÔÔà«_ýjŸ{MÓPWWç[.ƒ ŠŠŠ°dÉ’AÙÙû‹¾lïcÆŒA]]]vö­[·bÙ²e8uêžzê©AÔ>‚`’Š¢„+™LZŠ¢Øš¦Ù‚ Ø¢(RUUââb€Ö×׳ªª*¶|ùr¿½?OBù€CŒ±ÿ:‹¯eÃìTÓ<ˆqãÆ±ŠŠ *I5 ƒ†!Ú¶MAp4MslÛV(¥Ž7–ÕEѲ,ˤ”š²,‚ \Mî<žÁ lï~{Üĉi:Æo¼¾æškÈ}÷Ý'Þpà çtgÜÙÙ‰—_~{öìAGGêêêrÊÀ*>|+W®DSS~ÿûߣ®®.«÷ý\;‰!¸ú꫇ÔÜc‰¢è‡ÌŽ7hmmEcc#¶oߎ††ضí·pÑ„ "¹:òÜçW²Ú ¹ÂIyii)***PYYéïA›››ñæ›o¢°°ðœ¹zÃäÉ“ñÍo~§Nƒ>8´Îê¿ö5´=ÿ0$»ð`•–/_NùF™ªª,³––ÊÕôP(äH’d°Çá ºE±,˲c¦ ¦7?Ýï5ÀG…á)é^¯ºoÔ qýDv4µO:¥>ýôÓEï¼ó޼bÅ !—^³ñãÇãèÑ£øÍo~ƒ‘#GV¯^h4ŠK/½´ß£iTU…¦iîq,øø~œ¯‹€$IÐuÝO†„Ȳì‡Èa̘1èèèÀ©S§püøq;v 8~ü¸?b„'é÷”¦ßÁX¦öŸŸ#“5à.¡Pyyy(--EUUFŽ Y–ñÎ;ï`̘1=z´¿9èÿÏm·Ý†%K–øsôèQTVVöI¸‰—zËó•ÜÊsøßJŒ1<÷Üs¸ÿþû1aÂlÛ¶ 3gÎÿ{ýç9”8räV®\‰“'Oúß›0aêêêPTTtÎÎã½÷ÞÃÚµk‹Å¸ $år¶.NÁ*¹eYH$زe þüç?#™LÂ0 ضüü|ˆ¢˜5úއÆ[ ºÿ½to+àA|¼‘——‡h4в²2”——£¸¸EEEEo½õV–‚‘——‡E‹õ¨uuu8~ü8žyæ™ÓXÛÚÚ°jÕ*ìß¿ßÿÞ@Æ~ø!V®\‰S§Nùßã#ÝŽ9‚eË–aß¾}X±bn¿ýöm•Ùë}ùË_-I’ÓÑÑሢè455±êêjúÁ°ÒÒRV[[Ë€¡ë7'„Hp'j ÷®㜠·}BPM—$Iàû2™Rª(Š"÷ ”R]–e^:²÷|¿ {¶w®¢Î]{ªã8jccc$ 'Ož,¼õÖ[ÅÛn»mH.2X³f vïÞÝïß©¨¨ð'ª ‚vöþ‚Ó¡´½Ÿ)H·;ÎÆ>¡7'C_à©sçÎñÂ{j7 Ú郶úîê:¤R©œG ö˜Êq6ô cxæ™gFqÓM7åüûN:7/öɹª(P=Õ\ÑuÈ¡äHr$)˜Ÿïôü|yy ‘H((ŠÛOhY é4X<Ãéê‚‹awbÖæ¿ŒpX„®‹Ðu š&{¢‰ÛƒîRTȲ{H’ê©ç2DQ H9é@&Î¥6(5Rã¡pî-'çé´Ãppôè߃ñ(**ÂèÑ£1~üxTVVBUÕó:Úî\ÂSП¦”Þq¾Ï壊³BоílÄÑ£G ‚ ¦iŠÉdR’$IEQJ$Š$I§õ@8\–íM ÷f{M²(û IDATgŒ©pCb¤C‡‚þÅ/~!ûÀsA[[:::0nÜ8444àÁÄí·ßŽùóçûjìæÍ›±yóæ^ªÛ{ÝÄãq¬[·;wîìñâ/IæÍ›‡+®¸â¬ÚÙN:…•+Wfõâq.ìl€«`¬\¹mmmY¡1|‘D"˜9s&òòò‹Å‹Å|ß}ì]°RΫâ||^~~>òòò——çWFùÏH’„]»vá•W^9ÍÆÆ1fÌ,]ºeeeƒ~ͱX ßùÎw0oÞ<Ü|óÍþ÷-ËÂ믿Ž-[¶ôø%„`úôéX¸pá€ÆÜär~k×®Å{ï½wÚ¿™¦‰Í›7cÛ¶møû¿ÿ{üøÇ?FIIÉY;—<‚Þuë­·ŽèÍθÄè¿ýL „¼`cìÿŠÇÆ0úƒ\÷ ðHºa²ªªrpŸƒíï²Hºw(---¡?þñå555ôü£0Ð}‚ã8زe 6mÚ˲ôÓ§OÇ¢E‹u îÍÎÞ_ UÊ|ovöþ"cáÂ…ƒnÏ;pàV­Z…–––ý~II êêêÜòdÚÊú;M¥'ôe{?Û½;}ôQ444à‡?üáCe¥øóg?‹ØÆÐ¼q²ª¦eºGÎ¥pR~>¤¼<— ¸ zAH$„B ²ìîåR)—œwv‚zäÜéìĪô*ì/Øí…Ãq{»Ô+A—$’¤x]òÈy¦Ýwäšíõ¦s‚nÂq,8ŽáÍ;7ê¹ Ótmí†a£µu<ÚÚn‚¢((,,DMM ¦L™‚²²²óâŒ8_&ègÆY#èþô°S\ ÃEQ’ɤ …D^)wG‘$I!„¨¶m«ðSAtÇqxÜ_t¨”÷’ö®1Ʋišê¢³fÍRV¬X! ¦çëàÁƒxä‘Gð‡?ü{÷îE[[V®\‰ÎÎÎ3þî`ÒËû¥Û¶mÆ ú5‡¼°°×]w&OžO„áûY–%Ó4%Bˆì8Ž¢ªªÌ{Ó¹š̰¡”†DQÔ»õ{I{W !Ê¡C‡òwíÚ­©©¡sçÎUþéŸþIÈE½íËΞ+TUÅ‚ pÉ%—ät >Ó56WôwN{wôgªM.è>¡«« kÖ¬ñíìƒEmm-®»îºœ3PNœ8Ñï}À™ÐÛ”“sMÐÿò—¿`Ù²e¨©©Áï~÷»¾vÙ24ý÷C“eW=ç=‚ A ‡]‚ž—çùù !@(,)(ÉÏÑu·÷Ü0Àºº@»º@;:@;:àtvÂîìÄÓøR‘.„B"4M„¦IÐuWAW~¨P9@ÐeO=—³Ò¹ûÁµ¿»áq(uW=wIº«š[Awɹa¸ÖöTJÀ±c·ˆBQcòäɨ­­Eaaáyk=X¶lžzê©a‚ÞÎ:A÷Ÿè v6I’„¾lï¶m«,3’m0¶w•¢ `{¿çž{Ä[n¹ePÒí©S§°~ýzìÛ·ÿþïÿMÓp饗¢ººúŒ¿;”¶÷ØØ8†ÒÎÖÝÎÞ_ ¥m0 †,˸òÊ+ñ‰O|¢ßÍÞªú±±q ¥í½µµ÷Üsžþy\yå•9‘Þòòr,]ºtH,—=ÙÙƒç¸~ýz?~?üáqï½÷ž—Àºó w~éK_*=[vöÞ@¹À›Ã6÷aœ/ ÆöÎ úA¢.Ë2/è÷f{÷ ú8]QWÈÍÍÍ¡·Þz«è{ßû¹ûî»ûÜ'ttt`Íš5سgÏ¿7¹Ìïë;XÔÔÔ ®®®_n¦\íìý… þ>áLy±³÷Š¢ô{ŸÐŸÑ¦EwÛû¹&è€[jooGqq1Ž9‚––Ìš5+ëgv>ü0þä'К¢d‚át*'çá0dNÌóò yä\,*),ôh`š`±X{;h{»OÎŽ´wÀ¿ÈÏ!<{»;bMU]ÝUÎeœ+^€ì‰+.9w[y‚;…«ž;twŽcR¶my=3ëܲlŸœ†ƒS§!•šé÷1Ó§OǸqã …>¶­{=aÙ²exòÉ'ŸbŒÝy¾Ï壊sFС±½Û¶­Z–¥I’Ô§ívBHw5]C¶wBHø¿ø…˜«ŠÜ“½©© Û·oÇÈ‘#1mÚ4˜¦‰T*…‚‚‚>k0¶÷ÁÚØ8›âz&;{ …píµ×bÖ¬Yºh •‚Q\\Œºº:Œ?>çßeŒaûöíX¿~}¯vöþb0¶w˲°iÓ&lÙ²Žã ¹¹’$!¶íœð`,—}ÙÙmÛÆ–-[°uëV|ò“ŸÄ£>Š#FäüíX½z5n¼ñƎ믿¾älÙÙÏBÈh'˜;ÒrÃ8§Œí=×´wd¦Âøýî¶÷T*¥oÚ´©ôĉÚå—_NŸ~úiq„ Yç̯_¯¿þú€íìýE_û„¾®±C‰3ÙÞkgï/Îd{¬½¿èËö>vöþ‚ÛÞzè!ç– ñÄOàÞ{ïÅ}÷݇ýèG€#ü#Þüâ¡K’Kйrî©çÜÚ®D"k{AÄhÔµ·ƒD£®zÞÙ ÖÖÖÚ ÚÞÇ;ìÎNìíªÇ ‘5>A×´ AWU)  K$Ù'è® R Œ+æŸ+ÞΘãÙÛ-8ŽKÒm;s¸Ê¹í‡Â†ƒ®®tt|€Û^šŸŸ)S¦üÿìwx\Õþßsë4u«Û’-7lä.ض¤M€@B·©!›¥$$üXR–„€6KJ ,!„l#Œ±±+.÷Þ›º4å¶s~Ü{Ç#y$ÍH3#Ûè}žûŒ¬â9’Ǻç=ßÏ÷ýb̘10`ÀWªôôH”Pƒ|Ò^bï<ÏKªª:Ð ö«'=ôÔ|8akà8ëׯGaaaŸtX½z5¶nÝŠyóæÁwêj&L€ÐÒDÛ² ‡Ó ÉÆÛÝnHIIçTÏù´4péé¦A0$#ÈÌÀêêÀêê@ëëÍêyc#Œ¦&èÍÍØà݈ÚÔUp8¸vˆ»iÎȲiÎÍ4}Áªœ›¶1·÷7gÃáX»ê¹YA×­ê¹MÓ-ƒn@Qt¨ªU5àó9Q_?Œ9ƒSs 0qâD <’$}¥ªçgΜÁ׿þu¶zõê7cwôõzÎWõ‰A>yØ{RRßÖÖÖ)önWÓÑ5öî†y:Þ{‡uóEÎV__ï:vìXêC=Äç;ß û?¦¡¡ .ÄîÝ»#þ^ëêêpðàÁ`’ö–-[PXXØiŠz$ØûÁƒQSSÓ#œ=RE‚½÷gT„L˜0W_}u§Ø{"*¢(âŠ+®ÀÔ©S;=íôûýX²d 6lØÐ«ùê])ì½¾¾555Ø»wo—WKK .\ˆüü|LŸ>=ªuD‚\v…Z¶´´`É’%سgžxâ <ñÄ_©”p² z Hï+Ôœ2À§&1Æöt÷ùýêW<m{⌽«ª*¿ýöÛù©©©¸ÿþûûtW••…òòrÔÖÖÆgT%%%˜4i>øàƒ˜ãì‘Ê>ÀNOOGmmmÌqöH%IÊËËÑÐÐó‚E¤ª©©ÁĉûÔ ‡êé ðÎæÍxÐíÆp§²ª+Ûh»]=OJ‚`WÐSSÁ[W»êyF†Y=¯¯Ls^_ÚܬžëÍÍX¦®Ãgi[,sÎÁáàCðv’Ä º ˜¹A‚À·KÝ73†m§°+蔚— gXæ\ƒ®PUÝz4PUІ†oÃ00÷‘™™™7nFŒ·Ûý•In§”âå—_Æ~ð]×u5hŠc‰;ݼ€Ô§èg í;£”ò’$µÃÞí°®ër¨I‡½‡ö¨w†½[—È:”Àýë_ÿš·«Èš¦aÅŠX¹re§é쑈1†?ÿùÏØ¿?®»îº.V8œ-V8{¤ê g‹Î©:ÃÞ]ÁHOOGee%B1ÇXâì‘*ö®ª*–/_ŽÏ>û¬ÝL÷îdÏp=tèGØdØÎ{ï µ´©•+Wbúôéx饗z•ˆ­4­õõïÃçÛÆN‚X}f.p\22¾ ·;º ¤XÉ6è~¿?­ûÏŽŸ!©Œ±îSûÕ¯(VØ»®ërh5=Rìíóåææf÷Úµk3.¹äŒ9Òát:nÔ“’’0{öl\rÉ%øòË/±xñ⸒w%;Ÿ¤  «W¯Æ²eËúÄ£ªª N§µµµØ¼ysÜÉ;!eee(//‡ßïGuu5öïߟÐ5ÀâÅ‹1nܸó oyá¬üÑðWÃÀJñZv6œ²l¦¶Ûx»Õw.Ú=%Å4èÚÎ¥¥¤¥™q˜ªí{Ïíêyk+ô–TÓ•ø"}?œÎ³ˆ»,óâÎCˆ"oUÏy‚òÛm?[=§ÁG3&L@NNÎW"µýÌ™3xüñÇ7ß|“áuUUgŒµ ‡"„äØà~Ð_0uÞ¤Ø8'!Œ1ö“Ÿü„X8Š‹‹YNN*ŠBEá•$‰Š¢hÐcÏóºfòÍš(Š*!D%„(ìG…1¦^V’}9h6Ò&˲1bÄíôéÓîßþö·éyyy1=%OK;[$s»ÝhllÄ{g{ï½×þ™€EQ°sçNäää`çÎ}bÎs\ÙŽ; IvïÞÝ'æ0hvïÞ´´´¸‡Ðt¥C‡áàÁƒhllŒk›AW¢”b÷îÝ())ÁŽ;zeÎ`Ö¬Y())‰úÆÑÝkÔëõbéÒ¥øòË/ñ½ï}O?ýt\ç«wÔáÃÿƒÆÆ×!Ë8ˆb! çÇ9`  T!š¶'O¾…œœ[¶ÆóM„À#î!„T2³„Я~õ™B÷ O?ý4°Ð}‚MÝɲLu]7œN§¡(Š!‚a†N)Õyž×EQÔcš¦i*¥TEQaŒÙû5dŸºwpB4˜{æH6™çyêp8ÚÞ~ûíäòòr6qâĸUÓ» •e³gÏÆ¸qãâN¶3³gÏû;<997ÝtSÜÚxžÇå—_Ž™3g† ª+**Â}÷݇õë×ãã?ŽhälOäp8pÕUWaâĉa«¡—\r JKK±lÙ2¬^½:*²-…Õ}òÉ'qyŽhµò?ÿDUÁ‹¢™\nŸÝ©ëøïcÇðÏæf¼1adA‘$p¢ˆ"ˆ €ÀóÇŒšÖÚ è:˜ÏÖÖf&¸·µ¶µz½ > ¿F ‘P @)³Þf0 ]§à8]'Œà]Ž# ” šsB`UÎí€8s䮉µŸ½4Í€¦Ñ࣪Rø|¥Ð´9ÁŸ… (,,DYY²³³/zsN)Å+¯¼‚Ç\3 c'¥ônEQÖ‡û\ÆØ1BÈT?ðÕiÆïFçM=T±ÀÞ­‘j眔[—;Zì½±±1}РA.·Û·¥ÇAÓ4üîw¿Ãˆ#ðÔSO¡¢âì)\¼{¾Ã©cO|!Ú‘°ñÚ'Ø8{$£Þó€8ÖØ{(Îîv»#úšººº˜cïáö}1f­£,Z„Úo|.A€Sá ‡“NxË}>|gȈˆËGJŠ™Þž”>9Ù¬ž»Ý .ˆuÃðù@ÛÚÀZZ`´¶‚67CokƒÑÒÍë…îõb©¸ë [•s.øhWÏE‘ƒ(rÞÎYÕs“P±Í9À¬Ùç,hÎÍʹmÒ©U9§Ðu M3±vEɇ®€ÙËÎó< qùå—cðàÁ}ÖÎÚµk1wî\u×®]ª…³ÿ>š~Bàe/1Æ6Æm¡ç¹ÎKƒn«+œ-##ƒìÛ·‡½­“ísÒÞ)¥Á 9 e‹ {×4MR%sèСbùóçãöÛoOhRi[Û.ìÜy'¡G2ŽdÈr*D1‚žOÇyH`ŒÒ £šÖ Um‚¢4A–¯AAÁw¶æóÍ ÷«_绢™ {ï"z˜oçÙtÜ't‡½¿÷Þ{ù†a×_=é͸ȞLîU¬ö ÑN—é¨Xaïáì‘êÈ‘#¨®®Æ‰'zµŽÜÜ\TUU¡°°°G_+ì½cj¼­¾6èŒRüiìXøwï†KáųsÏíôv§ÓÄÛÝn0 ܺ|9þëŠ+pÓÄ‰à“’Ày<æårˆ¢YM7 @QÀ¼^³bÞÚ ÚÚ £­ zk+ ¯Z[4¯¤#ø¸ø 4è’d^¶A7Ñv „¸kži+ £ªZUmD ÐUucĈ¿$lÝÕÕÕ¸ñÆü~âÊöˆ˜Mú/˜Ï‹ý/›~õ«—JpÚ»æÞ!Ô¨÷ š¦9Ö¯_Ÿ‘››ËJJJ’³³³£>™ì gV½ tíjîz´êé>¡;œ=1ƂػßïêkNggïía³ªª=ÆÞ»ÛôµAßôÊ+XõðÃp ‚iÎENI šsÙátÑíq»ñÚ¾}ØÚ؈×n¾¼ÇÞåq:ÁÉ2Àó ˜¦ª êõ‚YUtúô¶6è^/4Ÿª×‹ÝüükäËœ“ 9—eÓ”Ÿ5èfÏùÙÞs“Û«çæ!>µªçÔªžS mgÐuU3¶ÅìEEEE˜Žý<\®$¸\)p8Ò ËéÅ„Lð|8.„8¬o £švªZE©C Ѐ@€á’K>JØÚÏWƒ„w”ÂL^M̨‚~õ+ Å#í½+ìÝþ3:ÁÞaè +V¬ÈÉË˦NJº3v³gÏŽ gT_|ñ/^Ñ(´hqöHí>!Zœ=Rù|>ÔÖÖbÓ¦MÝî!;v,ÊËËᲃÊb¤h°÷H÷}iÐ UÅ«ÅÅ@}=œ<—$™³ÏC ºätBv:!:Ün!ïvƒs¹ÀY月¢yb`º˜æÜçƒáõšÝë…îó5è¼/ŒßÎÃ,cN Š$‰„àígÍ9ÏŸí;?û_Ô ˆ3Ñv³ŠnVЙeÎÍGUe0Œ±„ fÀœ$I4h&OžŒÁƒ_´=çk׮żyóÔ;wögD„À§ÜÉë›dèë‚1è¶:ÃÙŽ9 >¼ΖHì]UÕÌ!C†ô{çy—]vfΜþràÀÌž=ÄÆ1dÈ,[¶ kÖ¬éQ‰ÃáÀ•W^Ù)Ωz‹½§¥¥¡¢¢Æ ëñàøñ㨮®î1ö^PP€ªª*äååõj»víÂÂ… ÑØØØ£¯ï gT6öþÉ'Ÿô(,'ô5úÎ;ï@ÜrË-ÁïØ±<ð6mÚ„Ÿÿüç¸ÿþûûä†tüøblÝúx<x<¦9w:Ms.I™ÅLB8.—3¹ÝJOC×OCÓNAUÏ 8¿¿>_+ÊÊ6$lýÕÕÕøÖ·¾Uïóùb»!?ð c,ºÒS¿ú•@õ{5ê±÷cÙºÃÞ0 ÒÎ;Sׯ_Ÿ^ZZJ®»î:.Ü>ã8Lž<³fÍŠ+«( –.]еk׆ÅÞ{‹³Gªææf,Z´Û·oûñÞâì‘êèÑ£¨®®ÆñãÇÃ~ / ú–7ßÄÇ÷ÜcVÏ­þs‡$™Ýá0/ˤ‹.Wð\.ðn7x§³½9çyÓÞTTQÀ¬08êõÂðû¡[}çºßÕ¾ŸÄÁ~«÷œ@ÍJº ˜&çaáíè¢zn÷Ÿ³ 1·Ãæ4ͼ€™Åi4祥¥˜2e rss/ÊQjuuuøÁ~`¼ñÆœ TUõÑžà쑊2À³žcŒíŒ×óœOºà º­Î°÷Îp¶h±w„Gßc޽<UUUÈÌÌìñÏbÕªU˜2e !øå/‰¶¶6 0 â¹à„\zé¥(//i²v´8› ˜>}:¦OŸóŸ«÷bŒaÓ¦M¨­­{w¹\(//ÇØ±ccÖ3­ë:V¬X+VDŒ½Gƒ³Gª¶¶6ÔÖÖbË–-Ó]½F÷»ßáÏþ3Ö­[‡n¸óçÏGNNNÌÖZ[`åÊ™p:Ux<¸\Ép¹Rápd@–@’² p8¦" IDAT¹„<’BR( ô ã4í$4í$å zøý ðz[1~üHT°è|€›nºé¼4è¡"„p_%Ô¬_¦zнBÚ…ÈÁ¢î¢ÅÞí ZBˆ]EqRJÓŠŠŠ8»;pà@TUU!;;;a?›S§N¡ºººÝ$”Ñ£GcöìÙ1ÁÙ#ÕÞ½{QSSƒúzsoKœ=R1ưaÃ,Y²$ˆ½;N\}õÕ˜0aB²SÂa館¬Ä!C"þ{úÒ ¿qùåhÚ¸±}8\¨A—eo1è‚Ó Áé4͹Ã^–A$ „çÏVÏ LQÀÔïõûaXF]÷ù ùýÐh~?”@ª¢à Ó‹/o‚,“ A7/Ó” ¹h{ûÑj°;¬ ëúÙË0’ Š•ó߆·ÛÑ£GG&_læœRŠßÿþ÷xì±Ç4]×c†³G+BÈËÖxƒ]¨F¶]°ˆ ö®ëz·iï1ë‘`ïMMMéì{×éð믿Ž^xS¦LÁC=œ‹ÝÙ &''•••Qãì‘*Rœmذa¨¨¨ˆ[¯²ßïÇÇŒõë×wjL !˜8q"®ºêª¨qöHÕØØˆ… b×®]~NopöHuèÐ!ÔÔÔt9#6’×èí·ßŽ?ýéO˜5kVp¾y_H×ýX²d:9ÇeUÏSát¦Ãá0͹(æ@óÁq…à¸Bn0v†qØ2èÇ¡ª§ (uðûëáó5Âï&NL\ˆè…`Эß}«<Ä[Ù×ëéW¿ºR¢ÓÞÑ{wØä1ÇEHÄO?ý4ëÀîY³fáá‡&¡SZ­-[¶`Æ ˜5k1Ç=œ ÃÀªU«pðàATTTÄgT6ö .8{¤ª««ÃÂ… 1hÐ L:5ê}@_ôc7âÍÉ“áâùvýçrH@œìp@r8 Zˆ»bÎyY'ËàDÏ›ÕsÆÎâíª ˜—ßošsû  Ðj UU¡hj&¶ %×€$«rn>šæÜ¬œsÜYs~6Î|´+ç&Þn›s€ã.Ó9;ÇqRSS1jÔ(Œ;ç]—Z¯µnÝ:Ì›7Oݱc‡ªªêc^í«ƒzBÈýþæÜôß÷Åâ­ófzO:뀳±mÛ¶qÇçTUe²,SŸÏG].—¡(Ša†@ç8Nçy^SUUã8NcŒ©Ç©”R€B ÎNç8Î~ŸÊS­G €}ÉÇÉ©©©gêëë¥ãÇŸƒ½ÇûtøÎ;ïÄwÞ ¿ß§Ó‰¶¶6TUUaÔ¨Q¸ì²Ë‚èœÃáÀ¬Y³0iÒ¤¸žðñ<éÓ§c̘1a±÷XáìÝÉét¢ªª ãÇGuu5Ž9Òîã………¨ªªBnnn\ב––†[n¹¥Sì=8{$8p î»ï>¬[·K—.m‡½GóRwûEQ8A‚û]× At]×uÃ0tUU Y–uI’‚³Ó ÃP9ŽS !*!D Ý/kn:ÅÂÞ5*Bôg̘q"333Íï÷sÏ>û¬káÂ…Ò/ùK.–[$R'NœÀ™3gpòäI 8°O*~^¯'OžÄ™3gpúôé>3èÁ¥}fÐOŸ>3gÎÀápÀëõž“Ò~¾jí‹/‚#1û°í²tðmBÀ±êÓÖtq«¿›ÓuйçÌþ;Óu@×AC º˜=€®(Кu©šUÓ è:Æmrcy–Eñ‚RÓï›f»½A5禘u6`tûmœÎJȲ¹gµ“Ú3220iÒ$Œ9ò¢ ƒ«¯¯ÇøCúÚk¯AÞ¶ÒÙc›¥c/Bþ  0åbŒ5÷åºb© Ú Û ½3Ƙ½Ÿ>}‡¹Ýn&I'Ë2U…E‘*ŠBA0†ah‚ è„M×u•1¦P !*¥Tåy^…uõͺõ¶ óÆ«Â<-×c:!DEÑEñÄž={\'=//+..Feee¯pöHeW€GgŸ}o¾ù&† ‚£G"33·Þz+RSSã¾[)))¸é¦›‚Ø{ss3¦OŸŽiӦŠgD¹¹¹¸çž{°iÓ&|ô‘þuÍ5×ÄgDÆ CII V®\‰+V %%%æ8{w²GøŒ=:˜önOˆæ5Êó|°?ï™gžÁßþö7¼ú꫸öÚkãµôvÚ¸ñ9œ:UƒädDQ‚ ÈAª( u:TQCÃ0 RªB žçuƘÆS)¥ªup¯BTjèÁ6ëö®ÁÜ#hŒ1Lj#t˜õ¶O?ý4}È!®§žzŠ<ðÀ ¹uœ“¾hÑ"lܸ•••1†ëLá»wÞy%%%¨¬¬L˜Q,ûꫯö8 ¶§ª««CMM öíÛÀìÓß½{w܉ºXHóû±ýoƒjέ=³Í¹õ¹fqÚêï¦Ô0@uÝü|ËBÌÆ2o×4Ó +JÐ Ûæ\S³‚®ªÐ4 šeÎ}ôÁÃ1jÌLlÜ4Œ¦NÀóÌÂÛYØê¹Ù{n>ê:À˜Nç$x<“Àqr°j.Š"†Š & //Ög§”Þ£(ÊÚ¾^—­‡O˜B™Ëû –S]ÝcŒY8{úé§€ž9s†ìß¿ŸS˜8ã8ŽŠ¢hø|>CCUURjȲ¬ ‚ éºnWÄϹá†^V]±>Gµ°·`5€œ””¤3Æü›7oN[¹r¥³¬¬ŒO„A·åv»q÷Ýwãî»ïÆÉ“'±téRÌŸ??ýéOñÜsÏ…M⎧JJJpÕUWa÷îݘ2eJB͹-BÊÊÊ‚“²²²„šs[‚ `Ê”)hllDiiiBÍy¨Ün7®¾újÌš5«WH¿øÅ/’’‚+V$Ä ·µÇ_ü7<‚ @D‚ž—ÁqëFj_" ÆZ`öž×ƒÒ30Œèz#t½ºÞMóAUýPÕ23§Çý{³¹ºó\ÖV „"ÆØá>^R¿ú‘z²O`hš¦SJ QuA4û@ß2éö^@é@ß…âþm“NÑHƒn>qâ„ô“Ÿü„üóŸÿä^xá~Ô¨Qqùþ»¹vêÔ)¼öÚk1­Ö™ºÊ¨Ù·o~ûÛßbÊ”)˜1cFÜzÑ»ÍjcÛ¶m[Ä#V{*UU±|ùr|öÙgçüªªŠ>ú›6mJø!~4Úÿé§0KïhÒmsR=§Œ™ÆœR]‡aŒRŽ3ÍyÞN5ÍDÜUÕ4çŠ#´r®ªPU5X9WCkVü×^‹€"!?ÿœ>½Àa DZ½ç¦Îšs€d¸\—Áí3LAsž²²2””” ##ã¢ê7_¿~=æÎ{^àìêF÷ðöõBb¥‹Ê ÑcﺮN§³ÇØ;1ËröM·Sì=++«.==]úîw¿›6|øpùùçŸçâ1Û ÁþkJ)¾öµ¯Rì7;tèV¬Xn¸!®hNCC.\;¶oß¾„¤´vÔ‘#GP]]'N¶oߎªª*&t¡©­›6mBii)***žž8¬Ú0 ¬^½Ë–-ƒªªØ½{w°¿'¯I’ðÔSOÿ¼lÙ2>|·Ýv[,—Ô† ¿ÇéEAÏKà8 „ÈÁG³¥”có±k¥ 0Œ3Ðõèz34­ªÚUõAUPYY³ã²î‹EÄìØjZ¿Û×ëéW¿"QOö ², ^¯×`ŒéŠ¢è¶Q·ô;ÅÞ9Ž ä`? ö.geeéßøÆ7|~¿_æy^¸ë®»Ò‹ŠŠ¤×^{‹•IVŸ|ò Ö¬Y6½=T_|ñvíÚ—4÷H§¼†+Và‹/¾ÀìÙ³ë‹cÇŽ¡¦¦¦Û)/>Ÿÿú׿ðù矣²²ùùù1]ǶmÛ‚YA]©®®üãÖ­ö.YröÊ =ÎÎØÙË®œˆU=g”‚Y áíªçº¬ ª CQ`¨*tU5 ºªB³zÎUMC€R´¦¤ iÜ8¨’EQ ËPXxš›¿@kë(Êqì—¶ýÈ ŠÙåAp:Ãá(!g_ÿÇÁétbذa9r$ŠŠŠ.*¤½¾¾O<ñýÃþ@Aø³•ÎÞ§8{$bŒ~cÿ™ò©žºP±÷ :$.Å"íÝ’ M{Ái‡ÂD›öÞÔÔä|ó›ßÄk¯½Öãµú¶oçñÀQTtö}¾3ø¿ÿ+…ÇC‘œìDR’çt&Ãå2G«™³ÏÓÁóéàù$ëÜcm0ŒFFTµªÚEi„ßß Ÿ¯ >Ÿ„ ÃôéËz¼æžèý÷ßÇÍ7ß|Æçóe%ô‰{!BÈ=ž0Īö«_”¢Ý' $í=tŸ€a³aÒÞC§Ä„Žek·OøòË/ÓÖ®]›ž““CŸyærÇwôj“ÍüóŽŠÕ<ôhçŸwT¬°÷p8{¤"„Ä {G£î‚dû"$î'MBãæÍpñ<<§ ˜óÏE²(B’$ˆ’|% ‚(‚·.ŽçÍ`8k¿A,ü†aVÏ5 TÓ`hšY=WUèšÝ2窦™Ý0àu8Ð0|8Ú®¼šÙÒrο5c|¾cÐõ&pœBpœIʆ œ›=ÀqxžGnn.JKK1jÔ(¤§§÷ }QJ±`Á<öØcš¦i»­töógV„é~ `%cì¾^OOtÑt ûW‡ÃÁåææ†M{ ʲUÚ»ðÚ]Úû‘#G’5MóÌŸ?Ÿ;vlL¿ç'N„ C §Q£F᪫®Â¾}û0yòdÀܹsqÙe—áæ›oî•aݹs'-ZÔí p{Îö•W^sœRŠõë×ãã?îv¸ÃáÀUW]…‰'ÆWRU5XÁènN}ZZæÌ™ƒáÇÇt @tsê» ÏëΠækñÈ‘#˜4i4M †ªt§¶­[Qÿ÷¿CÛ¼¥à].@–A% Ò„ ØYº{öüÉÉ$';áñ¸áv›ÝéL…,§A’R!)„d"Yý§ (m¥ÍдFhZ#¥ @ üþVx½mðzý¸ä’ÿC~~e·ëŒ¥.Dƒ„LÆØ™¾^G¿úÕSõ$í]Ó4RÚnvº®ër4iïÖ>¡Ýìtò©S§<õõõÎäädÞ0 ÏüùóùÑ£GGõ=:u 555aqöhÕì}ïÞ½X¸paÄ#W;SoBv»ÂÙ£•Ëåê1ön”ZO5`ÀTTTœ3‚-ÑÝßÔ„ŸfgÃÁœ<'Ï›æ\ Û=Ĥ‹¢ÁºxA0͹ œ —‚³Ï™eЩ…¹ªjšt«ß\·ªævßy€çÑ”ŸÆ™3A³³#kNöZ8ŽCJJ 1fÌ 4(î”DjÆ ˜;w®º}ûvUUÕǼržã쉘c¬…2¦çÝÑ×ëŠTâNÝálGŽá { †¡3Æ4I’Úaï!=è=ÂÞ ]×½?üpÚ°aÃä矞ëmp[$ãÄ:jÛ¶mسgf̘¼aäääà©§žÏó¸ãŽ;`FT%qöîdY±q¶h7"©#ÎÞjjj°qãÆ˜bï[·nÅâÅ‹ƒ}ïÝ©±±o¿ývL±÷žT0Ž9‚—_~¹WãçrssƒÿùçŸÇ»ï¾‹ `üøña?ß·?ö÷»àOŸ†#5δ4ÉÉà\.€çAumëWà‹Ö•Âã8ÂY€c$˜K©]oÇñ0«ç~††ÑMk‚ª6CQÚxø(àù¡ÈȸÊL—M`_Y¬L !9þ€oØ àjf6ßÇEŒ±3–Áùcìñzž~õ+^ê-ö®ëºÆq\XìÝž{·pø`ˆœ}eggë999>J©¸oß>cÚ´i©åååˆ{WK—.ÅÚµk»ÅÙ#•½Ïœ9—_~yD¿›››±hÑ"lß¾=&k0 +W® î"m‹gT=ÅÞCÛÚb¡ºº:¼ùæ›9r$æÌ™ÓgØûÁuë@ à8g·úÉ)ÎÅÚ Ã0ïÙVã7c ¼…»s«ç”šýç–I7ì zÈ¥iT]‡ªë0ŸÛ¶’Ь¬¿öIÈAArr2rrr0tèPŒ1.—ë¢é5ohhÀO\VÄúJTÐ;ª·Ø»®ë2!$¦Ø{cc£óèÑ£©·ß~»pß}÷E½3Æ‚ÉäápöHecï%%%0¬QñÛßþ¯¼ò æÍ›‡‡z¨Ó¯ïgTvšxVVÏ ‰]áì‘*ØûéÓ§QSSƒôèëØ`ï]áì‘ÊívŸ“xI=TÍÍÍxä‘GÐÒÒ‚wß=·eùøŸÿŒ“¿ø\² Wz:äŒ ˆàSSAœN0J¡û|X¡ÖbÞ—HM•”ä€Çã„ÇãËåÃá,'C’’ Šnð¼'Z± À0Ú ë­Ð´V(J+6øý^ø|>x½ \®'QRr#²²²âÞòªýë_øö·¿Ý« :!d€¿8 30åJ3Æ^ŒÑ2;{Þ_x–1ö\<Ÿ«_ýŠ·â½Bœ÷ aï>ŸÏ¹nݺ'OžtŽ3†Þu×]ÂÝwßv“°eË|øá‡=ÂÙ#UVV*++;›n¸/_¾š¦ÅmݵÇõgT‘`ï]µµÅJ¢(bÆŒ˜:u*|ðA‰« ¯yã üãž{L¼ãL¼] ó<$Q„,DѬœ DQÏ›Á®Ç/€gÍyˆA7¬*zРë:4˘kºÅ0E´ íºë@\®à6RYÀq\.ŠŠŠPTT„ÒÒÒ Î~1 íŒ1,X°>úèE³G*BÈL™Œ±¿öõZ"ÑWÒ =ÇÞˆ”R)^ØûÑ£G“EIzþùçùÎ*Œ Ω:žÈž$$ ƒÆàÁƒQTT„ÌÌÌ`jûÅ Ï?ÿsçÎU·mÛ¦Yéìã섘Ié£üš1öI<×OBîp3ÌâÅξ^O8}%÷pê ön†!Š¢ÁqœÑöÞÛ´÷‚‚Ã0 ß÷¿ÿý´’’ù…^è{ï Ω¶oßÄÞ§NŠœœ<ñÄÁ·¶¶bòäÉÈÏÏÇ‹/¾ˆÓ§OGŒ³G*J)>ûì3lݺ×^{-ÆŒÓåçG‹³Gªh±÷Žóec¥h°÷xV0B±÷žRvúÊ•+qï½÷âëYYøB’–gJ ³² ååÏÉIKtz}=êhü‚D0F‚ãPÌ{:ƒaÐu„(`ŒRç7À˜]÷Ã0üÐ4Å EñÁïÀçSáõª8qb2t½ééé ØÒÒBHvrÄdüßðߌ±7¬wg¨];cì#%â¹úÕ¯x«7Ø{Ç©0Ýaï0Ñ÷.±wA„Iq×®]ž+®¸‚~ë[ßBee%·yóæ˜Gª­[·ÓÞGŒ?ü;v$¶Å³#öž––Sœ=RuÄÞcгGªúúúà4–D©åäIPà,ÖÀ`0ƒRŒA§Äê+Lô]ày°Ê¹mÐí ºòNuÝ4Û¶97 ÓœTëR8Þ!C Q`F°*ÞQ¡ýå’$Ááp 77#FŒ@vv6 Ð'c€ã¥††üèG?¢¿ÿýï!Â;ªª> ÎN©ð'˜}„<€É‡]ªp-€ßHì<Ýuñ¼úz¨Ð0cŒÙ8ÛéÓ§áñx˜ÛífÎFEQ¤¢(RÃ<’ uAìþ3Õž{jp…çù Ig!óÔ;ÌGuÐc:!DÁ6l˜ÞÔÔä˜3gNÚ­·Þ*<ðÀAìÝÆÙkkkãz:¬i–,Yœ¿D’””„={öàg?û>úè#8N9r𦡏¸8¦ëhmmÅßþö7lذ•••ÈÎÎn÷ñXàì‘èĉX°`A§Ø{,yºÒîÝ»±ÿþN±÷DT0cX·nÖ¬Y—ëÜÄÓHuçwBxï=ìýôS8³³áHJ‚œ–iÀH99 ÁN'h]H}=övbΉ•'sÖœ›û\€RºnWÏ5«=]÷CUýP”%ŸO׫¢¾>~ÿ¸Ý233áñxrrîõzñÓŸþ”ÍŸ?Ÿ‰¢¸”’ àÆØÛQü5ßPàE«*÷ÿ\à6ÆXm–Ý©!< àñþd÷~]èŠfŸ ( u:Ôʰ1c†aZˆQW)¥A£k¿`çÙ„êÛngÔcŽüü|ýÆo 8p ÙëõÒ… ¦geeq}Q壔" @Q”„?¿-MÓà÷ûár¹útŠ¢@Qøýþ¸âý瓚-ƒN;kÐ;{QjÀ…T³™eÄ !ízÏmƒÎ Ì2é¶97 £9×(5 :cðåæ‚\w²²²@ASS“Ö³®l|‡ÃY–‘žžŽ””””” ++ )))Uc øÃðè£jªªî¦”Þ£(ÊšhþBÈ7¼àkVÀLG¿Ÿ’Ã;‡eÇ]Œ±£n²öJ „d˜Â‹£¾òÝcŒY˜=ýôÓ@Ïœ9CöïßÏŠ‹‹)L쥦¦ÒÓ§Oó’$QA UUu:¥T“eÙ>%רŒ1•šå;!sPatëlWÔ©¦SSSõ””åƒ>Húë_ÿšôüóÏóyyy¨®®Nèép}}ý9A$6Î:òäèÑ£X¶lŒ›o¾9æë8xð ^zé%Lž<9˜ökœ½;Ù‡#;vìbïv:{,yº“®ëX¶l¶lÙÄÞã³w'MÓzõ=oýÕ¯±jJÒÒàðxPÓÚŠÅÿ ŒAÙÙà Á•”€µ´§Ny½88&·¯šëº‰³išŽÓ( Ô°°5s¤ ¥(U¡ë 4-`™s€Y9oiq ®îÂ!999aóMÿú׿â»ßý®ÚÒÒrÊ0Œyº®/&„\`!¤˜1öóŽ_CPfÎþ´õ}˜½ç•~àãc‰ùÏÑ^Ì›¹ ‰ëW¿.xE²O8p ã8ŽŠ¢hø|>CÝï÷ë„RªóQ¿A£®p¶mÛ¶·Û I’X8ì]UUÃÌÓuBH{1à=ÆÞ OïÛ·Ïår¹ÒóóóêÒEQÄôéÓ1mÚ4‚€ÜÜ\ÔÔÔ`ïÞ½í>O–å`òº¦ip:xï½÷p÷ÝwcÀ€`Œõê—¡ÛíFyy9ÊÊÊ@AAAª««nL³³³QUU…bÊ”)ؼy3jkk{• ß 2•••ÈÈÈÀðáñråJ¬X±â¼GëÔ¶6¬º÷^$ñ<$Y†ètBp¹P”•…¿_f&ˆÓ‰Æúzß¼ÃyÆ©SÐÐJ½Ðu4çšF!Ÿ?ûÙÏØóÏ?ÏAøH×õÂÝèc«¬BÈ¥öÀ¼Y>À3ˆr:€ÀDÛyo1Æž´ÿBÈå0O…/‹¦ß,bŒm°9‘ÏÙ¯~%JÑbïö>ã8{|k§Ø;!D±ð÷ˆ°wBˆ^VV¦3¦‰ã8aß¾}®d̘1ƒD;/¼+ ‚€©S§âŠ+®è–$ ååå7nª««cž^îr¹põÕWcüøñ]î'’““ñ­o} &L@MM Μé©G ¯ÌÌLTVVvÛÖ—ŸŸyóæáóÏ?Ç’%KbÞšX\\ŒªªªvDc"•œ—×®z4èvåÜFÌ9Îì=ç8{GHï9`šskµŒºnUÒuëmͺTJ¡Ê2Ø-· äHŒ?ƒ ÏóHNNVÎíׇÓéÏóA*îb3å€I&¼öÚkxä‘G4UU÷PJïQáì¡"„ ðM3!7x&ê>Þ*&^T²°÷7€2æÞé?û {ï7è](ìÝJq¥.—ËPUÕÆÙ¢ÂÞC®N±wÇ£3Æü;vìH-((ð$%%Åý·ÌðáÃQQQаºŒŒ ÜvÛmؾ};-Z¬š‡Êív£²²×^{mzÿý÷ƒsC‡x „°³¸ˆûï¿ëÖ­ÃÒ¥K㎺;Ìš5 “&M ö&B0vìX >þã7¿ÁgÏÆ÷G‚ZWÅ¡A×ÏšsM3͹¹_5oîàœýAWØ;g"D2cŒíر#eË–-¿ÿû¿#//¯×û„HÂI;jÀ€¸ãŽ;°mÛ6,Z´¨×Ø;!ãÇÇÕW_ÕjqqqL±÷Ž8{$"Ö8¶‘#GbÉ’%øüóÏ{½OHNNÆœ9sÚMÓé ¥æç›¦œ`圳 ¸µÃÂÞB@ížtt‡³ñö“nXW;ƒÎÔ¤$ðßù ÊÊ0qâD :’$¦¶·‹BK {gÚ¸q#æÍ›§nݺ5jœÂÃ<ø¿ À‹Œ±?[úL¯ò_Öc9c¬ç3ƒ/,­ƒ¹ïú!¤6…O˜¾²cÖ¢•ÐÖg#GŽá†Î)ŠÂó<ÏéºÎAÅà\TJ©D¬¹é,d~:ÎŽU±ç¡º9Ž³Ç®8‰9+5t.ª=²E4 C JJJ¤xüòIOOGeee·FZÓ4,_¾«V­êg;sæ 6mÚ·Û©S§‚1†úúú. QèX¯®ÔÖÖ†ÚÚZlÙ²%時K/½åååðx<]~n<ÆÞٲѽ3ftd²gÏÔÔÔ ¡!öžHElÛ¶mܸ‘–––òÕÕÕ­æóáC‡Âãó!ÝãAJj*’32àÎΆ3;rN„ÌLðn7 iXôÙgX¿c¾_\ŒÀéÓx-eZòýp»y¸Ý<œN²ÌC–HžçÀó°zÏ)³Ç­èÐ4ªªCQ øý¼Þ"Æ-¹¹¹¸òÊ+Q\\ó›ùž={pÿý÷kË—/¥ôç”Ògmœ½+Y7Îu¶˜ àßck¬Šúf0Æ^"„Zaþžø@€T˜AscúÍD)kmK´1ƪúr-ýêW<Í>A„àütÃ0$J©ät:%UUÛn%„8 !NÃ0‚3Ó9Ž ¾maïG·:còÎ;S‡ªªªšYPP F:®4T©©©¨¨¨ˆj¼g8©ªŠåË—ã³Ï>ëö^PP€ÊÊÊspöhÕÒÒÒ+ì½3œ=Z;v¬Çió<ÏcÊ”)˜1cÂ÷Ýw€ÄÍA?µo~ŸÏ<ó ž}öY*‡@àh ³uSP0š1Öd½€õ° ºõ¾¼³šöþ·/+ç¡"„d(eŒ­èëµô«_ñVOö °Œ:ÏóRèìtÛ¨Ã:Ì·ö;î,ìÝ «ÀaýþTU•ß}÷ݼÒÒRRQQA"I©¡ÓI!½Q]]jjj°oß¾ˆ>?Ü|ñXhÿþýQaï‘âìшõ`^{II *++»,j$Ú ÀϧOÇáÕ«!™H–9·Mz;s¦z¬ [&½ãˆ61¨Œ×­·¢ô’K‚£~/¦ÔõHÅÃ믿ŽGyDUe¯ßᅦ1¶:š¿Ã¢ó>ð9€¿xæïëãï¸@:c¬Åz߃n‡IÄû‹DˆR`ÌßÕ“c§ãþœý=z…;%omm%ÅÅÅ&ÎÆåæærÎ&‚ ¨ª*Àª¦s4é9)G{SÞѤ+êhoÒe"cLlnnî5ö>bÄÌ™3Í^D]aïáÔÐЀíÛ·cÚ´i€;vàŠ+®À=÷ÜÓgF”Ò^cïápöh‹YõápöhÕÔÔ„E‹õ*ÌŽã8¬Y³†¦¥¥©n·Û „è6lòóóÕÕÕ½æ¥øuQ\ÍÍHw¹jtOZœééSS!ºÝà$ Ä0@ý~èÍÍPhhÀç'Oâñ“G &SÜr‹§“ƒÃÁC–Ms.Š&ÚÎqgoöŒQèºM£PU Eá ë×B̃ŸÜÜ\TTTàÿ³wæñQÕçþÿfNˆ(Ü}–ön/¸¸ŠŠŠÚ|ùå—ÇW{÷îíMHH¨õî!:{¬lß¾‹/®uœ`+á&Lhð8 >t]¯W{oˆÎ+@Ë—/Çúõëk'$''còäÉQéìÍ] 33¿ôÞ›=Û,έUs—UK‘ßaæD fuûu–â®YJ¼Î^9Ç!HûîwÑ­G 6 íÛ·ÿÖ«ë5ñÍ7ß`ƌʖ-[TEQ€©³G­¥XÞ“Ö(aæ—¬Ë?p 3Û­Çð€e¸æÂàõÌ\×u `}–þ3¿ÑÏwy:!$2µ¤½gddTIqL{‡Õ–¢L{Žá:ÒÞ“““Ï––––Ÿ‰Ig‹Eg¯””ÜrË-Ø»w/.\StY–±mÛ6jÇŽUf6ˆHƒÙãWŒeâáÔ®]ðÃçv›­YˆÌ“°®CS~?X×ÍYuU…î÷C+-E¨¬ þÒRtTU¼äK›ÃýM}½²’‘š*Zaqöêù…]×ð^uUmQ¼ nw³MШQ£·â|ß¾}¸óÎ;Õ•+WÂ0Œy†a<¥Î>f:ê;Ì\½W^øMf3MõŸnGD¾Î¥€Z·ˆ†0sôªƒÃ%FcÆ 4EQ4Ã04¯×«*ŠRcÚ»Õš­zÚ»b§½[AržN:i;w®0 C>wîœkåÊ•í†.F®Ä¦¦¦bÊ”)èÑ£G“¿?½{÷ªkï999˜:u*²³³›ôêK{—Î^^¯×]w¾óï\ÔF·>½¥afTTT ¡O>TË`Ã[E¹a}×a¦¶ÛápÖ„‡­¢Ü.Ìí°9=%žn@J¯^èÓ§†“Iøm¡¤¤=ôñÊ+¯@–åwE‰Ig` Ìb»œ™o‰¸¼úŠø¦˜`€?x[¦uk‹c㚥8œô¸ÐJ´w·õÝÖÞ½^¯7=''§Î*½¡:{´œ={ ,¨Wg«®³™Ñ¹sg|ùå—øóŸÿŒÛo¿lÐqD«½7Dg–X´÷¼¼í]{¶;œön·hCýiïöý4TM{Ö•öÞ«W/\sÍ5HNNn²÷&==·Þzk­:[m[§NÂ?»Ýnœ:u S¦LAQQdY޹][}iïñÐÙ냈0hÐ ôìÙ³V-­¡H’„Ñ£GcÀ€X´hv쨾X{AgOKKS:uê¤Û+æD¤±ÙÒþ;iJÿئM`†•öªU…2˜uE€5 †¢@ ª¬DÀï‡?@y(„ó#G¢ë °³x?rrVbð`¾}:ºwGÄê9¬Õs¯÷*$$ô† HIIÁðáÃ1pàÀ¸í]ûøãqÇw(çÎ+¶ÒÙ «Ýä/ÂÜ#†ˆò,€©šðý‰HàúX×ÁTÏþíeæ§âòbšžé0?§.2Np¹\z ÐEщHs»Ýšaš(ŠŠaŠÝŠ-¢3Lxu·eS`êš Ú¤I“Š>ܦ]»vúÆ“V®\™øÜsωƒnö÷&-- eyyy-Ö+''ùùùáŸ["B^^:w¼¼&ÝfИ¡P‡Æ–-[pæÌx:uB›±cQúé§æ9ÍJn™!Àê{n¦ÚñXöí ë;uìˆÄñ㑘ŸŽ;¢oß¾èÖ­[•œ¢ËM›6aúôéÑÙû™ys W×|ÀÌ¥¢Û¯êWœ=NÄ[{·OÀñÒÞOœ8‘‘——çEééé¸öÚkÃ'¢æ &-ZmÀ€˜?>@¸ QCMÚ;€¸éìÑR“ÎÖ[rr2n¾ùfìÛ·………8{ö,dYÆÖ­[9 ^¤³Ь¿1€b†bí‡dD©ZÛ¶ :Õ.Îu’ª‚‚Aš^=gMƒ A  Q áLn.2þó?±wï^$'_ YîIú ]»Cy¹¿8tèÛ7nwW$$ô†Ï×Ì QѳgOŒ9™™™q8pwÞy§º|ùr0óS–Î^“þå©~œ¶ À{ûË¿‚ÙwÔ ³×ymp¥5èÿ+€‰è83ÿ­±¯©©±fá+-;`3¯hécrph2N ‹¢¨‘¦iZxBßj©V§öŽ ã„š´wwçÎU"r·k×N[³foÒ¤I7n½úê«Bjjj³¼'GŽÁ‚ pâÄ æ¡Ñ£G㪫®j²=ß5±uëV,^¼ååæÖÚmÛ¶aòäÉèÛ·o³ƒ®ëX³f >ÿüs¨ªŠ;wbÆ ˜:u*:vìØlÇ š¦áèѣشiNœ8MÓiǃ$ çW¬€®ª`®ŠÛj»`/JD,N„‹sr^’F‚§S'¤¦¦¢k×®0`²³³/«Uó’’<üðÃÆË/¿ I’Þµtö*Ádöêv [â@D9þ `<&¢‘̼¦YÞ!.8zœ‰<33Û:ÛéÓ§‘˜˜È lél†,ˆ,ˆnn¾ÒEÑh²,‡÷¦†¡†¡Š¢î‰*Šb™«ëÖJ»ýå 2³JDQµ¤¤¤“ô*Š’vå•W VPM³âr¹0qâDôèÑß|ó ®¿þú˜Š¤ÈögŸ}¯½ö<ˆ>}ú„[‰E3Ûœ˜˜ˆï}ï{áv-Æ ‹ñ•ćììlLŸ>ü1XÅhNòóóqÓM7á±ÇãÇë;vT’““ ˜»ÓÌÐpáï+ü·ÈÌI±zô(,X€ãLJ/Û¾};öî݋ѣGãÊ+¯lTñtß}÷aøðáamþö·¿aòäÉh×®]ø²Í›7cÉ’%¨¨¨_¶nÝ:lÛ¶ “&MBÿþý| Ñ¢ë:¾øâ |þùçáÖ.@Ÿ|òIXgkŽýoŸ~ú)æÎËiiij¯^½TfÖ­óuv˜…bˆ0ÿæ†ac (ëzå•XÉ Y×!¨ºY €aö-×4 Ц!¤ë¨lÛ™wßa“'ãøñã(//¿h@ ˜’$Áív‡÷ú?~wÝu^{í5´oß¾Qï×Áƒq×]w©K—.3ÿÖ0Œ§jÑÙ£Â*Îaî;_ÌÌ$¢çªÝÌðKÕ/¯ @o«ˆh(3ץɷ~Ë߃÷†jï°Ô÷Æh﵈È-ËrpË–-ž›o¾Y5j”øì³Ï Ý]]g†;v„Ç ñÒÞ·lÙ‚%K–„uöú0 kÖ¬Á–-[0iÒ$ôëׯÑÇP]g†`0ˆÂÂBlܸ±E´wÃ0à÷ûqðàAìß¿•••õvÇ=$uîÝk¼Þ.ÀEQDVV† ‚¼¼<ø|¾ËFi·:úÓŸjÕÙ£ˆ:øÀmÌüÉ‹öþNqÞºpVЛ‘hS\ƒÁ äóùÂiï†a¸dYv‰¢èŠ\M§ˆ´wQ}ÌœÀѧ½ËäòòrOQQQÚ÷¾NÈE¡ IDAT÷=iΜ9ïD¿ß¥K—^¤±U'##×^{-òòòâò¼ååå3f ¶nÝŠÂÂBôë× ,ÀáÇë¼_çÎ1uêÔ*E}<Ù¿? qæLõn " 8W_}u\tìêœ8q³fÍâsçÎé¹¹¹¥³£Ý.̉(ÀÌ~þ•+W(((º`Á‚¨FM»¶lÁ³C† ‘>A€GàH0+K¶ tÅ0ÒuC‡¢ëm·aÌøñ€µk×âðáÃỽú‘˜˜ˆäädäää`РAHII3ãÉ'ŸÄ‚ °aÆðC¡~ûÛßòo~óCÅÖªyL:{-+è^«<ÅÌï['Ó•®‚µ‚óÁš›à:fþgCîßQ“˜ù-},-I´ãY–¥P($C¡ìñxdUUÝ’$¹¨ZÚ;y#WÑ£'x¸KJJ$I Ã<˜vóÍ7K÷Ýw_ÌÚ{ee%–.]ŠM›6EÝî´&›Ÿsúôi,X°‡jð1@nn.¦NŠÌÌÌÝ¿6=ˆ ÀÕW_}‘öÞT+芢àèѣذaöïß@ Ðàß'ˆ IѧOôêÕ ­²\SÀÌøûßÿŽûî»O ƒ¬tö‹töš¨iÝú9˜™¿&³;Ì;>‚ièÕ»‚îкp ôf¦.-!!AÐu]¬I{7 Ãe†Ë>ùÖ£½_¤¾×§½Ÿ8q"±¢¢¢Í¼yóÄ«®ºªÑ¯“™Ã[ }Oí0WÇOœ8Í›7cÓ¦M(--Eÿþýëìk*† †qãÆÅMg‹ÔÙ£Åëõ†u¶x(^š¦aÞ¼y¼dÉ’:uöˆU•*:»U ûAðð†á_¾|ùð^½z*,,¬×Æ9zô(/^Œ-|ÿÒ¥ð ÜV+0[«1C7 ¨n7<ßý.†þà?~<<N:…;vàÈ‘#…BÐ4-˜››‹ôôttêÔ mÛ¶½¨×4 ’$¡¢¢‹/Æ÷¿ÿý¨Þ³… âöÛoŸ³ ócxËÃÔR ÿÀHf¾Òú\x @wmäGî3o(DtLÛaCc«)!¢ÁVøOfþ ¥ÇÁ¡%i¬öno‹§ö¾bÅŠv§NòæççãÕW_†ZïëˆUg–Xµ÷P(„•+WbݺuõêìÑb‡ÎŽ;6êqB,:{´Ô¤½7EnÊÊʰsçNlÞ¼ÅÅÅQ¯üWGEȲ ŸÏ‡®]»¢wïÞh×®]³õ¶6oÞŒ3f(›6mªWg¯‰Úwëº0÷ ÿÀ˜‹/‘úÝ0[2¿Àõw‹qh!œ½…¨éܽ{w:räˆPPP „B!QÅðŠº,ËY×õ*…ºa^fö‚ൠöð‰×.Ø­^ê^f®~vÃÒÙ Ã Ãp8p 9;;Ûûâ‹/6X{oˆÆ‰Ë劋ö^]gß³gÖ¬Y—Ë…ýèGõÞ?11±ÑÚ{M:{¬´oß¾Ñ:ÛòåËñøãsjjªÚ®]»HÝ^9ëì|¡µ_Xgà'"?Ìdq?€Jþ%K–ŒéӧϤú t{‚bçÎ`fœß¸eŸ}Éï‡+^’àÊχ»W/t1#ÇŽ ï#gf”——# „#íÛ·‡ QÜW¬Xë®»'NÄûï¿_kÏßC‡áž{îQ-ZDžÖu}^4+ÚDÔÀOa¶%yÅ>ùU/Эÿƒ¥îeæ—-µ=æþó2ŽS¿Q"z¦"#3/Çc6DôS˜!Vÿhécqph Ä:N "Ù2qªê<‘…:_d± A¬qB„'hšæY¿~}[¯×‹„„¹sçΞgŸ}V¨­GwQQ,X€“'O6É{#ËrTÚ{¬:{¬$%%Õ«½7Dg•¬¬,L::uj’]Qœ>}[¶lÁž={PVVNnQár¹Ð¦Mdgg£oß¾ÈÌÌDBBB³&ö·$‘:»,Ëÿ…Bs¨³×¶½'€7LgæV±~ºÃ¥S ·0Í­½[×ÛJ[x–ÜZ•w!B{Ÿ6mš4gΊöƒ³¶TÖ†ÒPíýÔ©SuêìöjjYYþò—¿ oß¾6lX­é¨ ÕÞ£ÑÙ£ÅÖÞcMq=~ü8fÍšÅçÏŸo´ÎnQ%U2³¿°°pÊ€®/,,¬µ¡¸¦iøüóϱvíZhšVeÃPUh%%0ü~$tîŒÄädôë×/<£^Óßž®ëátö†œÔ<ˆùóçã¾û.Î% …Bxæ™gø×¿þµ!ŠâÊ@ p;3ïæq‰h2€ù¸Ðãû^[Ù®¡@³ù˜ápçaÑq)̫׶3óçñ~l‡¦§)µw]×ÃcD·=Î@ÞºukòÉ“'gÍš%Ìž=;¼=®¢¢"ÜÆ´9Æ—µiï§NBaaa£uöhÉÍÍŵ×^{Ñ8!:{´ú÷ïÂÂBȲ·™ qôèQlÙ²‡Bee%t]¯÷wl«ìn·h×®zõê…öíÛÃëõ^6ûÌ™o¼ñfÏž­ƒÁƒ–ÎÞàÄôZw À×þÂÌ ³ Û§ºY—ÍŽÃKqh&œ½Î&˲`†èr¹êÔÞa&"{ˆÈgÏ”s-‰ï5¬¦Ûßef–Ož<™XVVÖfÞ¼yâˆ#j=þ¦ÒØl¢ÕÞC¡V¬X/¿ü2*M×uìÚµ ›6mÂu×]‡6mÚ ¬¬¬ÆÙÜX´÷†èìÑmŠ«ªª˜7o/[¶Œ;wî¬x½ÞFëìˆX9·. ,X°`ÚàÁƒÿ£®ýÌ™3xÿý÷qòäI†Q¥¸¶OIIIèÖ­ºwïŽ.]º4[¢þ¦M›0{ölüéOBQQfΜ:}úô¹`0x3íãјééó ð€O˜ùzëúêz&€}ÎÂÜ'ö°õ;iRˆè f^ÛÔÏÓˆèz?p³ çà`í=rBŸ™½’$…'ôcW˜ýû÷§¬[·.#99Y/((çÍ›'V¬XÑ$ã€ú°µwÇÓ8 žDŽ‚Á`ÜuöhY²d ·Ý0 9r;wîÄÑ£GQVVn¯ÆV 5û»Úêñx Ë2222о}{äææ¢S§Np¹\—Ma˜ÇŒ3”7jªªþæjv,:{2€¡¾f«;K-úx@{˜V䘪ûYq#Bmš'Ž`B©Ž×aÆÑþýûÊÊJ£M›6á´w˜+ ªön[†a¨Öj¨3õ½JOTAlÙNrõÚ+í=++KÏÌÌ <òÈ#ÉYYYÞ_|Q¨ŠÒX=¢I{ß²e /^\%½>DQDï޽ѻwïðeË–-ÃÁƒ1bÄ ><|¹aX»v-¶nÝŠÉ“'ר³ÅCg¯hR\—.]Š'žx‚ÓÓÓÕž={^¤³sD:;.¬š×¨³[«ç•Dä'¢J]×ýÌEÑOD]×C0[™Öˆ®ë(//¯z‹¢A`<DQDŸ>}Э[7dff"))¦¶ê&''©©©øÎw¾Ãªªê~¯ëú“Qêìn?ð€ç\ÍÌ'ˆh€‡Ôª\0ói"ʲÞßfˆ²,!¢¿ÂlÃÖZ÷žm…Ù³õO·ð±88´ ¢'Ô”ö.I’@Óu]Ó4M•$)ÜF×uEÅptÃ0ÂcÔÓ&//OíÔ©SEEE…§M›64oÞ¼ô¼¼<B¡úé§q×Ù­â<`F@E¿®ëA·Û°oo½õÖí£FšYXXXk_”P(„P(„ŠŠ x½^¨ªŠ””ˆ¢ØbÁ0K—.ÅÌ™3C'Ož–º ¢df.méãpphm4D{o@W˜¨µ÷Ý»w§–””xzöì‰ìì줤¤¤&-Ô=Ƈ¡C‡Ö¸2»cÇ,Z´¥¥Mûñ‘œœŒk®¹½zõºè:{U½9Ô"† ŒÝ»wãÇ—â¹Ý0 ¨ªŠŠŠ TVVBQhš]×!Ë2\.’““ár¹àõzêûåÆÖ­[1cÆ eÆ ÕÙ=0 ñ>np=3«Dô €™¾ÏÌÔ¢¸§`m4 CëÆYAo¥Ô¥³¥¦¦ò‘#G¸ºÎfkï‚ è´êÚ;Å0Œº´÷*³åÖ…½êÖÞ™9°k×®ô.]º¸[¢ÀêׯÆ·Û ¿ß/¾øºõç_Ldeeáž{îÁÑ£GÃÅùgŸ}†äädüìg?Ã!CÂ-¿¶lÙÒ$ÇP’$aóæÍ …«³‡WÍA¨à×uÝoæDE1(IRP×uÅ*ÐUÃ04Ô¡¸€Ûí†Ûí®³µ]sqäÈÌš5Kýä“OÀ³–Î^ÉfæÕV‘>æÿ…h¨žþsÀ‡—=àÌÜ|K)õó+ÓˆhR+;®0D4 ÀOˆè*ëo×ÁÁÁ¢!Ú{äö8]×5]×µHíÝ0 UEÅÞg‚ö–¹º´÷=z¨V¼gÏeçÎi×_½ïÏ~; íꫯ®³{G¯^½Ð­[7üûßÿÆš5kâ®~K’„«®º £F ·ü¬ŽÝŽ­OŸ>Mž'Ë2¶oßΡPHÉÊÊÒöíÛ×Fâ¶Þ©©©HNNí¶Ú.ŠbX…¿)++ ëì’$½oéì 9¯¾³@?`|„ξf^}B½?u`æcÖþô ~­ §@oåÔ¦³>zôèE:[]Ú»5 W]q¾¨8Çí݇ zX{gf-!!A=|ø°Ïív§wèСY¦I3331uêTäææ†/›8q"ˆÂÂBìßUØvÌQ•½Þ999X·n6n܈n¸²,ãûßÿ>ŒÂÂÂfÑÙˆgϞž}û´œœ¥M›61éį̀^˜GêìÖìk™íÁš*˲¢™£½–Ó(b@Q<÷Üs˜;w®&Âj]×g2óÞšnËÌ_ÁRȈ¨Ì^¢Űwl"€ ÌüEÄc6}„oì< àJ˜{½ç´ð±ÔÆ_aª}s<Õ²‡âàÐ:i*í€JDŠ !MÓQëÕÞ­q†Çår IþóŸ¥›nº‰â5Nˆl' ²,cüøñ0`.\ˆ½{küØ™nݺaÊ”)¨­Í\uñ½ï}ƒŽ[û9"BII öìÙ£uèÐAIJJÒ™Ù`f@­Á­ Å.ÒEQ Â9o¾ù&î½÷^%Ôuýgš¦­nÄÃ%ÁüÝ]ÇÌ‘¡Jv”=Ð=s¬÷3˜A³Çñœ­§@¿D`f¶t6ž;w.¶oߎââbãÀܵkWÃÒÞ¹}ûö†¥³n·[WEgfM×uMÍ.Ôao!{ß"VÒ#~÷Ŷ {û¾n¯×«3sp÷îÝ)ÙÙÙ‰M¥³¹ÝnŒ;ǯQ™ÊÈÈÀ­·ÞÚä:[¤ÆÆÌ…̽ùóçã‘GÁm·Ý†;î¸ß|ó V¬X¾¾)øú믴´4¥cÇŽvË´zuöÈUsQ#uv;™½Ò0Œ°ÎnF]Ó4€êõzU»8EÝj)–/_Ž3f„Nœ8Q …îdæë¿WÌ%[‰(Ÿ™ël¦KDSÜ`|#¹Y`æ "š @­œZÕž'f.#¢‰0#uP}œÀ(..¦êã„”””ð8Áçó銢h†ahŠ¢h’$i‘6º®ÛûÓUÃ0ìñBÖXU­;{l¡edd¨7ÜpC ´´ÔKD´{÷îdI’’òòò4N¨Og¯ôôtüøÇ?ÆÎ;±hÑ"”””4ä0’’‚k®¹={ölÐýí>åÕÞm=%%%”““£³ú©YÛÛš|lïç¦Î>sæLeýúõšªªøcœÆDÇjX àõf.%¢[ì`æMqxN‡V†S _B´„öNDvÂk­Ú{RRÒÙ²²²òÓ§O§çææÆU{ïׯ&MšUº·­³}þùçX³fMÜ´wQqÕUWaôèÑaˆÂ­ÀF3f`éÒ¥¸÷Þ{qÅW 11»wö.I¶mÛÆŠ¢¨:tP­A“èìVq^Eg'"UÓ4™Õ`0¨û|>Íh‰¨Ü(9zô(î½÷^õ£><§ëúo8Š`62[ Í0æ îXƒí✈2kZM'¢á0[š<¹zÞš±õ9k?égDô03/káê3¥Èbæ-|H­–ÆŒPM{'"f—˜ðd¾®ë!ër»L¨.í=99Y%"wII‰¼lÙ²6]ºtÑoºé&ÑåŠÎ¶uöI“&Å%L´gÏžÈÏÏY{Fg[{ïÛ·/–,Y“önë슢(YYY€H{Î^Hiž>¥—)eeexôÑG—^z©±:{,ŒðGt_aæ·›ø9Z'$îÆZñ‚­³SÛ¶m…„„ÁÒÙMÓÄ`0(ù|>€¬ªª¤ëºK–åp8Œõaî1 ÃKDá A(gÇøT ‡±Ç@qÑÞkÒÙcáìÙ³qIq­žÊ ÌŒ®]»B<òÈ#¨¬¬l”ö^]g'"VQŽuvX*»ýÝÖÙ­/?…uvëqTI’MÓ4UU5Ç£…B!M’$]QãïÿûC“'O¾¿°°°ÕÄ´ªªŠçŸ=ö˜FDkÀ fÞÍ}ÉlEöo¸`¾¯]™ù¨uý`ëúÇìð/_Ãü= Ù¾ìqQÍ͆ÙÖ¬737Í~‘F@Dø/ßaæV™@ïàÐÚ¨>N8pà€`‡ÍÖ6Nƒ²a.Y–]vïtëË k,@D>]×}¢(ÚÁqõŽN:•´o߾䆢'γÇʹsçPXXX¯öÞ­[7\{íµHKKk’ã(**ªW{'"œ?ûöíÓ²³³"ª¾j|öÙgmÇŸ¯8‡ ¼õÖ[˜5k–Û˜¹1:ûEÑç’˜yPÄe¸@?nå¡®ñÃ)п5gÚ;y ðoScÚ{EEEJûöícÖÞëÓÙc¥¡Ú{]©¬ÑP^^ŽþóŸ:t(úöí‹wß}‹-B§NbVÃÖ¯_o¤¥¥)^¯·Ṅ*«æÕÒÙ#‹ó°ÎÎÌȼºÎ®ªªæóù4·Û­>|ØÈÊÊ2B¡ñÚk¯=rÍ5×ü¢µè+V¬ÀŒ3BÇŽ+ ƒw2ó‘×[ú矼ÅÌË«]—`€ßøÀR˜}E'1óÒˆÛÝsOôÿxÏz¼ÞgæCMõÚš"êÅÌ-Ó¼·ˆH‚”ó:3ÿ­¥ÇÁáRÁúÜ‹¹+Œö.‚Û.ÔaMæ‹¢è³ öˆ¯¨»Â”——{V¬X‘9aÂ1??¿ÊIÑãñ`üøñ2dH³$ïÚµ .¼H{OIIÁ”)SPPPÐäÇ`¾þúk|úé§iï‘:»µµÐ°Šr-b[½-!´bÅŠ'NlçèñcÛ¶m˜1cFƒuv"JðSëŸâZ:ÄT/Љè6¯}©˜yñÁQÜ¿Xû΀8iï†aØ{ÍjÔÞaí]GÚ{bbâÙ²²²²Ó§OgD«½Ç¢³GK¬)®ñÒØ’’’0}úôð¿ ÃÀG}„´´4üøÇ?®Wg³uvUU›Egw¹\õêì¡PÈ(//7éééÆöíÛõ$¸7ÇÇìÙ³Õ?üP ¢5M{¼ýv£`ˆè?aâýü†™?¶._d]^%‚˜™?ð™mÖl^fæÊx¾¦–À.Ήè*ßð@¤R×’0³FDã­A¨ƒƒC”ÄC{×4MµslDQTì1…÷©£šöŽªûÓ½¸`|¹­¶\÷Þ{/ñ†nõèÑÃ-Š"ˆ«¯¾:.:{´ //«V­ÂêÕæ‚èˆ#0räÈFëìÑ"† N{ß´i$IªWgGDan}:Ÿ‘ñ£¼¼>ú¨ñ¿ÿû¿$éUUï‹Ug'¢¡–áÂX¢-€ºÚ­¶#¢_Âl™6ÀwâüòÃ)п%Dž€™£O{—$I UÒÞ퀫XW"ÞÃiïöþõÚÒÞa*m.ŸÏw²¨¨Èçv»Ó³³³kœ o¬Î^Ѧ¸ÆšÊ ?üá1mÚ4lß¾™™™øä“OðÚk¯¡{÷îèÞ½{x•ÀÖÙ÷ï߯uèÐA±NÄ‘­ÓbJg¯IgG é캮‡DQTìÂ\UUÍëõj@@s¹\ºßï7*++ÔÔT>tèoß¾{÷îÍï¿ÿ~ ¯¶ùÐ4 /¾ø"~øaˆ¾°ÒÙwÛ×Qf¶ÝÁÃn¨V¼O‡Y´Ï°‹s‹Ö÷CõÆxÛˆèÛ¤^ü@€ÛZøXÂØO" ‘™µð!98\2Ô6N¨-íÝ'Øi着j‚ h’$…Ç ‘ûÓí=ê¢(Úc†\'TI{w»ÝêØ±cƒ•••^ŸÏ'>|Ø·{÷îT¯×+^ýõÍž@&Ë2ÆŸÏ>|xs !!C‡Å¿þõ/>s挡³GNÒ‡ÇÖXÍ^8 X)üäí·ßƬY³¿ßH×õÛjKg·¶ÆÝÊÌ¿­áºþþÀÒ¼ˆúCdL° ÀðÈ1ÃåƒS ˰OÀsçÎEmiï.—Ë$Ƀ¢Ëå2Ün·QSÚ»¦iª$I 3×›öND!K{Wa¶fQa*ón·Û­îÙ³§Šöo½>jKqml*k´x½^ <ð“Ÿü«V­ÂÇŒï~÷»ÈÏÏ3‡uöœœœ°ÎŽ 'd.„ûÕ¨³[éëa•Ý.Ò#ÓÙ\.×E:»ËåR‰H …Bº,ËzBB‚væÌ#++Ë`:tˆð˜1c쿱]Aÿ÷¿ÿéÓ§‹ŠŠÊ-½Êl™ýÌ— à$0óB"’‰H²¶Ølaæ×ª=Å0˜ï}}}yfȰ‹óû’ƒ™7Z«èZúXja0€Çˆh037.hÂÁá2òîêM{ì ™ön¯¦‹¢. Ãɲl'¼k' †®0>ŸOcf·¦i´eË–ô}ûöé}ôýîw¿GÕlïÉùóç±páBìÞmÖBû÷ïÇ”)SššÚlÇ ª*æÍ›Ç+V¬àŽ;†:tèpÑ>óˆ÷Î6Âô–çèdÇŽ˜9sfèË/¿4,ý¥ztöW„[©‘ÓÖÛà9Ó˜y¹ÌUÏ!ìbæ1z—í}ذaÆþýûÊÊJ£M›65jïÖŠº¢ªª"IR8íÝ.ÔQƒöQ¨{`L˜ÙMD®ÄÄDÝÖÞÓÓÓ]³fÍ¢üüüfoìו+WÆŽÛl›MRRþùÏâÌ™3xë­·°fÍÞ´i“Q^^Ž‚‚ÎÏÏWØmÌì4]»?m:;ÙE¹Ÿ™kÔÙ­Us¥ºÎ^QQ¡û› Jî IDAT|>MÓ4ãÌ™3Utöj…yäßX³ròäIÜwß}ê{ï½'ø_]ׯE/ï3¨( õð€«œˆ¸ª¦“ïHó™¹¾•­4U"Êð)=ÀÌÿŒò%µJ¬ÂwÙÎeGß ¾Iaæç­÷º-¬ctppˆž¦ÐÞ5MS¬írªµu®ºö^½-[xœ˜˜¨~ÿûß?´yóæô‚‚ÿܹsÛ´k×Îû /íÚµk²÷AÓ4¬Zµ «V­ª²n÷îÝØ¿?Fމ‘#GB’šv¸¼téRÌ›7ÓÒÒ”nݺEæÍ„uvk<ž '"»8½ ‚à·Æj1RQQ¹sç/¼ðK’ô/Kg¿(¹ˆR˜¹Äú9æßô}7à%çLf+¤Öú›ßŒúk/§6spþ¾ÍÔ¥³mß¾²²²X’$# †×ë­Q{—eYUUU  ‚֪ѕ¬+̬‚ DjïÖŒ¯[™]>ŸïäÙ³gÝ·ÞzkÚ¤I“ä|š»@>xð vì0³°rssѽ{÷f}~8|ø0î½÷^öûýz§N”þýûóÎ;¥'NP^^žBDjii)RRRü¨Ag‡™Ê@-:;€ùÙ ƒ»Hg—e9&0‹sû調Ñ4 /½ô~õ«_©Öéº>ƒ™wÅø0“ô©ïFVñ7ÀÏë¹é˜šÍ˜3æÏѧÌ\ãñµ:ˆHðCÑDn%AxÌ|Wý·rpp¨‹†n‹ÔÞeY¢ÐÞkÜGDªÏçS‡àêܹ³úÑGµ9r¤ñÓŸþT|à(ž-\³_¸p!Ο?_ãõš¦aåʕشi¦L™‚=zÄõùàØ±c˜={6—––êùùùõêì°ì»0·¾Â“ôDT©ëºS ÇÈ;#{î¹'TQQQdéì«jºýÀ$˜“ü°&ðoªåacæ¯"îKz £­ §É¼ƒS _T×ÞqAg£®]»rBB§¦¦š¦Õª½‹¢¨‘ªªª­½+vïSÔ£½[»—ͽékoºÞ³gOuýúõ‰cÇŽmóØc 'Nlò¥’’,\¸»v]¨ëÞ|óM`Ê”)HIIiêC@(ÂܹsyõêÕœ››«´mÛV §¤¤èW\qE%›aXjyy¹ñÁtIMM­5jÔÞôôôrª–Înéì~•5éìÌp»Ý5êìš¹$¢%&&jn·[BgGKç«V­ÂôéÓƒ‡.ƒw1ó{Mõ\DÔÀbÿÇÌkê¸é2®ºg–Žø"½lÍ–Ì“÷ûÜJÂÖb…ÍÄà©0Õ¼“g["z@)3;‘Å ¤ºö^Óö¸º´w{{\uí]E»P‡önoAÏÏÏ/Û¼ysêÂ… +/^ì{â‰'Ä1cÆ4úµV×Ù£¹ý[o½…=zÄM{WUO>ù$öÙgܱcÇPJJJT:;¬Iz"ŠljOÊû™ÙïèѳsçNÌœ93´nÝ:#"½®-j?Py0’™¯vÛ³ÕþÝ@*€{Üß~œý2¢1Ú»…f­ºÚÊ{d’k­Ú;¯¥R{`žl<4A\™™™zÛ¶mýO=õTÊ+¯¼âýÃþ tèÿm¯š¦aõêÕXµjL *»ví ël#FŒh2íã?Æï~÷;nÛ¶­ZPPP%½ºÎž˜˜¨Üxã›wìØ‘š””t–ˆ*8$˲کS§óÕÙëJgo-…ù©S§0gÎõwÞ¼¤ëúÜZtö¸@D½üÀQÿS×mÙ Ü«í:{`g0‡ˆnàÓ_[ ÖäÚý@¸ÝÙf^ß²Gf7€wˆh ;I· ¦)´÷ˆ ÙÓÞíUvTÓÞAPûõë§”¸\.‘™ËfÍš•Þ½{w×K/½$deÕ·÷bjÓÙ£%^ÚûâÅ‹ñôÓOszzº’ŸŸµÎŽ [Û«æÌ.ÎaèŽâ^?xüñÇ矞%Iš¯ªêìštö(ù€O£¸ÝDG̯çv­åÜêЂ8úeF¬Ú{›6mtY–u—Ë¥UTTh¡PH“eY£jiﺮ+mUêÕÞaêaí=//Oóûýîüài&Lz衸iï{öìÁÂ… qî\ÝÛªªbÅŠa-žÚû¡C‡0kÖ,ƒzAAA8Ýzýva~Q:{JJJðÊ+¯,‚¥«8p ÷ðáÃcÛ·o¿ñÚk¯}ïÛª³ëºŽ?þñxðÁUfþÒÒÙw6áS°@"€¿¸›ãЪ†™QOw8ÓØÇk%LðÝÆÌoµôÁ0ó|"šúÃü¢ ¡Ú»=N°µwA”ÈqBmÚ;×Ò†ˆT·Û­‘[×u÷Ñ£G…½{÷bÏž=ê~ðé h‹ä]»vaÑ¢EµêìÑ©½_sÍ51õH?zô(fÏžÍååå ÒÙ­â<\Œ[ÛÚ–Öî'¢ùu]wBâêàÝwßÅÝwß]¯ÎOˆh€_ø!×8w73omêãqhý8úeJŒÚ»ár¹ ŸÏgØ:›¢(š$Iš­`EÌ”7J{/((P¿ù曄qãÆ%?ôÐCÂäÉ“¬½Çª±Ùœ;wo¾ùf\t¶P(„Ç{Œ×¬Yù¹¹J»ví"gË5K¥RíÁ jIg·uö &ž9sfqEE8wüøqÏêÕ«œ››»`ðàÁAJ’tIëìkÖ¬ÁÏ~ö³àÁƒ+B¡ÐÝÌüN´÷%¢nN0sEŒO» f”]˜E¯f>óÄlëóÿð‹Kuµ—™ ‰èg¦À Ükq˜ùo@xuõ(ŠQP}œ¥ö^ë8Ú»(е¦½Sµ®0̬ ‚ M›6íÐÖ­[ÓóóóCëÖ­óŽ7®Íܹs…qãÆÕ:N8wî.\ˆ={öÄõ½9þ<Þ~ûmtïÞS¦LAZZZ­·U¿þõ¯yÕªUܱcÇPjjjÌ:;ÌÕñ@uÝþ·Uœt]êº~±&è€]»vaæÌ™¡µkתªþ f:{“Ÿ+¬IúO¼ÊÌÕu[§8w°q ôËœh´÷ŠŠ # ]¤³°“ZUkf<¬½ÃRµk*ÖëÓÞÛ¶m«·mÛ6ðÌ3Ϥ¼úê«Þ?üáBNNNÔ¯©±›Mcu¶ùóçã÷¿ÿ=gff^¤³Ð­É [÷S˜9ˆ ½LkMgÏÈÈð§§§ûu]øý~¨ª*lÞ¼ù7™™™×wíÚõŒ¢(ª5QbŸðµKAg?}ú4~þóŸkÿøÇ?ˆˆþ¨iÚÜh m"º fœ €XGc~fþ,Æû4„L}íS"êÆÌG›á9ã3¿ àM ¢tçZê閭™œ{oKˆƒÃ·…úÆ õiﺮk’$U'‚൮cœp‘öÞ¿…ˆÜ>ŸOùꫯ|?ýéO…!C†ˆ/½ô’о}ûð1«ªŠU«VaõêÕÔÇž={pàÀŒ1#G޼¨#ÌÂ… ñ»ßýŽ322¤³ó…}æá¬\XE¯´o#Šb@Ó´ Ûív ôjTVVâñÇ7ž{î¹xèì±ðÍp%€'™yn3<§Ã·§@w¨SgÛ¾}»—ËŶÎfŸ|«§½W×Þ#“\Q»öî%³·÷EÚ{×®]5¿ßï¾ùæ›ÓÆŽ+=úè£B}Ú{}©¬±Ò׃bÖ¬Y …jÕÙ­¯*:;E¤²Fæª#½K—.'»wï~ÿÉ“'¥ÌÌÌsD¤üýïÿ3•¤¦¦¾|ã7~u)èì/¿ü2~ùË_ª†a|­ëúÏ¢ÕÙÉ5>à'0g¨g«¥IŒ ¸OÌX³‰èf>˜­ß˜yKs<ñ€"Kyoéá‹VÑ|f^ÙÂÇâàð­¡±Ú»½=NÅÈ´÷‹´w»[mÚ;.¬¨»\¼fÍšvGŽ©¸é¦›<“'O–ùË_Ò¾}û°páB”””4Ë{£i>ûì³ð8¡  GŽÁìÙ³¹¢¢"=ú(¯[·Žsss·Û“ÎnτחÎn]!`ëìéé銮ëªËåRÓÒÒž?wîÜÊËË»'$$¬þüóÏÝÅÅÅéÓ¦M;ÖÚtö/¾øÓ§Oîß¿¿ÒÒÙcíþ+·ÁÌMóËo"Šólk‰¨ÀÌ\=õõRà6K\àãznÛ¤0ó"ºÀæ–<‡o+M¡½ëº®È²¬DçQiïÙÙÙÚ7ÞèAbfùã?Nýúë¯}ýû÷o– ×ê”””àí·ß3óªU«¸S§N¡´´4»(×£ÑÙ­¢ÛODA.ÒÙíëp¡ˆJ’ …B!MÓk»a]{œ/ víÚ…Ûo¿=ôÅ_4Xg'¢¸À)f¾¡‡ña,[ô"q t‡‹ˆFg“$ɨ®³ ‚`§‘k°t´H…ÍNpµ[®pÕ¶+JÄ—µhïÏ>ûlòk¯½æ{ñÅ…Ž;ÆMg–Ú´÷?üÏ=÷gffª=zôˆIg· s\He­3™C²,‰(™În½gÚ”)S>÷ù|Ÿjšfœ9sÆØ³gÏDMÓÞzõÕWµk×îæ1cÆZº0/..ÆÿüÏÿ¨o½õ–@D/kšöh´:» ‡™,>!†â¼ÜºÏé¹I`æãDÔÀ0«—̼߲*³o:·`K9fÞdG˜ê}iK‹ƒÃ·•xkU ’Cí]a.ÒÞ¸Ap•––¦lذNœ8¡M˜0Ajªn,µ!Ë2¶mÛÆº®WÑÙaŽÂÅy]:»uî¿(5èì.—+" ¹Ýn€êv»µË¹@¯¬¬ÄObĈV¯^}P×õ«ï¸ã»8¿ ÀW5ç{g^yåüâ¿P ÃXoéì;øpOø+×ßîk"mbf•™_®áú hÁ°3fÞà‡@D.ðÌ%fØÅù'¢ï2ó>¬—aþÿšÖÂÇáàð­$VíÝ'Ô¥½s=]aP»öî=zô‘.]º$û|>:räˆ$BzçΛ¼H""œ9s‡Ò²³³CŒæÒÙ½^¯BVûTk ^®z„Î~ÄJgYg'"fk€f.¯ç.MB‹h@—Ö ˜çÎ˽{÷æ¶mÛrqq±±uëVýüùóšßï×€–žž®z<EQÅív‡$I 0³_Ó´J"*'¢rQKEQ,PBD%†a”‚pæ*×yf.aæ¥ö•¨°VWýDðù|þ‚‚‚Sûöí+;|øp‹S={¿ýíoÔÔÔP§N‚¸øÙ"ÅVÒ+­×PNDeDT  T„’ˆ×}ŽÍÄïs†a”0s‰a%†a”Á\ù-'¢rMÓ*u]ˆ¢4 #¤ëz(111” ”——kn·[w¹\zqq±qþüyc̘1ÆÃ?¼ž™Ÿ´WÎa&–ï'¢ÿn®÷kݺuèׯ_pΜ9g+++oõûýW6´8'¢« CݽGí¶`žlk„™ç0ó††Gà `3ÕxÐúØ ³/ùb"jéÕƒ†ÑÀ>‡o5 'ø|¾ Ëåò3³_Q?3WXc…2QK™¹”ˆJA8/BøÜà¼u¾,!¢f.‹'têÔéLFFF©Çã©X½zuðµ×^ÓŠŠŠšlœÀÌØ°aƒQ^^ÈÎÎâÂJ¹BV;]hV <`æRûµ‚pÞ·Îÿç™ù<…ÇD†a”I’TaF…¦i•^¯× …‚åååáqÀ±cÇ4­!°³Ùؽ{7ÆŒúÑ~8}úôƒ@ '37t¯ùotpM Åy)Ì ‡¸á¬ ;DEiïJc´÷„„½²²²¢¸¸8#77×Ý\:›$Iغu+†¡vèÐá"Ý:ÞÈUs»UJXcC:»µß< èr¹‚º®+º®WÑÙ‘Î>†ˆúÂêÍMDW˜àunXÈZ­œ9s÷ß¿öÆo½béìQøˆ¨€,f^^íª10ßïÏ«Ý>æêD)€/Ü`13Ÿhô i¬ã¾‰ˆÂ{ê‰(Õœ¶z¬U¡èÎu÷zmŽc)"¢|ëÿ›ƒƒCoíÝî …öî Ã2l´¡C‡žZ³fM»õë׳¢(r—.]Ü¢ŸyCI’°}ûvÖu]ÉÊÊj½²²RÓ4Móz½š$Iº$IzBBs n1jN*++ñë_ÿÚøýïϲ,d¥³7ø\OD]üÀPf.®ã¦"Jä Ûò†Z-‘|a}984§@wˆšæÒÞ#î:µw˜'AWBBÂÉ£Gz%IJÏÉÉi²U;"ÂéÓ§QTT¤eggת³Û^cuvÃ0B.—+hØ£ÒÙcIgçªý6˜+Ò7èa½^ªí¾Ñ`^}õUÜÿýŠ®ë-}{´÷'¢10g¥o«áê^0u¿²ˆÛgXà1ó­I¡ÿkèñ·$̼0‹s‰èUs­¿“V58ÜD4 fÀN‹åØÅ9ý€/ømkçàp©ÐÚ;_è ÖÞ™Ù“˜˜¨Mž<9`†›ˆä/¿ü² ÉÇo°=Zήâ‚Ò^£În¯¦Û {4:;3WÑÙËÊÊ´P(¤%$$¨’$é@ÀƒFVV–ár¹ŒÆœ·/Þÿ}Ü}÷Ý¡²²²#V:ûçõß«^îpfòzm” súÍðÿÙ»óøºªrÿãŸï>''IÓ)”Ò Ê PTTTPTDE¯¢m¹"Þ«8ýôÒ¢ˆ€ÃEôŠ€âŒ€ ¨8`œPÊXh,h¡¥C’3ìýüþØk§§išf8ÉIÒçýzõÕœsöY{eÜûYëYÏê&8¿Ÿ´pí¨ÿ>¸Áãºë³ìNVŰ ЗjïQUB Þ9R®PH.ŸÏgÞ®³è[T{£Ó@c¡Pˆâ#<2~Ê”)ã'L˜°U¥÷H’„… &;ï¼siúôéýªÎN¨ÄJ7ÕÙ¶$IÚ£(ªž5/V*•P. åJ:åP;vl¥±±1^³fM2uêÔHZÝÒ=À_%i€Ò<‹$Ý|ÍÌžìË×ëÎ;ïä´ÓNëxä‘GÚŠÅâföÓÞ¾7¥-À•ÀIfvK7‡5ã$nf Áù-ÀEfv}_ú:œ…ÇãH·û°¤Î]ê«uÀÂ`O_+ô×Ò)ÀÇ%½"  9çQ×û„Þì ÓSµ÷P¼î¶[í=ÛÆÕB  ±­­­åÎ;ïÔ’%KJo~ó›Æß§û3cáÂ…ÉN;íTœ>}zŸª³gy68o}¨ÎEQ©¹¹¹´aÆJ¹\®466VZ[[+ëÖ­‹[[[“J¥’‹ÅdÒ¤I¶hÑ¢Q>üðÃÌ™3§ôç?ÿ9)—Ëÿø†õ½:û €'--Ö[íàÖ®÷M’òUçøpéö®Ýê¦]çúÌt×oUélÌ›7/ékÚ{’$å$I*¹\®Ç´wÒµÜݦ½‡ b¨H*Œ;6K{Ÿ´ûî»7 4í½–éì„ô53Û¤ªmÓ²tö,ý­P(kœÎÞk¶9õ¼|øéúí'%M ýßfzú3Ï<ÃYgUùá¨(Š.-—ËŸëéø®$½øé6YoÏf’»qi¡»_Jº x9ðq3û^oÏ5R„¯ÁÁÙcIÿüÐFÀ–lföCIË€êÜ•÷?¦ÿ®s_œÛaTÝ'Ô;í½2kÖ¬Ên»í¶aãÆM7nLV®\¹óóŸÿüÆÆÆÆ?‡¦³²çBP¾Ýtö,‹0ŽãROéì¹\Î&L˜Éþûïo>ø ]sÍ5Æ(\ƒÞÖÖÆ9眓|å+_©®ÎÞçtvI³IƒëI]žŸL§ËµJé.+?t˜¥Ë0ü°ÿŸ‰s½ãº¦½Çq\%Uº¦½³9 ïœQßVÚ{d«ÓÞW-[¶¬)ŸÏï§½géìO>ùdeÆŒéìa¤¼Oéìl¾wŽ”‡Qô63Ë‚ø¢™C•öš§³÷U-¾&üË|˜#é3;«úø$I¸ì²ËøÄ'>QŠãøîrÖŸ ì@`ðXÁ9À·H·@9ˆ´>ÁÉfvS?Î7¢„ ‡ÏJ:Ú̆ý~ßfö¥Eã>|«¯35èÃs¤û´;ç†Ø`¤½Å$I²`¼§´÷æêåqãÇ/O˜0¡ÑÌ ‹/n»ùæ›óGqDtØa‡m5›ž¥³?ùä“¥iÓ¦•$u­Î^! Ò‡à­DÕ}€6‡kÚ¢(Ú”}ÜS:{.—+ær¹nÓÙ¸­­-ÉårV(’ý÷ß?yðÁíïxGçà|6q2Z\{íµüçþgqýúõO…êìýJgWº¯ù'€wvs¯49üÿªãg×ï ÷xÎ ù WK’4oÞ<,X° Ú°aƒfΜ©Î–›6mZT*•rmmmù|>Ÿ/•Jy !I’BE’%5&IÒœ$Is.—kŠãxL.—Œ1³Ic̬#©9\€›Í¬h4³FI@hظqãø]vÙ¥×iïqs÷Ýw'“'O. …, ¯HŠû’ÎΖ#ä9[¦³w˜Y1ŸÏw¦³777wçcÆŒ©466ÆO>ùd2uêÔ¤X,&<òˆMžƒîjj°ÒÞ³ô5m^›^ÒÖcÚû¸qãâ¶¶¶kÖ¬™ôüç?›iïù|žûî»Ï€âŒ3âZ§³‡¶-ÒÙÃ^¦uIgï+K‹Ý\õÔ àå=ôP>Š¢ŸW*•ÙVU´­'’&§’nsY7‡ û™áz ßïCçŒú]’.¾9T? ýafË$œIú{[>$a`íZI›Ù³õè‡s;²¦½—£(*'IR½Åi·iït³+L.—+rÈ!%  )¿téÒ¶%K–ì_Éçóq±XLZZZ’îÒÙ¯¹æš!»*mmmÕÙs¹ÜýMg¯òqàIàSÝçfKúé û“ÞƒœâÁ¹«Ð]Íõ2­Oiï@g°ÎvÒÞÃ3[Ö.¨…1cƬZ¶lYS.—Û¹ºÚ»$V®\iO=õT1¤±é…81³Jxÿ¨Mgï/3{JÒ“$™”$ÉáwéìÄlÛ”H: ¸?n[o£æzÁÌ6H:ø*ð7àuîRÌlé66HÚ•4ãe¨ ßý°ŒôæÙ9W½¼OèSÚ{V¿& îg»Ãµ¹¨\6àŸÓDØ7˜Yc{{{~ñâÅ,]º´r '¬×æ\ñHŠÂ†Ì¥ÎàœÍkÍ;g˳tö8޳Ç[¤³SuP©TJ e \©T*’*íííq>ŸÛÚÚ’qãÆi`¾U:û~Ó†Àu×]Çé§Ÿ^\¿~ýS•Jå´r¹¼ FM—ØrÉ^w>I:ømfv_Îë\¿xŠ»t}I{onnÎmÚ´©h¨T*Y:[ǹ\®)I’fIcBú{¯ÓÞC:\4í=¿qãÆñ“'OžØÜܬûï¿¿´óÎ;olllLÂȾ±¹L¹êßilYŠ£0½¿$}8 ø‡™žëÜ®MÒ€÷“Žh¿ø,ðI3»0¼ž¥¸ç­Îûh$ág=ÛRìà6Ì×ÌIº€4‹â3»§N}Ëœíú’öžÝ't—öž-“4FR3›ï Z$!¤½g÷„eq¤Ë •J¥°jÕª±»îºk»™©­­-?f̘iÞ9{‚þl¼ó^À¶,W=`ßÇqgU÷$I:ÓÙ …B¥ú>`åÊ•IkkkÒØØo/}_Ë—âþè£2gΜâí·ßnårùs¤;²ôµ:{ p(p—m¹«‘¦ÇŸYÛ^;7x|Ý )´o› IDATºªt¶m¦½gél¥R)ÇqFÊã,-ŽãŠ…"/Q• 3êlÞv­Ç´÷0â] fV;vl¹££cݪU«g̘‡ À”®5ÃŒp–ÒžÍÒwçQmq¶ªtvÒóö‘šÎÞ_–VQÿž¤lvü@à:Iß%]Ûu*p”™-‘´„4@_·UÁy3ð Ò “¬ûÎ×Á§Io– ­Ê?¤Â߃Júœ™Ý8ÔçwÎmÖ›´÷ly\õ}]ÒÞ£(ª^W g;ª—Ç‘Þ+tnƒÖ¤t[ÑÐÐÐÐÐ0cÆŒ f=þøã­·ÝvÛÞpÀ¿>øà¥Ùu»j?öŽªtõ¶(Š:3é̬-Ü'´ÇqÜ!©=Š¢ŽõWjhhØ"=I’Š™%Åb1niiI¦OŸ¾Ã¤³ñ‹_´ /¼0Éår¿ éìËûÚŽ¤ó‰ÀMÀ›»2ª¾nnôóÝ ‰í¥³M:5ÉçóQ¸èn•ÎTÂ…·R×KQuéÙÿÙÖlÕiï¤ãfëR<ÎÌÆ×AšI¢Ð¿,½½sûºÌœgxv!Îf̶J¥Ò¹ÎRÓÙûÊÌ6†#M¿þÐ ¼ @Ò¾ÀwëÓ»Ñ+Ì优4ø]UïþôÄÒ5ÉKÚÙÌÖ áùK’.~(i¯¡<·snk½L{ßjy\uÚ{cccçÚôl¦»ªÚ{)›ñŽ¢h ipÞlfMa½ä%åh=öx®£££cñâÅ3$­¥*›Ž´Š|ç6jU)ìû™çr¹l¾ó>`[éìår9éèèH¦Nšæ£>ý¿øsçÎP:{\¹8øé¦×Ugî97y€î†TösÞ¼yÌ›7À,XÀc=¦™3gfàdÒ¤II©TJÚÚÚ’ &Ä›6mª$I²Eq ”ËåJI’”Šqg3ÜÙ>¤Ùw3鵉Íéî @CE93‹Øœ‰mÙì9éÚ´lÝZçzó®¾)C›²‘rª.ÌÙHy–ÎÇq¹R©” …B%ŽãÊØ±c+ñš5k:/ÈO<ñ„#nÖ¼'!Íì[À·$5³’îö¾BšæÞ•§@øY<:+Óþ‚tßôŸ×µc=4xXÒçÍìÿ†ê¼fö IwypîÜðÑÃ}‚Íœ93 iï6mÚ´­îâ8.•|>_.—Ëå,œ0X–Ç5‡AôfIÍÚ\¾@º«L^RdfÚwß}ŸÙo¿ýî—oÚ´)ºýöÛ_z衇ÞÑÚÚúl–—âÙlºÒbqI’t˜Y1KgJcÆŒÙ"}Ù²eIkkkÒÒÒ‹ÅdÒ¤I¶hÑ"{ðÁ{•Î>…töÒí·ßžô7:¯o?'ü?ÌÌÖKÚ8™43﹚vܹ!享‹í¥½OŸ>=I’d«t¶8Žã,È­T*•\.WJaMw‘4e­38'M¡ÍÖ‰5™YS6‹FÉó@¤Ð¥…á’ªôìžU\m'~Éår[¤±eéìI’ …BG×tö¦¦¦Ê¦M›FM:{w$M>œ<mfë U—téV&g‘ê+é¤äÀæëÏkéàG’ž3³ßÕ»3Ý1³uJ÷¨ýž¤_ØÀ*ööõܳ苇ê¼Î¹žõfyܶÒÞIƒôJ¥R)g³ça@¿¨Íû”7M]³ë$åÌ,EQV$.’¶¶¶1ëׯo¹þúëßÒI'}qüøñkIï3:r¹\GÒ‹á~#Ëæótö ­­sÏ=×.¸à‚,ý£ýIg¯ri±Ù7„û‹Wo¨Mo«/Ð]ÝT§³Qµî¬½½Ý-Zµ´´Ð]:[’$¥Þ+!ÞªÚ{EëÅI·T#©ÉÌšH‹Á̬!Keº”ˆ+[ºæ½DUÅölt<Š¢v3k )ïÕéì¹\®T*•JMMM;D:{FÒAÀu¤©Õ÷¯þƒ°§¨¤÷‘¦º¿!|íÞÞ:ø?à™V‡ý±G«0Ðq¤«ÌìII/0³‡ëܵ­˜Ùo$íi›×ÓG! ~Ð…µûwH:ÏÌ.Šs:綯¿iïÙ®0¤z©R©”“$)…ÝS:¢(ÊöQo”T)î ’òI’äs¹\”$I$É’$I€xÒ¤IñI'ôµ‡~xú„ ž2³âã?>a÷Ýw_I(›eÎÅq\"Mc/—ËårH¿¯DQ´Ã¦³Ÿ~úéÅçž{nYHgÿc šý>ðùp?ñN`é@ÿjжsu庫»ê poÓÙJ¥RœÏç;·< Ayç(9éú°"Ђéöœ7‘VmíœAÏRÜCw 1³r.—˶m+šYGõz3ÒÑ÷¬àËVéì@9KÉMéì’&“Ò_6³û«žß ¸ø´™}7¤,¯ö ¯ï \ ¼%\L¾<Ü |8¼*8û2°¸> ¼¸¨ ÎóÀ-J ô}ÈÌ«o϶TõýŸ ¼UÒÛÍlзC o³K%}×6×PpÎ ½I{/ ‰™%IKKK,).—ËqSSS9ŸÏ—;::Jù|¾˜Ëå q²Yó$I @Cø—Ïçó¹4A/GÇHÊŠÇV€ò>ûì³"ŽãÒSO=5îøÃÅ---¿?òÈ#¿²Ûn»mÈårÙ=H(—ËåJssseܸqñºuëâB¡`;R:ûâÅ‹™3gNéOúSR.—?OšÎ^îK’fs€‹ÍìÙ=ˆ¤³€Ã€7–wuñ;Ò¥vΠ»a£/él¹\.îèèÈFÊËYÚX(ºR’TŒ¢¨ƒ4}­IiA·&¥Û°4ær¹†$Iâ8ÎKÚb :GQ”UïÜK5Š¢lMY‡¤Ž8Ž‹Q•²4¶pÞ !½\.W²mRFQ:ûëHÓ׿œ=¾~W˜YVøm*iðýdx|°ÎÌHzp5i•ÕMUiÅ·…öDZqõ¤³ë~a­3«H:Œ4«¡¹ÞýéÁ¯€ß$ÍÂtfvƒ¤Û<8wnøÚÞ}Âa‡–,Y²$Y·n]2~üø$Š¢¸££#ŸÏç³}ÓK¤3åÒYïBEyÂÚó(Šr¤ËÞ¢8Ž•ËåH’Ä’$I¢(ªHÊRèË»í¶Ûê<ð==ôЇóùüú°÷y¹X,VÊår¥P(TbIq>Ÿs¹œí(éìíííœ{î¹vþùç'ù|¾ßéì’Þü”4^É‘îú±KÕë‡ß–'NëÚ†™½¾Ÿ…­$|X ÌŽ¿ »a¥§´÷¬Ú{±XÜ"-Š¢¸P(Äííí•\.WÎçó…$I²µfYa˜Æ(Š öB·´H\gµÖl :a=I’2PÎåreI¥8ŽK¹\®hfÙÿŬrü˜1cJ›6mª444”£(ªär¹¸½½½’ÏçGm:{oö. ©ÂŸŽ>`f¿ ǼØMÒÀA¤³·÷v×Xøš|FÒÿ^‘ô!à½Àefö£AýlF93[E:È€¤/7šÙõëÕ–Ìl©¤—“.GÊó®ƒÎå?1ß#ݹa§§´÷E‹ES§Nµ|>Ÿ¬Y³&N’$gfq¡P¨$IR–”ïèèh3ÝYš|Eùlí9…̺Î{Ò̺ˆã8Ž%Å åC=ôî—¼ä%njj*¯^½Z×_ý×vÚi§KßúÖ·þ=ŸÏÇ•J%ikkKr¹œ …"ýúë¯gîܹ½Ng—tðV3ûP—çßüxéþEÀ’f˜Ù2I§„×f†à|6é,zþ9·…pz)ðNÒß}׿¯g¿ºãº–zH{Ϫ½o‘ö^.—cIq©TªÄq\ Å` ù|¾”$I±¡¡¡t„¼Çqƒ™å%å•VÒAyé…8Ž¢([ë^Îåre3+‡t÷’¤rh·lfåR©T*fVillŒGK:{üéÚó7’þ±;ÈÒ}f3÷/ZHÓÜn¯ÑÁ¥ÂýØ8ø¤iõf¶´VŸÄŽ(d?Œ#]}’ £ýÀÍl-tVê½øº™=0Øç•48xðñÁ>Ÿs®zH{ïÜfçwNŠÅb’ËåâJ¥’‹¢¨’Ïç+I’äÊår®±±1>ÎGQ…¿‰‘$544P.—‰¢Èâ8¶lX3‹›ššâR©—ËåJ’$‰™UÖ¬YÓ`f«W­Zuó•W^yô1Çsç¬Y³âiÓ¦ÙŠ+vˆtö¹sço»í6ëc:û|ÒŒ»N’v.Þff ÂsÙ rc˜Tù iÊ{9̈~x P2³ µøœÜè!©•t¹ÃR`:iµÿ‹=ñݹ¾Ù^:Ûž{î©$I’õë×ÇI’Äår9nii©äóù|.—+wtt”$e©kù8Ž¢(Ê'I’O’$×ÐÐÅqekÍ’$±(Š ÎårqÇ•$I*¹\®”óù|9I’r>Ÿ¯äóùJ¹\®dEi$štvI'Ú^Jš¤àH`w`ÒQð‡ÃkSÂl-À'€ÏXºŸ|Ÿ…@¼smY}@Ò£ÀGÌìoýiwGgi¶3%ýÈÖóµfÁñ0a¤;4üIÒËl Ü™Ùs’NΠţ¼‚sÃØ¶îfÍš•,_¾\ÉÊ•+# ) QCCCœËå¢|>… =* Q’$¹8ŽÕÐР$IT,E‘¥5âH²T÷DR2f̘8I’dí򵃯±cãýöÛ¯}ï½÷þøµ×^ûíC=ôáÖÖÖäüóÏ?à“Ÿüäý’’ÑœÎþ¥/}ɾüå/'ù|þæÎ¾lûïìÑ—€²¸±À<ÒI€×˜Ùc’ö¦GI¼x£™=2ÀóºQ&Ôš¸x 8%,÷[žÒL½ÞòÝ {=¥½gél&Lˆr¹\²bÅŠ¤R©D•b±˜7³r¹\Î …\˜1Ï'I’+ QØ–%ÊårJ’$Ër·(Š’¸Ä¹\.N’$N’$nhh¨d²Ôú7Æ@ÜÚÚšŒ–tvIWïî•ôJK÷2ß–™¤G®4³ÏTµqðIG˜ÙÓáëÙ¯à¼;–õÚ'ôsu8ç@³™ýµVçÙQ˜Ù? sFýo’îÎ4³ÕõíYç Âé’n†d43û'i½çܰ­´wÀZ[[lêÔ©I>Ÿ6mÚ¤|>år¹hÆ ѦM›¢ææf555)I‘¦½*Š¢ÎöK¥’åóy3fLR(ÌÌ’¶¶6«T*I¹\¶b±˜”J¥dÓ¦Mɉ'žøÐ#hU…UG‹n¸¹sç×­[·<¤³ß:Ð6CÑÙ·Ggƒ¥¤©îW ˜¶…ÿ_LŽ2³§zn7z„:Wn5»Xœff±¤gÖ³Ûãº1zJ{?øàƒ“ÆÆÆhýúõ6qâDEQ”T*•8I’Ü„ Êqç²À¼­­-GºµZgpž£R© )©T*ÖÐÐDQGQ”DQW*•$Š¢¸¹¹9ÎçóqÇIkkkR*•’ÆÆFcô¤³ïIšnöbà:Ioìa1»ƒÙ9{BÒ1¤ÞŽ7³§«“avþüª§^M:ëù;3{ó`w4 ëøÞ\NZ+à–:w©“™];¼ÖÌ~0Øç”´iAÄ{ʤsÃ_7iï,Z´( 3êL›6-Y±b…žx≨µµU…BAqkìØ±êèèÐÆ£††ªƒs€ŽŽ …‚577'Åb‘ 6Xss³mذÁZZZlúôéÉòåË­Ë=@2oÞ¼—’îX2ª¶]²d sçÎ-.X°À*•ÊÙföõžÒÙ%í¼ÞÌ~Ü‹æßLºÝígÃÿÇX—GÌìß¡NÉ23{¢ÿŸ‰Âdà ¤;7ý:üŒü=¼viÖç{I·–<@w#Nwélk×®eÖ¬Y¶råJ›8q¢òù|R(”Ïç£gŸ}6jhhˆZZZrårYMMMQ{{{T(ÔÐРJ¥Ò ?ÞÚÚÚhiiIJ¥’µ··[¥RIÆŒ“­aKÚÛÛ“wÞ9Ù°aƒ7ÎHÓÙ-Kg?ûì³;û7Âój×’Vb ð=Iï ŸKÜå¸GHG²O“ô< •t4ûu]/¨ƒÍ̾.é¤ YZÓ·€gkØÜö™ÙC’^^5 v ðg [µ ûß–4Ó̺ÛR§–6/!…?iÏ圫‘êû„êõE‹ÙêÕ«µÏ>ûئM›X»v­¦N*€lö¼¥¥e«öÖ®]K©T¢­­Í&Nœh+W®´ÖÖV›>}ºí¿ÿþ¶hÑ"&OžÌ¬Y³ª×™? ügÖŽ¤ï®‰÷Y:ûù矟är¹^¥³KÚø'0QÒ3ÛÞÎ,/ ]Út™™ý¼ªwGšÙGÌìÏûlÜhªø¿XGš]ùþ…—-ɼø·™*i¿úõtû<@w#Ò6ÒÞâI“&iùòåIccc4iÒ¤¤±±QkÖ¬‰Ì¬²iÓ¦¨¥¥E'NT±XT{{»ªÛ-—ËH¢­­-) –$‰577[©TJªGËŸzꩤµµÕ²À|úôévõÕWo‘b7Ô_“kNþ ¼X| Ø"Ý=¤ ½—tÆüÀI ÃÔ%5:Ü =Ž'á¿9¤êßU>DU¿_" L/•ô3ûU}{fö[IG“êƒ}®bH±ø ½¼ø+i1Õ<þÈPw®/<@w#Z7iï„@ÝV¯^m6lÐÌ™35uêÔdÆ Ú{ï½ f?^7¦[O™2…U«V1eÊV­Z`¥RÉJ¥7nL;ì0[»v-»ï¾{²`Á›>}º]sÍ5võÕW£‘?k^m\(–u,é Å'%=Ü×õ@3»Ž´Šû° > éÌ,=YÒwH/þßôYõí ?Ë'Hz'p¤©c].zõè×_€¿„þ,2³¶žßÕïsý¸ œ«îŸ»s®oÂlzpo±ì,ì[åÕÖ®Ý\33 Ð:ê¨Îç²Ùò줧{3ûޤkìšôà13{®¯Ÿ×PY²d §Ÿ~zéücÒ›töÌ!]KþSIGWÍ€wÍÌ[D:¸þsI7‘‡Cº´iØ~ܰ±;p¾Um“ªt+¿€ç…ÇŸ#½|ˆp/1Üx€îF…®él^x³QòI“&©X,jÑ¢Eš5kÏ<óŒŠÅ"kÖ¬a¯½ö"ûxâĉ,^¼˜Ã;Ì-ZÄÑGmwÝu«W¯Î.¸¶hÑ"º®1%³æÕZ s?êcÛÿ%½ÀŽ(]Ö_|x° ¬Sš¶½½]¶þ;ø½¤¿_°-·Òrá¦û›@,é¸A1XçšIšñF3{|°Î㜫½®×ç®{õ1ÕA{WÙ5¿§¶{Ñ—5UÏ•4ÛÌ®ïK;ƒ­£££³:{E7—Ëå3x­\|ø)p£¤#Íì_„ÁŠ*?f¯¾|ÚÌFÕ:~70¡FÌ®ÀC]ŒŒtYZµÿÖ„lŒ·GnÉןÁ¦Açº5º¤³qöÙgÛüùó™7o³fͲéÓ§g#äš4iûï¿¿Ýÿý*‹Øm·Ý(‹´··ÀêÕ«ùýïo“'O°«¯¾ÚæÏŸ¯¬*ûÙgöغé¼A1³ûã¿.ª_—ÎÌîæV=u8éö]·VÙ]YŸž(ç¦f.fë‹à sÇW»±yyÃ`xœ´æÂU’erÎíPª³`}sÌÎv·køûÿVÒkÒ°šþå/Éœ9s:Ö®]»¢\.ÈÌþPƒfÇšÙÏB•ö €ß„ ¨ç€±ÙA–€{10ƒt½ðƜےš€¯$-w9ð¡ªCÛzKß}€€5¤úëÍlÓt·ßä÷n4«%`Þ¼y[Œg³ì™lk´®ªGÎGó͹¤?e3{m—çO&ù~x¥mÞã|D“´?p 0ÏÒ½1_ƒîFµ.Å®ºÙV¿§KL¿U[;ª0ò½ø‹m{ÛµÇÌ>WõÔ©À)’Î6³/Ö©[ÃZ—ÔöÏH:ÕÌ~¿­÷ IG×JzsX§^Sf¶ž´p"’¦›ÙòZŸÃ97|Ôáº=ð¤÷窓vttpÞyçÙyç—Ugh:{wª÷®û08‘4Åýo5>—eÂ’ÄŸÿÞ&"Òív§+¶õ^3;Ÿ-·åö<@w;„í¤³m÷=.µ#V3³÷J:ÈHšEZ=ö²Ì»- |X»½›™-ôMàk¤ËEø™¸ÓwpÎÕ’™ýYÒA¤EÑ[AYX«=(nºé&fϞݱvíÚ•¡:{-ÒÙ{f;ß켜aZ¨Ë +#ý½øœ?8 ¸…Á]ÞVÑöqnt±^ªw?]ý˜Ùcföhx˜'M¡ú³¤æ:vkX2³ØÌ.ÈUIßô¡®ËK†°?g“¦²e#îƒqŽEÀ·¯FûιWø›š¥·ŸÜ+é3µ>Ïc=Æ1ÇS<þøã‹+V¬˜×ÞÞ¾OÖš÷JÈÄz+p¸™Íªóº‘GRøiý›HÒ§?ß7³cºYs>ây€îœs=0³{Íì8àyªÈJú‹¤ÿ“´o»7-ΣŽ{ŒÚæj¿?•4NóiàøAjÛ9ç0³sד¨Üª®Nttt0oÞ¼dß}÷o»í¶_W*•½’$9 kÍûÃÌÖšÙC}^7|I/é­¡J{æ •´ àB`p°™]ÝåíFš?ây€îœëꯤ)Ë®J—‚=ç®yÚ@Ò„P@l‡gfß'ýº\ i0ú]ßÎtj­6³Š™­•TôæZ·ïœsfv›™]Ζt‹¤}úÓÖM7ÝÄ^{íÕqÁü»\.ÓÞÞ~¼™=UÃî:×oáZú›k1d&]vøÿHןŸifë%µJ:²ê¸ÿ1³÷ ]èι-˜Ù§Ììžz÷c83³ß˜Ù‰fö‹ðÔ’.¯Wj÷pbf«móÖ8— %Z‡~ÜAzaÌõ?—ô–A<‡sÎÜHZïãÛ}yÓã?αÇ[:þøã‹Ë—/ŸÒÙëZÔÓ¹j’Þ\ üšt&üeU/ï ¬^ff ÃñSI«²ïdÃd7™ZðÝ9çîëÀ«vx‡¤IõíÖ°p<éEt×zœÜÌ7³XÒTI?•ÔZãöïÎŽ«e»Î9ו™-7³“7Hš"é­Û:¾££ƒùóç'/xÁ * ,øUHgÿò§³—€/^œÛФç¯1³w÷Õ)îkÉÀ+Ãñ¯ ]ƒ~nÈÚu¼Š»sÎ PÊo ÿÅä> üHÒñfö«zö¯žÂLúG³Ç’>,µ°î*»’t°Õp«@3ûF­Úrιí±Íû8BZkãwÀñÕno¾ùfæÌ™ÓñÌ3Ϭ,—Ë*•Ju™17³{{ëqn7|I:ø0ð4i0~š™=^¾‡ª™qàvàýÀ$m _7³ß ]‡–èÎ9Wc¡HÙ’tðà 3[YÏþÕÙ$à›’>mf_ª“šÙ³’^ ¼¾–Áy5I{ï7³ÿ7í;ç\53»IÒþÀË«‚óè'?ù‰}÷»ß-Åq<ß̾ZpÎm‹¤sI'12óº,¹Hª7³ïIÚ8ˆ´HÜwÍlõà÷´~<@wιAbf¯z(à]À›H÷}Ý!™Ù—%ݬ4®K¾ÁÜ ICpÎÀI©A IDAT+€=–µù ðVàòZ´çœsý®+?ב¥’>+©P箹Q$L(|‡t‹´³j8à½Ãñ/œsÎ Sfv°+pª™-t–¤JªI 9™Ù­Uð/–t©¤ ƒ|ÎEföF3Û ©IÒÄ´yO(ÇŽVÐ97ü„ëÈûw“ëÛ7u·lKÒd`éýÊQÀ?I¾fô¢ÉÅô¢ŠûŽÆtçœÆBÑŸ›ªžÊçóëÔ¥¡v9ðà†ðœgwHÚµIz?ð·ZýÎ97fv=p€™-tÎŽ’¡åú/Ô‡¹h—ôªçÇ7ÿÞjf·‘ëÛ[Nñð°ÃUñÝ9çF°èóHƒt$-énI§Æ”E3»8 ø<€¤ýk8÷à\à)Ò óµp%ð,ðÙµçœsý–ÕüëÑ’tp}{å†#I‘¤óHƒp×î@“ù4ð0ðn3«HzpZoÚ6³ufÖQë>Ú\—Ç9çÜH#i*éÅð ÀQfKÚ-K‰m$]üp†™ý`ÏÓ@z[‘™Y2Àö¦ë̬X›:ç\mH:øu¸~ìbfO×»O®þ$å€ë€™ÀÛÌlI7Ç4E3K$ýi휅¤Aü,3{0÷ð'3;eÈ>ÌgÐsn3³•föE3{E¸¹ðIIzS½ûWkfv&ð`PGÝͬ‚ó±ÀÝ’Ž`{«Â¾Ä­’«Q7snÀÌì¦pýhî•t¥¤éõî—«»yÀ.ÀÝç!"I—cHët7}ðáAêç¨ãºsÎ"!uñ`à{ÀI3%½n(*¢3û¡™] i¶¤Ï…ïÁ8×Fà2ÒmØžWƒ&On®Q[Î9W3!ÃçµÀÀ×¥ïÀ$½†t™×)f¶¡‡ãüxÄÌ>îAŽèzœ™]®§®<ÅÝ9çF9I§[[ ¼ØFÑ~IÇ—·˜Ù‡ñ<{™Ùâµu-ð{3ûv-ÚsιÁ"él`A(þåv’טÙyÛ9n6ð9`uv.é’»=€eÅ]ßx€îœs;€PAü 3»5<¾¸…°î°® †>ÞÌ–‡í^Úk¤>Ì*|¢¿ëÒk±¦Ý9ç[˜ý*éLêiƒY÷à ’^üØÍÌVnçØ'«Ìì“’>Bš%ö6 1³5ƒßÛÑÉtçœÛÁ„¢.'0³Ÿ×¹K5#é«ÀIÀéfvó ´ÿ"ÒÍlöÛz0ÃÌ~V“Î9çÜ ÷–šÙÚ° LnôYI·Hz[}{7`Ÿ¾¼x07³û€—“.¨éÀ’ªA[Î97(Ìì>3[~ø§¤CêØ%7¸^ t[®‹g€e¤Û¯-&ÝIƃóðÝ9çvPf¶ÑÌÊááõÀ#À[²×%¸"AfV1³/™Ù—$½AÒ©5>Ç3ûGh®¤ýlç*Ò­hÆÕ²Î97ˆŽî ]cìF™°µÚâݽÞ*é’ a©ÖÞf6ÍÌ>lf›†²¯£™§¸;çœÛJHc|¸ø™=Zç.õ‹¤w—?5³¹5n;Ü ìmfÔ²}çœî$}(ßñÚ#_Ø^oð=3û`7¯_ ,6³Ïyçv >ƒîœsn+föo`àNÒ5fHz™¤AI,fv%éçq9€¤B˜!¨EÛÒ‚q?"li×_’þKÒ9µè—sÎ ¡gsH³ÜÈ·štÀå ê­Y%å%}›4sâìzunGá3èÎ9çzEÒ…ÀW˜ÙiõîOH𼑴"ñ½5n»x‰™ý­ï}%ð{àmfö«ZöË9瓤€1föTXòSô Þ#—¤I3Ãæ—‡ŸbàDÿÞ>ŸAwÎ9×+fvéèù·$M’t©¤CëÛ³>ù:°ôf£Ö^ ,ôî¾¾ÑÌþDZUÿï5ï•sÎ "3{ÖÌž ?üKÒ{ëÙ'7 ÿþ?x ¸¸—t)—çCÀgÐsÎõ‹¤™À×Hg¤÷4³¥uîR¯e{‘KÚŸt;™ßרݷ“ÞÄô{ ¶‚_0³öZôÉ9熊¤8 h1³¯×»?®$½8Œ´Rûef¶¬Î]Ú¡x€îœsn@$µf[ðHº辑U:Î$}ø?àÛfö±·=xÚúx¡•t0ÖÌÞUËþ8çÜP“téö[­w_œ)<ÅÝ9çÜ€Tí iú{8@R“¤ÉuéX/˜ÙwI÷|­É z×? kÓûâ|àIÇBŸœsn(ý1ÙUÎ >ƒîœsnÐH: ø pð¾áž¶-i6ð&àôª5•ýmkOàà£}-ü&i/`I_gßsÎ97²y€îœsnPIz9ð3û|x|°ÐÌ–×·g[“´ð 2³£kÐ^“™u„sf÷ñýÏJf¶b }qÎ9çÜðçºsι!%ii ü\3»¼ÎÝéV¶®>¤çO2³ °½—?Ž3³Çúð¾+Ý€W›Yy }pÎ9çÜðçkÐsÎ )3;Š´:ìï$½LÒç%íZ׎U©ZWÿ6àIŸ`“÷’nïvS¨rÜ[vàùsÎ97ø ºsιº’ô*à<`WàùÃmݵ¤×/6³ ØŽ€=ú2ƒÞW0³Ò@Îíœsι‘ÁtçœsÃBUZyøðÒíφÍþ«’ŽN>mfëÐÎç€ÇÌì§}xÏÉÀ"3»¿¿çuÎ9çÜðæ)îÎ9熅.Ûµ}tú^ï’ u騖ž%íׯØÎ“À’NéÃ{^ \+iüÏíœsιaÊgÐsÎ {’.Þ|ËÌæ×¹/ Àžfö¯ðñD3[ÝvŽî3³g{y|#pð?f¶¸¯çsÎ9çÜðç3èÎ9çF‚Ïs§³'$(iÌPwÄÌÊUUÝßüKÒûúÑÎ3{VR£¤O‡`¿§ã‹fvŠçÎ9çÜèå3èÎ9çFIS€;‰ÀÑfö:õCÀiÀ+ÌìÔ~¶±+°x xK¶oúvÞs)p•™ý¡?çtÎ9çÜðä3èÎ9çF3[켸@Ò{%ÍÊ5Ú–º, Î%í%éS¡Ð]oÛxŠt]û_zœOW§­éœsÎ97p>ƒîœsnTôà3À=föö:õáÀ•¤ô+ú³eœ¤=ÌlIÇø(p©™µ÷¯·Î9çœn<@wÎ97j„Àu‚™­“´pp9ð=3Û8D}lf ›ûDK:Ÿtmû1fvO/ŽïSûÎ9眾<ÅÝ9çܨRÎ×…‡«ï§ ’v‚>¬¯ ÎOôÚ>4ñià*àEÛ;PRÜ!in¿:ëœsιaÅgÐsÎí0$ÝL>ofW Áùš€ÿÞbfI?Ú˜ff+zxýdàÀ>föd¿;ëœsιºóÝ9çÜ#¤Ÿ¿xÒÌ~%igà¥Àoû<÷á¼y3«Hš@ZuþÚ^¾oiu÷ùföµŽ;ÈÌÖ¨»Î9眫ÐsÎí°$½¸X 0ØëÔ%½¸‘t[µãͬҋ÷¼¸ 8ÔÌVnçØý€‡s°Á9çœsƒÇtçœs;4Ic€W™Ù¯ÃãÏ¿ìMÝóí¼ÉÌ.éÃ{ fV çÌ,îæ˜<ð/ÒýÑÿ_Í:ìœsι!ãEâœsÎíÐ̬- ΃qÀ¥À™ƒt¾¥Yp.éHI–tÀvÞ“çg7Ijéæ˜ ðà!uß9çœs#ŒÏ ;çœs]H* f¶IÒ»€ÓHÓ̯êÏÞæ=œgðuà(`æöfì%Mnþjfs¶qŒo»æœsÎP ;çœs=ô|`6ð3;6<7ÅÌVÕð“Ílµ¤ðâž ¾IšäÌì™í´yp¥™mªU?sÎ97¸<ÅÝ9çœë™=ifŸ­ ΛG%Ý!écuøð•Àß$]fñ»;v™=#© éJI/ézLØÞíLàòZôÏ9çœsCÃtçœs®Búø,àwÀ:IH:¨mÿ8‚4í´Ã+À³ÀIûvi§8˜ iì@ûåœsι¡á)îÎ9çÜI: øð;3{c ÛÝ-´ûI3[±cÞÜP˵ñÎ9眫ŸAwÎ9çÈÌ.ž\éVh!MýÈ63€…!µ¾»s_of&iIìúº¤q!~êûâœsιAæºsÎ9Wf¶"¤¨´S?JzùÚ|ÒÌ^¼>«Ì.iú6߸HÒ¼.Ïov® {¥;çœsn˜òÝ9眫13[cfo'ýþ3€¤ ÂLö+ûÑÞý¡Ã%’>*¾Ws+ðjàÑ.Ï'À)À/¤?ŸsÎ9熆¯AwÎ9熀¤#€ÓgÌìÌð\Ÿ·k“tƒîœsÎ ’®Þ |ÁÌæõò=À›Ììºð¸ÐÝê’v3³¥áø†çœsÎ #>ƒîœsÎ fvpð €°uÚ’öéá=Ūà|ð„¤Sª‘t ð€¤W…ãÿžßk°>çœsÎõèÎ9çÜ0bf÷šÙ½ááDà0àžêö<œœ/i§ª6CºGû9Ùs’žO´[³Î2IIs%ýQÒÇëÝçœs®Ö<ÅÝ9牤fà3;«†mž|ÉÌž«U›nø«®ö.é×À#À·Ìì‘m_0³’$o~nfIÕóy3«Hú$ðNà¥6ÌoÂþï×/I àílfkëÚ1çœs#Ž$ æuo íû º¶$E^Ĩv$5Ô»}5ûÜŵ”f׸M7Ìu©ðþ=`?àI-’ZºŸ­Aß øp‡¤«ž¿ZÒ9fvðšœü¸h~Dz³{»å†IS%]ïK7Gø[s½¤—Ô»/ÎÕÀw$9 K:øH¿ß?̯É5'i0µËÓ ð °X üÖÌ:†¸k®JÌîÆû›ÙÆ:wiD ?÷ŸN5³kêÜ^ 3Åg'™Ù¯êÝŸ¾ ƒKw¯«®ÂÏöÅݼ¥¬&ý;ô(° TâîÚîÎÀíÀ‹»+æv<’>| ø1ðŸ]ƒíð3387{MÒáÀMáø«ÂÏå‰fö³¡íýöIšü ø?3;/°¸—ÇÏ~MÃÄ}íߎ8ƒþN¬C•ð"àÝÀ•ÀRI_³wÖ¤Wog¶…tfd:0~H:5ºí4{Ö»#ÐYøêmÛ9l ™‘;Cv"ps77ª9Òð®E¿Æ‡§¿‘´ÕL¹™­®NéúšÛa}›t«µ§«ð·„›iÌl™}ÑÌ,Ì0þ…4ã%¤?Kþž]&éƒCßý­IÊIúr¸ |p‘™2«¾¼¶Î]t¤Kú¥¤§Ç€•’—ô³‘TßÀ >I'~Sï¾ ’îJÊ×»?½!é(à¤ßË‘êc¤ƒÖÛ ¶%í.éÛ’Ïë%ýYÒù’&lë½föé–¦'õ§s#âaÜjfëî…P„çdàIgÚOÊŽí@>ÜC:ø3['i? `fˇ´g£Óû¯‘¦ˆ¯"Ñ¿¾‡cNf1|úÜWŸ$ý¶åföƒî^44ˆ¿$¬þV—C.n–ôƒážšì_¿1üËj|˜&éd3»±êðç€;€[€½CÐþ<àà£ÀQÀw‡°û[ ƒã?#­fÿ媊óͤY¾ tûûㆆ¤±ÀÀqÀ³H3ZH'B^Múwê\àìî2‚úx¾i¤?ìÙC#ÖRÒ¿A‹êÝ‘aäiÒ칇̬RïÎHºø®™Ý·CVÏ2B¿!°~5é}è¶Žy=é¤íãÀu¤ß$ýûv2p‚¤ãÍìm4óEÒY®êkwÔ}›Ìlé,Â:Ò5z¿Ê¶¯q5Õ¼½ÌìÉ¡èÈŽÀÌÚ¿×»UÆlï3ÛÜ9}©9I¯0³ÅýyHs›'©\$é·föhÕëKÃߨcHS¨œëdfí’ö^,„ÎmÖ ú¤¤¯T¥&îìBzr`=úú8†tPî¤KÏöÎnXCšþ5À·ÍìjI§Ö«Ÿ$M;G™Y×ëËBà›’Þ|8HÒ›¤OÞÌ@®ŽÌìNI; t°f41³'$Mf_“÷‘âv ›ÙÃ’&³>÷Åà¦mõ_Òé¤K¯æv™eÿ7p£¤¯î“t‚™ý¶k;f¶BÒ³’^mfìKwÄ÷^ ët—/«w_F©íhnTÛîÍ÷ߤ3€õuÒ”øCºyí'ÀÕàn2³ÄÌn1³Õá©1À :Yp.iWàÒÙù¡Âûž’¾)i¨ï®'Ñ8xWUpÞH ~+çMxv[½}–48?´›à¼“™}Ÿôûy,ð®žÓïFÔ šaø5éÍ$Úpës¯„ëÉ™lãMÒÒŒ² Íì´m¥À‡å†G¾ÕÃò„Ÿ’~÷‰Ï ÷ì>ÒµéÝRºÇìáÀ¾¤i ÍlYÇ¿xçìsÎZk¯½–ÞCWÕߌ1³~îþ~gÌÞ$O°ëÌlqøäùº )ŸCëñjß¡‘(^Ë[ÀQÝØÍ…Ð¬ÿWëÞ9G"CÕïÓ;ö*ôŒÎƒ¾SA7bfË¢Y죲ßì"Ü}œ™ý8ÁÌ®©*ä0»vƒ ÇI²ö<=Ý6²ðz‰ly$Ò½ÊpÉ ýC¤/î \œ³Û ÀEf¶B#} K(è-ðÓÐ,Ù3ÀTä.1ÄyúØ%h °ž™=Ö$lŒûé$ÁÈ̶G)qþŽ–s€7Ìì‡î^¿ŽaS´ökq3Û4ÕNÌ‚ƒýŸ™r÷¿•œS$ˆ‡ÖýØff‡)?°š™=|ÛÝH»NBœ×55) ÷íkÌl®7¤mQÊ©…Ѹ3d`xÃÌör÷› êõMÇþ¬™=‰ÆÚ·aáßÈÍö]3Û#k<«ÈgÑLO»‡gÑó±.0¾ÍÇ f2ï¡€_ ÷üõî¾›™-ff޲'  gf,Ÿ¥clŠ=Q°¾Àà}Ôoÿ´Îâ|Ê9p/l¢Î©hÉÒžèûû ¦Èëk'ÏEÌl Ú»J§÷<2¸×üîmfY%ÿE±j’1~$ð%$s<ü>óM*ÄÌ–DÆòu€¥‘þ rm•9ÅÑùú&MG2]ýäÂËî~C¦ÎÎÀÄäöÜ­ïÿ:šH8"-ýªí» ,º û8pWÙ7+-uyÙÝIë¡ ì÷pKU9(ÉÃ[!yáIt?î*©³7p·»O-Ùg :÷µ‘÷Š=qkUƒ´™-Š2S\”þ_ ø:ßÅѽïîw—´±Š…tgæÿ]Ñûñw¸nÿ•еX'íó2ökÐ×gܽpY_2ªn‹žƒåÐ5¹·ŠŒbŠX¾ ºžK!Yì^“«XtY4A·Xªö?¦ø$5ôLf3Û M†L.9îà+èzÔÆè$à^’-Ë”ú¬OíÞ¤þ pg É£ ³ÏClJAü«t öN®Ú¶»?ff7Çšâ}PWþŽ™=lL«ÜKwŸ£~hpü¤â¾/gÖm› ÃSQš£lÙÆhÖôŠ‚ö>B/‡qH‘ZW¾ROæÊlï“ÌMÀÜuu¶>Lí½‰„–…3å ¡€R3€] úÕ­5yØ(§OÓ€› êKõž6¬+[ }Ð.¨=ÿHùë<Œý3¿¾uõ \W·íxô¡š;¯?™ý.@€úíg!…}׺íK¤ûú,0_ãi,Jyõ@úmší0žž–)hÃÑãàgÀœ}úw"åu‡ºñ± 2n¼ lUpŒmÓq-(ÿ>Rú%¥_LÛ@Fƒ7Ñlv}½>(0ÕûÀ@¿LÙÂHy~蟶͓îõ‘a"{ÿû×µýÔç>}>mNÈÞ3ôŒž–ÊN*óŽ"­ß‹Ö­ž)_¹ý¾Mæyª8&þ[RÞ?û[ÚZ,íûÍ‚ò6Ó¿øÅ¯è‡°½„„—P*%ŠžÁ1Zšñ42†m’)›ˆ„Ùìþó¦gàƒô<^‰æ¿^sê/½Ó+ÉQuõDÊqýöQÈ» ôÿd~ì”ÞñÑ·Ô‘¢?1ó['ÓÖдϺȣkZzOþÿ}ÉLû6èëÀëÈX~iêןÐ7øn`ùõBßÔ+Ò7ápäõäiìß‘~çÕÕ›Š&;–IÏâ+È ñÛÌ>ÃÑw÷ƒt>¦kôçtn‘êÚ¿® ÜŽ 7 É™ß¥gò…ì5­«?0ÃVHþ{<õñ×è»>ɺ‡—\›éÀž%å›§>üe™8Ƀ¯"#ÇZÇÛš©¯ó¦÷ÇchÒë\ôíŸäç\™8½sú¤k÷~êËoÉÈ ißï¥{þHº§ yÿ½t…²Dºï§68—§ÐäÙy(íX$›ýX ¤î®hòáétŸŽKçò_$;.—öÛ=O¦ëö³>gûÔµûpXÉqG¢çï4ùs,Z³ýZº«—Ô“ÎÏг÷aº_§£gêÿФg®\Ý`\LBA+óÊvOç¾X“mnêåŽM$G_ØT›ÍžØ§ýGE¹ž;°}ÝöÓÐÇi@A½õÐÇ¥ÃMB/­Gõ5¯îOÑìæ¼- Š{€Jö» ø9‚ Zñ°DAÝaè%ö…œ²¿¥sZ¤ nÿ¼óI/š“œ[ž‚¾bº¾_.©×}X÷®Ûþ…tÔ›}++<éòQ} ö„„Üq—îß#Èê\tœ!Á`xAùÜèãú9Jt4£ý>°SAÛ–^hä”íŸ^œ#KúÞᣄ^²7¸¶… :¾>¢ü#¿+ëŒGÎÔˆf‰rËKŽ{>ðHIy3 úQéú-(¿¸¼™þÅ/~E?dÛÍr®Ÿž“cvþ.haR AàÚô ßš)ï  §ícÓû­ðy_·‘~é¾ÐBÝ+É9ÛG!elG¤€ +¨¿SzwÊHÌTÐÏHßó/ו÷Až#…Æ×Ì·ã2ëT¶Lú¾IRl Îç}`³œ²cÐòŒ… êNEŠÞýÈK3Of» /:Èlh¶}òÚËkÿF¤x½Î¯~bd~4a3X5§~MA?']ßs®ÑiÈx°lA ôt?BžóÖ• @Š^©q%³MA(s£ò˜Œ-hãld¬?)Yƒ ö;7ó>9ek ÙìŸLQ¢ £¥wÓÓ˜®Ÿ¤[9ÛUuNcù$êd)äù7êd{$³:ð? ®o¡‚ž®ëŒ4Nç©+˜ÆØ{äÈgiŸ1Èxp=2< ª+_ ÉÕçW ™zó!™j÷‚òãÿ4Ófª7$]³¢çîl´\´z›ÍvâÓþ£‚‚Žf3ÿ\V·}zé*‡i¿?‘#4§—ΛÀRõn @y/9VMAØ`¿aé!ݳnûRèE½GƒúcQNçì¶Q騛·pZRÐÓö;Š^F©|»ô2[(³mndyüiƒcŽFî_UÏc,Þuû‡\Ö;2Ò5|\P£-‹´ÿIDATw™T÷˜ÇXYoOÌ)+SÐoEîFemo>>2Û¥~ÿ¬…ûß²‚Ž {*ç´o½QSÐmPÿxĔԹ:oÜfÊ*èh–qô]²ß¯«Œ¿øÅ¯•,ßNãõ©N¶ÕÍ&]]{¦Ó»|ZfŸ\=~½ç‡ÜÄ ïuOÞÎÙ>*}_ÞÖ-©ßŒ‚> X­`ŸE’Œ°NÙâhvî„’ċd˜k Ê_Bù•óÊ eT9£ |*’?˾!ýŠÊRù}Àíe7¦þu0döé‹”ÉKsÊj úË(@`^ýyÓ9äÊ,(èÈãó¥¼ã¶8Vk ú%”xF¢åm+å”äœS<µ&%^ªiŸÁhÆ:Wî¥@AO²Àd”ò¬¨íMRÿ?S·}eJ&!JÚ딂ÎL/Ù\£Gæ¼îLcÕrÊǤ>ü‚bï†#ÒX*õ¨­«S{m\P~0©…±Ö7# ÊÞm¦Í95ŠûP3Û!óÛÉÌ2³³Ìl2²ì\ˬë£A–—¼ñzÛКæq9²L¯_P~¢+µV_AÆ•sK;¡`z—»TèðÉ»-‘k}·¡а̶mÐõĪÇë"† uRUŽ{bÚwHAù´õ_BVæfX”jëÏ×®{íbf?0³1h©ÅÏâ§%m<„Í èr\k Gï•ot²¹c˜9sñ±™-„ Š‘NóÓÅÐôwZ u§ ¤ïN=}Q ½sÊZaWwÏÍÑì ^;ÍÂÕs"RjÎ,jØÝ§£ñü 3™-Kkv#ô¼ºŽ–+~®¤ïNI*9o¼û!:ÊkYÆyÉqW¿‹€]Kd¡}½`½´+­ëfŽ•ª…dŠ#›¬×ˆ3Ó=+âj¤„ÿoAùÂȘóFAù9È ²>ë'$ùîÇÀaisUöE“4…QÀÝ}b%wÿ®w `0¹5bù/~Õ±ˆ Õr¼™’˜®ä`ùºm#PŒFv¦ˤ %5†Ó3¹µ¯E°ë RíÐÌb–hf¼Q^êgÓß•;ÛÉ:žCÎúë_£l\ ¦ºû;Žs?°bÝ}*c2ÐL(Û)û?Ìz]F èóEÁïÚÅðô·Š W †—îUÌèãÜ ‹PMAß…YßC' £Çȳ¼»ŸÚ gA Ú‚»¿ëJ5óH«m˜ÙÂHÀ<ÅÝ?L‘&!k‹.êjÐ=Ô¾-…žJ¨)–Eß§Zh³Ñ±Šx…|ãëÀ/½q€¨ß"¼~üÖÚ|¾¤îóÌ,µž_¸û+ Ž_Æ$”%§3‘¸¯G1cF”7º¾/Ó¼q{KàJw/“‡ºœdð¸Í„çñ¨gùeI×xSäÙh²æ§èšŽl°_–­€¿–jÉžE߀*Ï047Φ—Œ±y+·ˆ÷(Î4Q“ +O¨Ì©QÜ/u÷ƒZ¨7˜ËÌrÔ ‹"+ñ| ¬v³àîo¥HÚ `]Ç™ÙEÈ%¿0}[¼ÄÌ(¨5F Å»Ñ9-ˆ\³/$prËéV\‡¬n—×¹ÉÕG¬Ì]á²U×ËQMÂÞ ]=ÁeÀmf¶d%l7´f¸>Ê@4ZåKÐ.Ò1UMoQýztoPìRßÕ ¡ m^t7föy´fp3ûš+÷:Èkm.äö{˜»ÿ1í¿Òuj³•‡&wÍ ÷R3 ¬ÐBÝP̆)ÊzˆUÐ8mÆ ¾Yvƒ»O7³ À^(ÆÂ,$×QÈ­ºe’ëùé8K …èQä‰9ˆºTvÍ’Î㚟o•šBÙFÐlìRfff³ԔífÆL‘WÂ,$ƒÑ¤œÒ`÷yP°çú~uëõLž¿KÓÜõع}=êÀûH)âi`ÃÚ]ÉøEæÚ’ÜÊ:@(èÍñ8ð–»_Öî%—”_™Ù¯‘ûýÉÀ]f¶I#·‘j9³<®\Ödÿ^K3“Ý¥ˆÔsRwFîÁ˜Ù`©}tÎþO d—uW³¤œž‹PÁÊšÃãT¿Î5§ªÇyEFýM ³O"k}wóòú˜§‘ëVòôX–òÙŠ®æ?t¯‚ÞГ Ú™í€Ö`Ö”íÑ’1â3 ÍšOOû/‚ªžA2†rÞûq÷ÿšÙk´® ÷f‰šá êòÏ>™:YFÌì!2®Ïf6?JÙŠ Þf¶Šl=EéþSö[˜rwŠô휟™nÄí¦¦÷”GØ¢ÀëM*ç0óþÏUqÿ¹È3y|Œ®ËQ(`m#>¬« Ý=k}nÇõè þ,mfý dÞ§Á%åE¬þ&jkÓ+O¨Ì©kÐ[e ­¯em wÿØÝ‹¬´ó ü±­²*•<­ªLeÖ aÝFz‰þšY¢}xÈݧäT™‚”º®^×_•UÓßVÅ)T¿ÎÃQŠˆªë§ d+÷ñ º~­~j}®¢¯‚Þs¹Á‚ÚDw*èC‰ô ‡1³a(çjè{â(PV¹Ð,ߌ´ÿrhýù…Ek;ƒ^ÍCÀ׬º¨É€¾ ZÝ[yѪߵUÑ÷hR¼†kP,‘Ifvž™ý Ä—¶®°Æ=—DøN´F|Kw¿¾MkŒ‡ å®»ŒÛÏ£ˆÜEñ›ÚÍJ´6R»ÿUû½*å‘xì)”pZ…_v)lmæ¼[e´¯èyª_Uhíº·Êh\¯PP>)•µÉv¿¼Q‹Î¥–†íͪ †‚Þ÷ë'ëe·’"@Þˆ"R7M²¨®ˆ„¢,÷+'×Äf™|ÝÌ´P÷#µµ3ŒÖ5³šejW:‡«ñ zxöéä1[å´­•Ý=Àrf¶UÙNf67ð´UžBò}[è׈f‡êΠõû?½d«ôù»è…˜g´iSèÞôžXcÌá˜Ùúfö3ûðKàÛîþ¬»¿†Þ+Yùâaôý¹ßÌnAê =åÑtš“‘1æ+MÔ9ÍðuwDéÊd¢²èÀ'ßÛÏóm1³]2~ð:úöï Œt÷§:ÑÍmÑ,å÷+÷í k§¿Ý¢<¥I—'©pí»šdhZ‹ÖÎõi´Î¿Ê˜„‚Q7#L¡8x]Ï¡,E¥ÚɪßÇ5éÞ ”ÚäU®Œ–²Hü¥p«DòÛåd/RÀ‡o4³¼'ôæ¸)%ç´£ñ´Þ¤Œu™uI– fÖEG½ÕÝoÍ–¹R¤Ýœök†SÐ*LÿPÂëtòEìîÏ"Kò7ÍlUô"Ê Ðf”OŽ6³Å:sÜŠ¢6`fÃÑLÿ‘­XºS:”?§›Y™ÛÐÞH®übIé+öK×°™~Oý:©A¿òxE›/J•WvÜw‘[ße) Ó¬Þ>ÀQ©Nw1`fm®žÞKát+fö”³|4fnp÷Û2»ÔGø>­Œ¾a›¹ûõÝÑ× ëq÷{‹õ‘Uä†ôÍݹ{É0U¨«²5¤åÀ>Iè.c/ä|ô`à\wÿ—»ßåî'¸ûî~[(Õ›÷µsRXÜæîÝé¡u°»™-ÑÇØÍä^ÓlÅ$Cý ¥Ok´lø§j5Œµ­™5%/'ƒÇÍ(†U³“!5×î¦å³Ä€̬,Sɸ»6) Û»ÿ ÉŸe“(G«›Ùö›=ÍŽŸ^²Ïšô ½ Ò€?ØÔ̮˛96³¹Ìì[i­QeÌlMày3Ëá5³-‘¥é÷M<`f_Ë©·zXV)¨;¹ÝüÅrr^šØ¹^áp÷×Ñ@>ÒÌŽÍsw3³¯›Ùí9ƇÉÀz^h¸ ¹¶ïŠÖa•}LÎ@æ=f–»vÚÌ6K÷¢67³[ͬƒK™­‹rˆ?@ñì~B®J7Œ»½ÐÌÄÙîÞì,ýX¤TŽ3³Móv0³5Ì,/oë!èÅskžáÃÌV6³;ÒuÈ2½Zò.þžúÜáÃefk£ëþHÚ·;™Œ,×ížE_y"´# aäbf;!ƒ`ÍòÚ²:îþŒ»¯áîK¸ûîþp»û´£Ñ¬ãµfVh¤6³å‘Bò.ÊÝjÊý:l§Œ“QòÂ5âf¶8p­X£³Ìl¼™”Œç§v‡º{®‰»?ƒÄÀd3»ÈÌö6³ÝÌì ô KÎú{w?ø6z<`fg™Ùfvª™G‘D/É ¶pŠxx»™ígÊû~yÙK¹€kÑ‹q4 `W^û ‘;Í}fv…™6³Ììx3»Y×.k'‡;‘ ýƒfvujó{¦Tpw¡”o_l!øH¶ïÓÅ’Ì:îŽ5¥-ºÍ„ÙBÛl5ÝffךÙ“qå(3»åß,§î3ÀúÀb©_—˜Ù¾fv¨™]‡\[£Îu,ynL®2³¥±~iR¬«ôùcðŒçªtÍ¿ofWw£º[´Ù 0¯o3И¨OiØÕlÜ“Æu´ 3ëgf›Ù÷ÐríÜýRô¾ æ@Ü}°9zÏÝnfXf¦ÎÌ0³í§Å‡ÀîÞÙ¬/ åMÛu²BÒÚ€o'a–œí¦%Ž %þèœú޼ KrT¡"Ð"VMß»O<×̬o2ÔßOãôwë›Ù,ëlÓdÌHÁ:ÍÝíâ¾—’Öåˆ&\®³º˜Af6ÐÌ.Cßüfô—±I¾œ%}n2fŒG|ëD¿§?B2ø)õ}3-O¼)Úç7Ù¶£ñ¸š\êÌÌÖIç׿®î4`î4Ór¤¹ëê-gf·¢IšzÆ[Ö·Y±Ï#™M€?[7J:‡ñHž-4j´‘‰”(è‰Ý‘Gî“<»T¶Ð̆™ÙÀYÀÜý¤í ¥YoGwŸ£~èáߺ ÚéÜg/Fn w¡ow`ž‚:ÇŸmÐîh-Ã/ÐG`p ðù‚ý·F®RŸ¾‹"éNN¿,PñœæFÊöÏÑK~|êÇwP¤÷²º5×êß!…þj4ë;¸¤Î”óòaä"t0_¦üëÀ¨ ýÞ5]Û¾MÜ¿m³‘7 )÷‡ƒšcÑÌ=ÈBv<ðWô"ƒÙFm|¦âñæIãîR$¬ü)Õ_­Âù:°hƒý6C3·¦1ôôáY¡A½~uÏÃ_“PVTg¾t¬;Ñ óÇÙóHããxÀ{d@ös€mÔé“ÚÞ`¿/û73&R½/ ÊæNÇ^£ÙvëÚ¹­ÊøŠ_ü:ûC³0µ7kg¶×\û–«Û20®§û¿nˤw¯£ _w ƒõ kæªìw½ Q$]¤Âñ¾“Úž˜¾ƒ“ã2åCS_ÖmÐÎåÀM%åk µ´ï eå2dÜÿ8·P®BòØk©õ¿÷P­¿;äÔŠfYËú~Jº¶¯"Ùå/({„ôÝ\>k@NÝѬìöhýô¿Üv 8ú°Sɱ¦¶·jÐÇŸ”|§{–Ô­­/Ó¥H&ýÉË•;ÓΚ©¯#‘,ûnfÜ<œ®áÍÀb%mœ Ü_ñx[/!#ÒÍÀ•ÈX2MÜÍ]Rw*pjIù`dœùi¼(=[¥óú0° îÖÀË(oø8$wÿ=å_çÕCFœ×PTòËœvkÝ>/ ´™E}^½ÞCŠKSßg å ‹—ÔŒop½I÷wË*÷'Sï3è[֯¾û£w€§{y ŠÛäéoC}2=“¯ý›é§¥ÊÁ§Sð·[ÐöVO÷gNÃÌÆ"¥÷Ë=Ý—2Ìl[ôa^Ì»wMÙ‹™= |Õ+¡«Û^ eŸõx‰mÆû%à$w?6³ý`d[ÞÝŸÏlÿ3òøª’(˜ H³“¥_MY¼ÏÝß®Pw2ÎÞQñXë_D3p÷wºéÖÒƒmL*;¶)æJ_wÿ{É>ý‡ØÈñp:§ÒedfvVêèÔÇìÒÁ…—Þ†Àçÿu÷ó2u×^q÷çc5dx_)=wÔ¾5i¦t$p·×­UOqo¹ûnéü6E3ýе¼§LFÈ´=Ù‹baZò· ç,g1-¥{ÊÝ_.©ßX)ŠC‘B9ÁÝ+E5-W|XÕݧšÙ 蚯…ÒMD™ ¿¡Éb€»?Tñ˜ ¢{»2f<úý|ƒzSß»{©dòàX¹í¿Š®ËDo¹'õkt=—D)ܽ0í¡™-ÃÌõùÿ@ãiJ¦|CàE/Iýœ¼<ÖIÇ^îwe:(ëoáøÉìSi,Ô½¸È+dIç°z· C“c€©Uä/SûÜý„¦ú²Ý§›PÐ{–PЃ"Ìl4Cßh=`+m‹>Œ—tuÛÁœi=âšHy7m«)臻û™™}‹ô¾®ˆØA0Gaf»!¯¶µ'Ìì*`}wï¶ÔbY½»ŽÙSÔ+è=ÝŸ2ª*èA×`Ê\µ¿»jóq %>çåkÔ;kЃ`Π¤>@Ð>®A‘L;^ì i=×.ÈM1º3ëcfÇ/·Ó‰tX¡œs0;!E«¡çZ’µxý‚ èR’·Î`3[¶Í‡Úy·4¥œC(èA0§0xÇ'è\Áâ.Fkø»’ñÞ1ðb4¤>PPÚ6yØl œ†ÖznY±É7ÐšÒ `%ôLTac¿(ÿíéNÌaŒAÞ.mÁf¦¸n)5w(èA0›“ò-Pðž {¹„®O·Ór ‚J¤èÌ{šÙ½Àûfö­LÙ¼(žÁGÀÆî~2Z_Wefï.`•Vf‚`6e<°³5Hµkf£P Ü–„÷`ö!Íâ.ˆ–ÝÇuÀ&f6¬MíxºlíÊÓA’,w“Q΀ùQ„ÿ q¥éÒ4"î~PW¶ÌÞ¤à@W Œ7¢hÁWev¹¹µïìîšÙ(8SÃ5®T[AÌäd¸ºÝÌNnNÞTµ`Sà WøýÝ}\õ4è1lf.÷é(»ÔÿCYy‚nÂÝ?6³P”öv0Éá- ú§Ÿ;Qгpé¡wz¢ð=ìéu௢|A0{‘âÜ< ,SHôÇ( î Sžó­P:Ÿ­º¯§A0{àî/§(å¡tVÌìe” k¹ô÷¸¬4R{›x…ö)$½€çÐ’ÞÆhä^=‰=#€o÷ãî··±íÂôUˆ(îAA0›‘¢ÇÞŒÜ&÷q÷ì{r³<)_s÷ùSù`´^ï w±Ý}‚Ùäɶ$°,2šÿ3–ƒ5RºÔÅ€ÇÝýƒžîOл=‚ f3Ìì0`/`Zº´‚ýæB.ïºû)iÛ_ÑzôÒu´AAt=áâA³)*ûqÀŽeÊyâ``åZ>X3;X‹ÞéA³=¡ AÁìÅ^À«Àme;™Ù( ÌéÿýPƇSý ‚ º™PЃ ‚`öâ@à:o¼†m}`>`3[ å>ÿ‚»¿ÜîAAOoŒ>AA $÷ö¡@•Ü«ÓÓ߀Հ‘¡œAAÏ zAÌ> M_Ê+4³5Íì wŸˆò£/íî_t÷7º©AA zAÌ>,—þ®/0³y+‰µmî~»»ç*óAAt?¡ AÁìC-OùºÙf¶Ê‹~¯»_ßí½ ‚ ‚ ¡ AÁìÃãéïžf¶³™-ef{÷ÿè¹®AAÐkä5‚ ‚O fv1°wfÓ;ÀQî>¦‡ºAAEBA‚ ‚Ù3› .w÷ÿöl¯‚ ‚ ¨B(èAAAAÐ ˆ5èAAAAÐ =‚ ‚ ‚ z¡ AAAA/ ô ‚ ‚ ‚ è„‚AAA½€PЃ ‚ ‚ ‚  zAAAôBA‚ ‚ ‚ ‚^@(èAAAAÐ =‚ ‚ ‚ z¡ AAAA/ ô ‚ ‚ ‚ è„‚AAA½€ÿW`_ÉÇaIEND®B`‚vispy-0.14.1/doc/README.rst0000644000175000017510000000047614507104216014541 0ustar nileshnileshSphinx sources for the vispy website ==================================== Build Instructions ------------------ VisPy documentation should be generated via the `make` script in the VisPy code repository. A typical sequence of commands is:: python make website html See the VisPy python repository for details. vispy-0.14.1/doc/Makefile0000644000175000017510000000435314507104216014510 0ustar nileshnilesh# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " publish to publish the site on its public location" @echo " html_dev-pattern to make standalone HTML files with select sphinx-gallery pattern (ex. make html_dev-pattern PATTERN='/a')" clean: rm -rf $(BUILDDIR)/* -rm -f *~ rm -f api/*.rst rm -rf gallery/gloo rm -rf gallery/scene rm -rf gallery/plotting html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." html_dev-pattern: $(SPHINXBUILD) -D sphinx_gallery_conf.filename_pattern=$(PATTERN) -D sphinx_gallery_conf.run_stale_examples=True -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html" linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." vispy-0.14.1/doc/CNAME0000644000175000017510000000001314507104216013603 0ustar nileshnileshvispy.org vispy-0.14.1/doc/.nojekyll0000644000175000017510000000000014507104216014661 0ustar nileshnileshvispy-0.14.1/doc/.gitignore0000644000175000017510000000016314507104216015033 0ustar nileshnilesh_build _publish __pycache__ *.pyc # these are autogenerated api/*.rst gallery/gloo gallery/scene gallery/plotting vispy-0.14.1/ci/0000755000175000017510000000000014507104250012667 5ustar nileshnileshvispy-0.14.1/ci/requirements/0000755000175000017510000000000014507104250015412 5ustar nileshnileshvispy-0.14.1/ci/requirements/py38.yml0000644000175000017510000000012114507104216016734 0ustar nileshnileshname: vispy-tests channels: - conda-forge dependencies: - python=3.8 - pip vispy-0.14.1/ci/requirements/py310.yml0000644000175000017510000000012214507104216017006 0ustar nileshnileshname: vispy-tests channels: - conda-forge dependencies: - python=3.10 - pip vispy-0.14.1/ci/requirements/linux_website_deps_pip.txt0000644000175000017510000000024314507104216022720 0ustar nileshnilesh cython numpy numpydoc sphinx sphinx_bootstrap_theme sphinx_gallery sphinxcontrib-apidoc myst-parser pydata-sphinx-theme!=0.10.* pytest pillow pyopengl vispy-0.14.1/ci/requirements/linux_osmesa_deps_conda.txt0000644000175000017510000000017014507104216023040 0ustar nileshnileshcoveralls cython libglu mesalib <21.0.0 meshio numpy pillow pytest pytest-cov pytest-sugar scipy networkx hsluv imageio vispy-0.14.1/ci/requirements/linux_min_deps_conda.txt0000644000175000017510000000015414507104216022336 0ustar nileshnileshcoveralls cython meshio numpy numpydoc pillow pytest pytest-cov pytest-sugar scipy networkx hsluv kiwisolvervispy-0.14.1/ci/requirements/linux_full_newqtdeps_pip.txt0000644000175000017510000000005114507104216023274 0ustar nileshnileshpyopengltk pyqt6 <6.4.0 pyqt6-qt6 <6.4.0 vispy-0.14.1/ci/requirements/linux_full_newqtdeps_conda.txt0000644000175000017510000000052014507104216023571 0ustar nileshnileshcmake cython coveralls decorator freetype-py imageio # Do NOT include jupyter as it depends on qtconsole which pulls in PyQt5 # Do NOT depend on 'matplotlib' as it currently depends on PyQt5 matplotlib-base meshio networkx numpy pillow pytest pytest-cov pytest-sugar pyopengl scikit-image scipy networkx hsluv kiwisolver sphinx-gallery vispy-0.14.1/ci/requirements/linux_full_newqtdeps_apt.txt0000644000175000017510000000043314507104216023274 0ustar nileshnileshlibglu1-mesa-dev libgl1-mesa-dev libxi-dev libglfw3-dev libgles2-mesa-dev libsdl2-2.0-0 mesa-utils libxkbcommon-dev libxkbcommon-x11-dev libxcb1 libxcb1-dev libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1 libegl1-mesa x11-utils vispy-0.14.1/ci/requirements/linux_full_deps_pip.txt0000644000175000017510000000001314507104216022213 0ustar nileshnileshpyopengltk vispy-0.14.1/ci/requirements/linux_full_deps_conda.txt0000644000175000017510000000035714507104216022522 0ustar nileshnileshcmake cython coveralls decorator freetype-py glfw!=3.3.1 imageio jupyter matplotlib meshio networkx numpy pillow pyqt=5 pysdl2 pytest pytest-cov pytest-sugar pyopengl scikit-image scipy networkx hsluv kiwisolver sphinx-gallery jupyter-rfb vispy-0.14.1/ci/requirements/linux_full_deps_apt.txt0000644000175000017510000000015514507104216022216 0ustar nileshnileshlibglu1-mesa-dev libgl1-mesa-dev libxi-dev libglfw3-dev libgles2-mesa-dev libsdl2-2.0-0 mesa-utils x11-utils vispy-0.14.1/ci/build_website.sh0000755000175000017510000000061714507104216016055 0ustar nileshnilesh#!/usr/bin/env bash # Build sphinx documentation # Make "main" the tag so the website gets deployed # The website deployment checks for any tags by checking # this variable. export TRAVIS_TAG="main" cd doc make clean # TODO: Switch to sphinx-multiversion make html SPHINXOPTS="-W --keep-going" touch _build/html/.nojekyll # move back to source directory so we don't mess with future steps cd .. vispy-0.14.1/README.rst0000644000175000017510000001340414507104216013767 0ustar nileshnileshVisPy: interactive scientific visualization in Python ----------------------------------------------------- Main website: http://vispy.org |Build Status| |Coverage Status| |Zenodo Link| |Contributor Covenant| ---- 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). Releases -------- See `CHANGELOG.md <./CHANGELOG.md>`_. Announcements ------------- See the `VisPy Website `_. 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 ------------ Please follow the detailed `installation instructions `_ on the VisPy website. 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, jupyter notebook, and others). Relatively stable API. - **gloo**: a Pythonic, object-oriented interface to OpenGL. Relatively stable API. - **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. - **plot**: high-level plotting interfaces. The API of all public interfaces are subject to change in the future, although **app** and **gloo** are *relatively* stable at this point. Code of Conduct --------------- The VisPy community requires its members to abide by the `Code of Conduct <./CODE_OF_CONDUCT.md>`_. In this CoC you will find the expectations of members, the penalties for violating these expectations, and how violations can be reported to the members of the community in charge of enforcing this Code of Conduct. Governance ---------- The VisPy project maintainers make decisions about the project based on a simple consensus model. This is described in more detail on the `governance page `_ of the vispy website as well as the `list of maintainers `_. In addition to decisions about the VisPy project, there is also a steering committee for the overall VisPy organization. More information about this committee can also be found on the `steering committee page `_ of the vispy website, along with the organization's `charter `_ and other related documents (linked in the charter). Genesis ------- VisPy began when four developers with their own visualization libraries decided to team up: `Luke Campagnola `__ with `PyQtGraph `__, `Almar Klein `__ with `Visvis `__, `Cyrille Rossant `__ with `Galry `__, `Nicolas Rougier `__ with `Glumpy `__. Now VisPy looks to build on the expertise of these developers and the broader open-source community to build a high-performance OpenGL library. ---- External links -------------- - `User mailing list `__ - `Dev mailing list `__ - `Chat room `__ - `Developer chat room `__ - `Wiki `__ - `Gallery `__ - `Documentation `__ .. |Build Status| image:: https://github.com/vispy/vispy/workflows/CI/badge.svg :target: https://github.com/vispy/vispy/actions .. |Coverage Status| image:: https://img.shields.io/coveralls/vispy/vispy/main.svg :target: https://coveralls.io/r/vispy/vispy?branch=main .. |Zenodo Link| image:: https://zenodo.org/badge/5822/vispy/vispy.svg :target: http://dx.doi.org/10.5281/zenodo.17869 .. |Contributor Covenant| image:: https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg :target: CODE_OF_CONDUCT.md vispy-0.14.1/PKG-INFO0000644000175000017510000001626114507104251013400 0ustar nileshnileshMetadata-Version: 2.1 Name: vispy Version: 0.14.1 Summary: Interactive visualization in Python Home-page: http://vispy.org Download-URL: https://pypi.python.org/pypi/vispy Author: Vispy contributors Author-email: vispy@googlegroups.com License: BSD-3-Clause Keywords: visualization,OpenGl,ES,medical,imaging,3D,plotting,numpy,bigdata,ipython,jupyter,widgets 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 :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Framework :: IPython Provides: vispy Requires-Python: >=3.8 Description-Content-Type: text/x-rst Provides-Extra: ipython-static Provides-Extra: pyglet Provides-Extra: pyqt5 Provides-Extra: pyqt6 Provides-Extra: pyside Provides-Extra: pyside2 Provides-Extra: pyside6 Provides-Extra: sdl2 Provides-Extra: wx Provides-Extra: tk Provides-Extra: doc Provides-Extra: io License-File: LICENSE.txt VisPy: interactive scientific visualization in Python ----------------------------------------------------- Main website: http://vispy.org |Build Status| |Coverage Status| |Zenodo Link| |Contributor Covenant| ---- 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). Releases -------- See `CHANGELOG.md <./CHANGELOG.md>`_. Announcements ------------- See the `VisPy Website `_. 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 ------------ Please follow the detailed `installation instructions `_ on the VisPy website. 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, jupyter notebook, and others). Relatively stable API. - **gloo**: a Pythonic, object-oriented interface to OpenGL. Relatively stable API. - **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. - **plot**: high-level plotting interfaces. The API of all public interfaces are subject to change in the future, although **app** and **gloo** are *relatively* stable at this point. Code of Conduct --------------- The VisPy community requires its members to abide by the `Code of Conduct <./CODE_OF_CONDUCT.md>`_. In this CoC you will find the expectations of members, the penalties for violating these expectations, and how violations can be reported to the members of the community in charge of enforcing this Code of Conduct. Governance ---------- The VisPy project maintainers make decisions about the project based on a simple consensus model. This is described in more detail on the `governance page `_ of the vispy website as well as the `list of maintainers `_. In addition to decisions about the VisPy project, there is also a steering committee for the overall VisPy organization. More information about this committee can also be found on the `steering committee page `_ of the vispy website, along with the organization's `charter `_ and other related documents (linked in the charter). Genesis ------- VisPy began when four developers with their own visualization libraries decided to team up: `Luke Campagnola `__ with `PyQtGraph `__, `Almar Klein `__ with `Visvis `__, `Cyrille Rossant `__ with `Galry `__, `Nicolas Rougier `__ with `Glumpy `__. Now VisPy looks to build on the expertise of these developers and the broader open-source community to build a high-performance OpenGL library. ---- External links -------------- - `User mailing list `__ - `Dev mailing list `__ - `Chat room `__ - `Developer chat room `__ - `Wiki `__ - `Gallery `__ - `Documentation `__ .. |Build Status| image:: https://github.com/vispy/vispy/workflows/CI/badge.svg :target: https://github.com/vispy/vispy/actions .. |Coverage Status| image:: https://img.shields.io/coveralls/vispy/vispy/main.svg :target: https://coveralls.io/r/vispy/vispy?branch=main .. |Zenodo Link| image:: https://zenodo.org/badge/5822/vispy/vispy.svg :target: http://dx.doi.org/10.5281/zenodo.17869 .. |Contributor Covenant| image:: https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg :target: CODE_OF_CONDUCT.md vispy-0.14.1/MANIFEST.in0000644000175000017510000000152014507104216014032 0ustar nileshnilesh# Add documentation to the source tarball but not in the installation include doc/Makefile doc/.nojekyll doc/conf.py doc/CNAME include doc/*.rst recursive-include doc/_static * recursive-include vispy *.py recursive-include vispy *.pyx recursive-include vispy *.glsl recursive-include vispy *.frag recursive-include vispy *.vert include vispy/app/tests/qt-designer.ui include vispy/io/_data/spatial-filters.npy include vispy/util/fonts/data/*.ttf include vispy/version.py recursive-include examples *.py recursive-include examples *.ipynb recursive-include examples *.glsl include README.rst include LICENSE.txt include pyproject.toml include pytest.ini include .coveragerc recursive-exclude codegen * exclude Makefile exclude rtd_requirements.txt exclude stdeb.cfg exclude .spyderproject recursive-exclude vispy *.c recursive-exclude make * vispy-0.14.1/LICENSE.txt0000644000175000017510000000335614507104216014130 0ustar nileshnileshVispy licensing terms --------------------- Vispy is licensed under the terms of the (new) BSD license: Copyright (c) 2013-2023, Vispy Development Team. 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. * Neither the name of Vispy Development Team 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 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. Exceptions ---------- The examples code in the examples directory can be considered public domain, unless otherwise indicated in the corresponding source file. vispy-0.14.1/CONTRIBUTING.md0000644000175000017510000000024014507104216014523 0ustar nileshnilesh# Contributor's Guide See the official VisPy documentation for information on contributing to the project: https://vispy.org/dev_guide/contributor_guide.html vispy-0.14.1/CODE_OF_CONDUCT.md0000644000175000017510000001256314507104216015104 0ustar nileshnilesh# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at vispy-conduct@groups.io. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations vispy-0.14.1/CITATION.rst0000644000175000017510000000052714507104216014246 0ustar nileshnileshHow do I cite VisPy? -------------------- VisPy's source code is published on Zenodo.org: https://zenodo.org/record/4321173 DOI and citing information can be found on the right side of the Zenodo page. Each VisPy release has its own DOI as well as the project as a whole. The overall VisPy DOI URL is: https://doi.org/10.5281/zenodo.592490vispy-0.14.1/CHANGELOG.md0000644000175000017510000012417214507104216014116 0ustar nileshnilesh# Release Notes ## [v0.14.1](https://github.com/vispy/vispy/tree/v0.14.1) (2023-10-03) **Fixed bugs:** - return to oldest supported numpy [\#2535](https://github.com/vispy/vispy/pull/2535) ([brisvag](https://github.com/brisvag)) **Merged pull requests:** - Bump pypa/cibuildwheel from 2.16.0 to 2.16.1 [\#2534](https://github.com/vispy/vispy/pull/2534) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pypa/cibuildwheel from 2.15.0 to 2.16.0 [\#2531](https://github.com/vispy/vispy/pull/2531) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump docker/setup-qemu-action from 2 to 3 [\#2529](https://github.com/vispy/vispy/pull/2529) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump actions/checkout from 3 to 4 [\#2527](https://github.com/vispy/vispy/pull/2527) ([dependabot[bot]](https://github.com/apps/dependabot)) ## [v0.14.0](https://github.com/vispy/vispy/tree/v0.14.0) (2023-09-08) **Enhancements:** - Remove deprecated code from vispy.color.get\_colormap [\#2519](https://github.com/vispy/vispy/pull/2519) ([codypiersall](https://github.com/codypiersall)) - Cache make\_pattern on DashAtlas [\#2508](https://github.com/vispy/vispy/pull/2508) ([aganders3](https://github.com/aganders3)) - Add push/pop gl\_state for visuals \(used in picking\) [\#2502](https://github.com/vispy/vispy/pull/2502) ([aganders3](https://github.com/aganders3)) - Add primitive picking filters for Mesh and Markers visuals [\#2500](https://github.com/vispy/vispy/pull/2500) ([aganders3](https://github.com/aganders3)) **Fixed bugs:** - Fix spherical markers depth buffer with scaling='visual' [\#2506](https://github.com/vispy/vispy/pull/2506) ([brisvag](https://github.com/brisvag)) - Update \_glfw.py [\#2496](https://github.com/vispy/vispy/pull/2496) ([smyeungx](https://github.com/smyeungx)) - Fix mesh shading with flipped normals [\#2493](https://github.com/vispy/vispy/pull/2493) ([brisvag](https://github.com/brisvag)) **Merged pull requests:** - Bump pypa/cibuildwheel from 2.14.1 to 2.15.0 and add Python 3.12 wheels [\#2515](https://github.com/vispy/vispy/pull/2515) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pypa/cibuildwheel from 2.14.0 to 2.14.1 [\#2509](https://github.com/vispy/vispy/pull/2509) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pypa/cibuildwheel from 2.13.1 to 2.14.0 [\#2507](https://github.com/vispy/vispy/pull/2507) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pypa/cibuildwheel from 2.13.0 to 2.13.1 [\#2498](https://github.com/vispy/vispy/pull/2498) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pypa/cibuildwheel from 2.12.3 to 2.13.0 [\#2492](https://github.com/vispy/vispy/pull/2492) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump mamba-org/provision-with-micromamba from 15 to 16 [\#2490](https://github.com/vispy/vispy/pull/2490) ([dependabot[bot]](https://github.com/apps/dependabot)) - Add lasso selection example [\#2485](https://github.com/vispy/vispy/pull/2485) ([DanAurea](https://github.com/DanAurea)) ## [v0.13.0](https://github.com/vispy/vispy/tree/v0.13.0) (2023-05-12) **Enhancements:** - Switch MarkersVisual scaling option to string "fixed", "scene", or "visual" [\#2470](https://github.com/vispy/vispy/pull/2470) ([djhoese](https://github.com/djhoese)) - Add early-termination optimization to attenuated mip [\#2465](https://github.com/vispy/vispy/pull/2465) ([aganders3](https://github.com/aganders3)) - Add `InstancedMeshVisual` for faster and easier rendering of repeated meshes [\#2461](https://github.com/vispy/vispy/pull/2461) ([brisvag](https://github.com/brisvag)) - Instanced mesh example [\#2460](https://github.com/vispy/vispy/pull/2460) ([brisvag](https://github.com/brisvag)) - Use QNativeEventGesture for touchpad gesture input [\#2456](https://github.com/vispy/vispy/pull/2456) ([aganders3](https://github.com/aganders3)) **Fixed bugs:** - Fix TypeError with pinch-to-zoom [\#2483](https://github.com/vispy/vispy/pull/2483) ([aganders3](https://github.com/aganders3)) **Merged pull requests:** - Bump pypa/cibuildwheel from 2.12.1 to 2.12.3 [\#2472](https://github.com/vispy/vispy/pull/2472) ([dependabot[bot]](https://github.com/apps/dependabot)) - Cleanup site navbar with pydata-sphinx-theme 0.10+ [\#2371](https://github.com/vispy/vispy/pull/2371) ([djhoese](https://github.com/djhoese)) ## [v0.12.2](https://github.com/vispy/vispy/tree/v0.12.2) (2023-03-20) **Enhancements:** - Clean up and test computation of normals in MeshData [\#2444](https://github.com/vispy/vispy/pull/2444) ([asnt](https://github.com/asnt)) - Vectorize MeshData vertex normals computation [\#2434](https://github.com/vispy/vispy/pull/2434) ([asnt](https://github.com/asnt)) **Fixed bugs:** - Fix shader compilation error for MeshVisual when mesh.clim = 'auto' [\#2463](https://github.com/vispy/vispy/pull/2463) ([aganders3](https://github.com/aganders3)) - Bugfix: Use color.lower\(\) for \_color\_dict keys [\#2459](https://github.com/vispy/vispy/pull/2459) ([psobolewskiPhD](https://github.com/psobolewskiPhD)) - Fix f-string missing prefix in gloo/program.py error message [\#2457](https://github.com/vispy/vispy/pull/2457) ([aganders3](https://github.com/aganders3)) - Fix character codec error on Windows Python \<3.10 [\#2437](https://github.com/vispy/vispy/pull/2437) ([haesleinhuepf](https://github.com/haesleinhuepf)) **Merged pull requests:** - Bump pypa/cibuildwheel from 2.12.0 to 2.12.1 [\#2462](https://github.com/vispy/vispy/pull/2462) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pypa/cibuildwheel from 2.11.4 to 2.12.0 [\#2447](https://github.com/vispy/vispy/pull/2447) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump mamba-org/provision-with-micromamba from 14 to 15 [\#2446](https://github.com/vispy/vispy/pull/2446) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pypa/cibuildwheel from 2.11.3 to 2.11.4 [\#2441](https://github.com/vispy/vispy/pull/2441) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pypa/cibuildwheel from 2.11.2 to 2.11.3 [\#2438](https://github.com/vispy/vispy/pull/2438) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump docker/setup-qemu-action from 1 to 2 [\#2432](https://github.com/vispy/vispy/pull/2432) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump pypa/cibuildwheel from 2.1.1 to 2.11.2 [\#2431](https://github.com/vispy/vispy/pull/2431) ([dependabot[bot]](https://github.com/apps/dependabot)) ## [v0.12.1](https://github.com/vispy/vispy/tree/v0.12.1) (2022-11-14) **Fixed bugs:** - Fix: bug with new Markers.symbol and empty data [\#2428](https://github.com/vispy/vispy/pull/2428) ([brisvag](https://github.com/brisvag)) ## [v0.12.0](https://github.com/vispy/vispy/tree/v0.12.0) (2022-11-08) **Enhancements:** - Scale values when calculating attenuation in attenuated\_mip shader [\#2417](https://github.com/vispy/vispy/pull/2417) ([aganders3](https://github.com/aganders3)) - clipping planes performance: do not create a new function willy nilly [\#2383](https://github.com/vispy/vispy/pull/2383) ([brisvag](https://github.com/brisvag)) - Instance rendering in gloo. [\#2378](https://github.com/vispy/vispy/pull/2378) ([brisvag](https://github.com/brisvag)) - Allow setting array to `symbol` in `MarkersVisual` [\#2361](https://github.com/vispy/vispy/pull/2361) ([brisvag](https://github.com/brisvag)) **Fixed bugs:** - Improve PySide6 Support: swapBehavior for DoubleBuffer, BUTTONMAP as pyqt6, xfail test\_context [\#2411](https://github.com/vispy/vispy/pull/2411) ([psobolewskiPhD](https://github.com/psobolewskiPhD)) - fix: wx timer from float to int [\#2396](https://github.com/vispy/vispy/pull/2396) ([Bliss3d](https://github.com/Bliss3d)) - Fix custom image interpolation kernels to work with negative numbers [\#2382](https://github.com/vispy/vispy/pull/2382) ([brisvag](https://github.com/brisvag)) - Use correct regex when parsing variables [\#2380](https://github.com/vispy/vispy/pull/2380) ([brisvag](https://github.com/brisvag)) - Update tube.py to support Python 3.10 and higher. [\#2370](https://github.com/vispy/vispy/pull/2370) ([tralfaz](https://github.com/tralfaz)) - Fix marker size with anisotropic scaling [\#2359](https://github.com/vispy/vispy/pull/2359) ([brisvag](https://github.com/brisvag)) - Fix MouseEvent `.buttons` not including triggering mouse button [\#2355](https://github.com/vispy/vispy/pull/2355) ([tushar5526](https://github.com/tushar5526)) - Implement turntable camera `roll` programmatically and clarify transformation docstrings [\#2352](https://github.com/vispy/vispy/pull/2352) ([harripj](https://github.com/harripj)) - Fix dtype casting for Texture objects [\#2350](https://github.com/vispy/vispy/pull/2350) ([brisvag](https://github.com/brisvag)) **Merged pull requests:** - Bump mamba-org/provision-with-micromamba from 7 to 14 [\#2425](https://github.com/vispy/vispy/pull/2425) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump actions/setup-python from 2 to 4 [\#2424](https://github.com/vispy/vispy/pull/2424) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump actions/checkout from 2 to 3 [\#2423](https://github.com/vispy/vispy/pull/2423) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump actions/download-artifact from 2 to 3 [\#2422](https://github.com/vispy/vispy/pull/2422) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump actions/upload-artifact from 2 to 3 [\#2421](https://github.com/vispy/vispy/pull/2421) ([dependabot[bot]](https://github.com/apps/dependabot)) - Allow 180 fov and clarify docstring [\#2412](https://github.com/vispy/vispy/pull/2412) ([brisvag](https://github.com/brisvag)) - Address deprecation warnings with the usage of distuilts for LooseVersion [\#2404](https://github.com/vispy/vispy/pull/2404) ([hmaarrfk](https://github.com/hmaarrfk)) - Add governance and steering committee information to README [\#2397](https://github.com/vispy/vispy/pull/2397) ([djhoese](https://github.com/djhoese)) - New instanced rendering example [\#2376](https://github.com/vispy/vispy/pull/2376) ([brisvag](https://github.com/brisvag)) - Add initial organization charter and project governance [\#2367](https://github.com/vispy/vispy/pull/2367) ([djhoese](https://github.com/djhoese)) - Fix a few comment and docstring typos [\#2353](https://github.com/vispy/vispy/pull/2353) ([timgates42](https://github.com/timgates42)) - Use `cubic` instead of `bicubic` as name for interpolation [\#2348](https://github.com/vispy/vispy/pull/2348) ([brisvag](https://github.com/brisvag)) - Fix test for `Image` custom interpolation [\#2347](https://github.com/vispy/vispy/pull/2347) ([brisvag](https://github.com/brisvag)) - Add realtime data visualization tutorial examples [\#2339](https://github.com/vispy/vispy/pull/2339) ([djhoese](https://github.com/djhoese)) ## [v0.11.0](https://github.com/vispy/vispy/tree/v0.11.0) (2022-07-04) **Enhancements:** - Reduce memory copies in LineVisual [\#2327](https://github.com/vispy/vispy/pull/2327) ([christiansandberg](https://github.com/christiansandberg)) - Add shader interpolation methods for VolumeVisual [\#2322](https://github.com/vispy/vispy/pull/2322) ([brisvag](https://github.com/brisvag)) - Add 'custom' ImageVisual interpolation with custom kernel on GPU [\#2319](https://github.com/vispy/vispy/pull/2319) ([brisvag](https://github.com/brisvag)) **Fixed bugs:** - Fix clipping planes logic for `Volume` and `PlanesClipper` [\#2329](https://github.com/vispy/vispy/pull/2329) ([brisvag](https://github.com/brisvag)) - Enable text depth testing [\#2328](https://github.com/vispy/vispy/pull/2328) ([almarklein](https://github.com/almarklein)) - Fix HIDPI on pyglet [\#2326](https://github.com/vispy/vispy/pull/2326) ([almarklein](https://github.com/almarklein)) - Add `blend_equation` \(add\) to GL\_PRESETS [\#2324](https://github.com/vispy/vispy/pull/2324) ([psobolewskiPhD](https://github.com/psobolewskiPhD)) **Merged pull requests:** - Fix link to CHANGELOG.md in README.rst [\#2338](https://github.com/vispy/vispy/pull/2338) ([psobolewskiPhD](https://github.com/psobolewskiPhD)) - Typo fix in docstring [\#2332](https://github.com/vispy/vispy/pull/2332) ([ksohan](https://github.com/ksohan)) ## [v0.10.0](https://github.com/vispy/vispy/tree/v0.10.0) (2022-04-18) **Enhancements:** - Add GL\_MIN/GL\_MAX constants for glBlendEquation [\#2320](https://github.com/vispy/vispy/pull/2320) ([djhoese](https://github.com/djhoese)) - Add cutoff to mip and minip volume projection for fragment discard [\#2308](https://github.com/vispy/vispy/pull/2308) ([brisvag](https://github.com/brisvag)) - Optimize shader regular expression compilation [\#2297](https://github.com/vispy/vispy/pull/2297) ([PydPiper](https://github.com/PydPiper)) - Add ComplexImageVisual for viewing complex image data [\#1999](https://github.com/vispy/vispy/pull/1999) ([tlambert03](https://github.com/tlambert03)) - Allow depth in Texture2D. [\#1310](https://github.com/vispy/vispy/pull/1310) ([keunhong](https://github.com/keunhong)) **Fixed bugs:** - Set depth in `VolumeVisual` for `mip`/`minip`/`attenuated_mip` rendering modes [\#2305](https://github.com/vispy/vispy/pull/2305) ([alisterburt](https://github.com/alisterburt)) **Merged pull requests:** - Fix typo in Nested Viewbox name in example [\#2321](https://github.com/vispy/vispy/pull/2321) ([jawjay](https://github.com/jawjay)) - Expand docs on panzoom rect setter [\#2317](https://github.com/vispy/vispy/pull/2317) ([dstansby](https://github.com/dstansby)) - Cleanup debug logic in line draw example [\#2306](https://github.com/vispy/vispy/pull/2306) ([olinickalls](https://github.com/olinickalls)) ## [v0.9.6](https://github.com/vispy/vispy/tree/v0.9.6) (2022-02-04) **Fixed bugs:** - Fix TypeError for membership test of KeyboardModifiers [\#2293](https://github.com/vispy/vispy/pull/2293) ([rayg-ssec](https://github.com/rayg-ssec)) ## [v0.9.5](https://github.com/vispy/vispy/tree/v0.9.5) (2022-02-04) **Fixed bugs:** - Set depth buffer in Volume plane rendering [\#2289](https://github.com/vispy/vispy/pull/2289) ([brisvag](https://github.com/brisvag)) - Fix numpy error with array edge\_width in Markers [\#2285](https://github.com/vispy/vispy/pull/2285) ([brisvag](https://github.com/brisvag)) - Fix touch event handling issue for Qt6-based backends [\#2284](https://github.com/vispy/vispy/pull/2284) ([mars0001](https://github.com/mars0001)) - remove utf-8 encode in \_vispy\_set\_title [\#2269](https://github.com/vispy/vispy/pull/2269) ([Llewyllen](https://github.com/Llewyllen)) **Merged pull requests:** - MNT: add documentation of usage of Triangle/triangle, see \#1029 [\#2268](https://github.com/vispy/vispy/pull/2268) ([kmuehlbauer](https://github.com/kmuehlbauer)) - Add typing to `create_visual_node` [\#2264](https://github.com/vispy/vispy/pull/2264) ([tlambert03](https://github.com/tlambert03)) ## [v0.9.4](https://github.com/vispy/vispy/tree/v0.9.4) (2021-11-24) **Fixed bugs:** - Fix MeshNormals and WireframeFilter with empty MeshData [\#2262](https://github.com/vispy/vispy/pull/2262) ([brisvag](https://github.com/brisvag)) - Update clims in color transform whenever texture.\_data\_limits changes [\#2245](https://github.com/vispy/vispy/pull/2245) ([tlambert03](https://github.com/tlambert03)) **Merged pull requests:** - Clarified docstring for ArrowVisual [\#2261](https://github.com/vispy/vispy/pull/2261) ([pauljurczak](https://github.com/pauljurczak)) - Use stacklevel in DeprecationWarnings. [\#2257](https://github.com/vispy/vispy/pull/2257) ([Carreau](https://github.com/Carreau)) - Add optional dependencies section to installation instructions [\#2251](https://github.com/vispy/vispy/pull/2251) ([pauljurczak](https://github.com/pauljurczak)) - Add table for list of backend supported [\#2246](https://github.com/vispy/vispy/pull/2246) ([anirudhbagri](https://github.com/anirudhbagri)) ## [v0.9.3](https://github.com/vispy/vispy/tree/v0.9.3) (2021-10-27) **Fixed bugs:** - Noop on clicking both mouse buttons [\#2244](https://github.com/vispy/vispy/pull/2244) ([brisvag](https://github.com/brisvag)) - Fix performance issues with 0.9.1 [\#2243](https://github.com/vispy/vispy/pull/2243) ([brisvag](https://github.com/brisvag)) - Remove unnecessary if clauses from Volume [\#2242](https://github.com/vispy/vispy/pull/2242) ([brisvag](https://github.com/brisvag)) - Fix volume rendering's wrong offset [\#2239](https://github.com/vispy/vispy/pull/2239) ([brisvag](https://github.com/brisvag)) - Update PlanesClipper interpolation to occur in the fragment shader [\#2226](https://github.com/vispy/vispy/pull/2226) ([brisvag](https://github.com/brisvag)) **Merged pull requests:** - Include tutorial image from vispy/images to remove remote reference [\#2240](https://github.com/vispy/vispy/pull/2240) ([draco2003](https://github.com/draco2003)) ## [v0.9.2](https://github.com/vispy/vispy/tree/v0.9.2) (2021-10-21) **Fixed bugs:** - Fix colorbar orientation [\#2238](https://github.com/vispy/vispy/pull/2238) ([Dive576](https://github.com/Dive576)) ## [v0.9.1](https://github.com/vispy/vispy/tree/v0.9.1) (2021-10-20) **Merged pull requests:** - Documentation for third party projects [\#2235](https://github.com/vispy/vispy/pull/2235) ([Dive576](https://github.com/Dive576)) - Remove "Jupyter Widget" section part [\#2233](https://github.com/vispy/vispy/pull/2233) ([djhoese](https://github.com/djhoese)) - Move module globals to class attributes [\#2227](https://github.com/vispy/vispy/pull/2227) ([brisvag](https://github.com/brisvag)) ## [v0.9.0](https://github.com/vispy/vispy/tree/v0.9.0) (2021-09-29) **Enhancements:** - Spherical \(3D-looking\) `Markers` symbols [\#2209](https://github.com/vispy/vispy/pull/2209) ([brisvag](https://github.com/brisvag)) - Add PlanesClipper filter for visually clipping visuals by a 2D plane [\#2197](https://github.com/vispy/vispy/pull/2197) ([brisvag](https://github.com/brisvag)) - Add skinny cross marker \(++\) to MarkersVisual [\#2193](https://github.com/vispy/vispy/pull/2193) ([mars0001](https://github.com/mars0001)) - Improve depth handling for VolumeVisual iso rendering [\#2190](https://github.com/vispy/vispy/pull/2190) ([kevinyamauchi](https://github.com/kevinyamauchi)) **Fixed bugs:** - Fix integer division when creating directed graphs [\#2223](https://github.com/vispy/vispy/pull/2223) ([Maks1mS](https://github.com/Maks1mS)) - Cleanup some refs to webgl [\#2217](https://github.com/vispy/vispy/pull/2217) ([almarklein](https://github.com/almarklein)) - Fix key event handling in Tk backend [\#2205](https://github.com/vispy/vispy/pull/2205) ([matthiasverstraete](https://github.com/matthiasverstraete)) **Merged pull requests:** - Fix "Edit this page" links for API docs [\#2220](https://github.com/vispy/vispy/pull/2220) ([djhoese](https://github.com/djhoese)) ## [v0.8.1](https://github.com/vispy/vispy/tree/v0.8.1) (2021-08-27) **Fixed bugs:** - Fix PyQt5 backend gesture event handling [\#2202](https://github.com/vispy/vispy/pull/2202) ([djhoese](https://github.com/djhoese)) - Fix PinchGesture attribute error on pyqt6 [\#2200](https://github.com/vispy/vispy/pull/2200) ([tlambert03](https://github.com/tlambert03)) - Fix PyQt scaling issue [\#2189](https://github.com/vispy/vispy/pull/2189) ([mars0001](https://github.com/mars0001)) **Merged pull requests:** - Ditch example symlinks [\#2181](https://github.com/vispy/vispy/pull/2181) ([almarklein](https://github.com/almarklein)) ## [v0.8.0](https://github.com/vispy/vispy/tree/v0.8.0) (2021-08-20) **Enhancements:** - Add PyQt6 backend [\#2172](https://github.com/vispy/vispy/pull/2172) ([mars0001](https://github.com/mars0001)) - Remove 'VNC' backend [\#2164](https://github.com/vispy/vispy/pull/2164) ([almarklein](https://github.com/almarklein)) - Refactor texture\_lut\(\) Colormap method to BaseColormap for cleaner usage [\#2160](https://github.com/vispy/vispy/pull/2160) ([almarklein](https://github.com/almarklein)) - Rendering arbitrary planes in the `VolumeVisual` [\#2149](https://github.com/vispy/vispy/pull/2149) ([alisterburt](https://github.com/alisterburt)) - Switch examples and website gallery to sphinx-gallery [\#2148](https://github.com/vispy/vispy/pull/2148) ([djhoese](https://github.com/djhoese)) - Add "jupyter\_rfb" backend for inline Jupyter Notebook/Lab display [\#2142](https://github.com/vispy/vispy/pull/2142) ([almarklein](https://github.com/almarklein)) - Migrate from string formatting to template \($\) variables in VolumeVisual shaders [\#2117](https://github.com/vispy/vispy/pull/2117) ([brisvag](https://github.com/brisvag)) - Add clipping planes to `VolumeVisual` [\#2116](https://github.com/vispy/vispy/pull/2116) ([brisvag](https://github.com/brisvag)) **Fixed bugs:** - Fix volume\_plane.py example not having a toggle for the animation [\#2179](https://github.com/vispy/vispy/pull/2179) ([djhoese](https://github.com/djhoese)) - Fix minor bug in volume\_clipping.py example [\#2175](https://github.com/vispy/vispy/pull/2175) ([kevinyamauchi](https://github.com/kevinyamauchi)) ## [v0.7.3](https://github.com/vispy/vispy/tree/v0.7.3) (2021-07-21) **Fixed bugs:** - Fix `VolumeVisual.cmap` setter not working for most colormaps [\#2150](https://github.com/vispy/vispy/pull/2150) ([alisterburt](https://github.com/alisterburt)) ## [v0.7.2](https://github.com/vispy/vispy/tree/v0.7.2) (2021-07-20) **Fixed bugs:** - Add filter keyword arguments to subclassed filters [\#2144](https://github.com/vispy/vispy/pull/2144) ([clarebcook](https://github.com/clarebcook)) - Fix scalable textures clim\_normalized when auto clims are used [\#2140](https://github.com/vispy/vispy/pull/2140) ([djhoese](https://github.com/djhoese)) ## [v0.7.1](https://github.com/vispy/vispy/tree/v0.7.1) (2021-07-13) **Fixed bugs:** - Fix auto clim calculation if all data is non-finite [\#2131](https://github.com/vispy/vispy/pull/2131) ([almarklein](https://github.com/almarklein)) - Update light direction in mesh shading examples [\#2125](https://github.com/vispy/vispy/pull/2125) ([asnt](https://github.com/asnt)) **Merged pull requests:** - Set stacklevel for colormap deprecation. [\#2134](https://github.com/vispy/vispy/pull/2134) ([Carreau](https://github.com/Carreau)) - Make meshes upright and face the camera in mesh examples [\#2126](https://github.com/vispy/vispy/pull/2126) ([asnt](https://github.com/asnt)) ## [v0.7.0](https://github.com/vispy/vispy/tree/v0.7.0) (2021-06-30) **Enhancements:** - Change Visual GL state so it is only set if drawing [\#2111](https://github.com/vispy/vispy/pull/2111) ([djhoese](https://github.com/djhoese)) - Add handling of NaNs in ImageVisual [\#2106](https://github.com/vispy/vispy/pull/2106) ([djhoese](https://github.com/djhoese)) - Improve specular light in phong shading [\#2091](https://github.com/vispy/vispy/pull/2091) ([almarklein](https://github.com/almarklein)) - Fix SceneCanvas Node leaking reference to itself [\#2089](https://github.com/vispy/vispy/pull/2089) ([djhoese](https://github.com/djhoese)) - Improve infinity/NaN handling in VolumeVisual and ImageVisual clim calculations [\#2085](https://github.com/vispy/vispy/pull/2085) ([almarklein](https://github.com/almarklein)) - Change builtin colormaps to all be instances [\#2066](https://github.com/vispy/vispy/pull/2066) ([djhoese](https://github.com/djhoese)) - Add setter for colorbar label text [\#2057](https://github.com/vispy/vispy/pull/2057) ([djhoese](https://github.com/djhoese)) - Add average intensity projection \(average\) rendering mode to `VolumeVisual` [\#2055](https://github.com/vispy/vispy/pull/2055) ([alisterburt](https://github.com/alisterburt)) - Add attenuated MIP \(attenuated\_mip\) rendering mode to `VolumeVisual` [\#2047](https://github.com/vispy/vispy/pull/2047) ([alisterburt](https://github.com/alisterburt)) - Add minimum intensity projection \(minip\) shading to `VolumeVisual` [\#2046](https://github.com/vispy/vispy/pull/2046) ([alisterburt](https://github.com/alisterburt)) - Add more options to control the length of normals in MeshNormals visual [\#2043](https://github.com/vispy/vispy/pull/2043) ([asnt](https://github.com/asnt)) - Add visual for displaying mesh normals [\#2031](https://github.com/vispy/vispy/pull/2031) ([asnt](https://github.com/asnt)) - Identify and expose Phong shading parameters in mesh ShadingFilter [\#2029](https://github.com/vispy/vispy/pull/2029) ([asnt](https://github.com/asnt)) - Add the ability to make the wireframe transparent with the wireframe filter [\#2026](https://github.com/vispy/vispy/pull/2026) ([asnt](https://github.com/asnt)) - Add ability to show only a mesh wireframe with the wireframe filter [\#2025](https://github.com/vispy/vispy/pull/2025) ([asnt](https://github.com/asnt)) - Optimize SurfacePlotVisual when only color is updated [\#2002](https://github.com/vispy/vispy/pull/2002) ([djhoese](https://github.com/djhoese)) - Add PySide6 backend [\#1978](https://github.com/vispy/vispy/pull/1978) ([Kusefiru](https://github.com/Kusefiru)) - Add networkx layout to GraphVisual [\#1941](https://github.com/vispy/vispy/pull/1941) ([cvanelteren](https://github.com/cvanelteren)) - Overhaul vispy website [\#1931](https://github.com/vispy/vispy/pull/1931) ([djhoese](https://github.com/djhoese)) - Add 'texture\_format' kwarg to ImageVisual for floating point textures [\#1920](https://github.com/vispy/vispy/pull/1920) ([djhoese](https://github.com/djhoese)) - Add Tkinter backend [\#1918](https://github.com/vispy/vispy/pull/1918) ([ThenTech](https://github.com/ThenTech)) - Add 'texture\_format' kwarg to VolumeVisual for floating point textures [\#1912](https://github.com/vispy/vispy/pull/1912) ([djhoese](https://github.com/djhoese)) - Let camera link be limited to specified properties [\#1886](https://github.com/vispy/vispy/pull/1886) ([povik](https://github.com/povik)) - Speed up arcball and turntable cameras [\#1884](https://github.com/vispy/vispy/pull/1884) ([povik](https://github.com/povik)) - Fix jupyter lab extension to use newest vispy.js [\#1866](https://github.com/vispy/vispy/pull/1866) ([mjlbach](https://github.com/mjlbach)) - Allow for 2D X and Y coordinates in SurfacePlotVisual [\#1863](https://github.com/vispy/vispy/pull/1863) ([dvsphanindra](https://github.com/dvsphanindra)) - Add ImageVisual gamma and smarter in-shader contrast limits [\#1844](https://github.com/vispy/vispy/pull/1844) ([tlambert03](https://github.com/tlambert03)) - Implement volume contrast limits in shader, add gamma [\#1842](https://github.com/vispy/vispy/pull/1842) ([tlambert03](https://github.com/tlambert03)) - Make demos easier to switch to PySide2 from PyQt5 [\#1835](https://github.com/vispy/vispy/pull/1835) ([fedepell](https://github.com/fedepell)) - Use meshio fall back for reading and writing mesh files [\#1824](https://github.com/vispy/vispy/pull/1824) ([nschloe](https://github.com/nschloe)) - Add nearest interpolation to volume visual [\#1803](https://github.com/vispy/vispy/pull/1803) ([sofroniewn](https://github.com/sofroniewn)) - Remove isosurface green color in VolumeVisual [\#1802](https://github.com/vispy/vispy/pull/1802) ([sofroniewn](https://github.com/sofroniewn)) - Add "transparent" color to internal color\_dict [\#1794](https://github.com/vispy/vispy/pull/1794) ([HagaiHargil](https://github.com/HagaiHargil)) - Make all AxisVisual parameters easily updatable after instantiation [\#1792](https://github.com/vispy/vispy/pull/1792) ([tlambert03](https://github.com/tlambert03)) - Make it possible to get and set the face, bold, and italic properties of Text via properties [\#1777](https://github.com/vispy/vispy/pull/1777) ([astrofrog](https://github.com/astrofrog)) - Check for GUI eventloop when testing for jupyter kernel [\#1714](https://github.com/vispy/vispy/pull/1714) ([hmaarrfk](https://github.com/hmaarrfk)) - Add lines\_adjacency and line\_strip\_adjacency OpenGL primitives. [\#1705](https://github.com/vispy/vispy/pull/1705) ([proto3](https://github.com/proto3)) - Add ability to pass webGL context arguments for notebook backend canvas initialization [\#1693](https://github.com/vispy/vispy/pull/1693) ([klarh](https://github.com/klarh)) - Add mesh wireframe filter [\#1689](https://github.com/vispy/vispy/pull/1689) ([asnt](https://github.com/asnt)) - Allow changing spectrogram parameters after it has been drawn [\#1670](https://github.com/vispy/vispy/pull/1670) ([cimbi](https://github.com/cimbi)) - Fix event loop detection triggering on blocked events [\#1590](https://github.com/vispy/vispy/pull/1590) ([kne42](https://github.com/kne42)) - Replace Grid Widget cassowary solver with kiwisolver [\#1501](https://github.com/vispy/vispy/pull/1501) ([MatthieuDartiailh](https://github.com/MatthieuDartiailh)) - Add ShadingFilter for meshes by separating it from MeshVisual [\#1463](https://github.com/vispy/vispy/pull/1463) ([asnt](https://github.com/asnt)) - Refactor MeshVisual indexing for easier and more flexible filter creation [\#1462](https://github.com/vispy/vispy/pull/1462) ([asnt](https://github.com/asnt)) - Changed vispy.plot.Fig.\_\_init\_\_\(\) to allow passing 'keys' argument [\#1449](https://github.com/vispy/vispy/pull/1449) ([jimofthecorn](https://github.com/jimofthecorn)) - Add TextureFilter for adding textures to MeshVisuals [\#1444](https://github.com/vispy/vispy/pull/1444) ([asnt](https://github.com/asnt)) **Fixed bugs:** - Fix VolumeVisual artifacts with mip/minip if nothing was found [\#2115](https://github.com/vispy/vispy/pull/2115) ([brisvag](https://github.com/brisvag)) - Fix inconsistent picking behavior regarding depth testing [\#2110](https://github.com/vispy/vispy/pull/2110) ([djhoese](https://github.com/djhoese)) - Fix grid solver not updating variables when height/width changed [\#2100](https://github.com/vispy/vispy/pull/2100) ([djhoese](https://github.com/djhoese)) - Fix alpha handling in 'translucent' Visuals and add 'alpha' keyword argument to Canvas.render [\#2090](https://github.com/vispy/vispy/pull/2090) ([djhoese](https://github.com/djhoese)) - Fix PanZoomCamera 'center' property not updating view [\#2079](https://github.com/vispy/vispy/pull/2079) ([djhoese](https://github.com/djhoese)) - Fix VolumeVisual bounds representing the wrong axis [\#2070](https://github.com/vispy/vispy/pull/2070) ([djhoese](https://github.com/djhoese)) - make agg lines write correct depth value [\#2063](https://github.com/vispy/vispy/pull/2063) ([almarklein](https://github.com/almarklein)) - Fix glfw not sizing visuals correctly on initial draw [\#2059](https://github.com/vispy/vispy/pull/2059) ([djhoese](https://github.com/djhoese)) - Dont force selection of gl2 backend [\#2058](https://github.com/vispy/vispy/pull/2058) ([almarklein](https://github.com/almarklein)) - Fix typo in double shader [\#2051](https://github.com/vispy/vispy/pull/2051) ([theGiallo](https://github.com/theGiallo)) - Fix changing mesh shading mode when initially None [\#2042](https://github.com/vispy/vispy/pull/2042) ([asnt](https://github.com/asnt)) - Prevent translucent window with QOpenGLWidget [\#2040](https://github.com/vispy/vispy/pull/2040) ([asnt](https://github.com/asnt)) - Fix various issues with shading in the MeshVisual [\#2028](https://github.com/vispy/vispy/pull/2028) ([asnt](https://github.com/asnt)) - Fix face normal in cube geometry \(create\_cube function\) [\#2027](https://github.com/vispy/vispy/pull/2027) ([asnt](https://github.com/asnt)) - Fix axis labeling issue when flipped [\#2022](https://github.com/vispy/vispy/pull/2022) ([Kusefiru](https://github.com/Kusefiru)) - Fix TextVisual producing log message about unused uniform [\#2004](https://github.com/vispy/vispy/pull/2004) ([djhoese](https://github.com/djhoese)) - Add workaround for MacOS dlopen [\#1975](https://github.com/vispy/vispy/pull/1975) ([rayg-ssec](https://github.com/rayg-ssec)) - Fix data type issue in create\_sphere\(\) [\#1956](https://github.com/vispy/vispy/pull/1956) ([desteemy](https://github.com/desteemy)) - Fix ImportError on Python 3.9 [\#1914](https://github.com/vispy/vispy/pull/1914) ([cgohlke](https://github.com/cgohlke)) - Fix ImageVisual not updating color transform after texture update [\#1911](https://github.com/vispy/vispy/pull/1911) ([tlambert03](https://github.com/tlambert03)) - Fix OpenGL to ctypes type mapping [\#1883](https://github.com/vispy/vispy/pull/1883) ([cgohlke](https://github.com/cgohlke)) - Fix ImageVisual updating vertex coordinates on every draw [\#1853](https://github.com/vispy/vispy/pull/1853) ([djhoese](https://github.com/djhoese)) - Fix GitHub raw file download base URL [\#1821](https://github.com/vispy/vispy/pull/1821) ([djhoese](https://github.com/djhoese)) - Fix LinePlotVisual not remembering styles [\#1807](https://github.com/vispy/vispy/pull/1807) ([tlambert03](https://github.com/tlambert03)) - Fix grid\_widget when Fig gets a single element [\#1101](https://github.com/vispy/vispy/pull/1101) ([gouarin](https://github.com/gouarin)) **Merged pull requests:** - Replace CI environment variable checks with constants [\#2119](https://github.com/vispy/vispy/pull/2119) ([djhoese](https://github.com/djhoese)) - Refactor ImageVisual for easier subclassing [\#2105](https://github.com/vispy/vispy/pull/2105) ([djhoese](https://github.com/djhoese)) - Restore ambient light behavior as in v0.6.0 [\#2088](https://github.com/vispy/vispy/pull/2088) ([asnt](https://github.com/asnt)) - Document OpenGL state presets [\#2084](https://github.com/vispy/vispy/pull/2084) ([asnt](https://github.com/asnt)) - Add Code of Conduct [\#2076](https://github.com/vispy/vispy/pull/2076) ([djhoese](https://github.com/djhoese)) - Rename all HUSL usages to HSLuv [\#2061](https://github.com/vispy/vispy/pull/2061) ([djhoese](https://github.com/djhoese)) - Remove unused py2/3 compatibility module [\#2060](https://github.com/vispy/vispy/pull/2060) ([djhoese](https://github.com/djhoese)) - Fix dead link in shader docstring [\#2050](https://github.com/vispy/vispy/pull/2050) ([theGiallo](https://github.com/theGiallo)) - Fix website deploy commit message references [\#2038](https://github.com/vispy/vispy/pull/2038) ([djhoese](https://github.com/djhoese)) - Fix URL to contributor guide [\#2032](https://github.com/vispy/vispy/pull/2032) ([asnt](https://github.com/asnt)) - Fixed multiple code style issues. [\#1983](https://github.com/vispy/vispy/pull/1983) ([Aaru143](https://github.com/Aaru143)) - Removed Python 2.7 wrapper on GzipFile [\#1943](https://github.com/vispy/vispy/pull/1943) ([irajasyed](https://github.com/irajasyed)) - Replaced bundled 'pypng' \(png\) dependency with pillow [\#1934](https://github.com/vispy/vispy/pull/1934) ([Kartik-byte](https://github.com/Kartik-byte)) - Remove six dependency [\#1933](https://github.com/vispy/vispy/pull/1933) ([sgaist](https://github.com/sgaist)) - Fix typo in examples/basics/gloo/hello\_fbo.py [\#1930](https://github.com/vispy/vispy/pull/1930) ([BioGeek](https://github.com/BioGeek)) - Fix EGL docstring copy/paste error [\#1921](https://github.com/vispy/vispy/pull/1921) ([BioGeek](https://github.com/BioGeek)) - Fix various numpy deprecation warnings [\#1913](https://github.com/vispy/vispy/pull/1913) ([GuillaumeFavelier](https://github.com/GuillaumeFavelier)) - Fix styling issues due to new flake8 version [\#1864](https://github.com/vispy/vispy/pull/1864) ([djhoese](https://github.com/djhoese)) - Update vispy.js submodule to 0.3.0 [\#1837](https://github.com/vispy/vispy/pull/1837) ([djhoese](https://github.com/djhoese)) - Fix DataBuffer.set\_subdata docstring with wrong offset units [\#1825](https://github.com/vispy/vispy/pull/1825) ([asnt](https://github.com/asnt)) - DOC: Drop installation instructions from readme [\#1806](https://github.com/vispy/vispy/pull/1806) ([hoechenberger](https://github.com/hoechenberger)) - Add new example for mouse editing/drawing of shapes [\#1480](https://github.com/vispy/vispy/pull/1480) ([fschill](https://github.com/fschill)) ## [v0.6.5](https://github.com/vispy/vispy/tree/v0.6.5) (2020-09-23) - Patch release to create Python 3.8 wheels and future proof pyproject.toml ## [v0.6.4](https://github.com/vispy/vispy/tree/v0.6.4) (2019-12-13) **Enhancements:** - Filter unnecessary QSocketNotifier warning when using QtConsole [\#1789](https://github.com/vispy/vispy/pull/1789) ([hmaarrfk](https://github.com/hmaarrfk)) - FIX: Nest triangle and skimage imports [\#1781](https://github.com/vispy/vispy/pull/1781) ([larsoner](https://github.com/larsoner)) - Switch to setuptools\_scm version.py usage to avoid import overhead [\#1780](https://github.com/vispy/vispy/pull/1780) ([djhoese](https://github.com/djhoese)) **Fixed bugs:** - Skip bad font in test\_font [\#1772](https://github.com/vispy/vispy/pull/1772) ([hmaarrfk](https://github.com/hmaarrfk)) ## [v0.6.3](https://github.com/vispy/vispy/tree/v0.6.3) (2019-11-27) **Enhancements:** - Improve AxisVisual visual by providing property getters and setters [\#1744](https://github.com/vispy/vispy/pull/1744) ([astrofrog](https://github.com/astrofrog)) - Fix MarkerVisual scaling when rotating in 3D space [\#1702](https://github.com/vispy/vispy/pull/1702) ([sofroniewn](https://github.com/sofroniewn)) **Fixed bugs:** - Fix string formatting of array shape [\#1768](https://github.com/vispy/vispy/pull/1768) ([cgohlke](https://github.com/cgohlke)) - Fix texture alignment to use data itemsize [\#1758](https://github.com/vispy/vispy/pull/1758) ([djhoese](https://github.com/djhoese)) - Fix shader version handling in webgl backend [\#1756](https://github.com/vispy/vispy/pull/1756) ([djhoese](https://github.com/djhoese)) - Fix 2D mesh bounds IndexError when viewed in 3D [\#1749](https://github.com/vispy/vispy/pull/1749) ([sofroniewn](https://github.com/sofroniewn)) - Fix xaxis labels being wrong initially in PlotWidget [\#1748](https://github.com/vispy/vispy/pull/1748) ([tlambert03](https://github.com/tlambert03)) - Various bug fixes related to AxisVisual [\#1743](https://github.com/vispy/vispy/pull/1743) ([astrofrog](https://github.com/astrofrog)) - Fixed a bug in linking two flycameras [\#1557](https://github.com/vispy/vispy/pull/1557) ([SuyiWang](https://github.com/SuyiWang)) **Merged pull requests:** - Fix additional numpy warnings when dealing with dtypes of size 1 [\#1766](https://github.com/vispy/vispy/pull/1766) ([djhoese](https://github.com/djhoese)) ## [v0.6.2](https://github.com/vispy/vispy/tree/v0.6.2) (2019-11-04) **Enhancements:** - Switch to setuptools\_scm for automatic version numbering [\#1706](https://github.com/vispy/vispy/pull/1706) ([djhoese](https://github.com/djhoese)) - Improve PanZoom camera performance when non-+z direction is used [\#1682](https://github.com/vispy/vispy/pull/1682) ([os-gabe](https://github.com/os-gabe)) **Fixed bugs:** - Fix Python 3.8 compatibility in Canvas 'keys' update [\#1730](https://github.com/vispy/vispy/pull/1730) ([GuillaumeFavelier](https://github.com/GuillaumeFavelier)) - Fix VolumeVisual modifying user provided data in-place [\#1728](https://github.com/vispy/vispy/pull/1728) ([tlambert03](https://github.com/tlambert03)) - Fix depth buffer precision [\#1724](https://github.com/vispy/vispy/pull/1724) ([h3fang](https://github.com/h3fang)) - Volume visual has unset variable texture2D\_LUT [\#1712](https://github.com/vispy/vispy/pull/1712) ([liuyenting](https://github.com/liuyenting)) - Fix MarkersVisual.set\_data crash when pos is None [\#1703](https://github.com/vispy/vispy/pull/1703) ([proto3](https://github.com/proto3)) - Fix numpy futurewarning in dtype creation [\#1691](https://github.com/vispy/vispy/pull/1691) ([djhoese](https://github.com/djhoese)) ## v0.6.1 - Fix discrete colormap ordering (#1668) - Fix various examples (#1671, #1676) - Fix Jupyter extension zoom direction (#1679) ## v0.6.0 - Update PyQt5/PySide2 to use newer GL API - Update to PyQt5 as default backend - New Cython-based text rendering option - New WindbarbVisual - Improved JupyterLab/Notebook widget (experimental) - Fix various memory leaks - Various optimizations and bug fixes ## v0.5.3 - Workaround added to fix ImportError with matplotlib 2.2+ (#1437) ## v0.5.2 - Fix PyPI packaging to include LICENSE.txt - Fix initial axis limits in PlotWidget (#1386) - Fix zoom event position in Pyglet backend (#1388) - Fix camera importing (#1389, #1172) - Refactor `EllipseVisual` and `RectangleVisual` (#1387, #1349) - Fix `one_scene_four_cams.py` example (#1391, #1124) - Add `two_qt_widgets.py` example (#1392, #1298) - Fix order of alignment values for proper processing (#1395, #641) ## v0.5.1 - Fix 'doc' directory being installed with source tarball - Fix 'ArrowVisual' when used with a Scene camera and in 3D space - Fix 'SphereVisual' rows/cols order in 'latitude' method - Fix DPI calculation on linux when xrandr returns 0mm screen dimension ## v0.5.0 - Major refactor of all cameras and visuals - Add support for wxPython 4.0+ (project phoenix) - Improve Jupyter Notebook support (not full support) - Improve Python 3 support - Add colormaps - Add various new visuals `GridMesh`, `BoxVisual`, `PlaneVisual`, etc. - Various bug fixes and performance improvements (177+ pull requests) - Remove experimental matplotlib backend (`mpl_plot`) - Drop Python 2.6 support ## v0.4.0 There have been many changes, which include: - minor tweaks and bugfixes to gloo - experimental support for "collections" (batched GL draw calls) - many new Visuals (Volume, Isocurve, etc.) - improvements and extensions of the SceneGraph system - proper HiDPI support - an experimental native high-level plotting interface vispy.plot ## v0.3.0 Many changes: - Added multiple new application backends, including a IPython browser backend. - Experimental support for high-level visualizations through '`vispy.scene`` and ``vispy.scene.visuals``. - Experimental support for matplotlib plotting through ``vispy.mpl_plot``. - Loads of bugfixes. ## v0.2.1 Small fix in the setup script. The buf prevented pip from working. ## v0.2.0 In this release we focussed on improving and finalizing the object oriented OpenGL interface ``vispy.gloo``. Some major (backward incompatible) changes were done. However, from this release we consider the ``vispy.gloo`` package relatively stable and we try to minimize backward incompatibilities. Changes in more detail: - ``vispy.oogl`` is renamed to ``vispy.gloo`` - ``vispy.gl`` is moved to ``vispy.gloo.gl`` since most users will use gloo to interface with OpenGL. - Improved (and thus changed) several parts of the gloo API. - Some parts of gloo were refactored and should be more robust. - Much better coverage of the test suite. - Compatibility with Python 2.6 (Jerome Kieffer) - More examples and a gallery on the website to show them off. ## v0.1.0 First release. We have an initial version of the object oriented interface to OpenGL, called `vispy.oogl`. \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* vispy-0.14.1/.gitignore0000644000175000017510000000056714507104216014276 0ustar nileshnilesh*.c *.so *.pyc *.pyo *.DS_Store doc/_build /_website /_gh-pages /_images /_demo-data .idea build dist MANIFEST .vispy-coverage* .coverage* !.coveragerc htmlcov vispy.egg-info *.swp .ipynb_checkpoints .cache/ .eggs/ examples/basics/scene/animation.gif .pytest_cache/ js/node_modules js/dist node_modules/ vispy/static/ vispy/version.py .vscode vispy/visuals/text/_sdf_cpu*.pydvispy-0.14.1/.github_changelog_generator0000644000175000017510000000055614507104216017644 0ustar nileshnileshissues=false add_issues_wo_labels=false add_pr_wo_labels=false unreleased=true filter_issues_by_milestone=false compare_link=false enhancement_labels=type: enhancement,type: feature,type: performance bug_labels=type: bug exclude_labels=duplicate,question,invalid,wontfix,type: question,build enhancement_prefix=**Enhancements:** unreleased_only=true max_issues=1000 vispy-0.14.1/.gitattributes0000644000175000017510000000003714507104216015171 0ustar nileshnilesh.git_archival.txt export-substvispy-0.14.1/.git_archival.txt0000644000175000017510000000017414507104216015553 0ustar nileshnileshnode: $Format:%H$ node-date: $Format:%cI$ describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$ ref-names: $Format:%D$vispy-0.14.1/.coveragerc0000644000175000017510000000070314507104216014417 0ustar nileshnilesh[run] relative_files = True branch = True source = vispy #include = */vispy/* # Omit fonts/_quartz.py and _win32 for platform specificity omit = */vispy/app/backends/_egl.py */vispy/gloo/gl/es2.py */vispy/gloo/gl/_es2.py */vispy/testing/* */vispy/ext/* */vispy/geometry/_triangulation_debugger.py */vispy/util/fonts/_quartz.py */vispy/util/fonts/_win32.py */vispy/util/dpi/_quartz.py */vispy/util/dpi/_win32.py