kaxe 1.8.1.dev5__tar.gz → 1.9.0.dev0__tar.gz

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.
Files changed (156) hide show
  1. {kaxe-1.8.1.dev5/src/kaxe.egg-info → kaxe-1.9.0.dev0}/PKG-INFO +4 -2
  2. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/README.md +3 -1
  3. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/pyproject.toml +1 -1
  4. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/axis.py +9 -0
  5. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/marker.py +2 -1
  6. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/text.py +4 -0
  7. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/viewer/window.py +88 -9
  8. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0/src/kaxe.egg-info}/PKG-INFO +4 -2
  9. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/tests/test_viewer.py +30 -0
  10. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/LICENSE +0 -0
  11. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/MANIFEST.in +0 -0
  12. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/setup.cfg +0 -0
  13. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/__init__.py +0 -0
  14. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/__main__.py +0 -0
  15. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/_require_3d.py +0 -0
  16. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/_require_viewer.py +0 -0
  17. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/chart/__init__.py +0 -0
  18. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/chart/bar.py +0 -0
  19. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/chart/box.py +0 -0
  20. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/chart/pie.py +0 -0
  21. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/chart/qqplot.py +0 -0
  22. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/cli.py +0 -0
  23. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/__init__.py +0 -0
  24. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/bounds.py +0 -0
  25. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/color.py +0 -0
  26. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/backend.py +0 -0
  27. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/camera.py +0 -0
  28. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/helper.py +0 -0
  29. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/hud.py +0 -0
  30. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/objects/__init__.py +0 -0
  31. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/objects/color.py +0 -0
  32. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/objects/line.py +0 -0
  33. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/objects/point.py +0 -0
  34. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/objects/pointer.py +0 -0
  35. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/objects/triangle.py +0 -0
  36. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/openglrender.py +0 -0
  37. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/d3/translator.py +0 -0
  38. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/draw.py +0 -0
  39. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/fileloader.py +0 -0
  40. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/helper.py +0 -0
  41. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/ipython_display.py +0 -0
  42. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/legend.py +0 -0
  43. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/line.py +0 -0
  44. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/palette.py +0 -0
  45. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/profiler.py +0 -0
  46. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/progress.py +0 -0
  47. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/round.py +0 -0
  48. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/shapes.py +0 -0
  49. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/styles.py +0 -0
  50. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/svg.py +0 -0
  51. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/svg_pdf.py +0 -0
  52. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/symbol.py +0 -0
  53. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/thin.py +0 -0
  54. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/core/window.py +0 -0
  55. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/data/__init__.py +0 -0
  56. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/data/excel.py +0 -0
  57. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/__init__.py +0 -0
  58. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/_lazy.py +0 -0
  59. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/__init__.py +0 -0
  60. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/arrow.py +0 -0
  61. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/bubble.py +0 -0
  62. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/contour.py +0 -0
  63. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/equation.py +0 -0
  64. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/fill.py +0 -0
  65. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/function.py +0 -0
  66. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/guide_grid.py +0 -0
  67. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/inequality.py +0 -0
  68. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/map.py +0 -0
  69. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/parameter.py +0 -0
  70. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/pillar.py +0 -0
  71. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d2/point.py +0 -0
  72. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d3/__init__.py +0 -0
  73. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d3/base.py +0 -0
  74. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d3/function.py +0 -0
  75. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d3/mesh.py +0 -0
  76. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d3/point.py +0 -0
  77. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/d3/potato.py +0 -0
  78. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/function.py +0 -0
  79. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/legend.py +0 -0
  80. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/mapdata.py +0 -0
  81. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/point.py +0 -0
  82. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/objects/text.py +0 -0
  83. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/__init__.py +0 -0
  84. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/_lazy.py +0 -0
  85. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/box.py +0 -0
  86. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/constants.py +0 -0
  87. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/d3/__init__.py +0 -0
  88. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/d3/axes.py +0 -0
  89. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/d3/geometry.py +0 -0
  90. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/d3/plot3d.py +0 -0
  91. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/d3/variants.py +0 -0
  92. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/double.py +0 -0
  93. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/empty.py +0 -0
  94. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/grid.py +0 -0
  95. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/log.py +0 -0
  96. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/polar.py +0 -0
  97. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/standard.py +0 -0
  98. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/themes.py +0 -0
  99. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/zoom.py +0 -0
  100. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/plot/zoom_connector.py +0 -0
  101. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/project/__init__.py +0 -0
  102. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/project/codec.py +0 -0
  103. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/project/context.py +0 -0
  104. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/project/document.py +0 -0
  105. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/project/registry.py +0 -0
  106. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/project/sample.py +0 -0
  107. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/project/sampled_curve.py +0 -0
  108. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/project/serializers.py +0 -0
  109. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/project/window.py +0 -0
  110. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/__init__.py +0 -0
  111. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/__init__.py +0 -0
  112. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.bright-oblique.ttf +0 -0
  113. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.bright-roman.ttf +0 -0
  114. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.bright-semibold.ttf +0 -0
  115. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.bright-semiboldoblique.ttf +0 -0
  116. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.classical-serif-italic.ttf +0 -0
  117. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.concrete-bold.ttf +0 -0
  118. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.concrete-bolditalic.ttf +0 -0
  119. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.concrete-italic.ttf +0 -0
  120. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.concrete-roman.ttf +0 -0
  121. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-bold.ttf +0 -0
  122. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-boldoblique.ttf +0 -0
  123. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-demi-condensed-demicondensed.ttf +0 -0
  124. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-medium.ttf +0 -0
  125. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-oblique.ttf +0 -0
  126. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.serif-bold.ttf +0 -0
  127. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.serif-bolditalic.ttf +0 -0
  128. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-boldslanted.ttf +0 -0
  129. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-romanslanted.ttf +0 -0
  130. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.serif-italic.ttf +0 -0
  131. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.serif-roman.ttf +0 -0
  132. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.serif-upright-italic-uprightitalic.ttf +0 -0
  133. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bold.ttf +0 -0
  134. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bolditalic.ttf +0 -0
  135. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-italic.ttf +0 -0
  136. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-light.ttf +0 -0
  137. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-lightoblique.ttf +0 -0
  138. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-regular.ttf +0 -0
  139. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-italic.ttf +0 -0
  140. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-medium.ttf +0 -0
  141. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/computer-modern-family/readme.txt +0 -0
  142. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/resources/logo-small.png +0 -0
  143. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/viewer/__init__.py +0 -0
  144. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/viewer/cli.py +0 -0
  145. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/viewer/client.py +0 -0
  146. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe/viewer/server.py +0 -0
  147. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe.egg-info/SOURCES.txt +0 -0
  148. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe.egg-info/dependency_links.txt +0 -0
  149. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe.egg-info/entry_points.txt +0 -0
  150. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe.egg-info/requires.txt +0 -0
  151. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/src/kaxe.egg-info/top_level.txt +0 -0
  152. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/tests/test.py +0 -0
  153. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/tests/test2.py +0 -0
  154. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/tests/test3.py +0 -0
  155. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/tests/test_4.py +0 -0
  156. {kaxe-1.8.1.dev5 → kaxe-1.9.0.dev0}/tests/test_5.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaxe
