vispy 0.15.0__cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.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.

Files changed (521) hide show
  1. vispy/__init__.py +33 -0
  2. vispy/app/__init__.py +15 -0
  3. vispy/app/_default_app.py +76 -0
  4. vispy/app/_detect_eventloop.py +148 -0
  5. vispy/app/application.py +263 -0
  6. vispy/app/backends/__init__.py +52 -0
  7. vispy/app/backends/_egl.py +264 -0
  8. vispy/app/backends/_glfw.py +513 -0
  9. vispy/app/backends/_jupyter_rfb.py +278 -0
  10. vispy/app/backends/_offscreen_util.py +121 -0
  11. vispy/app/backends/_osmesa.py +235 -0
  12. vispy/app/backends/_pyglet.py +451 -0
  13. vispy/app/backends/_pyqt4.py +36 -0
  14. vispy/app/backends/_pyqt5.py +36 -0
  15. vispy/app/backends/_pyqt6.py +40 -0
  16. vispy/app/backends/_pyside.py +37 -0
  17. vispy/app/backends/_pyside2.py +52 -0
  18. vispy/app/backends/_pyside6.py +53 -0
  19. vispy/app/backends/_qt.py +1003 -0
  20. vispy/app/backends/_sdl2.py +444 -0
  21. vispy/app/backends/_template.py +244 -0
  22. vispy/app/backends/_test.py +8 -0
  23. vispy/app/backends/_tk.py +800 -0
  24. vispy/app/backends/_wx.py +476 -0
  25. vispy/app/backends/tests/__init__.py +0 -0
  26. vispy/app/backends/tests/test_offscreen_util.py +52 -0
  27. vispy/app/backends/tests/test_rfb.py +77 -0
  28. vispy/app/base.py +294 -0
  29. vispy/app/canvas.py +828 -0
  30. vispy/app/qt.py +92 -0
  31. vispy/app/tests/__init__.py +0 -0
  32. vispy/app/tests/qt-designer.ui +58 -0
  33. vispy/app/tests/test_app.py +442 -0
  34. vispy/app/tests/test_backends.py +164 -0
  35. vispy/app/tests/test_canvas.py +122 -0
  36. vispy/app/tests/test_context.py +92 -0
  37. vispy/app/tests/test_qt.py +47 -0
  38. vispy/app/tests/test_simultaneous.py +134 -0
  39. vispy/app/timer.py +174 -0
  40. vispy/color/__init__.py +17 -0
  41. vispy/color/_color_dict.py +193 -0
  42. vispy/color/color_array.py +447 -0
  43. vispy/color/color_space.py +181 -0
  44. vispy/color/colormap.py +1213 -0
  45. vispy/color/tests/__init__.py +0 -0
  46. vispy/color/tests/test_color.py +378 -0
  47. vispy/conftest.py +12 -0
  48. vispy/ext/__init__.py +0 -0
  49. vispy/ext/cocoapy.py +1522 -0
  50. vispy/ext/cubehelix.py +138 -0
  51. vispy/ext/egl.py +375 -0
  52. vispy/ext/fontconfig.py +118 -0
  53. vispy/ext/gdi32plus.py +206 -0
  54. vispy/ext/osmesa.py +105 -0
  55. vispy/geometry/__init__.py +23 -0
  56. vispy/geometry/_triangulation_debugger.py +171 -0
  57. vispy/geometry/calculations.py +162 -0
  58. vispy/geometry/curves.py +399 -0
  59. vispy/geometry/generation.py +643 -0
  60. vispy/geometry/isocurve.py +175 -0
  61. vispy/geometry/isosurface.py +465 -0
  62. vispy/geometry/meshdata.py +700 -0
  63. vispy/geometry/normals.py +78 -0
  64. vispy/geometry/parametric.py +56 -0
  65. vispy/geometry/polygon.py +137 -0
  66. vispy/geometry/rect.py +210 -0
  67. vispy/geometry/tests/__init__.py +0 -0
  68. vispy/geometry/tests/test_calculations.py +23 -0
  69. vispy/geometry/tests/test_generation.py +56 -0
  70. vispy/geometry/tests/test_meshdata.py +106 -0
  71. vispy/geometry/tests/test_triangulation.py +594 -0
  72. vispy/geometry/torusknot.py +142 -0
  73. vispy/geometry/triangulation.py +876 -0
  74. vispy/gloo/__init__.py +56 -0
  75. vispy/gloo/buffer.py +505 -0
  76. vispy/gloo/context.py +272 -0
  77. vispy/gloo/framebuffer.py +257 -0
  78. vispy/gloo/gl/__init__.py +234 -0
  79. vispy/gloo/gl/_constants.py +332 -0
  80. vispy/gloo/gl/_es2.py +986 -0
  81. vispy/gloo/gl/_gl2.py +1365 -0
  82. vispy/gloo/gl/_proxy.py +499 -0
  83. vispy/gloo/gl/_pyopengl2.py +362 -0
  84. vispy/gloo/gl/dummy.py +24 -0
  85. vispy/gloo/gl/es2.py +62 -0
  86. vispy/gloo/gl/gl2.py +98 -0
  87. vispy/gloo/gl/glplus.py +168 -0
  88. vispy/gloo/gl/pyopengl2.py +97 -0
  89. vispy/gloo/gl/tests/__init__.py +0 -0
  90. vispy/gloo/gl/tests/test_basics.py +282 -0
  91. vispy/gloo/gl/tests/test_functionality.py +568 -0
  92. vispy/gloo/gl/tests/test_names.py +246 -0
  93. vispy/gloo/gl/tests/test_use.py +71 -0
  94. vispy/gloo/glir.py +1824 -0
  95. vispy/gloo/globject.py +101 -0
  96. vispy/gloo/preprocessor.py +67 -0
  97. vispy/gloo/program.py +543 -0
  98. vispy/gloo/tests/__init__.py +0 -0
  99. vispy/gloo/tests/test_buffer.py +558 -0
  100. vispy/gloo/tests/test_context.py +119 -0
  101. vispy/gloo/tests/test_framebuffer.py +195 -0
  102. vispy/gloo/tests/test_glir.py +307 -0
  103. vispy/gloo/tests/test_globject.py +35 -0
  104. vispy/gloo/tests/test_program.py +302 -0
  105. vispy/gloo/tests/test_texture.py +732 -0
  106. vispy/gloo/tests/test_use_gloo.py +187 -0
  107. vispy/gloo/tests/test_util.py +60 -0
  108. vispy/gloo/tests/test_wrappers.py +261 -0
  109. vispy/gloo/texture.py +1046 -0
  110. vispy/gloo/util.py +129 -0
  111. vispy/gloo/wrappers.py +762 -0
  112. vispy/glsl/__init__.py +42 -0
  113. vispy/glsl/antialias/antialias.glsl +7 -0
  114. vispy/glsl/antialias/cap-butt.glsl +31 -0
  115. vispy/glsl/antialias/cap-round.glsl +29 -0
  116. vispy/glsl/antialias/cap-square.glsl +30 -0
  117. vispy/glsl/antialias/cap-triangle-in.glsl +30 -0
  118. vispy/glsl/antialias/cap-triangle-out.glsl +30 -0
  119. vispy/glsl/antialias/cap.glsl +67 -0
  120. vispy/glsl/antialias/caps.glsl +67 -0
  121. vispy/glsl/antialias/filled.glsl +50 -0
  122. vispy/glsl/antialias/outline.glsl +40 -0
  123. vispy/glsl/antialias/stroke.glsl +43 -0
  124. vispy/glsl/arrowheads/angle.glsl +99 -0
  125. vispy/glsl/arrowheads/arrowheads.frag +60 -0
  126. vispy/glsl/arrowheads/arrowheads.glsl +12 -0
  127. vispy/glsl/arrowheads/arrowheads.vert +83 -0
  128. vispy/glsl/arrowheads/curved.glsl +48 -0
  129. vispy/glsl/arrowheads/inhibitor.glsl +26 -0
  130. vispy/glsl/arrowheads/stealth.glsl +46 -0
  131. vispy/glsl/arrowheads/triangle.glsl +97 -0
  132. vispy/glsl/arrowheads/util.glsl +13 -0
  133. vispy/glsl/arrows/angle-30.glsl +12 -0
  134. vispy/glsl/arrows/angle-60.glsl +12 -0
  135. vispy/glsl/arrows/angle-90.glsl +12 -0
  136. vispy/glsl/arrows/arrow.frag +39 -0
  137. vispy/glsl/arrows/arrow.vert +49 -0
  138. vispy/glsl/arrows/arrows.glsl +17 -0
  139. vispy/glsl/arrows/common.glsl +187 -0
  140. vispy/glsl/arrows/curved.glsl +63 -0
  141. vispy/glsl/arrows/stealth.glsl +50 -0
  142. vispy/glsl/arrows/triangle-30.glsl +12 -0
  143. vispy/glsl/arrows/triangle-60.glsl +12 -0
  144. vispy/glsl/arrows/triangle-90.glsl +12 -0
  145. vispy/glsl/arrows/util.glsl +98 -0
  146. vispy/glsl/build_spatial_filters.py +660 -0
  147. vispy/glsl/collections/agg-fast-path.frag +20 -0
  148. vispy/glsl/collections/agg-fast-path.vert +78 -0
  149. vispy/glsl/collections/agg-glyph.frag +60 -0
  150. vispy/glsl/collections/agg-glyph.vert +33 -0
  151. vispy/glsl/collections/agg-marker.frag +35 -0
  152. vispy/glsl/collections/agg-marker.vert +48 -0
  153. vispy/glsl/collections/agg-path.frag +55 -0
  154. vispy/glsl/collections/agg-path.vert +166 -0
  155. vispy/glsl/collections/agg-point.frag +21 -0
  156. vispy/glsl/collections/agg-point.vert +35 -0
  157. vispy/glsl/collections/agg-segment.frag +32 -0
  158. vispy/glsl/collections/agg-segment.vert +75 -0
  159. vispy/glsl/collections/marker.frag +38 -0
  160. vispy/glsl/collections/marker.vert +48 -0
  161. vispy/glsl/collections/raw-path.frag +15 -0
  162. vispy/glsl/collections/raw-path.vert +24 -0
  163. vispy/glsl/collections/raw-point.frag +14 -0
  164. vispy/glsl/collections/raw-point.vert +31 -0
  165. vispy/glsl/collections/raw-segment.frag +18 -0
  166. vispy/glsl/collections/raw-segment.vert +26 -0
  167. vispy/glsl/collections/raw-triangle.frag +13 -0
  168. vispy/glsl/collections/raw-triangle.vert +26 -0
  169. vispy/glsl/collections/sdf-glyph-ticks.vert +69 -0
  170. vispy/glsl/collections/sdf-glyph.frag +80 -0
  171. vispy/glsl/collections/sdf-glyph.vert +59 -0
  172. vispy/glsl/collections/tick-labels.vert +71 -0
  173. vispy/glsl/colormaps/autumn.glsl +20 -0
  174. vispy/glsl/colormaps/blues.glsl +20 -0
  175. vispy/glsl/colormaps/color-space.glsl +17 -0
  176. vispy/glsl/colormaps/colormaps.glsl +24 -0
  177. vispy/glsl/colormaps/cool.glsl +20 -0
  178. vispy/glsl/colormaps/fire.glsl +21 -0
  179. vispy/glsl/colormaps/gray.glsl +20 -0
  180. vispy/glsl/colormaps/greens.glsl +20 -0
  181. vispy/glsl/colormaps/hot.glsl +22 -0
  182. vispy/glsl/colormaps/ice.glsl +20 -0
  183. vispy/glsl/colormaps/icefire.glsl +23 -0
  184. vispy/glsl/colormaps/parse.py +40 -0
  185. vispy/glsl/colormaps/reds.glsl +20 -0
  186. vispy/glsl/colormaps/spring.glsl +20 -0
  187. vispy/glsl/colormaps/summer.glsl +20 -0
  188. vispy/glsl/colormaps/user.glsl +22 -0
  189. vispy/glsl/colormaps/util.glsl +41 -0
  190. vispy/glsl/colormaps/wheel.glsl +21 -0
  191. vispy/glsl/colormaps/winter.glsl +20 -0
  192. vispy/glsl/lines/agg.frag +320 -0
  193. vispy/glsl/lines/agg.vert +241 -0
  194. vispy/glsl/markers/arrow.glsl +12 -0
  195. vispy/glsl/markers/asterisk.glsl +16 -0
  196. vispy/glsl/markers/chevron.glsl +14 -0
  197. vispy/glsl/markers/clover.glsl +20 -0
  198. vispy/glsl/markers/club.glsl +31 -0
  199. vispy/glsl/markers/cross.glsl +17 -0
  200. vispy/glsl/markers/diamond.glsl +12 -0
  201. vispy/glsl/markers/disc.glsl +9 -0
  202. vispy/glsl/markers/ellipse.glsl +67 -0
  203. vispy/glsl/markers/hbar.glsl +9 -0
  204. vispy/glsl/markers/heart.glsl +15 -0
  205. vispy/glsl/markers/infinity.glsl +15 -0
  206. vispy/glsl/markers/marker-sdf.frag +74 -0
  207. vispy/glsl/markers/marker-sdf.vert +41 -0
  208. vispy/glsl/markers/marker.frag +36 -0
  209. vispy/glsl/markers/marker.vert +46 -0
  210. vispy/glsl/markers/markers.glsl +24 -0
  211. vispy/glsl/markers/pin.glsl +18 -0
  212. vispy/glsl/markers/ring.glsl +11 -0
  213. vispy/glsl/markers/spade.glsl +28 -0
  214. vispy/glsl/markers/square.glsl +10 -0
  215. vispy/glsl/markers/tag.glsl +11 -0
  216. vispy/glsl/markers/triangle.glsl +14 -0
  217. vispy/glsl/markers/vbar.glsl +9 -0
  218. vispy/glsl/math/circle-through-2-points.glsl +30 -0
  219. vispy/glsl/math/constants.glsl +48 -0
  220. vispy/glsl/math/double.glsl +114 -0
  221. vispy/glsl/math/functions.glsl +20 -0
  222. vispy/glsl/math/point-to-line-distance.glsl +31 -0
  223. vispy/glsl/math/point-to-line-projection.glsl +29 -0
  224. vispy/glsl/math/signed-line-distance.glsl +27 -0
  225. vispy/glsl/math/signed-segment-distance.glsl +30 -0
  226. vispy/glsl/misc/regular-grid.frag +244 -0
  227. vispy/glsl/misc/spatial-filters.frag +1407 -0
  228. vispy/glsl/misc/viewport-NDC.glsl +20 -0
  229. vispy/glsl/transforms/azimuthal-equal-area.glsl +32 -0
  230. vispy/glsl/transforms/azimuthal-equidistant.glsl +38 -0
  231. vispy/glsl/transforms/hammer.glsl +44 -0
  232. vispy/glsl/transforms/identity.glsl +6 -0
  233. vispy/glsl/transforms/identity_forward.glsl +23 -0
  234. vispy/glsl/transforms/identity_inverse.glsl +23 -0
  235. vispy/glsl/transforms/linear-scale.glsl +127 -0
  236. vispy/glsl/transforms/log-scale.glsl +126 -0
  237. vispy/glsl/transforms/mercator-transverse-forward.glsl +40 -0
  238. vispy/glsl/transforms/mercator-transverse-inverse.glsl +40 -0
  239. vispy/glsl/transforms/panzoom.glsl +10 -0
  240. vispy/glsl/transforms/polar.glsl +41 -0
  241. vispy/glsl/transforms/position.glsl +44 -0
  242. vispy/glsl/transforms/power-scale.glsl +139 -0
  243. vispy/glsl/transforms/projection.glsl +7 -0
  244. vispy/glsl/transforms/pvm.glsl +13 -0
  245. vispy/glsl/transforms/rotate.glsl +45 -0
  246. vispy/glsl/transforms/trackball.glsl +15 -0
  247. vispy/glsl/transforms/translate.glsl +35 -0
  248. vispy/glsl/transforms/transverse_mercator.glsl +38 -0
  249. vispy/glsl/transforms/viewport-clipping.glsl +14 -0
  250. vispy/glsl/transforms/viewport-transform.glsl +16 -0
  251. vispy/glsl/transforms/viewport.glsl +50 -0
  252. vispy/glsl/transforms/x.glsl +24 -0
  253. vispy/glsl/transforms/y.glsl +19 -0
  254. vispy/glsl/transforms/z.glsl +14 -0
  255. vispy/io/__init__.py +20 -0
  256. vispy/io/_data/spatial-filters.npy +0 -0
  257. vispy/io/datasets.py +94 -0
  258. vispy/io/image.py +231 -0
  259. vispy/io/mesh.py +122 -0
  260. vispy/io/stl.py +167 -0
  261. vispy/io/tests/__init__.py +0 -0
  262. vispy/io/tests/test_image.py +47 -0
  263. vispy/io/tests/test_io.py +121 -0
  264. vispy/io/wavefront.py +350 -0
  265. vispy/plot/__init__.py +36 -0
  266. vispy/plot/fig.py +58 -0
  267. vispy/plot/plotwidget.py +522 -0
  268. vispy/plot/tests/__init__.py +0 -0
  269. vispy/plot/tests/test_plot.py +46 -0
  270. vispy/scene/__init__.py +43 -0
  271. vispy/scene/cameras/__init__.py +27 -0
  272. vispy/scene/cameras/_base.py +38 -0
  273. vispy/scene/cameras/arcball.py +105 -0
  274. vispy/scene/cameras/base_camera.py +551 -0
  275. vispy/scene/cameras/fly.py +474 -0
  276. vispy/scene/cameras/magnify.py +163 -0
  277. vispy/scene/cameras/panzoom.py +311 -0
  278. vispy/scene/cameras/perspective.py +338 -0
  279. vispy/scene/cameras/tests/__init__.py +0 -0
  280. vispy/scene/cameras/tests/test_cameras.py +27 -0
  281. vispy/scene/cameras/tests/test_link.py +53 -0
  282. vispy/scene/cameras/tests/test_perspective.py +122 -0
  283. vispy/scene/cameras/turntable.py +183 -0
  284. vispy/scene/canvas.py +639 -0
  285. vispy/scene/events.py +85 -0
  286. vispy/scene/node.py +644 -0
  287. vispy/scene/subscene.py +20 -0
  288. vispy/scene/tests/__init__.py +0 -0
  289. vispy/scene/tests/test_canvas.py +119 -0
  290. vispy/scene/tests/test_node.py +142 -0
  291. vispy/scene/tests/test_visuals.py +141 -0
  292. vispy/scene/visuals.py +276 -0
  293. vispy/scene/widgets/__init__.py +18 -0
  294. vispy/scene/widgets/anchor.py +25 -0
  295. vispy/scene/widgets/axis.py +88 -0
  296. vispy/scene/widgets/colorbar.py +176 -0
  297. vispy/scene/widgets/console.py +351 -0
  298. vispy/scene/widgets/grid.py +509 -0
  299. vispy/scene/widgets/label.py +50 -0
  300. vispy/scene/widgets/tests/__init__.py +0 -0
  301. vispy/scene/widgets/tests/test_colorbar.py +47 -0
  302. vispy/scene/widgets/viewbox.py +199 -0
  303. vispy/scene/widgets/widget.py +478 -0
  304. vispy/testing/__init__.py +51 -0
  305. vispy/testing/_runners.py +448 -0
  306. vispy/testing/_testing.py +416 -0
  307. vispy/testing/image_tester.py +494 -0
  308. vispy/testing/rendered_array_tester.py +85 -0
  309. vispy/testing/tests/__init__.py +0 -0
  310. vispy/testing/tests/test_testing.py +20 -0
  311. vispy/util/__init__.py +32 -0
  312. vispy/util/bunch.py +15 -0
  313. vispy/util/check_environment.py +57 -0
  314. vispy/util/config.py +490 -0
  315. vispy/util/dpi/__init__.py +19 -0
  316. vispy/util/dpi/_linux.py +69 -0
  317. vispy/util/dpi/_quartz.py +26 -0
  318. vispy/util/dpi/_win32.py +34 -0
  319. vispy/util/dpi/tests/__init__.py +0 -0
  320. vispy/util/dpi/tests/test_dpi.py +16 -0
  321. vispy/util/eq.py +41 -0
  322. vispy/util/event.py +774 -0
  323. vispy/util/fetching.py +276 -0
  324. vispy/util/filter.py +44 -0
  325. vispy/util/fonts/__init__.py +14 -0
  326. vispy/util/fonts/_freetype.py +73 -0
  327. vispy/util/fonts/_quartz.py +192 -0
  328. vispy/util/fonts/_triage.py +36 -0
  329. vispy/util/fonts/_vispy_fonts.py +20 -0
  330. vispy/util/fonts/_win32.py +105 -0
  331. vispy/util/fonts/data/OpenSans-Bold.ttf +0 -0
  332. vispy/util/fonts/data/OpenSans-BoldItalic.ttf +0 -0
  333. vispy/util/fonts/data/OpenSans-Italic.ttf +0 -0
  334. vispy/util/fonts/data/OpenSans-Regular.ttf +0 -0
  335. vispy/util/fonts/tests/__init__.py +0 -0
  336. vispy/util/fonts/tests/test_font.py +45 -0
  337. vispy/util/fourier.py +69 -0
  338. vispy/util/frozen.py +25 -0
  339. vispy/util/gallery_scraper.py +268 -0
  340. vispy/util/keys.py +91 -0
  341. vispy/util/logs.py +358 -0
  342. vispy/util/osmesa_gl.py +17 -0
  343. vispy/util/profiler.py +135 -0
  344. vispy/util/ptime.py +16 -0
  345. vispy/util/quaternion.py +229 -0
  346. vispy/util/svg/__init__.py +18 -0
  347. vispy/util/svg/base.py +20 -0
  348. vispy/util/svg/color.py +219 -0
  349. vispy/util/svg/element.py +51 -0
  350. vispy/util/svg/geometry.py +478 -0
  351. vispy/util/svg/group.py +66 -0
  352. vispy/util/svg/length.py +81 -0
  353. vispy/util/svg/number.py +25 -0
  354. vispy/util/svg/path.py +332 -0
  355. vispy/util/svg/shapes.py +57 -0
  356. vispy/util/svg/style.py +59 -0
  357. vispy/util/svg/svg.py +40 -0
  358. vispy/util/svg/transform.py +223 -0
  359. vispy/util/svg/transformable.py +28 -0
  360. vispy/util/svg/viewport.py +73 -0
  361. vispy/util/tests/__init__.py +0 -0
  362. vispy/util/tests/test_config.py +58 -0
  363. vispy/util/tests/test_docstring_parameters.py +123 -0
  364. vispy/util/tests/test_emitter_group.py +262 -0
  365. vispy/util/tests/test_event_emitter.py +743 -0
  366. vispy/util/tests/test_fourier.py +35 -0
  367. vispy/util/tests/test_gallery_scraper.py +112 -0
  368. vispy/util/tests/test_import.py +127 -0
  369. vispy/util/tests/test_key.py +22 -0
  370. vispy/util/tests/test_logging.py +45 -0
  371. vispy/util/tests/test_run.py +14 -0
  372. vispy/util/tests/test_transforms.py +42 -0
  373. vispy/util/tests/test_vispy.py +48 -0
  374. vispy/util/transforms.py +201 -0
  375. vispy/util/wrappers.py +155 -0
  376. vispy/version.py +21 -0
  377. vispy/visuals/__init__.py +50 -0
  378. vispy/visuals/_scalable_textures.py +487 -0
  379. vispy/visuals/axis.py +678 -0
  380. vispy/visuals/border.py +208 -0
  381. vispy/visuals/box.py +79 -0
  382. vispy/visuals/collections/__init__.py +30 -0
  383. vispy/visuals/collections/agg_fast_path_collection.py +219 -0
  384. vispy/visuals/collections/agg_path_collection.py +197 -0
  385. vispy/visuals/collections/agg_point_collection.py +52 -0
  386. vispy/visuals/collections/agg_segment_collection.py +142 -0
  387. vispy/visuals/collections/array_list.py +401 -0
  388. vispy/visuals/collections/base_collection.py +482 -0
  389. vispy/visuals/collections/collection.py +253 -0
  390. vispy/visuals/collections/path_collection.py +23 -0
  391. vispy/visuals/collections/point_collection.py +19 -0
  392. vispy/visuals/collections/polygon_collection.py +25 -0
  393. vispy/visuals/collections/raw_path_collection.py +119 -0
  394. vispy/visuals/collections/raw_point_collection.py +113 -0
  395. vispy/visuals/collections/raw_polygon_collection.py +77 -0
  396. vispy/visuals/collections/raw_segment_collection.py +112 -0
  397. vispy/visuals/collections/raw_triangle_collection.py +78 -0
  398. vispy/visuals/collections/segment_collection.py +19 -0
  399. vispy/visuals/collections/triangle_collection.py +16 -0
  400. vispy/visuals/collections/util.py +168 -0
  401. vispy/visuals/colorbar.py +699 -0
  402. vispy/visuals/cube.py +41 -0
  403. vispy/visuals/ellipse.py +162 -0
  404. vispy/visuals/filters/__init__.py +10 -0
  405. vispy/visuals/filters/base_filter.py +242 -0
  406. vispy/visuals/filters/clipper.py +60 -0
  407. vispy/visuals/filters/clipping_planes.py +122 -0
  408. vispy/visuals/filters/color.py +181 -0
  409. vispy/visuals/filters/markers.py +28 -0
  410. vispy/visuals/filters/mesh.py +801 -0
  411. vispy/visuals/filters/picking.py +60 -0
  412. vispy/visuals/filters/tests/__init__.py +3 -0
  413. vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
  414. vispy/visuals/filters/tests/test_wireframe_filter.py +16 -0
  415. vispy/visuals/glsl/__init__.py +1 -0
  416. vispy/visuals/glsl/antialiasing.py +133 -0
  417. vispy/visuals/glsl/color.py +63 -0
  418. vispy/visuals/graphs/__init__.py +1 -0
  419. vispy/visuals/graphs/graph.py +240 -0
  420. vispy/visuals/graphs/layouts/__init__.py +55 -0
  421. vispy/visuals/graphs/layouts/circular.py +49 -0
  422. vispy/visuals/graphs/layouts/force_directed.py +211 -0
  423. vispy/visuals/graphs/layouts/networkx_layout.py +87 -0
  424. vispy/visuals/graphs/layouts/random.py +52 -0
  425. vispy/visuals/graphs/tests/__init__.py +1 -0
  426. vispy/visuals/graphs/tests/test_layouts.py +139 -0
  427. vispy/visuals/graphs/tests/test_networkx_layout.py +47 -0
  428. vispy/visuals/graphs/util.py +120 -0
  429. vispy/visuals/gridlines.py +161 -0
  430. vispy/visuals/gridmesh.py +98 -0
  431. vispy/visuals/histogram.py +58 -0
  432. vispy/visuals/image.py +701 -0
  433. vispy/visuals/image_complex.py +130 -0
  434. vispy/visuals/infinite_line.py +199 -0
  435. vispy/visuals/instanced_mesh.py +152 -0
  436. vispy/visuals/isocurve.py +213 -0
  437. vispy/visuals/isoline.py +241 -0
  438. vispy/visuals/isosurface.py +113 -0
  439. vispy/visuals/line/__init__.py +6 -0
  440. vispy/visuals/line/arrow.py +289 -0
  441. vispy/visuals/line/dash_atlas.py +90 -0
  442. vispy/visuals/line/line.py +545 -0
  443. vispy/visuals/line_plot.py +135 -0
  444. vispy/visuals/linear_region.py +199 -0
  445. vispy/visuals/markers.py +819 -0
  446. vispy/visuals/mesh.py +373 -0
  447. vispy/visuals/mesh_normals.py +159 -0
  448. vispy/visuals/plane.py +54 -0
  449. vispy/visuals/polygon.py +145 -0
  450. vispy/visuals/rectangle.py +196 -0
  451. vispy/visuals/regular_polygon.py +56 -0
  452. vispy/visuals/scrolling_lines.py +197 -0
  453. vispy/visuals/shaders/__init__.py +17 -0
  454. vispy/visuals/shaders/compiler.py +206 -0
  455. vispy/visuals/shaders/expression.py +99 -0
  456. vispy/visuals/shaders/function.py +788 -0
  457. vispy/visuals/shaders/multiprogram.py +145 -0
  458. vispy/visuals/shaders/parsing.py +140 -0
  459. vispy/visuals/shaders/program.py +161 -0
  460. vispy/visuals/shaders/shader_object.py +162 -0
  461. vispy/visuals/shaders/tests/__init__.py +0 -0
  462. vispy/visuals/shaders/tests/test_function.py +486 -0
  463. vispy/visuals/shaders/tests/test_multiprogram.py +78 -0
  464. vispy/visuals/shaders/tests/test_parsing.py +57 -0
  465. vispy/visuals/shaders/variable.py +272 -0
  466. vispy/visuals/spectrogram.py +169 -0
  467. vispy/visuals/sphere.py +80 -0
  468. vispy/visuals/surface_plot.py +192 -0
  469. vispy/visuals/tests/__init__.py +0 -0
  470. vispy/visuals/tests/test_arrows.py +109 -0
  471. vispy/visuals/tests/test_axis.py +120 -0
  472. vispy/visuals/tests/test_collections.py +15 -0
  473. vispy/visuals/tests/test_colorbar.py +179 -0
  474. vispy/visuals/tests/test_colormap.py +97 -0
  475. vispy/visuals/tests/test_ellipse.py +122 -0
  476. vispy/visuals/tests/test_gridlines.py +30 -0
  477. vispy/visuals/tests/test_histogram.py +24 -0
  478. vispy/visuals/tests/test_image.py +392 -0
  479. vispy/visuals/tests/test_image_complex.py +36 -0
  480. vispy/visuals/tests/test_infinite_line.py +53 -0
  481. vispy/visuals/tests/test_instanced_mesh.py +50 -0
  482. vispy/visuals/tests/test_isosurface.py +22 -0
  483. vispy/visuals/tests/test_linear_region.py +152 -0
  484. vispy/visuals/tests/test_markers.py +54 -0
  485. vispy/visuals/tests/test_mesh.py +261 -0
  486. vispy/visuals/tests/test_mesh_normals.py +218 -0
  487. vispy/visuals/tests/test_polygon.py +112 -0
  488. vispy/visuals/tests/test_rectangle.py +163 -0
  489. vispy/visuals/tests/test_regular_polygon.py +111 -0
  490. vispy/visuals/tests/test_scalable_textures.py +196 -0
  491. vispy/visuals/tests/test_sdf.py +73 -0
  492. vispy/visuals/tests/test_spectrogram.py +42 -0
  493. vispy/visuals/tests/test_surface_plot.py +57 -0
  494. vispy/visuals/tests/test_text.py +95 -0
  495. vispy/visuals/tests/test_volume.py +542 -0
  496. vispy/visuals/tests/test_windbarb.py +33 -0
  497. vispy/visuals/text/__init__.py +7 -0
  498. vispy/visuals/text/_sdf_cpu.cpython-313-aarch64-linux-gnu.so +0 -0
  499. vispy/visuals/text/_sdf_cpu.pyx +112 -0
  500. vispy/visuals/text/_sdf_gpu.py +316 -0
  501. vispy/visuals/text/text.py +675 -0
  502. vispy/visuals/transforms/__init__.py +34 -0
  503. vispy/visuals/transforms/_util.py +191 -0
  504. vispy/visuals/transforms/base_transform.py +233 -0
  505. vispy/visuals/transforms/chain.py +300 -0
  506. vispy/visuals/transforms/interactive.py +98 -0
  507. vispy/visuals/transforms/linear.py +564 -0
  508. vispy/visuals/transforms/nonlinear.py +398 -0
  509. vispy/visuals/transforms/tests/__init__.py +0 -0
  510. vispy/visuals/transforms/tests/test_transforms.py +243 -0
  511. vispy/visuals/transforms/transform_system.py +339 -0
  512. vispy/visuals/tube.py +173 -0
  513. vispy/visuals/visual.py +923 -0
  514. vispy/visuals/volume.py +1366 -0
  515. vispy/visuals/windbarb.py +291 -0
  516. vispy/visuals/xyz_axis.py +34 -0
  517. vispy-0.15.0.dist-info/METADATA +243 -0
  518. vispy-0.15.0.dist-info/RECORD +521 -0
  519. vispy-0.15.0.dist-info/WHEEL +6 -0
  520. vispy-0.15.0.dist-info/licenses/LICENSE.txt +36 -0
  521. vispy-0.15.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,876 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import division, print_function
