vispy 0.15.0__cp313-cp313-macosx_10_13_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of vispy might be problematic. Click here for more details.
- vispy/__init__.py +33 -0
- vispy/app/__init__.py +15 -0
- vispy/app/_default_app.py +76 -0
- vispy/app/_detect_eventloop.py +148 -0
- vispy/app/application.py +263 -0
- vispy/app/backends/__init__.py +52 -0
- vispy/app/backends/_egl.py +264 -0
- vispy/app/backends/_glfw.py +513 -0
- vispy/app/backends/_jupyter_rfb.py +278 -0
- vispy/app/backends/_offscreen_util.py +121 -0
- vispy/app/backends/_osmesa.py +235 -0
- vispy/app/backends/_pyglet.py +451 -0
- vispy/app/backends/_pyqt4.py +36 -0
- vispy/app/backends/_pyqt5.py +36 -0
- vispy/app/backends/_pyqt6.py +40 -0
- vispy/app/backends/_pyside.py +37 -0
- vispy/app/backends/_pyside2.py +52 -0
- vispy/app/backends/_pyside6.py +53 -0
- vispy/app/backends/_qt.py +1003 -0
- vispy/app/backends/_sdl2.py +444 -0
- vispy/app/backends/_template.py +244 -0
- vispy/app/backends/_test.py +8 -0
- vispy/app/backends/_tk.py +800 -0
- vispy/app/backends/_wx.py +476 -0
- vispy/app/backends/tests/__init__.py +0 -0
- vispy/app/backends/tests/test_offscreen_util.py +52 -0
- vispy/app/backends/tests/test_rfb.py +77 -0
- vispy/app/base.py +294 -0
- vispy/app/canvas.py +828 -0
- vispy/app/qt.py +92 -0
- vispy/app/tests/__init__.py +0 -0
- vispy/app/tests/qt-designer.ui +58 -0
- vispy/app/tests/test_app.py +442 -0
- vispy/app/tests/test_backends.py +164 -0
- vispy/app/tests/test_canvas.py +122 -0
- vispy/app/tests/test_context.py +92 -0
- vispy/app/tests/test_qt.py +47 -0
- vispy/app/tests/test_simultaneous.py +134 -0
- vispy/app/timer.py +174 -0
- vispy/color/__init__.py +17 -0
- vispy/color/_color_dict.py +193 -0
- vispy/color/color_array.py +447 -0
- vispy/color/color_space.py +181 -0
- vispy/color/colormap.py +1213 -0
- vispy/color/tests/__init__.py +0 -0
- vispy/color/tests/test_color.py +378 -0
- vispy/conftest.py +12 -0
- vispy/ext/__init__.py +0 -0
- vispy/ext/cocoapy.py +1522 -0
- vispy/ext/cubehelix.py +138 -0
- vispy/ext/egl.py +375 -0
- vispy/ext/fontconfig.py +118 -0
- vispy/ext/gdi32plus.py +206 -0
- vispy/ext/osmesa.py +105 -0
- vispy/geometry/__init__.py +23 -0
- vispy/geometry/_triangulation_debugger.py +171 -0
- vispy/geometry/calculations.py +162 -0
- vispy/geometry/curves.py +399 -0
- vispy/geometry/generation.py +643 -0
- vispy/geometry/isocurve.py +175 -0
- vispy/geometry/isosurface.py +465 -0
- vispy/geometry/meshdata.py +700 -0
- vispy/geometry/normals.py +78 -0
- vispy/geometry/parametric.py +56 -0
- vispy/geometry/polygon.py +137 -0
- vispy/geometry/rect.py +210 -0
- vispy/geometry/tests/__init__.py +0 -0
- vispy/geometry/tests/test_calculations.py +23 -0
- vispy/geometry/tests/test_generation.py +56 -0
- vispy/geometry/tests/test_meshdata.py +106 -0
- vispy/geometry/tests/test_triangulation.py +594 -0
- vispy/geometry/torusknot.py +142 -0
- vispy/geometry/triangulation.py +876 -0
- vispy/gloo/__init__.py +56 -0
- vispy/gloo/buffer.py +505 -0
- vispy/gloo/context.py +272 -0
- vispy/gloo/framebuffer.py +257 -0
- vispy/gloo/gl/__init__.py +234 -0
- vispy/gloo/gl/_constants.py +332 -0
- vispy/gloo/gl/_es2.py +986 -0
- vispy/gloo/gl/_gl2.py +1365 -0
- vispy/gloo/gl/_proxy.py +499 -0
- vispy/gloo/gl/_pyopengl2.py +362 -0
- vispy/gloo/gl/dummy.py +24 -0
- vispy/gloo/gl/es2.py +62 -0
- vispy/gloo/gl/gl2.py +98 -0
- vispy/gloo/gl/glplus.py +168 -0
- vispy/gloo/gl/pyopengl2.py +97 -0
- vispy/gloo/gl/tests/__init__.py +0 -0
- vispy/gloo/gl/tests/test_basics.py +282 -0
- vispy/gloo/gl/tests/test_functionality.py +568 -0
- vispy/gloo/gl/tests/test_names.py +246 -0
- vispy/gloo/gl/tests/test_use.py +71 -0
- vispy/gloo/glir.py +1824 -0
- vispy/gloo/globject.py +101 -0
- vispy/gloo/preprocessor.py +67 -0
- vispy/gloo/program.py +543 -0
- vispy/gloo/tests/__init__.py +0 -0
- vispy/gloo/tests/test_buffer.py +558 -0
- vispy/gloo/tests/test_context.py +119 -0
- vispy/gloo/tests/test_framebuffer.py +195 -0
- vispy/gloo/tests/test_glir.py +307 -0
- vispy/gloo/tests/test_globject.py +35 -0
- vispy/gloo/tests/test_program.py +302 -0
- vispy/gloo/tests/test_texture.py +732 -0
- vispy/gloo/tests/test_use_gloo.py +187 -0
- vispy/gloo/tests/test_util.py +60 -0
- vispy/gloo/tests/test_wrappers.py +261 -0
- vispy/gloo/texture.py +1046 -0
- vispy/gloo/util.py +129 -0
- vispy/gloo/wrappers.py +762 -0
- vispy/glsl/__init__.py +42 -0
- vispy/glsl/antialias/antialias.glsl +7 -0
- vispy/glsl/antialias/cap-butt.glsl +31 -0
- vispy/glsl/antialias/cap-round.glsl +29 -0
- vispy/glsl/antialias/cap-square.glsl +30 -0
- vispy/glsl/antialias/cap-triangle-in.glsl +30 -0
- vispy/glsl/antialias/cap-triangle-out.glsl +30 -0
- vispy/glsl/antialias/cap.glsl +67 -0
- vispy/glsl/antialias/caps.glsl +67 -0
- vispy/glsl/antialias/filled.glsl +50 -0
- vispy/glsl/antialias/outline.glsl +40 -0
- vispy/glsl/antialias/stroke.glsl +43 -0
- vispy/glsl/arrowheads/angle.glsl +99 -0
- vispy/glsl/arrowheads/arrowheads.frag +60 -0
- vispy/glsl/arrowheads/arrowheads.glsl +12 -0
- vispy/glsl/arrowheads/arrowheads.vert +83 -0
- vispy/glsl/arrowheads/curved.glsl +48 -0
- vispy/glsl/arrowheads/inhibitor.glsl +26 -0
- vispy/glsl/arrowheads/stealth.glsl +46 -0
- vispy/glsl/arrowheads/triangle.glsl +97 -0
- vispy/glsl/arrowheads/util.glsl +13 -0
- vispy/glsl/arrows/angle-30.glsl +12 -0
- vispy/glsl/arrows/angle-60.glsl +12 -0
- vispy/glsl/arrows/angle-90.glsl +12 -0
- vispy/glsl/arrows/arrow.frag +39 -0
- vispy/glsl/arrows/arrow.vert +49 -0
- vispy/glsl/arrows/arrows.glsl +17 -0
- vispy/glsl/arrows/common.glsl +187 -0
- vispy/glsl/arrows/curved.glsl +63 -0
- vispy/glsl/arrows/stealth.glsl +50 -0
- vispy/glsl/arrows/triangle-30.glsl +12 -0
- vispy/glsl/arrows/triangle-60.glsl +12 -0
- vispy/glsl/arrows/triangle-90.glsl +12 -0
- vispy/glsl/arrows/util.glsl +98 -0
- vispy/glsl/build_spatial_filters.py +660 -0
- vispy/glsl/collections/agg-fast-path.frag +20 -0
- vispy/glsl/collections/agg-fast-path.vert +78 -0
- vispy/glsl/collections/agg-glyph.frag +60 -0
- vispy/glsl/collections/agg-glyph.vert +33 -0
- vispy/glsl/collections/agg-marker.frag +35 -0
- vispy/glsl/collections/agg-marker.vert +48 -0
- vispy/glsl/collections/agg-path.frag +55 -0
- vispy/glsl/collections/agg-path.vert +166 -0
- vispy/glsl/collections/agg-point.frag +21 -0
- vispy/glsl/collections/agg-point.vert +35 -0
- vispy/glsl/collections/agg-segment.frag +32 -0
- vispy/glsl/collections/agg-segment.vert +75 -0
- vispy/glsl/collections/marker.frag +38 -0
- vispy/glsl/collections/marker.vert +48 -0
- vispy/glsl/collections/raw-path.frag +15 -0
- vispy/glsl/collections/raw-path.vert +24 -0
- vispy/glsl/collections/raw-point.frag +14 -0
- vispy/glsl/collections/raw-point.vert +31 -0
- vispy/glsl/collections/raw-segment.frag +18 -0
- vispy/glsl/collections/raw-segment.vert +26 -0
- vispy/glsl/collections/raw-triangle.frag +13 -0
- vispy/glsl/collections/raw-triangle.vert +26 -0
- vispy/glsl/collections/sdf-glyph-ticks.vert +69 -0
- vispy/glsl/collections/sdf-glyph.frag +80 -0
- vispy/glsl/collections/sdf-glyph.vert +59 -0
- vispy/glsl/collections/tick-labels.vert +71 -0
- vispy/glsl/colormaps/autumn.glsl +20 -0
- vispy/glsl/colormaps/blues.glsl +20 -0
- vispy/glsl/colormaps/color-space.glsl +17 -0
- vispy/glsl/colormaps/colormaps.glsl +24 -0
- vispy/glsl/colormaps/cool.glsl +20 -0
- vispy/glsl/colormaps/fire.glsl +21 -0
- vispy/glsl/colormaps/gray.glsl +20 -0
- vispy/glsl/colormaps/greens.glsl +20 -0
- vispy/glsl/colormaps/hot.glsl +22 -0
- vispy/glsl/colormaps/ice.glsl +20 -0
- vispy/glsl/colormaps/icefire.glsl +23 -0
- vispy/glsl/colormaps/parse.py +40 -0
- vispy/glsl/colormaps/reds.glsl +20 -0
- vispy/glsl/colormaps/spring.glsl +20 -0
- vispy/glsl/colormaps/summer.glsl +20 -0
- vispy/glsl/colormaps/user.glsl +22 -0
- vispy/glsl/colormaps/util.glsl +41 -0
- vispy/glsl/colormaps/wheel.glsl +21 -0
- vispy/glsl/colormaps/winter.glsl +20 -0
- vispy/glsl/lines/agg.frag +320 -0
- vispy/glsl/lines/agg.vert +241 -0
- vispy/glsl/markers/arrow.glsl +12 -0
- vispy/glsl/markers/asterisk.glsl +16 -0
- vispy/glsl/markers/chevron.glsl +14 -0
- vispy/glsl/markers/clover.glsl +20 -0
- vispy/glsl/markers/club.glsl +31 -0
- vispy/glsl/markers/cross.glsl +17 -0
- vispy/glsl/markers/diamond.glsl +12 -0
- vispy/glsl/markers/disc.glsl +9 -0
- vispy/glsl/markers/ellipse.glsl +67 -0
- vispy/glsl/markers/hbar.glsl +9 -0
- vispy/glsl/markers/heart.glsl +15 -0
- vispy/glsl/markers/infinity.glsl +15 -0
- vispy/glsl/markers/marker-sdf.frag +74 -0
- vispy/glsl/markers/marker-sdf.vert +41 -0
- vispy/glsl/markers/marker.frag +36 -0
- vispy/glsl/markers/marker.vert +46 -0
- vispy/glsl/markers/markers.glsl +24 -0
- vispy/glsl/markers/pin.glsl +18 -0
- vispy/glsl/markers/ring.glsl +11 -0
- vispy/glsl/markers/spade.glsl +28 -0
- vispy/glsl/markers/square.glsl +10 -0
- vispy/glsl/markers/tag.glsl +11 -0
- vispy/glsl/markers/triangle.glsl +14 -0
- vispy/glsl/markers/vbar.glsl +9 -0
- vispy/glsl/math/circle-through-2-points.glsl +30 -0
- vispy/glsl/math/constants.glsl +48 -0
- vispy/glsl/math/double.glsl +114 -0
- vispy/glsl/math/functions.glsl +20 -0
- vispy/glsl/math/point-to-line-distance.glsl +31 -0
- vispy/glsl/math/point-to-line-projection.glsl +29 -0
- vispy/glsl/math/signed-line-distance.glsl +27 -0
- vispy/glsl/math/signed-segment-distance.glsl +30 -0
- vispy/glsl/misc/regular-grid.frag +244 -0
- vispy/glsl/misc/spatial-filters.frag +1407 -0
- vispy/glsl/misc/viewport-NDC.glsl +20 -0
- vispy/glsl/transforms/azimuthal-equal-area.glsl +32 -0
- vispy/glsl/transforms/azimuthal-equidistant.glsl +38 -0
- vispy/glsl/transforms/hammer.glsl +44 -0
- vispy/glsl/transforms/identity.glsl +6 -0
- vispy/glsl/transforms/identity_forward.glsl +23 -0
- vispy/glsl/transforms/identity_inverse.glsl +23 -0
- vispy/glsl/transforms/linear-scale.glsl +127 -0
- vispy/glsl/transforms/log-scale.glsl +126 -0
- vispy/glsl/transforms/mercator-transverse-forward.glsl +40 -0
- vispy/glsl/transforms/mercator-transverse-inverse.glsl +40 -0
- vispy/glsl/transforms/panzoom.glsl +10 -0
- vispy/glsl/transforms/polar.glsl +41 -0
- vispy/glsl/transforms/position.glsl +44 -0
- vispy/glsl/transforms/power-scale.glsl +139 -0
- vispy/glsl/transforms/projection.glsl +7 -0
- vispy/glsl/transforms/pvm.glsl +13 -0
- vispy/glsl/transforms/rotate.glsl +45 -0
- vispy/glsl/transforms/trackball.glsl +15 -0
- vispy/glsl/transforms/translate.glsl +35 -0
- vispy/glsl/transforms/transverse_mercator.glsl +38 -0
- vispy/glsl/transforms/viewport-clipping.glsl +14 -0
- vispy/glsl/transforms/viewport-transform.glsl +16 -0
- vispy/glsl/transforms/viewport.glsl +50 -0
- vispy/glsl/transforms/x.glsl +24 -0
- vispy/glsl/transforms/y.glsl +19 -0
- vispy/glsl/transforms/z.glsl +14 -0
- vispy/io/__init__.py +20 -0
- vispy/io/_data/spatial-filters.npy +0 -0
- vispy/io/datasets.py +94 -0
- vispy/io/image.py +231 -0
- vispy/io/mesh.py +122 -0
- vispy/io/stl.py +167 -0
- vispy/io/tests/__init__.py +0 -0
- vispy/io/tests/test_image.py +47 -0
- vispy/io/tests/test_io.py +121 -0
- vispy/io/wavefront.py +350 -0
- vispy/plot/__init__.py +36 -0
- vispy/plot/fig.py +58 -0
- vispy/plot/plotwidget.py +522 -0
- vispy/plot/tests/__init__.py +0 -0
- vispy/plot/tests/test_plot.py +46 -0
- vispy/scene/__init__.py +43 -0
- vispy/scene/cameras/__init__.py +27 -0
- vispy/scene/cameras/_base.py +38 -0
- vispy/scene/cameras/arcball.py +105 -0
- vispy/scene/cameras/base_camera.py +551 -0
- vispy/scene/cameras/fly.py +474 -0
- vispy/scene/cameras/magnify.py +163 -0
- vispy/scene/cameras/panzoom.py +311 -0
- vispy/scene/cameras/perspective.py +338 -0
- vispy/scene/cameras/tests/__init__.py +0 -0
- vispy/scene/cameras/tests/test_cameras.py +27 -0
- vispy/scene/cameras/tests/test_link.py +53 -0
- vispy/scene/cameras/tests/test_perspective.py +122 -0
- vispy/scene/cameras/turntable.py +183 -0
- vispy/scene/canvas.py +639 -0
- vispy/scene/events.py +85 -0
- vispy/scene/node.py +644 -0
- vispy/scene/subscene.py +20 -0
- vispy/scene/tests/__init__.py +0 -0
- vispy/scene/tests/test_canvas.py +119 -0
- vispy/scene/tests/test_node.py +142 -0
- vispy/scene/tests/test_visuals.py +141 -0
- vispy/scene/visuals.py +276 -0
- vispy/scene/widgets/__init__.py +18 -0
- vispy/scene/widgets/anchor.py +25 -0
- vispy/scene/widgets/axis.py +88 -0
- vispy/scene/widgets/colorbar.py +176 -0
- vispy/scene/widgets/console.py +351 -0
- vispy/scene/widgets/grid.py +509 -0
- vispy/scene/widgets/label.py +50 -0
- vispy/scene/widgets/tests/__init__.py +0 -0
- vispy/scene/widgets/tests/test_colorbar.py +47 -0
- vispy/scene/widgets/viewbox.py +199 -0
- vispy/scene/widgets/widget.py +478 -0
- vispy/testing/__init__.py +51 -0
- vispy/testing/_runners.py +448 -0
- vispy/testing/_testing.py +416 -0
- vispy/testing/image_tester.py +494 -0
- vispy/testing/rendered_array_tester.py +85 -0
- vispy/testing/tests/__init__.py +0 -0
- vispy/testing/tests/test_testing.py +20 -0
- vispy/util/__init__.py +32 -0
- vispy/util/bunch.py +15 -0
- vispy/util/check_environment.py +57 -0
- vispy/util/config.py +490 -0
- vispy/util/dpi/__init__.py +19 -0
- vispy/util/dpi/_linux.py +69 -0
- vispy/util/dpi/_quartz.py +26 -0
- vispy/util/dpi/_win32.py +34 -0
- vispy/util/dpi/tests/__init__.py +0 -0
- vispy/util/dpi/tests/test_dpi.py +16 -0
- vispy/util/eq.py +41 -0
- vispy/util/event.py +774 -0
- vispy/util/fetching.py +276 -0
- vispy/util/filter.py +44 -0
- vispy/util/fonts/__init__.py +14 -0
- vispy/util/fonts/_freetype.py +73 -0
- vispy/util/fonts/_quartz.py +192 -0
- vispy/util/fonts/_triage.py +36 -0
- vispy/util/fonts/_vispy_fonts.py +20 -0
- vispy/util/fonts/_win32.py +105 -0
- vispy/util/fonts/data/OpenSans-Bold.ttf +0 -0
- vispy/util/fonts/data/OpenSans-BoldItalic.ttf +0 -0
- vispy/util/fonts/data/OpenSans-Italic.ttf +0 -0
- vispy/util/fonts/data/OpenSans-Regular.ttf +0 -0
- vispy/util/fonts/tests/__init__.py +0 -0
- vispy/util/fonts/tests/test_font.py +45 -0
- vispy/util/fourier.py +69 -0
- vispy/util/frozen.py +25 -0
- vispy/util/gallery_scraper.py +268 -0
- vispy/util/keys.py +91 -0
- vispy/util/logs.py +358 -0
- vispy/util/osmesa_gl.py +17 -0
- vispy/util/profiler.py +135 -0
- vispy/util/ptime.py +16 -0
- vispy/util/quaternion.py +229 -0
- vispy/util/svg/__init__.py +18 -0
- vispy/util/svg/base.py +20 -0
- vispy/util/svg/color.py +219 -0
- vispy/util/svg/element.py +51 -0
- vispy/util/svg/geometry.py +478 -0
- vispy/util/svg/group.py +66 -0
- vispy/util/svg/length.py +81 -0
- vispy/util/svg/number.py +25 -0
- vispy/util/svg/path.py +332 -0
- vispy/util/svg/shapes.py +57 -0
- vispy/util/svg/style.py +59 -0
- vispy/util/svg/svg.py +40 -0
- vispy/util/svg/transform.py +223 -0
- vispy/util/svg/transformable.py +28 -0
- vispy/util/svg/viewport.py +73 -0
- vispy/util/tests/__init__.py +0 -0
- vispy/util/tests/test_config.py +58 -0
- vispy/util/tests/test_docstring_parameters.py +123 -0
- vispy/util/tests/test_emitter_group.py +262 -0
- vispy/util/tests/test_event_emitter.py +743 -0
- vispy/util/tests/test_fourier.py +35 -0
- vispy/util/tests/test_gallery_scraper.py +112 -0
- vispy/util/tests/test_import.py +127 -0
- vispy/util/tests/test_key.py +22 -0
- vispy/util/tests/test_logging.py +45 -0
- vispy/util/tests/test_run.py +14 -0
- vispy/util/tests/test_transforms.py +42 -0
- vispy/util/tests/test_vispy.py +48 -0
- vispy/util/transforms.py +201 -0
- vispy/util/wrappers.py +155 -0
- vispy/version.py +21 -0
- vispy/visuals/__init__.py +50 -0
- vispy/visuals/_scalable_textures.py +487 -0
- vispy/visuals/axis.py +678 -0
- vispy/visuals/border.py +208 -0
- vispy/visuals/box.py +79 -0
- vispy/visuals/collections/__init__.py +30 -0
- vispy/visuals/collections/agg_fast_path_collection.py +219 -0
- vispy/visuals/collections/agg_path_collection.py +197 -0
- vispy/visuals/collections/agg_point_collection.py +52 -0
- vispy/visuals/collections/agg_segment_collection.py +142 -0
- vispy/visuals/collections/array_list.py +401 -0
- vispy/visuals/collections/base_collection.py +482 -0
- vispy/visuals/collections/collection.py +253 -0
- vispy/visuals/collections/path_collection.py +23 -0
- vispy/visuals/collections/point_collection.py +19 -0
- vispy/visuals/collections/polygon_collection.py +25 -0
- vispy/visuals/collections/raw_path_collection.py +119 -0
- vispy/visuals/collections/raw_point_collection.py +113 -0
- vispy/visuals/collections/raw_polygon_collection.py +77 -0
- vispy/visuals/collections/raw_segment_collection.py +112 -0
- vispy/visuals/collections/raw_triangle_collection.py +78 -0
- vispy/visuals/collections/segment_collection.py +19 -0
- vispy/visuals/collections/triangle_collection.py +16 -0
- vispy/visuals/collections/util.py +168 -0
- vispy/visuals/colorbar.py +699 -0
- vispy/visuals/cube.py +41 -0
- vispy/visuals/ellipse.py +162 -0
- vispy/visuals/filters/__init__.py +10 -0
- vispy/visuals/filters/base_filter.py +242 -0
- vispy/visuals/filters/clipper.py +60 -0
- vispy/visuals/filters/clipping_planes.py +122 -0
- vispy/visuals/filters/color.py +181 -0
- vispy/visuals/filters/markers.py +28 -0
- vispy/visuals/filters/mesh.py +801 -0
- vispy/visuals/filters/picking.py +60 -0
- vispy/visuals/filters/tests/__init__.py +3 -0
- vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
- vispy/visuals/filters/tests/test_wireframe_filter.py +16 -0
- vispy/visuals/glsl/__init__.py +1 -0
- vispy/visuals/glsl/antialiasing.py +133 -0
- vispy/visuals/glsl/color.py +63 -0
- vispy/visuals/graphs/__init__.py +1 -0
- vispy/visuals/graphs/graph.py +240 -0
- vispy/visuals/graphs/layouts/__init__.py +55 -0
- vispy/visuals/graphs/layouts/circular.py +49 -0
- vispy/visuals/graphs/layouts/force_directed.py +211 -0
- vispy/visuals/graphs/layouts/networkx_layout.py +87 -0
- vispy/visuals/graphs/layouts/random.py +52 -0
- vispy/visuals/graphs/tests/__init__.py +1 -0
- vispy/visuals/graphs/tests/test_layouts.py +139 -0
- vispy/visuals/graphs/tests/test_networkx_layout.py +47 -0
- vispy/visuals/graphs/util.py +120 -0
- vispy/visuals/gridlines.py +161 -0
- vispy/visuals/gridmesh.py +98 -0
- vispy/visuals/histogram.py +58 -0
- vispy/visuals/image.py +701 -0
- vispy/visuals/image_complex.py +130 -0
- vispy/visuals/infinite_line.py +199 -0
- vispy/visuals/instanced_mesh.py +152 -0
- vispy/visuals/isocurve.py +213 -0
- vispy/visuals/isoline.py +241 -0
- vispy/visuals/isosurface.py +113 -0
- vispy/visuals/line/__init__.py +6 -0
- vispy/visuals/line/arrow.py +289 -0
- vispy/visuals/line/dash_atlas.py +90 -0
- vispy/visuals/line/line.py +545 -0
- vispy/visuals/line_plot.py +135 -0
- vispy/visuals/linear_region.py +199 -0
- vispy/visuals/markers.py +819 -0
- vispy/visuals/mesh.py +373 -0
- vispy/visuals/mesh_normals.py +159 -0
- vispy/visuals/plane.py +54 -0
- vispy/visuals/polygon.py +145 -0
- vispy/visuals/rectangle.py +196 -0
- vispy/visuals/regular_polygon.py +56 -0
- vispy/visuals/scrolling_lines.py +197 -0
- vispy/visuals/shaders/__init__.py +17 -0
- vispy/visuals/shaders/compiler.py +206 -0
- vispy/visuals/shaders/expression.py +99 -0
- vispy/visuals/shaders/function.py +788 -0
- vispy/visuals/shaders/multiprogram.py +145 -0
- vispy/visuals/shaders/parsing.py +140 -0
- vispy/visuals/shaders/program.py +161 -0
- vispy/visuals/shaders/shader_object.py +162 -0
- vispy/visuals/shaders/tests/__init__.py +0 -0
- vispy/visuals/shaders/tests/test_function.py +486 -0
- vispy/visuals/shaders/tests/test_multiprogram.py +78 -0
- vispy/visuals/shaders/tests/test_parsing.py +57 -0
- vispy/visuals/shaders/variable.py +272 -0
- vispy/visuals/spectrogram.py +169 -0
- vispy/visuals/sphere.py +80 -0
- vispy/visuals/surface_plot.py +192 -0
- vispy/visuals/tests/__init__.py +0 -0
- vispy/visuals/tests/test_arrows.py +109 -0
- vispy/visuals/tests/test_axis.py +120 -0
- vispy/visuals/tests/test_collections.py +15 -0
- vispy/visuals/tests/test_colorbar.py +179 -0
- vispy/visuals/tests/test_colormap.py +97 -0
- vispy/visuals/tests/test_ellipse.py +122 -0
- vispy/visuals/tests/test_gridlines.py +30 -0
- vispy/visuals/tests/test_histogram.py +24 -0
- vispy/visuals/tests/test_image.py +392 -0
- vispy/visuals/tests/test_image_complex.py +36 -0
- vispy/visuals/tests/test_infinite_line.py +53 -0
- vispy/visuals/tests/test_instanced_mesh.py +50 -0
- vispy/visuals/tests/test_isosurface.py +22 -0
- vispy/visuals/tests/test_linear_region.py +152 -0
- vispy/visuals/tests/test_markers.py +54 -0
- vispy/visuals/tests/test_mesh.py +261 -0
- vispy/visuals/tests/test_mesh_normals.py +218 -0
- vispy/visuals/tests/test_polygon.py +112 -0
- vispy/visuals/tests/test_rectangle.py +163 -0
- vispy/visuals/tests/test_regular_polygon.py +111 -0
- vispy/visuals/tests/test_scalable_textures.py +196 -0
- vispy/visuals/tests/test_sdf.py +73 -0
- vispy/visuals/tests/test_spectrogram.py +42 -0
- vispy/visuals/tests/test_surface_plot.py +57 -0
- vispy/visuals/tests/test_text.py +95 -0
- vispy/visuals/tests/test_volume.py +542 -0
- vispy/visuals/tests/test_windbarb.py +33 -0
- vispy/visuals/text/__init__.py +7 -0
- vispy/visuals/text/_sdf_cpu.cpython-313-darwin.so +0 -0
- vispy/visuals/text/_sdf_cpu.pyx +112 -0
- vispy/visuals/text/_sdf_gpu.py +316 -0
- vispy/visuals/text/text.py +675 -0
- vispy/visuals/transforms/__init__.py +34 -0
- vispy/visuals/transforms/_util.py +191 -0
- vispy/visuals/transforms/base_transform.py +233 -0
- vispy/visuals/transforms/chain.py +300 -0
- vispy/visuals/transforms/interactive.py +98 -0
- vispy/visuals/transforms/linear.py +564 -0
- vispy/visuals/transforms/nonlinear.py +398 -0
- vispy/visuals/transforms/tests/__init__.py +0 -0
- vispy/visuals/transforms/tests/test_transforms.py +243 -0
- vispy/visuals/transforms/transform_system.py +339 -0
- vispy/visuals/tube.py +173 -0
- vispy/visuals/visual.py +923 -0
- vispy/visuals/volume.py +1366 -0
- vispy/visuals/windbarb.py +291 -0
- vispy/visuals/xyz_axis.py +34 -0
- vispy-0.15.0.dist-info/METADATA +243 -0
- vispy-0.15.0.dist-info/RECORD +521 -0
- vispy-0.15.0.dist-info/WHEEL +6 -0
- vispy-0.15.0.dist-info/licenses/LICENSE.txt +36 -0
- vispy-0.15.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
3
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
4
|
+
|
|
5
|
+
from __future__ import division
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from .base_camera import BaseCamera
|
|
10
|
+
from ...geometry import Rect
|
|
11
|
+
from ...visuals.transforms import STTransform, MatrixTransform
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
DEFAULT_RECT_TUPLE = (0, 0, 1, 1)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PanZoomCamera(BaseCamera):
|
|
18
|
+
"""Camera implementing 2D pan/zoom mouse interaction.
|
|
19
|
+
|
|
20
|
+
For this camera, the ``scale_factor`` indicates the zoom level, and
|
|
21
|
+
the ``center`` indicates the center position of the view.
|
|
22
|
+
|
|
23
|
+
By default, this camera inverts the y axis of the scene. This usually
|
|
24
|
+
results in the scene +y axis pointing upward because widgets (including
|
|
25
|
+
ViewBox) have their +y axis pointing downward.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
rect : Rect
|
|
30
|
+
A Rect object or 4-element tuple that specifies the rectangular
|
|
31
|
+
area to show.
|
|
32
|
+
aspect : float | None
|
|
33
|
+
The aspect ratio (i.e. scaling) between x and y dimension of
|
|
34
|
+
the scene. E.g. to show a square image as square, the aspect
|
|
35
|
+
should be 1. If None (default) the x and y dimensions are scaled
|
|
36
|
+
independently.
|
|
37
|
+
**kwargs : dict
|
|
38
|
+
Keyword arguments to pass to `BaseCamera`.
|
|
39
|
+
|
|
40
|
+
Notes
|
|
41
|
+
-----
|
|
42
|
+
Interaction:
|
|
43
|
+
|
|
44
|
+
* LMB: pan the view
|
|
45
|
+
* RMB or scroll: zooms the view
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
_state_props = BaseCamera._state_props + ('rect', )
|
|
50
|
+
|
|
51
|
+
def __init__(self, rect=DEFAULT_RECT_TUPLE, aspect=None, **kwargs):
|
|
52
|
+
super(PanZoomCamera, self).__init__(**kwargs)
|
|
53
|
+
|
|
54
|
+
self.transform = STTransform()
|
|
55
|
+
self.tf_mat = MatrixTransform()
|
|
56
|
+
|
|
57
|
+
# Set camera attributes
|
|
58
|
+
self.aspect = aspect
|
|
59
|
+
self._rect = None
|
|
60
|
+
self.rect = rect
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def aspect(self):
|
|
64
|
+
"""The ratio between the x and y dimension. E.g. to show a
|
|
65
|
+
square image as square, the aspect should be 1. If None, the
|
|
66
|
+
dimensions are scaled automatically, dependening on the
|
|
67
|
+
available space. Otherwise the ratio between the dimensions
|
|
68
|
+
is fixed.
|
|
69
|
+
"""
|
|
70
|
+
return self._aspect
|
|
71
|
+
|
|
72
|
+
@aspect.setter
|
|
73
|
+
def aspect(self, value):
|
|
74
|
+
if value is None:
|
|
75
|
+
self._aspect = None
|
|
76
|
+
else:
|
|
77
|
+
self._aspect = float(value)
|
|
78
|
+
self.view_changed()
|
|
79
|
+
|
|
80
|
+
def zoom(self, factor, center=None):
|
|
81
|
+
"""Zoom in (or out) at the given center
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
factor : float or tuple
|
|
86
|
+
Fraction by which the scene should be zoomed (e.g. a factor of 2
|
|
87
|
+
causes the scene to appear twice as large).
|
|
88
|
+
center : tuple of 2-4 elements
|
|
89
|
+
The center of the view. If not given or None, use the
|
|
90
|
+
current center.
|
|
91
|
+
"""
|
|
92
|
+
# Init some variables
|
|
93
|
+
center = center if (center is not None) else self.center
|
|
94
|
+
assert len(center) in (2, 3, 4)
|
|
95
|
+
# Get scale factor, take scale ratio into account
|
|
96
|
+
if np.isscalar(factor):
|
|
97
|
+
scale = [factor, factor]
|
|
98
|
+
else:
|
|
99
|
+
if len(factor) != 2:
|
|
100
|
+
raise TypeError("factor must be scalar or length-2 sequence.")
|
|
101
|
+
scale = list(factor)
|
|
102
|
+
if self.aspect is not None:
|
|
103
|
+
scale[0] = scale[1]
|
|
104
|
+
|
|
105
|
+
# Make a new object (copy), so that allocation will
|
|
106
|
+
# trigger view_changed:
|
|
107
|
+
rect = Rect(self.rect)
|
|
108
|
+
# Get space from given center to edges
|
|
109
|
+
left_space = center[0] - rect.left
|
|
110
|
+
right_space = rect.right - center[0]
|
|
111
|
+
bottom_space = center[1] - rect.bottom
|
|
112
|
+
top_space = rect.top - center[1]
|
|
113
|
+
# Scale these spaces
|
|
114
|
+
rect.left = center[0] - left_space * scale[0]
|
|
115
|
+
rect.right = center[0] + right_space * scale[0]
|
|
116
|
+
rect.bottom = center[1] - bottom_space * scale[1]
|
|
117
|
+
rect.top = center[1] + top_space * scale[1]
|
|
118
|
+
self.rect = rect
|
|
119
|
+
|
|
120
|
+
def pan(self, *pan):
|
|
121
|
+
"""Pan the view.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
*pan : length-2 sequence
|
|
126
|
+
The distance to pan the view, in the coordinate system of the
|
|
127
|
+
scene.
|
|
128
|
+
"""
|
|
129
|
+
if len(pan) == 1:
|
|
130
|
+
pan = pan[0]
|
|
131
|
+
self.rect = self.rect + pan
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def rect(self):
|
|
135
|
+
"""The rectangular border of the ViewBox visible area.
|
|
136
|
+
|
|
137
|
+
This is expressed in the coordinate system of the scene.
|
|
138
|
+
See :class:`~vispy.geometry.rect.Rect` for different ways this can
|
|
139
|
+
be specified.
|
|
140
|
+
|
|
141
|
+
Note that the rectangle can have negative width or height, in
|
|
142
|
+
which case the corresponding dimension is flipped (this flipping
|
|
143
|
+
is independent from the camera's ``flip`` property).
|
|
144
|
+
"""
|
|
145
|
+
return self._rect
|
|
146
|
+
|
|
147
|
+
@rect.setter
|
|
148
|
+
def rect(self, args):
|
|
149
|
+
if isinstance(args, tuple):
|
|
150
|
+
rect = Rect(*args)
|
|
151
|
+
else:
|
|
152
|
+
rect = Rect(args)
|
|
153
|
+
|
|
154
|
+
if self._rect != rect:
|
|
155
|
+
self._rect = rect
|
|
156
|
+
self.view_changed()
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def center(self):
|
|
160
|
+
rect = self._rect
|
|
161
|
+
return (*rect.center, 0)
|
|
162
|
+
|
|
163
|
+
@center.setter
|
|
164
|
+
def center(self, center):
|
|
165
|
+
if not (isinstance(center, (tuple, list)) and len(center) in (2, 3)):
|
|
166
|
+
raise ValueError('center must be a 2 or 3 element tuple')
|
|
167
|
+
rect = Rect(self.rect) or Rect(*DEFAULT_RECT_TUPLE) # make a copy of self.rect
|
|
168
|
+
rect.center = center[:2]
|
|
169
|
+
self.rect = rect
|
|
170
|
+
|
|
171
|
+
def _set_range(self, init):
|
|
172
|
+
if init and self._rect is not None:
|
|
173
|
+
return
|
|
174
|
+
# Convert limits to rect
|
|
175
|
+
w = self._xlim[1] - self._xlim[0]
|
|
176
|
+
h = self._ylim[1] - self._ylim[0]
|
|
177
|
+
self.rect = self._xlim[0], self._ylim[0], w, h
|
|
178
|
+
|
|
179
|
+
def viewbox_resize_event(self, event):
|
|
180
|
+
"""Modify the data aspect and scale factor, to adjust to
|
|
181
|
+
the new window size.
|
|
182
|
+
|
|
183
|
+
Parameters
|
|
184
|
+
----------
|
|
185
|
+
event : instance of Event
|
|
186
|
+
The event.
|
|
187
|
+
"""
|
|
188
|
+
self.view_changed()
|
|
189
|
+
|
|
190
|
+
def viewbox_mouse_event(self, event):
|
|
191
|
+
"""
|
|
192
|
+
The SubScene received a mouse event; update transform
|
|
193
|
+
accordingly.
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
event : instance of Event
|
|
198
|
+
The event.
|
|
199
|
+
"""
|
|
200
|
+
if event.handled or not self.interactive:
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
# Scrolling
|
|
204
|
+
BaseCamera.viewbox_mouse_event(self, event)
|
|
205
|
+
|
|
206
|
+
if event.type == 'mouse_wheel':
|
|
207
|
+
center = self._scene_transform.imap(event.pos)
|
|
208
|
+
self.zoom((1 + self.zoom_factor)**(-event.delta[1] * 30), center)
|
|
209
|
+
event.handled = True
|
|
210
|
+
elif event.type == 'gesture_zoom':
|
|
211
|
+
center = self._scene_transform.imap(event.pos)
|
|
212
|
+
self.zoom(1 - event.scale, center)
|
|
213
|
+
event.handled = True
|
|
214
|
+
elif event.type == 'mouse_move':
|
|
215
|
+
if event.press_event is None:
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
modifiers = event.mouse_event.modifiers
|
|
219
|
+
p1 = event.mouse_event.press_event.pos
|
|
220
|
+
p2 = event.mouse_event.pos
|
|
221
|
+
|
|
222
|
+
if 1 in event.buttons and not modifiers:
|
|
223
|
+
# Translate
|
|
224
|
+
p1 = np.array(event.last_event.pos)[:2]
|
|
225
|
+
p2 = np.array(event.pos)[:2]
|
|
226
|
+
p1s = self._transform.imap(p1)
|
|
227
|
+
p2s = self._transform.imap(p2)
|
|
228
|
+
self.pan(p1s - p2s)
|
|
229
|
+
event.handled = True
|
|
230
|
+
elif 2 in event.buttons and not modifiers:
|
|
231
|
+
# Zoom
|
|
232
|
+
p1c = np.array(event.last_event.pos)[:2]
|
|
233
|
+
p2c = np.array(event.pos)[:2]
|
|
234
|
+
scale = ((1 + self.zoom_factor)**((p1c - p2c) *
|
|
235
|
+
np.array([1, -1])))
|
|
236
|
+
center = self._transform.imap(event.press_event.pos[:2])
|
|
237
|
+
self.zoom(scale, center)
|
|
238
|
+
event.handled = True
|
|
239
|
+
else:
|
|
240
|
+
event.handled = False
|
|
241
|
+
elif event.type == 'mouse_press':
|
|
242
|
+
# accept the event if it is button 1 or 2.
|
|
243
|
+
# This is required in order to receive future events
|
|
244
|
+
event.handled = event.button in [1, 2]
|
|
245
|
+
else:
|
|
246
|
+
event.handled = False
|
|
247
|
+
|
|
248
|
+
def _update_transform(self):
|
|
249
|
+
if self._resetting: # base camera linking operation
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
rect = self.rect
|
|
253
|
+
self._real_rect = Rect(rect)
|
|
254
|
+
vbr = self._viewbox.rect.flipped(x=self.flip[0], y=(not self.flip[1]))
|
|
255
|
+
d = self.depth_value
|
|
256
|
+
|
|
257
|
+
# apply scale ratio constraint
|
|
258
|
+
if self._aspect is not None:
|
|
259
|
+
# Aspect ratio of the requested range
|
|
260
|
+
requested_aspect = (rect.width /
|
|
261
|
+
rect.height if rect.height != 0 else 1)
|
|
262
|
+
# Aspect ratio of the viewbox
|
|
263
|
+
view_aspect = vbr.width / vbr.height if vbr.height != 0 else 1
|
|
264
|
+
# View aspect ratio needed to obey the scale constraint
|
|
265
|
+
constrained_aspect = abs(view_aspect / self._aspect)
|
|
266
|
+
|
|
267
|
+
if requested_aspect > constrained_aspect:
|
|
268
|
+
# view range needs to be taller than requested
|
|
269
|
+
dy = 0.5 * (rect.width / constrained_aspect - rect.height)
|
|
270
|
+
self._real_rect.top += dy
|
|
271
|
+
self._real_rect.bottom -= dy
|
|
272
|
+
else:
|
|
273
|
+
# view range needs to be wider than requested
|
|
274
|
+
dx = 0.5 * (rect.height * constrained_aspect - rect.width)
|
|
275
|
+
self._real_rect.left -= dx
|
|
276
|
+
self._real_rect.right += dx
|
|
277
|
+
|
|
278
|
+
# Apply mapping between viewbox and cam view
|
|
279
|
+
self.transform.set_mapping(self._real_rect, vbr, update=False)
|
|
280
|
+
# Scale z, so that the clipping planes are between -alot and +alot
|
|
281
|
+
self.transform.zoom((1, 1, 1 / d))
|
|
282
|
+
|
|
283
|
+
# We've now set self.transform, which represents our 2D
|
|
284
|
+
# transform When up is +z this is all. In other cases,
|
|
285
|
+
# self.transform is now set up correctly to allow pan/zoom, but
|
|
286
|
+
# for the scene we need a different (3D) mapping. When there
|
|
287
|
+
# is a minus in up, we simply look at the scene from the other
|
|
288
|
+
# side (as if z was flipped).
|
|
289
|
+
if self.up == '+z':
|
|
290
|
+
self.tf_mat.matrix = self.transform.as_matrix().matrix
|
|
291
|
+
else:
|
|
292
|
+
rr = self._real_rect
|
|
293
|
+
d = d if (self.up[0] == '+') else -d
|
|
294
|
+
pp1 = [(vbr.left, vbr.bottom, 0), (vbr.left, vbr.top, 0),
|
|
295
|
+
(vbr.right, vbr.bottom, 0), (vbr.left, vbr.bottom, 1)]
|
|
296
|
+
# Get Mapping
|
|
297
|
+
if self.up[1] == 'z':
|
|
298
|
+
pp2 = [(rr.left, rr.bottom, 0), (rr.left, rr.top, 0),
|
|
299
|
+
(rr.right, rr.bottom, 0), (rr.left, rr.bottom, d)]
|
|
300
|
+
elif self.up[1] == 'y':
|
|
301
|
+
pp2 = [(rr.left, 0, rr.bottom), (rr.left, 0, rr.top),
|
|
302
|
+
(rr.right, 0, rr.bottom), (rr.left, d, rr.bottom)]
|
|
303
|
+
elif self.up[1] == 'x':
|
|
304
|
+
pp2 = [(0, rr.left, rr.bottom), (0, rr.left, rr.top),
|
|
305
|
+
(0, rr.right, rr.bottom), (d, rr.left, rr.bottom)]
|
|
306
|
+
|
|
307
|
+
# Apply
|
|
308
|
+
self.tf_mat.set_mapping(np.array(pp2), np.array(pp1))
|
|
309
|
+
|
|
310
|
+
# Set on viewbox
|
|
311
|
+
self._set_scene_transform(self.tf_mat)
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
3
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
4
|
+
|
|
5
|
+
from __future__ import division
|
|
6
|
+
|
|
7
|
+
import math
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from .base_camera import BaseCamera
|
|
11
|
+
from ...util import keys, transforms
|
|
12
|
+
from ...visuals.transforms import MatrixTransform
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PerspectiveCamera(BaseCamera):
|
|
16
|
+
"""Base class for 3D cameras supporting orthographic and
|
|
17
|
+
perspective projections.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
fov : float
|
|
22
|
+
Field of view. Default 60.0.
|
|
23
|
+
scale_factor : scalar
|
|
24
|
+
A measure for the scale/range of the scene that the camera
|
|
25
|
+
should show. The exact meaning differs per camera type.
|
|
26
|
+
**kwargs : dict
|
|
27
|
+
Keyword arguments to pass to `BaseCamera`.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
_state_props = ('scale_factor', 'center', 'fov')
|
|
31
|
+
|
|
32
|
+
def __init__(self, fov=60.0, scale_factor=None, center=None, **kwargs):
|
|
33
|
+
super(PerspectiveCamera, self).__init__(**kwargs)
|
|
34
|
+
# Camera transform
|
|
35
|
+
self.transform = MatrixTransform()
|
|
36
|
+
|
|
37
|
+
# Set camera attributes
|
|
38
|
+
self.fov = fov
|
|
39
|
+
self._scale_factor = None
|
|
40
|
+
self._center = None
|
|
41
|
+
|
|
42
|
+
# Only set if they are given. They're set during _set_range if None
|
|
43
|
+
if scale_factor is not None:
|
|
44
|
+
self.scale_factor = scale_factor
|
|
45
|
+
if center is not None:
|
|
46
|
+
self.center = center
|
|
47
|
+
|
|
48
|
+
def viewbox_mouse_event(self, event):
|
|
49
|
+
"""The ViewBox received a mouse event; update transform
|
|
50
|
+
accordingly.
|
|
51
|
+
Default implementation adjusts scale factor when scolling.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
event : instance of Event
|
|
56
|
+
The event.
|
|
57
|
+
"""
|
|
58
|
+
BaseCamera.viewbox_mouse_event(self, event)
|
|
59
|
+
if event.type == 'mouse_wheel':
|
|
60
|
+
s = 1.1 ** - event.delta[1]
|
|
61
|
+
self._scale_factor *= s
|
|
62
|
+
if self._distance is not None:
|
|
63
|
+
self._distance *= s
|
|
64
|
+
self.view_changed()
|
|
65
|
+
elif event.type == 'gesture_zoom':
|
|
66
|
+
s = 1 - event.scale
|
|
67
|
+
self._scale_factor *= s
|
|
68
|
+
if self._distance is not None:
|
|
69
|
+
self._distance *= s
|
|
70
|
+
self.view_changed()
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def scale_factor(self):
|
|
74
|
+
"""The measure for the scale or range that the camera should cover
|
|
75
|
+
|
|
76
|
+
For the PanZoomCamera and TurnTableCamera this translates to
|
|
77
|
+
zooming: set to smaller values to zoom in.
|
|
78
|
+
"""
|
|
79
|
+
return self._scale_factor
|
|
80
|
+
|
|
81
|
+
@scale_factor.setter
|
|
82
|
+
def scale_factor(self, value):
|
|
83
|
+
value = abs(float(value))
|
|
84
|
+
if value == self._scale_factor:
|
|
85
|
+
return
|
|
86
|
+
self._scale_factor = value
|
|
87
|
+
self.view_changed()
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def near_clip_distance(self):
|
|
91
|
+
"""The distance of the near clipping plane from the camera's position."""
|
|
92
|
+
return self._near_clip_distance
|
|
93
|
+
|
|
94
|
+
def _set_range(self, init):
|
|
95
|
+
"""Reset the camera view using the known limits."""
|
|
96
|
+
if init and (self._scale_factor is not None):
|
|
97
|
+
return # We don't have to set our scale factor
|
|
98
|
+
|
|
99
|
+
# Get window size (and store factor now to sync with resizing)
|
|
100
|
+
w, h = self._viewbox.size
|
|
101
|
+
w, h = float(w), float(h)
|
|
102
|
+
|
|
103
|
+
if (w == 0) or (h == 0):
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
# Get range and translation for x and y
|
|
107
|
+
x1, y1, z1 = self._xlim[0], self._ylim[0], self._zlim[0]
|
|
108
|
+
x2, y2, z2 = self._xlim[1], self._ylim[1], self._zlim[1]
|
|
109
|
+
rx, ry, rz = (x2 - x1), (y2 - y1), (z2 - z1)
|
|
110
|
+
|
|
111
|
+
# Correct ranges for window size. Note that the window width
|
|
112
|
+
# influences the x and y data range, while the height influences
|
|
113
|
+
# the z data range.
|
|
114
|
+
if w / h > 1:
|
|
115
|
+
rx /= w / h
|
|
116
|
+
ry /= w / h
|
|
117
|
+
else:
|
|
118
|
+
rz /= h / w
|
|
119
|
+
|
|
120
|
+
# Convert to screen coordinates. In screen x, only x and y have effect.
|
|
121
|
+
# In screen y, all three dimensions have effect. The idea of the lines
|
|
122
|
+
# below is to calculate the range on screen when that will fit the
|
|
123
|
+
# data under any rotation.
|
|
124
|
+
rxs = (rx**2 + ry**2)**0.5
|
|
125
|
+
rys = (rx**2 + ry**2 + rz**2)**0.5
|
|
126
|
+
|
|
127
|
+
self.scale_factor = max(rxs, rys) * 1.04 # 4% extra space
|
|
128
|
+
|
|
129
|
+
def viewbox_resize_event(self, event):
|
|
130
|
+
"""The ViewBox resize handler to update the transform
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
event : instance of Event
|
|
135
|
+
The event.
|
|
136
|
+
"""
|
|
137
|
+
self.view_changed()
|
|
138
|
+
|
|
139
|
+
def _update_transform(self, event=None):
|
|
140
|
+
# Do we have a viewbox
|
|
141
|
+
if self._viewbox is None:
|
|
142
|
+
return
|
|
143
|
+
if self._resetting: # base camera linking operation
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
# Calculate viewing range for x and y
|
|
147
|
+
fx = fy = self._scale_factor
|
|
148
|
+
|
|
149
|
+
# Correct for window size
|
|
150
|
+
w, h = self._viewbox.size
|
|
151
|
+
|
|
152
|
+
if (w == 0) or (h == 0):
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
if w / h > 1:
|
|
156
|
+
fx *= w / h
|
|
157
|
+
else:
|
|
158
|
+
fy *= h / w
|
|
159
|
+
|
|
160
|
+
self._update_projection_transform(fx, fy)
|
|
161
|
+
|
|
162
|
+
# assemble complete transform mapping to viewbox bounds
|
|
163
|
+
unit = [[-1, 1], [1, -1]]
|
|
164
|
+
vrect = [[0, 0], self._viewbox.size]
|
|
165
|
+
self._viewbox_tr.set_mapping(unit, vrect)
|
|
166
|
+
transforms = [n.transform for n in
|
|
167
|
+
self._viewbox.scene.node_path_to_child(self)[1:]]
|
|
168
|
+
camera_tr = self._transform_cache.get(transforms).inverse
|
|
169
|
+
full_tr = self._transform_cache.get([self._viewbox_tr,
|
|
170
|
+
self._projection,
|
|
171
|
+
camera_tr])
|
|
172
|
+
self._transform_cache.roll()
|
|
173
|
+
self._set_scene_transform(full_tr)
|
|
174
|
+
|
|
175
|
+
def _update_projection_transform(self, fx, fy):
|
|
176
|
+
d = self.depth_value
|
|
177
|
+
fov = max(0.01, self._fov)
|
|
178
|
+
dist = fy / (2 * math.tan(math.radians(fov)/2))
|
|
179
|
+
val = math.sqrt(d)
|
|
180
|
+
self._projection.set_perspective(fov, fx/fy, dist/val, dist*val)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class Base3DRotationCamera(PerspectiveCamera):
|
|
184
|
+
"""Base class for TurntableCamera and ArcballCamera"""
|
|
185
|
+
|
|
186
|
+
def __init__(self, fov=0.0, **kwargs):
|
|
187
|
+
super(Base3DRotationCamera, self).__init__(fov=fov, **kwargs)
|
|
188
|
+
self._actual_distance = 0.0
|
|
189
|
+
self._event_value = None
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def distance(self):
|
|
193
|
+
"""The user-set distance. If None (default), the distance is
|
|
194
|
+
internally calculated from the scale factor and fov.
|
|
195
|
+
"""
|
|
196
|
+
return self._distance
|
|
197
|
+
|
|
198
|
+
@distance.setter
|
|
199
|
+
def distance(self, distance):
|
|
200
|
+
if distance is None:
|
|
201
|
+
self._distance = None
|
|
202
|
+
else:
|
|
203
|
+
self._distance = float(distance)
|
|
204
|
+
self.view_changed()
|
|
205
|
+
|
|
206
|
+
def viewbox_mouse_event(self, event):
|
|
207
|
+
"""
|
|
208
|
+
The viewbox received a mouse event; update transform
|
|
209
|
+
accordingly.
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
event : instance of Event
|
|
214
|
+
The event.
|
|
215
|
+
"""
|
|
216
|
+
if event.handled or not self.interactive:
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
PerspectiveCamera.viewbox_mouse_event(self, event)
|
|
220
|
+
|
|
221
|
+
if event.type == 'mouse_release':
|
|
222
|
+
self._event_value = None # Reset
|
|
223
|
+
elif event.type == 'mouse_press':
|
|
224
|
+
event.handled = True
|
|
225
|
+
elif event.type == 'mouse_move':
|
|
226
|
+
if event.press_event is None:
|
|
227
|
+
return
|
|
228
|
+
if 1 in event.buttons and 2 in event.buttons:
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
modifiers = event.mouse_event.modifiers
|
|
232
|
+
p1 = event.mouse_event.press_event.pos
|
|
233
|
+
p2 = event.mouse_event.pos
|
|
234
|
+
d = p2 - p1
|
|
235
|
+
|
|
236
|
+
if 1 in event.buttons and not modifiers:
|
|
237
|
+
# Rotate
|
|
238
|
+
self._update_rotation(event)
|
|
239
|
+
|
|
240
|
+
elif 2 in event.buttons and not modifiers:
|
|
241
|
+
# Zoom
|
|
242
|
+
if self._event_value is None:
|
|
243
|
+
self._event_value = (self._scale_factor, self._distance)
|
|
244
|
+
zoomy = (1 + self.zoom_factor) ** d[1]
|
|
245
|
+
|
|
246
|
+
self.scale_factor = self._event_value[0] * zoomy
|
|
247
|
+
# Modify distance if its given
|
|
248
|
+
if self._distance is not None:
|
|
249
|
+
self._distance = self._event_value[1] * zoomy
|
|
250
|
+
self.view_changed()
|
|
251
|
+
|
|
252
|
+
elif 1 in event.buttons and keys.SHIFT in modifiers:
|
|
253
|
+
# Translate
|
|
254
|
+
norm = np.mean(self._viewbox.size)
|
|
255
|
+
if self._event_value is None or len(self._event_value) == 2:
|
|
256
|
+
self._event_value = self.center
|
|
257
|
+
dist = (p1 - p2) / norm * self._scale_factor
|
|
258
|
+
dist[1] *= -1
|
|
259
|
+
# Black magic part 1: turn 2D into 3D translations
|
|
260
|
+
dx, dy, dz = self._dist_to_trans(dist)
|
|
261
|
+
# Black magic part 2: take up-vector and flipping into account
|
|
262
|
+
ff = self._flip_factors
|
|
263
|
+
up, forward, right = self._get_dim_vectors()
|
|
264
|
+
dx, dy, dz = right * dx + forward * dy + up * dz
|
|
265
|
+
dx, dy, dz = ff[0] * dx, ff[1] * dy, dz * ff[2]
|
|
266
|
+
c = self._event_value
|
|
267
|
+
self.center = c[0] + dx, c[1] + dy, c[2] + dz
|
|
268
|
+
|
|
269
|
+
elif 2 in event.buttons and keys.SHIFT in modifiers:
|
|
270
|
+
# Change fov
|
|
271
|
+
if self._event_value is None:
|
|
272
|
+
self._event_value = self._fov
|
|
273
|
+
fov = self._event_value - d[1] / 5.0
|
|
274
|
+
self.fov = min(180.0, max(0.0, fov))
|
|
275
|
+
|
|
276
|
+
def _update_camera_pos(self):
|
|
277
|
+
"""Set the camera position and orientation"""
|
|
278
|
+
# transform will be updated several times; do not update camera
|
|
279
|
+
# transform until we are done.
|
|
280
|
+
ch_em = self.events.transform_change
|
|
281
|
+
with ch_em.blocker(self._update_transform):
|
|
282
|
+
up, forward, right = self._get_dim_vectors()
|
|
283
|
+
|
|
284
|
+
# Create mapping so correct dim is up
|
|
285
|
+
pp1 = np.array([(0, 0, 0), (0, 0, -1), (1, 0, 0), (0, 1, 0)])
|
|
286
|
+
pp2 = np.array([(0, 0, 0), forward, right, up])
|
|
287
|
+
pos = -self._actual_distance * forward
|
|
288
|
+
scale = [1.0/a for a in self._flip_factors]
|
|
289
|
+
|
|
290
|
+
self.transform.matrix = np.linalg.multi_dot((
|
|
291
|
+
transforms.affine_map(pp1, pp2).T,
|
|
292
|
+
transforms.translate(pos),
|
|
293
|
+
self._get_rotation_tr(),
|
|
294
|
+
transforms.scale(scale),
|
|
295
|
+
transforms.translate(self.center)
|
|
296
|
+
))
|
|
297
|
+
|
|
298
|
+
def _get_dim_vectors(self):
|
|
299
|
+
# Specify up and forward vector
|
|
300
|
+
M = {'+z': [(0, 0, +1), (0, 1, 0)],
|
|
301
|
+
'-z': [(0, 0, -1), (0, 1, 0)],
|
|
302
|
+
'+y': [(0, +1, 0), (1, 0, 0)],
|
|
303
|
+
'-y': [(0, -1, 0), (1, 0, 0)],
|
|
304
|
+
'+x': [(+1, 0, 0), (0, 0, 1)],
|
|
305
|
+
'-x': [(-1, 0, 0), (0, 0, 1)],
|
|
306
|
+
}
|
|
307
|
+
up, forward = M[self.up]
|
|
308
|
+
right = np.cross(forward, up)
|
|
309
|
+
return np.array(up), np.array(forward), right
|
|
310
|
+
|
|
311
|
+
def _update_projection_transform(self, fx, fy):
|
|
312
|
+
d = self.depth_value
|
|
313
|
+
if self._fov == 0:
|
|
314
|
+
self._projection.set_ortho(-0.5*fx, 0.5*fx, -0.5*fy, 0.5*fy, -d, d)
|
|
315
|
+
self._actual_distance = self._distance or 0.0
|
|
316
|
+
else:
|
|
317
|
+
# Figure distance to center in order to have correct FoV and fy.
|
|
318
|
+
# Use that auto-distance, or the given distance (if not None).
|
|
319
|
+
fov = max(0.01, self._fov)
|
|
320
|
+
dist = fy / (2 * math.tan(math.radians(fov)/2))
|
|
321
|
+
self._actual_distance = dist = self._distance or dist
|
|
322
|
+
val = math.sqrt(d*10)
|
|
323
|
+
self._projection.set_perspective(fov, fx/fy, dist/val, dist*val)
|
|
324
|
+
# Update camera pos, which will use our calculated _distance to offset
|
|
325
|
+
# the camera
|
|
326
|
+
self._update_camera_pos()
|
|
327
|
+
|
|
328
|
+
def _update_rotation(self, event):
|
|
329
|
+
"""Update rotation parmeters based on mouse movement"""
|
|
330
|
+
raise NotImplementedError
|
|
331
|
+
|
|
332
|
+
def _rotate_tr(self):
|
|
333
|
+
"""Rotate the transformation matrix based on camera parameters"""
|
|
334
|
+
raise NotImplementedError
|
|
335
|
+
|
|
336
|
+
def _dist_to_trans(self, dist):
|
|
337
|
+
"""Convert mouse x, y movement into x, y, z translations"""
|
|
338
|
+
raise NotImplementedError
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# -----------------------------------------------------------------------------
|
|
3
|
+
# Copyright (c) Vispy Development Team. All Rights Reserved.
|
|
4
|
+
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
|
|
5
|
+
# -----------------------------------------------------------------------------
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pytest
|
|
8
|
+
from vispy.scene.cameras import TurntableCamera
|
|
9
|
+
from vispy.testing import run_tests_if_main
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.parametrize(
|
|
13
|
+
"elevation, azimuth, roll, expected",
|
|
14
|
+
[
|
|
15
|
+
[0, 0, 0, np.eye(4)],
|
|
16
|
+
[90, 0, 0, [[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]],
|
|
17
|
+
[0, 90, 0, [[0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]],
|
|
18
|
+
[0, 0, 90, [[0, 0, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]],
|
|
19
|
+
],
|
|
20
|
+
)
|
|
21
|
+
def test_turntable_camera_transform(elevation, azimuth, roll, expected):
|
|
22
|
+
camera = TurntableCamera(elevation=elevation, azimuth=azimuth, roll=roll)
|
|
23
|
+
matrix = camera._get_rotation_tr()
|
|
24
|
+
np.testing.assert_allclose(matrix, expected, atol=1e-5)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
run_tests_if_main()
|