vispy 0.15.0__cp313-cp313-macosx_11_0_arm64.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,339 @@
|
|
|
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
|
+
from .linear import STTransform, NullTransform
|
|
8
|
+
from .chain import ChainTransform
|
|
9
|
+
from ._util import TransformCache
|
|
10
|
+
from ...util.event import EventEmitter
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TransformSystem(object):
|
|
16
|
+
"""TransformSystem encapsulates information about the coordinate
|
|
17
|
+
systems needed to draw a Visual.
|
|
18
|
+
|
|
19
|
+
Visual rendering operates in six coordinate systems:
|
|
20
|
+
|
|
21
|
+
* **Visual** - arbitrary local coordinate frame of the visual. Vertex
|
|
22
|
+
buffers used by the visual are usually specified in this coordinate
|
|
23
|
+
system.
|
|
24
|
+
|
|
25
|
+
* **Scene** - This is an isometric coordinate system used mainly for
|
|
26
|
+
lighting calculations.
|
|
27
|
+
|
|
28
|
+
* **Document** - This coordinate system has units of _logical_ pixels, and
|
|
29
|
+
should usually represent the pixel coordinates of the canvas being drawn
|
|
30
|
+
to. Visuals use this coordinate system to make measurements for font
|
|
31
|
+
size, line width, and in general anything that is specified in physical
|
|
32
|
+
units (px, pt, mm, in, etc.). In most circumstances, this is exactly the
|
|
33
|
+
same as the canvas coordinate system.
|
|
34
|
+
|
|
35
|
+
* **Canvas** - This coordinate system represents the logical pixel
|
|
36
|
+
coordinates of the canvas. It has its origin in the top-left corner of
|
|
37
|
+
the canvas, and is typically the coordinate system that mouse and touch
|
|
38
|
+
events are reported in. Note that, by convention, _logical_ pixels
|
|
39
|
+
are not necessarily the same size as the _physical_ pixels in the
|
|
40
|
+
framebuffer that is being rendered to.
|
|
41
|
+
|
|
42
|
+
* **Framebuffer** - The buffer coordinate system has units of _physical_
|
|
43
|
+
pixels, and should usually represent the coordinates of the current
|
|
44
|
+
framebuffer (on the canvas or an FBO) being rendered to. Visuals use this
|
|
45
|
+
coordinate system primarily for antialiasing calculations. It is also the
|
|
46
|
+
coorinate system used by glFragCoord. In most cases,
|
|
47
|
+
this will have the same scale as the document and canvas coordinate
|
|
48
|
+
systems because the active framebuffer is the
|
|
49
|
+
back buffer of the canvas, and the canvas will have _logical_ and
|
|
50
|
+
_physical_ pixels of the same size. However, the scale may be different
|
|
51
|
+
in the case of high-resolution displays, or when rendering to an
|
|
52
|
+
off-screen framebuffer with different scaling or boundaries than the
|
|
53
|
+
canvas.
|
|
54
|
+
|
|
55
|
+
* **Render** - This coordinate system is the obligatory system for
|
|
56
|
+
vertices returned by a vertex shader. It has coordinates (-1, -1) to
|
|
57
|
+
(1, 1) across the current glViewport. In OpenGL terminology, this is
|
|
58
|
+
called clip coordinates.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
canvas : Canvas
|
|
63
|
+
The canvas being drawn to.
|
|
64
|
+
dpi : float
|
|
65
|
+
The dot-per-inch resolution of the document coordinate system. By
|
|
66
|
+
default this is set to the resolution of the canvas.
|
|
67
|
+
|
|
68
|
+
Notes
|
|
69
|
+
-----
|
|
70
|
+
By default, TransformSystems are configured such that the document
|
|
71
|
+
coordinate system matches the logical pixels of the canvas,
|
|
72
|
+
|
|
73
|
+
Examples
|
|
74
|
+
--------
|
|
75
|
+
1. To convert local vertex coordinates to normalized device coordinates in
|
|
76
|
+
the vertex shader, we first need a vertex shader that supports configurable
|
|
77
|
+
transformations::
|
|
78
|
+
|
|
79
|
+
vec4 a_position;
|
|
80
|
+
void main() {
|
|
81
|
+
gl_Position = $transform(a_position);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Next, we supply the complete chain of transforms when drawing the visual:
|
|
85
|
+
|
|
86
|
+
def draw(tr_sys):
|
|
87
|
+
tr = tr_sys.get_full_transform()
|
|
88
|
+
self.program['transform'] = tr.shader_map()
|
|
89
|
+
self.program['a_position'] = self.vertex_buffer
|
|
90
|
+
self.program.draw('triangles')
|
|
91
|
+
|
|
92
|
+
2. Draw a line whose width is given in mm. To start, we need normal vectors
|
|
93
|
+
for each vertex, which tell us the direction the vertex should move in
|
|
94
|
+
order to set the line width::
|
|
95
|
+
|
|
96
|
+
vec4 a_position;
|
|
97
|
+
vec4 a_normal;
|
|
98
|
+
float u_line_width;
|
|
99
|
+
float u_dpi;
|
|
100
|
+
void main() {
|
|
101
|
+
// map vertex position and normal vector to the document cs
|
|
102
|
+
vec4 doc_pos = $visual_to_doc(a_position);
|
|
103
|
+
vec4 doc_normal = $visual_to_doc(a_position + a_normal) - doc_pos;
|
|
104
|
+
|
|
105
|
+
// Use DPI to convert mm line width to logical pixels
|
|
106
|
+
float px_width = (u_line_width / 25.4) * dpi;
|
|
107
|
+
|
|
108
|
+
// expand by line width
|
|
109
|
+
doc_pos += normalize(doc_normal) * px_width;
|
|
110
|
+
|
|
111
|
+
// finally, map the remainder of the way to normalized device
|
|
112
|
+
// coordinates.
|
|
113
|
+
gl_Position = $doc_to_render(a_position);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
In this case, we need to access
|
|
117
|
+
the transforms independently, so ``get_full_transform()`` is not useful
|
|
118
|
+
here::
|
|
119
|
+
|
|
120
|
+
def draw(tr_sys):
|
|
121
|
+
# Send two parts of the full transform separately
|
|
122
|
+
self.program['visual_to_doc'] = tr_sys.visual_to_doc.shader_map()
|
|
123
|
+
doc_to_render = (tr_sys.framebuffer_transform *
|
|
124
|
+
tr_sys.document_transform)
|
|
125
|
+
self.program['visual_to_doc'] = doc_to_render.shader_map()
|
|
126
|
+
|
|
127
|
+
self.program['u_line_width'] = self.line_width
|
|
128
|
+
self.program['u_dpi'] = tr_sys.dpi
|
|
129
|
+
self.program['a_position'] = self.vertex_buffer
|
|
130
|
+
self.program['a_normal'] = self.normal_buffer
|
|
131
|
+
self.program.draw('triangles')
|
|
132
|
+
|
|
133
|
+
3. Draw a triangle with antialiasing at the edge.
|
|
134
|
+
|
|
135
|
+
4. Using inverse transforms in the fragment shader
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(self, canvas=None, dpi=None):
|
|
139
|
+
self.changed = EventEmitter(source=self, type='transform_changed')
|
|
140
|
+
self._canvas = None
|
|
141
|
+
self._fbo_bounds = None
|
|
142
|
+
self.canvas = canvas
|
|
143
|
+
self._cache = TransformCache()
|
|
144
|
+
self._dpi = dpi
|
|
145
|
+
self._mappings = {'ct0': None, 'ct1': None, 'ft0': None}
|
|
146
|
+
|
|
147
|
+
# Assign a ChainTransform for each step. This allows us to always
|
|
148
|
+
# return the same transform objects regardless of how the user
|
|
149
|
+
# configures the system.
|
|
150
|
+
self._visual_transform = ChainTransform([NullTransform()])
|
|
151
|
+
self._scene_transform = ChainTransform([NullTransform()])
|
|
152
|
+
self._document_transform = ChainTransform([NullTransform()])
|
|
153
|
+
self._canvas_transform = ChainTransform([STTransform(),
|
|
154
|
+
STTransform()])
|
|
155
|
+
self._framebuffer_transform = ChainTransform([STTransform()])
|
|
156
|
+
|
|
157
|
+
for tr in (self._visual_transform, self._scene_transform,
|
|
158
|
+
self._document_transform, self._canvas_transform,
|
|
159
|
+
self._framebuffer_transform):
|
|
160
|
+
tr.changed.connect(self.changed)
|
|
161
|
+
|
|
162
|
+
def _update_if_maps_changed(self, transform, map_key, new_maps):
|
|
163
|
+
"""Helper to store and check current (from, to) maps against new
|
|
164
|
+
ones being provided. The new mappings are only applied if a change
|
|
165
|
+
has occurred (and also stored in the current mappings).
|
|
166
|
+
"""
|
|
167
|
+
if self._mappings[map_key] is None:
|
|
168
|
+
self._mappings[map_key] = new_maps
|
|
169
|
+
transform.set_mapping(new_maps[0], new_maps[1])
|
|
170
|
+
else:
|
|
171
|
+
if np.any(self._mappings[map_key] != new_maps):
|
|
172
|
+
self._mappings[map_key] = new_maps
|
|
173
|
+
transform.set_mapping(new_maps[0], new_maps[1])
|
|
174
|
+
|
|
175
|
+
def configure(self, viewport=None, fbo_size=None, fbo_rect=None,
|
|
176
|
+
canvas=None):
|
|
177
|
+
"""Automatically configure the TransformSystem:
|
|
178
|
+
|
|
179
|
+
* canvas_transform maps from the Canvas logical pixel
|
|
180
|
+
coordinate system to the framebuffer coordinate system, taking into
|
|
181
|
+
account the logical/physical pixel scale factor, current FBO
|
|
182
|
+
position, and y-axis inversion.
|
|
183
|
+
* framebuffer_transform maps from the current GL viewport on the
|
|
184
|
+
framebuffer coordinate system to clip coordinates (-1 to 1).
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
==========
|
|
189
|
+
viewport : tuple or None
|
|
190
|
+
The GL viewport rectangle (x, y, w, h). If None, then it
|
|
191
|
+
is assumed to cover the entire canvas.
|
|
192
|
+
fbo_size : tuple or None
|
|
193
|
+
The size of the active FBO. If None, then it is assumed to have the
|
|
194
|
+
same size as the canvas's framebuffer.
|
|
195
|
+
fbo_rect : tuple or None
|
|
196
|
+
The position and size (x, y, w, h) of the FBO in the coordinate
|
|
197
|
+
system of the canvas's framebuffer. If None, then the bounds are
|
|
198
|
+
assumed to cover the entire active framebuffer.
|
|
199
|
+
canvas : Canvas instance
|
|
200
|
+
Optionally set the canvas for this TransformSystem. See the
|
|
201
|
+
`canvas` property.
|
|
202
|
+
"""
|
|
203
|
+
# TODO: check that d2f and f2r transforms still contain a single
|
|
204
|
+
# STTransform (if the user has modified these, then auto-config should
|
|
205
|
+
# either fail or replace the transforms)
|
|
206
|
+
if canvas is not None:
|
|
207
|
+
self.canvas = canvas
|
|
208
|
+
canvas = self._canvas
|
|
209
|
+
if canvas is None:
|
|
210
|
+
raise RuntimeError("No canvas assigned to this TransformSystem.")
|
|
211
|
+
|
|
212
|
+
# By default, this should invert the y axis--canvas origin is in top
|
|
213
|
+
# left, whereas framebuffer origin is in bottom left.
|
|
214
|
+
map_from = [(0, 0), canvas.size]
|
|
215
|
+
map_to = [(0, canvas.physical_size[1]), (canvas.physical_size[0], 0)]
|
|
216
|
+
self._update_if_maps_changed(self._canvas_transform.transforms[1],
|
|
217
|
+
'ct1', np.array((map_from, map_to)))
|
|
218
|
+
if fbo_rect is None:
|
|
219
|
+
self._canvas_transform.transforms[0].scale = (1, 1, 1)
|
|
220
|
+
self._canvas_transform.transforms[0].translate = (0, 0, 0)
|
|
221
|
+
else:
|
|
222
|
+
# Map into FBO coordinates
|
|
223
|
+
map_from = [(fbo_rect[0], fbo_rect[1]),
|
|
224
|
+
(fbo_rect[0] + fbo_rect[2], fbo_rect[1] + fbo_rect[3])]
|
|
225
|
+
map_to = [(0, 0), fbo_size]
|
|
226
|
+
self._update_if_maps_changed(self._canvas_transform.transforms[0],
|
|
227
|
+
'ct0', np.array((map_from, map_to)))
|
|
228
|
+
if viewport is None:
|
|
229
|
+
if fbo_size is None:
|
|
230
|
+
# viewport covers entire canvas
|
|
231
|
+
map_from = [(0, 0), canvas.physical_size]
|
|
232
|
+
else:
|
|
233
|
+
# viewport covers entire FBO
|
|
234
|
+
map_from = [(0, 0), fbo_size]
|
|
235
|
+
else:
|
|
236
|
+
map_from = [viewport[:2],
|
|
237
|
+
(viewport[0] + viewport[2], viewport[1] + viewport[3])]
|
|
238
|
+
map_to = [(-1, -1), (1, 1)]
|
|
239
|
+
self._update_if_maps_changed(self._framebuffer_transform.transforms[0],
|
|
240
|
+
'ft0', np.array((map_from, map_to)))
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def canvas(self):
|
|
244
|
+
"""The Canvas being drawn to."""
|
|
245
|
+
return self._canvas
|
|
246
|
+
|
|
247
|
+
@canvas.setter
|
|
248
|
+
def canvas(self, canvas):
|
|
249
|
+
self._canvas = canvas
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def dpi(self):
|
|
253
|
+
"""Physical resolution of the document coordinate system (dots per inch)."""
|
|
254
|
+
if self._dpi is None:
|
|
255
|
+
if self._canvas is None:
|
|
256
|
+
return None
|
|
257
|
+
else:
|
|
258
|
+
return self.canvas.dpi
|
|
259
|
+
else:
|
|
260
|
+
return self._dpi
|
|
261
|
+
|
|
262
|
+
@dpi.setter
|
|
263
|
+
def dpi(self, dpi):
|
|
264
|
+
assert dpi > 0
|
|
265
|
+
self._dpi = dpi
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def visual_transform(self):
|
|
269
|
+
"""Transform mapping from visual local coordinate frame to scene coordinate frame."""
|
|
270
|
+
return self._visual_transform
|
|
271
|
+
|
|
272
|
+
@visual_transform.setter
|
|
273
|
+
def visual_transform(self, tr):
|
|
274
|
+
self._visual_transform.transforms = tr
|
|
275
|
+
|
|
276
|
+
@property
|
|
277
|
+
def scene_transform(self):
|
|
278
|
+
"""Transform mapping from scene coordinate frame to document coordinate frame."""
|
|
279
|
+
return self._scene_transform
|
|
280
|
+
|
|
281
|
+
@scene_transform.setter
|
|
282
|
+
def scene_transform(self, tr):
|
|
283
|
+
self._scene_transform.transforms = tr
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def document_transform(self):
|
|
287
|
+
"""Transform mapping from document coordinate frame to the framebuffer (physical pixel) coordinate frame."""
|
|
288
|
+
return self._document_transform
|
|
289
|
+
|
|
290
|
+
@document_transform.setter
|
|
291
|
+
def document_transform(self, tr):
|
|
292
|
+
self._document_transform.transforms = tr
|
|
293
|
+
|
|
294
|
+
@property
|
|
295
|
+
def canvas_transform(self):
|
|
296
|
+
"""Transform mapping from canvas coordinate frame to framebuffer coordinate frame."""
|
|
297
|
+
return self._canvas_transform
|
|
298
|
+
|
|
299
|
+
@canvas_transform.setter
|
|
300
|
+
def canvas_transform(self, tr):
|
|
301
|
+
self._canvas_transform.transforms = tr
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def framebuffer_transform(self):
|
|
305
|
+
"""Transform mapping from pixel coordinate frame to rendering coordinate frame."""
|
|
306
|
+
return self._framebuffer_transform
|
|
307
|
+
|
|
308
|
+
@framebuffer_transform.setter
|
|
309
|
+
def framebuffer_transform(self, tr):
|
|
310
|
+
self._framebuffer_transform.transforms = tr
|
|
311
|
+
|
|
312
|
+
def get_transform(self, map_from='visual', map_to='render'):
|
|
313
|
+
"""Return a transform mapping between any two coordinate systems.
|
|
314
|
+
|
|
315
|
+
Parameters
|
|
316
|
+
----------
|
|
317
|
+
map_from : str
|
|
318
|
+
The starting coordinate system to map from. Must be one of: visual,
|
|
319
|
+
scene, document, canvas, framebuffer, or render.
|
|
320
|
+
map_to : str
|
|
321
|
+
The ending coordinate system to map to. Must be one of: visual,
|
|
322
|
+
scene, document, canvas, framebuffer, or render.
|
|
323
|
+
"""
|
|
324
|
+
tr = ['visual', 'scene', 'document', 'canvas', 'framebuffer', 'render']
|
|
325
|
+
ifrom = tr.index(map_from)
|
|
326
|
+
ito = tr.index(map_to)
|
|
327
|
+
|
|
328
|
+
if ifrom < ito:
|
|
329
|
+
trs = [getattr(self, '_' + t + '_transform')
|
|
330
|
+
for t in tr[ifrom:ito]][::-1]
|
|
331
|
+
else:
|
|
332
|
+
trs = [getattr(self, '_' + t + '_transform').inverse
|
|
333
|
+
for t in tr[ito:ifrom]]
|
|
334
|
+
return self._cache.get(trs)
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def pixel_scale(self):
|
|
338
|
+
tr = self._canvas_transform
|
|
339
|
+
return (tr.map((1, 0)) - tr.map((0, 0)))[0]
|
vispy/visuals/tube.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from __future__ import division
|
|
2
|
+
|
|
3
|
+
from .mesh import MeshVisual
|
|
4
|
+
import numpy as np
|
|
5
|
+
from numpy.linalg import norm
|
|
6
|
+
from ..util.transforms import rotate
|
|
7
|
+
from ..color import ColorArray
|
|
8
|
+
|
|
9
|
+
import collections
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TubeVisual(MeshVisual):
|
|
13
|
+
"""Displays a tube around a piecewise-linear path.
|
|
14
|
+
|
|
15
|
+
The tube mesh is corrected following its Frenet curvature and
|
|
16
|
+
torsion such that it varies smoothly along the curve, including if
|
|
17
|
+
the tube is closed.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
points : ndarray
|
|
22
|
+
An array of (x, y, z) points describing the path along which the
|
|
23
|
+
tube will be extruded.
|
|
24
|
+
radius : float | ndarray
|
|
25
|
+
The radius of the tube. Use array of floats as input to set radii of
|
|
26
|
+
points individually. Defaults to 1.0.
|
|
27
|
+
closed : bool
|
|
28
|
+
Whether the tube should be closed, joining the last point to the
|
|
29
|
+
first. Defaults to False.
|
|
30
|
+
color : Color | ColorArray
|
|
31
|
+
The color(s) to use when drawing the tube. The same color is
|
|
32
|
+
applied to each vertex of the mesh surrounding each point of
|
|
33
|
+
the line. If the input is a ColorArray, the argument will be
|
|
34
|
+
cycled; for instance if 'red' is passed then the entire tube
|
|
35
|
+
will be red, or if ['green', 'blue'] is passed then the points
|
|
36
|
+
will alternate between these colours. Defaults to 'purple'.
|
|
37
|
+
tube_points : int
|
|
38
|
+
The number of points in the circle-approximating polygon of the
|
|
39
|
+
tube's cross section. Defaults to 8.
|
|
40
|
+
shading : str | None
|
|
41
|
+
Same as for the `MeshVisual` class. Defaults to 'smooth'.
|
|
42
|
+
vertex_colors: ndarray | None
|
|
43
|
+
Same as for the `MeshVisual` class.
|
|
44
|
+
face_colors: ndarray | None
|
|
45
|
+
Same as for the `MeshVisual` class.
|
|
46
|
+
mode : str
|
|
47
|
+
Same as for the `MeshVisual` class. Defaults to 'triangles'.
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, points, radius=1.0,
|
|
52
|
+
closed=False,
|
|
53
|
+
color='purple',
|
|
54
|
+
tube_points=8,
|
|
55
|
+
shading='smooth',
|
|
56
|
+
vertex_colors=None,
|
|
57
|
+
face_colors=None,
|
|
58
|
+
mode='triangles'):
|
|
59
|
+
|
|
60
|
+
# make sure we are working with floats
|
|
61
|
+
points = np.array(points).astype(float)
|
|
62
|
+
|
|
63
|
+
tangents, normals, binormals = _frenet_frames(points, closed)
|
|
64
|
+
|
|
65
|
+
segments = len(points) - 1
|
|
66
|
+
|
|
67
|
+
# if single radius, convert to list of radii
|
|
68
|
+
if not isinstance(radius, collections.abc.Iterable):
|
|
69
|
+
radius = [radius] * len(points)
|
|
70
|
+
elif len(radius) != len(points):
|
|
71
|
+
raise ValueError('Length of radii list must match points.')
|
|
72
|
+
|
|
73
|
+
# get the positions of each vertex
|
|
74
|
+
grid = np.zeros((len(points), tube_points, 3))
|
|
75
|
+
for i in range(len(points)):
|
|
76
|
+
pos = points[i]
|
|
77
|
+
normal = normals[i]
|
|
78
|
+
binormal = binormals[i]
|
|
79
|
+
r = radius[i]
|
|
80
|
+
|
|
81
|
+
# Add a vertex for each point on the circle
|
|
82
|
+
v = np.arange(tube_points,
|
|
83
|
+
dtype=np.float32) / tube_points * 2 * np.pi
|
|
84
|
+
cx = -1. * r * np.cos(v)
|
|
85
|
+
cy = r * np.sin(v)
|
|
86
|
+
grid[i] = (pos + cx[:, np.newaxis]*normal +
|
|
87
|
+
cy[:, np.newaxis]*binormal)
|
|
88
|
+
|
|
89
|
+
# construct the mesh
|
|
90
|
+
indices = []
|
|
91
|
+
for i in range(segments):
|
|
92
|
+
for j in range(tube_points):
|
|
93
|
+
ip = (i+1) % segments if closed else i+1
|
|
94
|
+
jp = (j+1) % tube_points
|
|
95
|
+
|
|
96
|
+
index_a = i*tube_points + j
|
|
97
|
+
index_b = ip*tube_points + j
|
|
98
|
+
index_c = ip*tube_points + jp
|
|
99
|
+
index_d = i*tube_points + jp
|
|
100
|
+
|
|
101
|
+
indices.append([index_a, index_b, index_d])
|
|
102
|
+
indices.append([index_b, index_c, index_d])
|
|
103
|
+
|
|
104
|
+
vertices = grid.reshape(grid.shape[0]*grid.shape[1], 3)
|
|
105
|
+
|
|
106
|
+
color = ColorArray(color)
|
|
107
|
+
if vertex_colors is None:
|
|
108
|
+
point_colors = np.resize(color.rgba,
|
|
109
|
+
(len(points), 4))
|
|
110
|
+
vertex_colors = np.repeat(point_colors, tube_points, axis=0)
|
|
111
|
+
|
|
112
|
+
indices = np.array(indices, dtype=np.uint32)
|
|
113
|
+
|
|
114
|
+
MeshVisual.__init__(self, vertices, indices,
|
|
115
|
+
vertex_colors=vertex_colors,
|
|
116
|
+
face_colors=face_colors,
|
|
117
|
+
shading=shading,
|
|
118
|
+
mode=mode)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _frenet_frames(points, closed):
|
|
122
|
+
"""Calculates and returns the tangents, normals and binormals for
|
|
123
|
+
the tube.
|
|
124
|
+
"""
|
|
125
|
+
tangents = np.zeros((len(points), 3))
|
|
126
|
+
normals = np.zeros((len(points), 3))
|
|
127
|
+
|
|
128
|
+
epsilon = 0.0001
|
|
129
|
+
|
|
130
|
+
# Compute tangent vectors for each segment
|
|
131
|
+
tangents = np.roll(points, -1, axis=0) - np.roll(points, 1, axis=0)
|
|
132
|
+
if not closed:
|
|
133
|
+
tangents[0] = points[1] - points[0]
|
|
134
|
+
tangents[-1] = points[-1] - points[-2]
|
|
135
|
+
mags = np.sqrt(np.sum(tangents * tangents, axis=1))
|
|
136
|
+
tangents /= mags[:, np.newaxis]
|
|
137
|
+
|
|
138
|
+
# Get initial normal and binormal
|
|
139
|
+
t = np.abs(tangents[0])
|
|
140
|
+
|
|
141
|
+
smallest = np.argmin(t)
|
|
142
|
+
normal = np.zeros(3)
|
|
143
|
+
normal[smallest] = 1.
|
|
144
|
+
|
|
145
|
+
vec = np.cross(tangents[0], normal)
|
|
146
|
+
|
|
147
|
+
normals[0] = np.cross(tangents[0], vec)
|
|
148
|
+
|
|
149
|
+
# Compute normal and binormal vectors along the path
|
|
150
|
+
for i in range(1, len(points)):
|
|
151
|
+
normals[i] = normals[i-1]
|
|
152
|
+
|
|
153
|
+
vec = np.cross(tangents[i-1], tangents[i])
|
|
154
|
+
if norm(vec) > epsilon:
|
|
155
|
+
vec /= norm(vec)
|
|
156
|
+
theta = np.arccos(np.clip(tangents[i-1].dot(tangents[i]), -1, 1))
|
|
157
|
+
normals[i] = rotate(-np.degrees(theta),
|
|
158
|
+
vec)[:3, :3].dot(normals[i])
|
|
159
|
+
|
|
160
|
+
if closed:
|
|
161
|
+
theta = np.arccos(np.clip(normals[0].dot(normals[-1]), -1, 1))
|
|
162
|
+
theta /= len(points) - 1
|
|
163
|
+
|
|
164
|
+
if tangents[0].dot(np.cross(normals[0], normals[-1])) > 0:
|
|
165
|
+
theta *= -1.
|
|
166
|
+
|
|
167
|
+
for i in range(1, len(points)):
|
|
168
|
+
normals[i] = rotate(-np.degrees(theta*i),
|
|
169
|
+
tangents[i])[:3, :3].dot(normals[i])
|
|
170
|
+
|
|
171
|
+
binormals = np.cross(tangents, normals)
|
|
172
|
+
|
|
173
|
+
return tangents, normals, binormals
|