3
- Version: 1.8.1.dev5
3
+ Version: 1.9.0.dev0
4
4
  Summary: A small graphing tool for functions, points, equations and more
5
5
  Author-email: Valter Yde Daugberg <valteryde@hotmail.com>
6
6
  Project-URL: Homepage, https://github.com/valteryde/kaxe
@@ -87,7 +87,9 @@ When the viewer is open, `.show()` instantly streams plots to cards on your canv
87
87
  **Canvas & Card Controls:**
88
88
  * **Click & Drag on a Card**: Move that card freely across the canvas
89
89
  * **Click & Drag on Empty Space**: Pan the camera across the workspace
90
- * **Mouse Scroll**: Smooth zoom anchored to cursor
90
+ * **Two-Finger Scroll / Trackpad**: Pan canvas side-to-side and up/down without zooming
91
+ * **`[Shift]` + Scroll**: Pan horizontally (for 1D mouse wheels)
92
+ * **`[Cmd]` / `[Ctrl]` + Scroll**: Smooth zoom anchored to cursor
91
93
  * **`[A]`**: Auto-arrange all cards in a neat row
92
94
  * **`[Space]` or `[R]`**: Fit all cards on the canvas into view
93
95
  * **`[1]`**: View canvas at 100% native pixel scale
@@ -54,7 +54,9 @@ When the viewer is open, `.show()` instantly streams plots to cards on your canv
54
54
  **Canvas & Card Controls:**