4
+
5
+ from itertools import permutations
6
+ import numpy as np
7
+
8
+ from collections import OrderedDict
9
+
10
+ from .calculations import _cross_2d
11
+
12
+
13
+ class Triangulation(object):
14
+ """Constrained delaunay triangulation
15
+
16
+ Implementation based on [1]_.
17
+
18
+ Parameters
19
+ ----------
20
+ pts : array
21
+ Nx2 array of points.
22
+ edges : array
23
+ Nx2 array of edges (dtype=int).
24
+
25
+ Notes
26
+ -----
27
+ * Delaunay legalization is not yet implemented. This produces a proper
28
+ triangulation, but adding legalisation would produce fewer thin
29
+ triangles.
30
+ * The pts and edges arrays may be modified.
31
+
32
+ References
33
+ ----------
34
+ .. [1] Domiter, V. and Žalik, B. Sweep‐line algorithm for constrained
35
+ Delaunay triangulation
36
+ """
37
+
38
+ def __init__(self, pts, edges):
39
+ self.pts = pts[:, :2].astype(np.float32)
40
+ self.edges = edges
41
+ if self.pts.ndim != 2 or self.pts.shape[1] != 2:
42
+ raise TypeError('pts argument must be ndarray of shape (N, 2).')
43
+ if self.edges.ndim != 2 or self.edges.shape[1] != 2:
44
+ raise TypeError('edges argument must be ndarray of shape (N, 2).')
45
+
46
+ # described in initialize()
47
+ self._front = None
48
+ self.tris = OrderedDict()
49
+ self._edges_lookup = {}
50
+
51
+ def _normalize(self):
52
+ # Clean up data (not discussed in original publication)
53
+
54
+ # (i) Split intersecting edges. Every edge that intersects another
55
+ # edge or point is split. This extends self.pts and self.edges.
56
+ self._split_intersecting_edges()
57
+
58
+ # (ii) Merge identical points. If any two points are found to be equal,
59
+ # the second is removed and the edge table is updated accordingly.
60
+ self._merge_duplicate_points()
61
+
62
+ # (iii) Remove duplicate edges
63
+ # TODO
64
+
65
+ def _initialize(self):
66
+ self._normalize()
67
+ # Initialization (sec. 3.3)
68
+
69
+ # sort points by y, then x
70
+ flat_shape = self.pts.shape[0] * self.pts.shape[1]
71
+ pts = self.pts.reshape(flat_shape).view([('x', np.float32),
72
+ ('y', np.float32)])
73
+ order = np.argsort(pts, order=('y', 'x'))
74
+ pts = pts[order]
75
+ # update edges to match new point order
76
+ invorder = np.argsort(order)
77
+ self.edges = invorder[self.edges]
78
+ self.pts = pts.view(np.float32).reshape(len(pts), 2)
79
+
80
+ # make artificial points P-1 and P-2
81
+ xmax = self.pts[:, 0].max()
82
+ xmin = self.pts[:, 0].min()
83
+ ymax = self.pts[:, 1].max()
84
+ ymin = self.pts[:, 1].min()
85
+ xa = (xmax-xmin) * 0.3
86
+ ya = (ymax-ymin) * 0.3
87
+ p1 = (xmin - xa, ymin - ya)
88
+ p2 = (xmax + xa, ymin - ya)
89
+
90
+ # prepend artificial points to point list
91
+ newpts = np.empty((self.pts.shape[0]+2, 2), dtype=float)
92
+ newpts[0] = p1
93
+ newpts[1] = p2
94
+ newpts[2:] = self.pts
95
+ self.pts = newpts
96
+ self.edges += 2
97
+
98
+ # find topmost point in each edge
99
+ self._tops = self.edges.max(axis=1)
100
+ self._bottoms = self.edges.min(axis=1)
101
+
102
+ # initialize sweep front
103
+ # values in this list are indexes into self.pts
104
+ self._front = [0, 2, 1]
105
+
106
+ # empty triangle list.
107
+ # This will contain [(a, b, c), ...] where a,b,c are indexes into
108
+ # self.pts
109
+ self.tris = OrderedDict()
110
+
111
+ # For each triangle, maps (a, b): c
112
+ # This is used to look up the thrid point in a triangle, given any
113
+ # edge. Since each edge has two triangles, they are independently
114
+ # stored as (a, b): c and (b, a): d
115
+ self._edges_lookup = {}
116
+
117
+ def triangulate(self):
118
+ """Do the triangulation."""
119
+ self._initialize()
120
+
121
+ pts = self.pts
122
+ front = self._front
123
+
124
+ # Begin sweep (sec. 3.4)
125
+ for i in range(3, pts.shape[0]):
126
+ pi = pts[i]
127
+
128
+ # First, triangulate from front to new point
129
+ # This applies to both "point events" (3.4.1)
130
+ # and "edge events" (3.4.2).
131
+
132
+ # get index along front that intersects pts[i]
133
+ idx = 0
134
+ while pts[front[idx+1], 0] <= pi[0]:
135
+ idx += 1
136
+ pl = pts[front[idx]]
137
+
138
+ # "(i) middle case"
139
+ if pi[0] > pl[0]:
140
+ # Add a single triangle connecting pi,pl,pr
141
+ self._add_tri(front[idx], front[idx+1], i)
142
+ front.insert(idx+1, i)
143
+ # "(ii) left case"
144
+ else:
145
+ # Add triangles connecting pi,pl,ps and pi,pl,pr
146
+ self._add_tri(front[idx], front[idx+1], i)
147
+ self._add_tri(front[idx-1], front[idx], i)
148
+ front[idx] = i
149
+
150
+ # Continue adding triangles to smooth out front
151
+ # (heuristics shown in figs. 9, 10)
152
+ for direction in -1, 1:
153
+ while True:
154
+ # Find point connected to pi
155
+ ind0 = front.index(i)
156
+ ind1 = ind0 + direction
157
+ ind2 = ind1 + direction
158
+ if ind2 < 0 or ind2 >= len(front):
159
+ break
160
+
161
+ # measure angle made with front
162
+ p1 = pts[front[ind1]]
163
+ p2 = pts[front[ind2]]
164
+ err = np.geterr()
165
+ np.seterr(invalid='ignore')
166
+ try:
167
+ angle = np.arccos(self._cosine(pi, p1, p2))
168
+ finally:
169
+ np.seterr(**err)
170
+
171
+ # if angle is < pi/2, make new triangle
172
+ if angle > np.pi/2. or np.isnan(angle):
173
+ break
174
+
175
+ assert (i != front[ind1] and
176
+ front[ind1] != front[ind2] and
177
+ front[ind2] != i)
178
+ self._add_tri(i, front[ind1], front[ind2])
179
+ front.pop(ind1)
180
+
181
+ # "edge event" (sec. 3.4.2)
182
+ # remove any triangles cut by completed edges and re-fill
183
+ # the holes.
184
+ if i in self._tops:
185
+ for j in self._bottoms[self._tops == i]:
186
+ # Make sure edge (j, i) is present in mesh
187
+ # because edge event may have created a new front list
188
+ self._edge_event(i, int(j))
189
+ front = self._front
190
+
191
+ self._finalize()
192
+
193
+ self.tris = np.array(list(self.tris.keys()), dtype=int)
194
+
195
+ def _finalize(self):
196
+ # Finalize (sec. 3.5)
197
+
198
+ # (i) Add bordering triangles to fill hull
199
+ front = list(OrderedDict.fromkeys(self._front))
200
+
201
+ idx = len(front) - 2
202
+ k = 1
203
+ while k < idx-1:
204
+ # if edges lie in counterclockwise direction, then signed area
205
+ # is positive
206
+ if self._orientation((front[k], front[k+1]), front[k+2]) < 0:
207
+ self._add_tri(front[k], front[k+1], front[k+2])
208
+ front.pop(k+1)
209
+ idx -= 1
210
+ continue
211
+ k += 1
212
+
213
+ # (ii) Remove all triangles not inside the hull
214
+ # (not described in article)
215
+
216
+ tris = [] # triangles to check
217
+ tri_state = {} # 0 for outside, 1 for inside
218
+
219
+ # find a starting triangle
220
+ for t in self.tris:
221
+ if 0 in t or 1 in t:
222
+ tri_state[t] = 0
223
+ tris.append(t)
224
+ break
225
+
226
+ while tris:
227
+ next_tris = []
228
+ for t in tris:
229
+ v = tri_state[t]
230
+ for i in (0, 1, 2):
231
+ edge = (t[i], t[(i + 1) % 3])
232
+ pt = t[(i + 2) % 3]
233
+ t2 = self._adjacent_tri(edge, pt)
234
+ if t2 is None:
235
+ continue
236
+ t2a = t2[1:3] + t2[0:1]
237
+ t2b = t2[2:3] + t2[0:2]
238
+ if t2 in tri_state or t2a in tri_state or t2b in tri_state:
239
+ continue
240
+ if self._is_constraining_edge(edge):
241
+ tri_state[t2] = 1 - v
242
+ else:
243
+ tri_state[t2] = v
244
+ next_tris.append(t2)
245
+ tris = next_tris
246
+
247
+ for t, v in tri_state.items():
248
+ if v == 0:
249
+ self._remove_tri(*t)
250
+
251
+ def _edge_event(self, i, j):
252
+ """Force edge (i, j) to be present in mesh.
253
+
254
+ This works by removing intersected triangles and filling holes up to
255
+ the cutting edge.
256
+ """
257
+ front_index = self._front.index(i)
258
+
259
+ front = self._front
260
+
261
+ # First just see whether this edge is already present
262
+ # (this is not in the published algorithm)
263
+ if (i, j) in self._edges_lookup or (j, i) in self._edges_lookup:
264
+ return
265
+
266
+ # traverse in two different modes:
267
+ # 1. If cutting edge is below front, traverse through triangles. These
268
+ # must be removed and the resulting hole re-filled. (fig. 12)
269
+ # 2. If cutting edge is above the front, then follow the front until
270
+ # crossing under again. (fig. 13)
271
+ # We must be able to switch back and forth between these
272
+ # modes (fig. 14)
273
+
274
+ # Collect points that draw the open polygons on either side of the
275
+ # cutting edge. Note that our use of 'upper' and 'lower' is not strict;
276
+ # in some cases the two may be swapped.
277
+ upper_polygon = [i]
278
+ lower_polygon = [i]
279
+
280
+ # Keep track of which section of the front must be replaced
281
+ # and with what it should be replaced
282
+ front_holes = [] # contains indexes for sections of front to remove
283
+
284
+ next_tri = None # next triangle to cut (already set if in mode 1)
285
+ last_edge = None # or last triangle edge crossed (if in mode 1)
286
+
287
+ # Which direction to traverse front
288
+ front_dir = 1 if self.pts[j][0] > self.pts[i][0] else -1
289
+
290
+ # Initialize search state
291
+ if self._edge_below_front((i, j), front_index):
292
+ mode = 1 # follow triangles
293
+ tri = self._find_cut_triangle((i, j))
294
+ last_edge = self._edge_opposite_point(tri, i)
295
+ next_tri = self._adjacent_tri(last_edge, i)
296
+ assert next_tri is not None
297
+ self._remove_tri(*tri)
298
+ # todo: does this work? can we count on last_edge to be clockwise
299
+ # around point i?
300
+ lower_polygon.append(last_edge[1])
301
+ upper_polygon.append(last_edge[0])
302
+ else:
303
+ mode = 2 # follow front
304
+
305
+ # Loop until we reach point j
306
+ while True:
307
+ if mode == 1:
308
+ # crossing from one triangle into another
309
+ if j in next_tri:
310
+ # reached endpoint!
311
+ # update front / polygons
312
+ upper_polygon.append(j)
313
+ lower_polygon.append(j)
314
+ self._remove_tri(*next_tri)
315
+ break
316
+ else:
317
+ # next triangle does not contain the end point; we will
318
+ # cut one of the two far edges.
319
+ tri_edges = self._edges_in_tri_except(next_tri, last_edge)
320
+
321
+ # select the edge that is cut
322
+ last_edge = self._intersected_edge(tri_edges, (i, j))
323
+ last_tri = next_tri
324
+ next_tri = self._adjacent_tri(last_edge, last_tri)
325
+ self._remove_tri(*last_tri)
326
+
327
+ # Crossing an edge adds one point to one of the polygons
328
+ if lower_polygon[-1] == last_edge[0]:
329
+ upper_polygon.append(last_edge[1])
330
+ elif lower_polygon[-1] == last_edge[1]:
331
+ upper_polygon.append(last_edge[0])
332
+ elif upper_polygon[-1] == last_edge[0]:
333
+ lower_polygon.append(last_edge[1])
334
+ elif upper_polygon[-1] == last_edge[1]:
335
+ lower_polygon.append(last_edge[0])
336
+ else:
337
+ raise RuntimeError("Something went wrong..")
338
+
339
+ # If we crossed the front, go to mode 2
340
+ x = self._edge_in_front(last_edge)
341
+ if x >= 0: # crossing over front
342
+ mode = 2
343
+ next_tri = None
344
+
345
+ # where did we cross the front?
346
+ # nearest to new point
347
+ front_index = x + (1 if front_dir == -1 else 0)
348
+
349
+ # Select the correct polygon to be lower_polygon
350
+ # (because mode 2 requires this).
351
+ # We know that last_edge is in the front, and
352
+ # front[front_index] is the point _above_ the front.
353
+ # So if this point is currently the last element in
354
+ # lower_polygon, then the polys must be swapped.
355
+ if lower_polygon[-1] == front[front_index]:
356
+ tmp = lower_polygon, upper_polygon
357
+ upper_polygon, lower_polygon = tmp
358
+ else:
359
+ assert upper_polygon[-1] == front[front_index]
360
+
361
+ else:
362
+ assert next_tri is not None
363
+
364
+ else: # mode == 2
365
+ # At each iteration, we require:
366
+ # * front_index is the starting index of the edge _preceding_
367
+ # the edge that will be handled in this iteration
368
+ # * lower_polygon is the polygon to which points should be
369
+ # added while traversing the front
370
+
371
+ front_index += front_dir
372
+ next_edge = (front[front_index], front[front_index+front_dir])
373
+
374
+ assert front_index >= 0
375
+ if front[front_index] == j:
376
+ # found endpoint!
377
+ lower_polygon.append(j)
378
+ upper_polygon.append(j)
379
+ break
380
+
381
+ # Add point to lower_polygon.
382
+ # The conditional is because there are cases where the
383
+ # point was already added if we just crossed from mode 1.
384
+ if lower_polygon[-1] != front[front_index]:
385
+ lower_polygon.append(front[front_index])
386
+
387
+ front_holes.append(front_index)
388
+
389
+ if self._edges_intersect((i, j), next_edge):
390
+ # crossing over front into triangle
391
+ mode = 1
392
+
393
+ last_edge = next_edge
394
+
395
+ # we are crossing the front, so this edge only has one
396
+ # triangle.
397
+ next_tri = self._tri_from_edge(last_edge)
398
+
399
+ upper_polygon.append(front[front_index+front_dir])
400
+
401
+ # (iii) triangulate empty areas
402
+
403
+ for polygon in [lower_polygon, upper_polygon]:
404
+ dist = self._distances_from_line((i, j), polygon)
405
+ while len(polygon) > 2:
406
+ ind = np.argmax(dist)
407
+ self._add_tri(polygon[ind], polygon[ind-1],
408
+ polygon[ind+1])
409
+ polygon.pop(ind)
410
+ dist.pop(ind)
411
+
412
+ # update front by removing points in the holes (places where front
413
+ # passes below the cut edge)
414
+ front_holes.sort(reverse=True)
415
+ for i in front_holes:
416
+ front.pop(i)
417
+
418
+ def _find_cut_triangle(self, edge):
419
+ """
420
+ Return the triangle that has edge[0] as one of its vertices and is
421
+ bisected by edge.
422
+
423
+ Return None if no triangle is found.
424
+ """
425
+ edges = [] # opposite edge for each triangle attached to edge[0]
426
+ for tri in self.tris:
427
+ if edge[0] in tri:
428
+ edges.append(self._edge_opposite_point(tri, edge[0]))
429
+
430
+ for oedge in edges:
431
+ o1 = self._orientation(edge, oedge[0])
432
+ o2 = self._orientation(edge, oedge[1])
433
+ if o1 != o2:
434
+ return (edge[0], oedge[0], oedge[1])
435
+
436
+ return None
437
+
438
+ def _edge_in_front(self, edge):
439
+ """Return the index where *edge* appears in the current front.
440
+
441
+ If the edge is not in the front, return -1
442
+ """
443
+ e = (list(edge), list(edge)[::-1])
444
+ for i in range(len(self._front)-1):
445
+ if self._front[i:i+2] in e:
446
+ return i
447
+ return -1
448
+
449
+ def _edge_opposite_point(self, tri, i):
450
+ """Given a triangle, return the edge that is opposite point i.
451
+
452
+ Vertexes are returned in the same orientation as in tri.
453
+ """
454
+ ind = tri.index(i)
455
+ return (tri[(ind+1) % 3], tri[(ind+2) % 3])
456
+
457
+ def _adjacent_tri(self, edge, i):
458
+ """Given a triangle formed by edge and i, return the triangle that shares
459
+ edge. *i* may be either a point or the entire triangle.
460
+ """
461
+ if not np.isscalar(i):
462
+ i = [x for x in i if x not in edge][0]
463
+
464
+ try:
465
+ pt1 = self._edges_lookup[edge]
466
+ pt2 = self._edges_lookup[(edge[1], edge[0])]
467
+ except KeyError:
468
+ return None
469
+
470
+ if pt1 == i:
471
+ return (edge[1], edge[0], pt2)
472
+ elif pt2 == i:
473
+ return (edge[1], edge[0], pt1)
474
+ else:
475
+ raise RuntimeError("Edge %s and point %d do not form a triangle "
476
+ "in this mesh." % (edge, i))
477
+
478
+ def _tri_from_edge(self, edge):
479
+ """Return the only tri that contains *edge*.
480
+
481
+ If two tris share this edge, raise an exception.
482
+ """
483
+ edge = tuple(edge)
484
+ p1 = self._edges_lookup.get(edge, None)
485
+ p2 = self._edges_lookup.get(edge[::-1], None)
486
+ if p1 is None:
487
+ if p2 is None:
488
+ raise RuntimeError("No tris connected to edge %r" % (edge,))
489
+ return edge + (p2,)
490
+ elif p2 is None:
491
+ return edge + (p1,)
492
+ else:
493
+ raise RuntimeError("Two triangles connected to edge %r" % (edge,))
494
+
495
+ def _edges_in_tri_except(self, tri, edge):
496
+ """Return the edges in *tri*, excluding *edge*."""
497
+ edges = [(tri[i], tri[(i+1) % 3]) for i in range(3)]
498
+ try:
499
+ edges.remove(tuple(edge))
500
+ except ValueError:
501
+ edges.remove(tuple(edge[::-1]))
502
+ return edges
503
+
504
+ def _edge_below_front(self, edge, front_index):
505
+ """Return True if *edge* is below the current front.
506
+
507
+ One of the points in *edge* must be _on_ the front, at *front_index*.
508
+ """
509
+ f0 = self._front[front_index-1]
510
+ f1 = self._front[front_index+1]
511
+ return (self._orientation(edge, f0) > 0 and
512
+ self._orientation(edge, f1) < 0)
513
+
514
+ def _is_constraining_edge(self, edge):
515
+ mask1 = self.edges == edge[0]
516
+ mask2 = self.edges == edge[1]
517
+ return (np.any(mask1[:, 0] & mask2[:, 1]) or
518
+ np.any(mask2[:, 0] & mask1[:, 1]))
519
+
520
+ def _intersected_edge(self, edges, cut_edge):
521
+ """Given a list of *edges*, return the first that is intersected by
522
+ *cut_edge*.
523
+ """
524
+ for edge in edges:
525
+ if self._edges_intersect(edge, cut_edge):
526
+ return edge
527
+
528
+ def _find_edge_intersections(self):
529
+ """Return a dictionary containing, for each edge in self.edges, a list
530
+ of the positions at which the edge should be split.
531
+ """
532
+ edges = self.pts[self.edges]
533
+ cuts = {} # { edge: [(intercept, point), ...], ... }
534
+ for i in range(edges.shape[0]-1):
535
+ # intersection of edge i onto all others
536
+ int1 = self._intersect_edge_arrays(edges[i:i+1], edges[i+1:])
537
+ # intersection of all edges onto edge i
538
+ int2 = self._intersect_edge_arrays(edges[i+1:], edges[i:i+1])
539
+
540
+ # select for pairs that intersect
541
+ err = np.geterr()
542
+ np.seterr(divide='ignore', invalid='ignore')
543
+ try:
544
+ mask1 = (int1 >= 0) & (int1 <= 1)
545
+ mask2 = (int2 >= 0) & (int2 <= 1)
546
+ mask3 = mask1 & mask2 # all intersections
547
+ finally:
548
+ np.seterr(**err)
549
+
550
+ # compute points of intersection
551
+ inds = np.argwhere(mask3)[:, 0]
552
+ if len(inds) == 0:
553
+ continue
554
+ h = int2[inds][:, np.newaxis]
555
+ pts = (edges[i, 0][np.newaxis, :] * (1.0 - h) +
556
+ edges[i, 1][np.newaxis, :] * h)
557
+
558
+ # record for all edges the location of cut points
559
+ edge_cuts = cuts.setdefault(i, [])
560
+ for j, ind in enumerate(inds):
561
+ if 0 < int2[ind] < 1:
562
+ edge_cuts.append((int2[ind], pts[j]))
563
+ if 0 < int1[ind] < 1:
564
+ other_cuts = cuts.setdefault(ind+i+1, [])
565
+ other_cuts.append((int1[ind], pts[j]))
566
+
567
+ # sort all cut lists by intercept, remove duplicates
568
+ for k, v in cuts.items():
569
+ v.sort(key=lambda x: x[0])
570
+ for i in range(len(v)-2, -1, -1):
571
+ if v[i][0] == v[i+1][0]:
572
+ v.pop(i+1)
573
+ return cuts
574
+
575
+ def _split_intersecting_edges(self):
576
+ # we can do all intersections at once, but this has excessive memory
577
+ # overhead.
578
+
579
+ # measure intersection point between all pairs of edges
580
+ all_cuts = self._find_edge_intersections()
581
+
582
+ # cut edges at each intersection
583
+ add_pts = []
584
+ add_edges = []
585
+ for edge, cuts in all_cuts.items():
586
+ if len(cuts) == 0:
587
+ continue
588
+
589
+ # add new points
590
+ pt_offset = self.pts.shape[0] + len(add_pts)
591
+ new_pts = [x[1] for x in cuts]
592
+ add_pts.extend(new_pts)
593
+
594
+ # list of point indexes for all new edges
595
+ pt_indexes = list(range(pt_offset, pt_offset + len(cuts)))
596
+ pt_indexes.append(self.edges[edge, 1])
597
+
598
+ # modify original edge
599
+ self.edges[edge, 1] = pt_indexes[0]
600
+
601
+ # add new edges
602
+ new_edges = [[pt_indexes[i-1], pt_indexes[i]]
603
+ for i in range(1, len(pt_indexes))]
604
+ add_edges.extend(new_edges)
605
+
606
+ if add_pts:
607
+ add_pts = np.array(add_pts, dtype=self.pts.dtype)
608
+ self.pts = np.append(self.pts, add_pts, axis=0)
609
+ if add_edges:
610
+ add_edges = np.array(add_edges, dtype=self.edges.dtype)
611
+ self.edges = np.append(self.edges, add_edges, axis=0)
612
+
613
+ def _merge_duplicate_points(self):
614
+ # generate a list of all pairs (i,j) of identical points
615
+ dups = []
616
+ for i in range(self.pts.shape[0]-1):
617
+ test_pt = self.pts[i:i+1]
618
+ comp_pts = self.pts[i+1:]
619
+ eq = test_pt == comp_pts
620
+ eq = eq[:, 0] & eq[:, 1]
621
+ for j in np.argwhere(eq)[:, 0]:
622
+ dups.append((i, i+1+j))
623
+
624
+ dups_arr = np.array(dups)
625
+ # remove duplicate points
626
+ pt_mask = np.ones(self.pts.shape[0], dtype=bool)
627
+ for i, inds in enumerate(dups_arr):
628
+ # remove j from points
629
+ # (note we pull the index from the original dups instead of
630
+ # dups_arr because the indexes in pt_mask do not change)
631
+ pt_mask[dups[i][1]] = False
632
+
633
+ i, j = inds
634
+
635
+ # rewrite edges to use i instead of j
636
+ self.edges[self.edges == j] = i
637
+
638
+ # decrement all point indexes > j
639
+ self.edges[self.edges > j] -= 1
640
+ dups_arr[dups_arr > j] -= 1
641
+
642
+ self.pts = self.pts[pt_mask]
643
+
644
+ # remove zero-length edges
645
+ mask = self.edges[:, 0] != self.edges[:, 1]
646
+ self.edges = self.edges[mask]
647
+
648
+ def _distances_from_line(self, edge, points):
649
+ # Distance of a set of points from a given line
650
+ e1 = self.pts[edge[0]]
651
+ e2 = self.pts[edge[1]]
652
+ distances = []
653
+ for i in points:
654
+ p = self.pts[i]
655
+ proj = self._projection(e1, p, e2)
656
+ distances.append(((p - proj)**2).sum()**0.5)
657
+ assert distances[0] == 0 and distances[-1] == 0
658
+ return distances
659
+
660
+ def _projection(self, a, b, c):
661
+ """Return projection of (a,b) onto (a,c)
662
+ Arguments are point locations, not indexes.
663
+ """
664
+ ab = b - a
665
+ ac = c - a
666
+ return a + ((ab*ac).sum() / (ac*ac).sum()) * ac
667
+
668
+ def _cosine(self, A, B, C):
669
+ # Cosine of angle ABC
670
+ a = ((C - B)**2).sum()
671
+ b = ((C - A)**2).sum()
672
+ c = ((B - A)**2).sum()
673
+ d = (a + c - b) / ((4 * a * c)**0.5)
674
+ return d
675
+
676
+ def _edges_intersect(self, edge1, edge2):
677
+ """Return 1 if edges intersect completely (endpoints excluded)"""
678
+ h12 = self._intersect_edge_arrays(self.pts[np.array(edge1)],
679
+ self.pts[np.array(edge2)])
680
+ h21 = self._intersect_edge_arrays(self.pts[np.array(edge2)],
681
+ self.pts[np.array(edge1)])
682
+ err = np.geterr()
683
+ np.seterr(divide='ignore', invalid='ignore')
684
+ try:
685
+ out = (0 < h12 < 1) and (0 < h21 < 1)
686
+ finally:
687
+ np.seterr(**err)
688
+ return out
689
+
690
+ def _intersect_edge_arrays(self, lines1, lines2):
691
+ """Return the intercepts of all lines defined in *lines1* as they
692
+ intersect all lines in *lines2*.
693
+
694
+ Arguments are of shape (..., 2, 2), where axes are:
695
+
696
+ 0: number of lines
697
+ 1: two points per line
698
+ 2: x,y pair per point
699
+
700
+ Lines are compared elementwise across the arrays (lines1[i] is compared
701
+ against lines2[i]). If one of the arrays has N=1, then that line is
702
+ compared against all lines in the other array.
703
+
704
+ Returns an array of shape (N,) where each value indicates the intercept
705
+ relative to the defined line segment. A value of 0 indicates
706
+ intersection at the first endpoint, and a value of 1 indicates
707
+ intersection at the second endpoint. Values between 1 and 0 are on the
708
+ segment, whereas values outside 1 and 0 are off of the segment.
709
+ """
710
+ # vector for each line in lines1
711
+ l1 = lines1[..., 1, :] - lines1[..., 0, :]
712
+ # vector for each line in lines2
713
+ l2 = lines2[..., 1, :] - lines2[..., 0, :]
714
+ # vector between first point of each line
715
+ diff = lines1[..., 0, :] - lines2[..., 0, :]
716
+
717
+ p = l1.copy()[..., ::-1] # vectors perpendicular to l1
718
+ p[..., 0] *= -1
719
+
720
+ f = (l2 * p).sum(axis=-1) # l2 dot p
721
+ # tempting, but bad idea!
722
+ err = np.geterr()
723
+ np.seterr(divide='ignore', invalid='ignore')
724
+ try:
725
+ h = (diff * p).sum(axis=-1) / f # diff dot p / f
726
+ finally:
727
+ np.seterr(**err)
728
+
729
+ return h
730
+
731
+ def _orientation(self, edge, point):
732
+ """Returns +1 if edge[0]->point is clockwise from edge[0]->edge[1],
733
+ -1 if counterclockwise, and 0 if parallel.
734
+ """
735
+ v1 = self.pts[point] - self.pts[edge[0]]
736
+ v2 = self.pts[edge[1]] - self.pts[edge[0]]
737
+ c = _cross_2d(v1, v2) # positive if v1 is CW from v2
738
+ return 1 if c > 0 else (-1 if c < 0 else 0)
739
+
740
+ def _add_tri(self, a, b, c):
741
+ # sanity check
742
+ assert a != b and b != c and c != a
743
+
744
+ # ignore tris with duplicate points
745
+ pa = self.pts[a]
746
+ pb = self.pts[b]
747
+ pc = self.pts[c]
748
+ if np.all(pa == pb) or np.all(pb == pc) or np.all(pc == pa):
749
+ return
750
+
751
+ # check this tri is unique
752
+ for t in permutations((a, b, c)):
753
+ if t in self.tris:
754
+ raise Exception("Cannot add %s; already have %s" %
755
+ ((a, b, c), t))
756
+
757
+ # ignore lines
758
+ orientation = self._orientation((a, b), c)
759
+ if orientation == 0:
760
+ return
761
+
762
+ # TODO: should add to edges_lookup after legalization??
763
+ if orientation < 0:
764
+ assert (a, b) not in self._edges_lookup
765
+ assert (b, c) not in self._edges_lookup
766
+ assert (c, a) not in self._edges_lookup
767
+ self._edges_lookup[(a, b)] = c
768
+ self._edges_lookup[(b, c)] = a
769
+ self._edges_lookup[(c, a)] = b
770
+ else:
771
+ assert (b, a) not in self._edges_lookup
772
+ assert (c, b) not in self._edges_lookup
773
+ assert (a, c) not in self._edges_lookup
774
+ self._edges_lookup[(b, a)] = c
775
+ self._edges_lookup[(c, b)] = a
776
+ self._edges_lookup[(a, c)] = b
777
+
778
+ tri = (a, b, c)
779
+
780
+ self.tris[tri] = None
781
+
782
+ def _remove_tri(self, a, b, c):
783
+ for k in permutations((a, b, c)):
784
+ if k in self.tris:
785
+ break
786
+ del self.tris[k]
787
+ (a, b, c) = k
788
+
789
+ if self._edges_lookup.get((a, b), -1) == c:
790
+ del self._edges_lookup[(a, b)]
791
+ del self._edges_lookup[(b, c)]
792
+ del self._edges_lookup[(c, a)]
793
+ elif self._edges_lookup.get((b, a), -1) == c:
794
+ del self._edges_lookup[(b, a)]
795
+ del self._edges_lookup[(a, c)]
796
+ del self._edges_lookup[(c, b)]
797
+ else:
798
+ raise RuntimeError("Lost edges_lookup for tri (%d, %d, %d)" %
799
+ (a, b, c))
800
+
801
+ return k
802
+
803
+
804
+ def _triangulate_python(vertices_2d, segments):
805
+ segments = segments.reshape(len(segments) // 2, 2)
806
+ T = Triangulation(vertices_2d, segments)
807
+ T.triangulate()
808
+ vertices_2d = T.pts
809
+ triangles = T.tris.ravel()
810
+ return vertices_2d, triangles
811
+
812
+
813
+ def _triangulate_cpp(vertices_2d, segments):
814
+ import triangle
815
+ T = triangle.triangulate({'vertices': vertices_2d,
816
+ 'segments': segments}, "p")
817
+ vertices_2d = T["vertices"]
818
+ triangles = T["triangles"]
819
+ return vertices_2d, triangles
820
+
821
+
822
+ def triangulate(vertices):
823
+ """Triangulate a set of vertices.
824
+
825
+ This uses a pure Python implementation based on [1]_.
826
+
827
+ If `Triangle` by Jonathan R. Shewchuk [2]_ and the Python bindings `triangle` [3]_
828
+ are installed, this will be used instead. Users need to acknowledge and adhere to
829
+ the licensing terms of these packages.
830
+
831
+ In the VisPy `PolygonCollection Example` [4]_ a speedup of 97% using
832
+ `Triangle`/`triangle` can be achieved compared to the pure Python implementation.
833
+
834
+ Parameters
835
+ ----------
836
+ vertices : array-like
837
+ The vertices.
838
+
839
+ Returns
840
+ -------
841
+ vertices : array-like
842
+ The vertices.
843
+ triangles : array-like
844
+ The triangles.
845
+
846
+ References
847
+ ----------
848
+ .. [1] Domiter, V. and Žalik, B. Sweep‐line algorithm for constrained
849
+ Delaunay triangulation
850
+ .. [2] Shewchuk J.R. (1996) Triangle: Engineering a 2D quality mesh generator and
851
+ Delaunay triangulator. In: Lin M.C., Manocha D. (eds) Applied Computational
852
+ Geometry Towards Geometric Engineering. WACG 1996. Lecture Notes in Computer
853
+ Science, vol 1148. Springer, Berlin, Heidelberg.
854
+ https://doi.org/10.1007/BFb0014497
855
+ .. [3] https://rufat.be/triangle/
856
+ .. [4] https://github.com/vispy/vispy/blob/main/examples/collections/polygon_collection.py
857
+ """
858
+ n = len(vertices)
859
+ vertices = np.asarray(vertices)
860
+ zmean = vertices[:, 2].mean()
861
+ vertices_2d = vertices[:, :2]
862
+ segments = np.repeat(np.arange(n + 1), 2)[1:-1]
863
+ segments[-2:] = n - 1, 0
864
+
865
+ try:
866
+ import triangle # noqa: F401
867
+ except (ImportError, AssertionError):
868
+ vertices_2d, triangles = _triangulate_python(vertices_2d, segments)
869
+ else:
870
+ segments_2d = segments.reshape((-1, 2))
871
+ vertices_2d, triangles = _triangulate_cpp(vertices_2d, segments_2d)
872
+
873
+ vertices = np.empty((len(vertices_2d), 3))
874
+ vertices[:, :2] = vertices_2d
875
+ vertices[:, 2] = zmean
876
+ return vertices, triangles