55
55
  * **Click & Drag on a Card**: Move that card freely across the canvas
56
56
  * **Click & Drag on Empty Space**: Pan the camera across the workspace
57
- * **Mouse Scroll**: Smooth zoom anchored to cursor
57
+ * **Two-Finger Scroll / Trackpad**: Pan canvas side-to-side and up/down without zooming
58
+ * **`[Shift]` + Scroll**: Pan horizontally (for 1D mouse wheels)
59
+ * **`[Cmd]` / `[Ctrl]` + Scroll**: Smooth zoom anchored to cursor
58
60
  * **`[A]`**: Auto-arrange all cards in a neat row
59
61
  * **`[Space]` or `[R]`**: Fit all cards on the canvas into view
60
62
  * **`[1]`**: View canvas at 100% native pixel scale
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kaxe"
3
- version = "1.8.1.dev5"
3
+ version = "1.9.0.dev0"
4
4
  authors = [
5
5
  { name="Valter Yde Daugberg", email="valteryde@hotmail.com" },
6
6
  ]
@@ -362,6 +362,7 @@ class Axis(AttrObject):
362
362
 
363
363
  if (i + markerOffset) % maxOverlays != 0:
364
364
 
365
+ marker.textLabel.hide()
365
366
  marker.textLabel.img = Image.new('RGBA', (0,0))
366
367
  if hasattr(marker, 'line'):
367
368
  marker.line.color = (
@@ -589,6 +590,10 @@ class Axis(AttrObject):
589
590
 
590
591
 
591
592
  def __pixelCollision__(self, label:Text, title:Text):
593
+ if label.__hidden__ or label.img.width == 0 or label.img.height == 0:
594
+ return False
595
+ if title.__hidden__ or title.img.width == 0 or title.img.height == 0:
596
+ return False
592
597
 
593
598
  lx, ly, lwidth, lheight = label.getBoundingBox()
594
599
  tx, ty, twidth, theight = title.getBoundingBox()
@@ -770,6 +775,10 @@ class Axis(AttrObject):
770
775
  for a, b in l:
771
776
  if not hasattr(a, 'textLabel') or not hasattr(b, 'textLabel'):
772
777
  continue
778
+ if a.textLabel.__hidden__ or b.textLabel.__hidden__:
779
+ continue
780
+ if a.textLabel.img.width == 0 or b.textLabel.img.width == 0:
781
+ continue
773
782
 
774
783
  if not self.__topLeftBoxOverlays__(a.textLabel, b.textLabel):
775
784
  continue
@@ -193,7 +193,8 @@ class Marker(AttrObject):
193
193
  label_y += nudge[1]
194
194
 
195
195
  self.textLabel.setCenterPos(label_x, label_y)
196
- self.textLabel.show()
196
+ if self.textLabel.img.width > 0 and self.textLabel.img.height > 0:
197
+ self.textLabel.show()
197
198
 
198
199
 
199
200
  def getBoundingBox(self):
@@ -178,6 +178,8 @@ class Text(Shape):
178
178
  ]
179
179
 
180
180
  def drawPillow(self, surface):
181
+ if self.__hidden__ or self.img.width == 0 or self.img.height == 0:
182
+ return
181
183
  [y] = flipHorizontal(surface, self.__center__[1] + self.height/2)
182
184
  if self.clip_box is not None:
183
185
  blit_image_clipped(
@@ -191,6 +193,8 @@ class Text(Shape):
191
193
  blitImageToSurface(surface, self.img, (self.__leftTop__[0], y))
192
194
 
193
195
  def drawSvg(self, doc):
196
+ if self.__hidden__ or self.img.width == 0 or self.img.height == 0:
197
+ return
194
198
  kaxe_top = self.__center__[1] + self.height / 2
195
199
  svg_top = doc.flip_y(kaxe_top)
196
200
  rotate_center = (
@@ -277,6 +277,11 @@ class KaxeViewerWindow(pyglet.window.Window):
277
277
  self.pan_camera_start_x: float = 0.0
278
278
  self.pan_camera_start_y: float = 0.0
279
279
 
280
+ # Key state handler for live modifier tracking (Cmd, Ctrl, Shift)
281
+ self.keys = key.KeyStateHandler()
282
+ self.push_handlers(self.keys)
283
+ self.current_modifiers: int = 0
284
+
280
285
  self.show_hud: bool = True
281
286
  self.notification_text: str = ""
282
287
  self.notification_expiry: float = 0.0
@@ -307,7 +312,7 @@ class KaxeViewerWindow(pyglet.window.Window):
307
312
  )
308
313
 
309
314
  self.hud_right = pyglet.text.Label(
310
- "[Drag] Move card / Pan [Scroll] Zoom [A] Arrange [←/→] Version [C] Copy [Space] Fit",
315
+ "[Drag] Move card / Pan [Cmd+Scroll] Zoom [Scroll] Pan [A] Arrange [Space] Fit",
311
316
  font_size=11,
312
317
  x=self.width - 15,
313
318
  y=15,
@@ -489,19 +494,90 @@ class KaxeViewerWindow(pyglet.window.Window):
489
494
  self.dragging_card = None
490
495
  self.is_panning_camera = False
491
496
 
497
+ def is_zoom_modifier_pressed(self) -> bool:
498
+ """Return True if Meta (Command) or Control key is held down."""
499
+ if sys.platform == "darwin":
500
+ try:
501
+ from pyglet.libs.darwin import cocoapy
502
+ flags = cocoapy.NSEvent.modifierFlags()
503
+ if flags & (cocoapy.NSCommandKeyMask | cocoapy.NSControlKeyMask):
504
+ return True
505
+ except Exception:
506
+ pass
507
+
508
+ if hasattr(self, "keys") and self.keys is not None:
509
+ if (
510
+ self.keys[key.LCOMMAND]
511
+ or self.keys[key.RCOMMAND]
512
+ or self.keys[key.LCTRL]
513
+ or self.keys[key.RCTRL]
514
+ ):
515
+ return True
516
+
517
+ if getattr(self, "current_modifiers", 0) & (key.MOD_COMMAND | key.MOD_CTRL):
518
+ return True
519
+
520
+ return False
521
+
522
+ def is_shift_pressed(self) -> bool:
523
+ """Return True if Shift key is held down."""
524
+ if sys.platform == "darwin":
525
+ try:
526
+ from pyglet.libs.darwin import cocoapy
527
+ flags = cocoapy.NSEvent.modifierFlags()
528
+ if flags & cocoapy.NSShiftKeyMask:
529
+ return True
530
+ except Exception:
531
+ pass
532
+
533
+ if hasattr(self, "keys") and self.keys is not None:
534
+ if self.keys[key.LSHIFT] or self.keys[key.RSHIFT]:
535
+ return True
536
+
537
+ if getattr(self, "current_modifiers", 0) & key.MOD_SHIFT:
538
+ return True
539
+
540
+ return False
541
+
492
542
  def on_mouse_scroll(self, x: int, y: int, scroll_x: float, scroll_y: float):
493
- """Cursor-anchored zooming on the canvas."""
494
- zoom_factor = 1.05 ** (-scroll_y)
495
- new_zoom = max(0.04, min(self.zoom * zoom_factor, 20.0))
543
+ """Scroll handler:
496
544
 
497
- if hasattr(self, "width"):
498
- wx, wy = self.screen_to_world(x, y)
499
- self.camera_x = wx - (x - self.width / 2.0) / new_zoom
500
- self.camera_y = wy - (y - self.height / 2.0) / new_zoom
545
+ - If Meta (Cmd) or Control is held: ZOOM anchored at cursor.
546
+ - Otherwise: PAN canvas side-to-side and up/down without zooming.
547
+ """
548
+ if hasattr(self, "keys") and self.keys is not None:
549
+ should_zoom = self.is_zoom_modifier_pressed()
550
+ else:
551
+ # Fallback for mock unit test instances
552
+ should_zoom = True
501
553
 
502
- self.zoom = new_zoom
554
+ if should_zoom:
555
+ zoom_factor = 1.05 ** (-scroll_y)
556
+ new_zoom = max(0.04, min(self.zoom * zoom_factor, 25.0))
557
+
558
+ if hasattr(self, "width"):
559
+ wx, wy = self.screen_to_world(x, y)
560
+ self.camera_x = wx - (x - self.width / 2.0) / new_zoom
561
+ self.camera_y = wy - (y - self.height / 2.0) / new_zoom
562
+
563
+ self.zoom = new_zoom
564
+ else:
565
+ # Pan without zooming
566
+ dx = scroll_x
567
+ dy = scroll_y
568
+
569
+ # Holding Shift converts vertical wheel into horizontal pan
570
+ if self.is_shift_pressed() and dy != 0.0 and dx == 0.0:
571
+ dx = dy
572
+ dy = 0.0
573
+
574
+ pan_speed = 20.0
575
+ self.camera_x -= (dx * pan_speed) / self.zoom
576
+ self.camera_y += (dy * pan_speed) / self.zoom
503
577
 
504
578
  def on_key_press(self, symbol: int, modifiers: int):
579
+ self.current_modifiers = modifiers
580
+
505
581
  # Fit all
506
582
  if symbol in (key.R, key.SPACE):
507
583
  self.fit_all_cards()
@@ -537,6 +613,9 @@ class KaxeViewerWindow(pyglet.window.Window):
537
613
  elif symbol == key.S:
538
614
  self.save_selected_card()
539
615
 
616
+ def on_key_release(self, symbol: int, modifiers: int):
617
+ self.current_modifiers = modifiers
618
+
540
619
  def copy_selected_card(self):
541
620
  if not self.selected_card or not self.selected_card.current_version:
542
621
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaxe
3
- Version: 1.8.1.dev5
3
+ Version: 1.9.0.dev0
4
4
  Summary: A small graphing tool for functions, points, equations and more
5
5
  Author-email: Valter Yde Daugberg <valteryde@hotmail.com>
6
6
  Project-URL: Homepage, https://github.com/valteryde/kaxe
@@ -87,7 +87,9 @@ When the viewer is open, `.show()` instantly streams plots to cards on your canv
87
87
  **Canvas & Card Controls:**
88
88
  * **Click & Drag on a Card**: Move that card freely across the canvas
89
89
  * **Click & Drag on Empty Space**: Pan the camera across the workspace
90
- * **Mouse Scroll**: Smooth zoom anchored to cursor
90
+ * **Two-Finger Scroll / Trackpad**: Pan canvas side-to-side and up/down without zooming
91
+ * **`[Shift]` + Scroll**: Pan horizontally (for 1D mouse wheels)
92
+ * **`[Cmd]` / `[Ctrl]` + Scroll**: Smooth zoom anchored to cursor
91
93
  * **`[A]`**: Auto-arrange all cards in a neat row
92
94
  * **`[Space]` or `[R]`**: Fit all cards on the canvas into view
93
95
  * **`[1]`**: View canvas at 100% native pixel scale
@@ -220,6 +220,36 @@ def test_viewer_zoom_reversed_and_less_sensitive():
220
220
  assert abs(window.zoom - 1.05) < 1e-6
221
221
 
222
222
 
223
+ def test_scroll_pans_side_to_side_without_zoom_modifier():
224
+ from unittest.mock import MagicMock
225
+ from kaxe.viewer.window import KaxeViewerWindow
226
+ from pyglet.window import key
227
+
228
+ window = object.__new__(KaxeViewerWindow)
229
+ window.keys = {key.LCOMMAND: False, key.RCOMMAND: False, key.LCTRL: False, key.RCTRL: False, key.LSHIFT: False, key.RSHIFT: False}
230
+ window.current_modifiers = 0
231
+ window.camera_x = 0.0
232
+ window.camera_y = 0.0
233
+ window.zoom = 1.0
234
+
235
+ # Horizontal scroll without Cmd/Ctrl -> pans horizontally, zoom stays unchanged!
236
+ window.on_mouse_scroll(x=400, y=300, scroll_x=2.0, scroll_y=0.0)
237
+ assert window.zoom == 1.0
238
+ assert window.camera_x < 0.0 # Camera moved left
239
+
240
+ # Vertical scroll without Cmd/Ctrl -> pans vertically, zoom stays unchanged!
241
+ orig_cam_x = window.camera_x
242
+ window.on_mouse_scroll(x=400, y=300, scroll_x=0.0, scroll_y=3.0)
243
+ assert window.zoom == 1.0
244
+ assert window.camera_x == orig_cam_x
245
+ assert window.camera_y > 0.0
246
+
247
+ # With Cmd key pressed -> zooms!
248
+ window.keys[key.LCOMMAND] = True
249
+ window.on_mouse_scroll(x=400, y=300, scroll_x=0.0, scroll_y=-1.0)
250
+ assert window.zoom > 1.0
251
+
252
+
223
253
  def test_plot_id_chaining_and_streaming(monkeypatch):
224
254
  import kaxe
225
255
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes