kaxe 1.5.5.dev0__tar.gz → 1.5.6__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 (147) hide show
  1. {kaxe-1.5.5.dev0/src/kaxe.egg-info → kaxe-1.5.6}/PKG-INFO +1 -1
  2. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/pyproject.toml +1 -1
  3. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/helper.py +26 -0
  4. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/contour.py +94 -70
  5. {kaxe-1.5.5.dev0 → kaxe-1.5.6/src/kaxe.egg-info}/PKG-INFO +1 -1
  6. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/LICENSE +0 -0
  7. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/MANIFEST.in +0 -0
  8. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/README.md +0 -0
  9. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/setup.cfg +0 -0
  10. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/__init__.py +0 -0
  11. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/_require_3d.py +0 -0
  12. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/chart/__init__.py +0 -0
  13. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/chart/bar.py +0 -0
  14. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/chart/box.py +0 -0
  15. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/chart/pie.py +0 -0
  16. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/chart/qqplot.py +0 -0
  17. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/__init__.py +0 -0
  18. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/axis.py +0 -0
  19. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/bounds.py +0 -0
  20. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/color.py +0 -0
  21. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/backend.py +0 -0
  22. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/camera.py +0 -0
  23. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/helper.py +0 -0
  24. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/hud.py +0 -0
  25. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/objects/__init__.py +0 -0
  26. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/objects/color.py +0 -0
  27. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/objects/line.py +0 -0
  28. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/objects/point.py +0 -0
  29. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/objects/pointer.py +0 -0
  30. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/objects/triangle.py +0 -0
  31. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/openglrender.py +0 -0
  32. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/d3/translator.py +0 -0
  33. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/draw.py +0 -0
  34. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/fileloader.py +0 -0
  35. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/ipython_display.py +0 -0
  36. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/legend.py +0 -0
  37. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/line.py +0 -0
  38. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/marker.py +0 -0
  39. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/profiler.py +0 -0
  40. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/progress.py +0 -0
  41. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/round.py +0 -0
  42. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/shapes.py +0 -0
  43. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/styles.py +0 -0
  44. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/svg.py +0 -0
  45. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/svg_pdf.py +0 -0
  46. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/symbol.py +0 -0
  47. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/text.py +0 -0
  48. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/core/window.py +0 -0
  49. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/data/__init__.py +0 -0
  50. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/data/excel.py +0 -0
  51. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/__init__.py +0 -0
  52. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/_lazy.py +0 -0
  53. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/__init__.py +0 -0
  54. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/arrow.py +0 -0
  55. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/bubble.py +0 -0
  56. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/equation.py +0 -0
  57. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/fill.py +0 -0
  58. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/function.py +0 -0
  59. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/inequality.py +0 -0
  60. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/map.py +0 -0
  61. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/parameter.py +0 -0
  62. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/pillar.py +0 -0
  63. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d2/point.py +0 -0
  64. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d3/__init__.py +0 -0
  65. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d3/base.py +0 -0
  66. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d3/function.py +0 -0
  67. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d3/mesh.py +0 -0
  68. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d3/point.py +0 -0
  69. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/d3/potato.py +0 -0
  70. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/function.py +0 -0
  71. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/legend.py +0 -0
  72. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/mapdata.py +0 -0
  73. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/point.py +0 -0
  74. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/objects/text.py +0 -0
  75. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/__init__.py +0 -0
  76. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/_lazy.py +0 -0
  77. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/box.py +0 -0
  78. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/constants.py +0 -0
  79. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/d3/__init__.py +0 -0
  80. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/d3/axes.py +0 -0
  81. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/d3/geometry.py +0 -0
  82. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/d3/plot3d.py +0 -0
  83. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/d3/variants.py +0 -0
  84. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/double.py +0 -0
  85. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/empty.py +0 -0
  86. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/grid.py +0 -0
  87. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/log.py +0 -0
  88. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/polar.py +0 -0
  89. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/standard.py +0 -0
  90. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/themes.py +0 -0
  91. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/zoom.py +0 -0
  92. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/plot/zoom_connector.py +0 -0
  93. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/project/__init__.py +0 -0
  94. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/project/codec.py +0 -0
  95. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/project/context.py +0 -0
  96. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/project/document.py +0 -0
  97. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/project/registry.py +0 -0
  98. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/project/sample.py +0 -0
  99. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/project/sampled_curve.py +0 -0
  100. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/project/serializers.py +0 -0
  101. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/project/window.py +0 -0
  102. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/__init__.py +0 -0
  103. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/__init__.py +0 -0
  104. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.bright-oblique.ttf +0 -0
  105. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.bright-roman.ttf +0 -0
  106. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.bright-semibold.ttf +0 -0
  107. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.bright-semiboldoblique.ttf +0 -0
  108. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.classical-serif-italic.ttf +0 -0
  109. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.concrete-bold.ttf +0 -0
  110. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.concrete-bolditalic.ttf +0 -0
  111. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.concrete-italic.ttf +0 -0
  112. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.concrete-roman.ttf +0 -0
  113. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-bold.ttf +0 -0
  114. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-boldoblique.ttf +0 -0
  115. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-demi-condensed-demicondensed.ttf +0 -0
  116. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-medium.ttf +0 -0
  117. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-oblique.ttf +0 -0
  118. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.serif-bold.ttf +0 -0
  119. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.serif-bolditalic.ttf +0 -0
  120. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-boldslanted.ttf +0 -0
  121. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-romanslanted.ttf +0 -0
  122. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.serif-italic.ttf +0 -0
  123. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.serif-roman.ttf +0 -0
  124. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.serif-upright-italic-uprightitalic.ttf +0 -0
  125. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bold.ttf +0 -0
  126. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bolditalic.ttf +0 -0
  127. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-italic.ttf +0 -0
  128. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-light.ttf +0 -0
  129. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-lightoblique.ttf +0 -0
  130. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-regular.ttf +0 -0
  131. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-italic.ttf +0 -0
  132. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-medium.ttf +0 -0
  133. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/computer-modern-family/readme.txt +0 -0
  134. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/logo-small.png +0 -0
  135. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/symbolcross.png +0 -0
  136. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/symboldonut.png +0 -0
  137. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/symbollollipop.png +0 -0
  138. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe/resources/symboltriangle.png +0 -0
  139. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe.egg-info/SOURCES.txt +0 -0
  140. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe.egg-info/dependency_links.txt +0 -0
  141. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe.egg-info/requires.txt +0 -0
  142. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/src/kaxe.egg-info/top_level.txt +0 -0
  143. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/tests/test.py +0 -0
  144. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/tests/test2.py +0 -0
  145. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/tests/test3.py +0 -0
  146. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/tests/test_4.py +0 -0
  147. {kaxe-1.5.5.dev0 → kaxe-1.5.6}/tests/test_5.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaxe
3
- Version: 1.5.5.dev0
3
+ Version: 1.5.6
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kaxe"
3
- version = "1.5.5.dev0"
3
+ version = "1.5.6"
4
4
  authors = [
5
5
  { name="Valter Yde Daugberg", email="valteryde@hotmail.com" },
6
6
  ]
@@ -71,6 +71,32 @@ def contour_label_angle(polyline, index):
71
71
  return angle
72
72
 
73
73
 
74
+ def bbox_overlaps(a, b, padding=0):
75
+ """Return True if two axis-aligned boxes overlap.
76
+
77
+ Each box is ``(left, top, width, height)`` as returned by ``Text.getBoundingBox()``.
78
+ """
79
+ ax, ay, aw, ah = a
80
+ bx, by, bw, bh = b
81
+
82
+ a_center = (ax + aw / 2, ay + ah / 2)
83
+ b_center = (bx + bw / 2, by + bh / 2)
84
+ a_size = (aw + 2 * padding, ah + 2 * padding)
85
+ b_size = (bw + 2 * padding, bh + 2 * padding)
86
+
87
+ a_top_right = (a_center[0] + a_size[0] / 2, a_center[1] + a_size[1] / 2)
88
+ a_bottom_left = (a_center[0] - a_size[0] / 2, a_center[1] - a_size[1] / 2)
89
+ b_top_right = (b_center[0] + b_size[0] / 2, b_center[1] + b_size[1] / 2)
90
+ b_bottom_left = (b_center[0] - b_size[0] / 2, b_center[1] - b_size[1] / 2)
91
+
92
+ return not (
93
+ a_top_right[0] < b_bottom_left[0]
94
+ or a_bottom_left[0] > b_top_right[0]
95
+ or a_top_right[1] < b_bottom_left[1]
96
+ or a_bottom_left[1] > b_top_right[1]
97
+ )
98
+
99
+
74
100
  def resample_polyline(points, spacing):
75
101
  """
76
102
  Emit points every `spacing` pixels along polyline arc length.
@@ -4,13 +4,13 @@ import math
4
4
  from ...core.styles import *
5
5
  from ...core.shapes import shapes
6
6
  from ...core.symbol import symbol as symbols
7
- from ...core.helper import contour_label_angle
7
+ from ...core.helper import bbox_overlaps, contour_label_angle, resample_polyline
8
8
  from ...core.text import Text
9
9
  from ...core.round import koundTeX
10
10
  from ...plot import identities
11
11
  from .equation import Equation, trace_contour_polylines
12
12
  from types import FunctionType
13
- from typing import Union
13
+ from typing import Optional, Union
14
14
  from ...core.color import Colormaps, Colormap, to_rgba
15
15
 
16
16
 
@@ -47,30 +47,6 @@ def _simplify_polyline(polyline):
47
47
  return simplified
48
48
 
49
49
 
50
- def _point_at_arc_length(polyline, target):
51
- if not polyline:
52
- return 0, (0, 0)
53
-
54
- if target <= 0:
55
- return 0, polyline[0]
56
-
57
- walked = 0.0
58
- for i in range(1, len(polyline)):
59
- dx = polyline[i][0] - polyline[i - 1][0]
60
- dy = polyline[i][1] - polyline[i - 1][1]
61
- seg_len = math.hypot(dx, dy)
62
- if seg_len == 0:
63
- continue
64
- if walked + seg_len >= target:
65
- t = (target - walked) / seg_len
66
- px = polyline[i - 1][0] + t * dx
67
- py = polyline[i - 1][1] + t * dy
68
- return i, (px, py)
69
- walked += seg_len
70
-
71
- return len(polyline) - 1, polyline[-1]
72
-
73
-
74
50
  def _closest_polyline_index(polyline, point):
75
51
  best_index = 0
76
52
  best_dist = float('inf')
@@ -82,7 +58,7 @@ def _closest_polyline_index(polyline, point):
82
58
  return best_index
83
59
 
84
60
 
85
- def _label_positions(polyline, spacing, max_labels=2):
61
+ def _label_candidates(polyline, spacing):
86
62
  polyline = _simplify_polyline(polyline)
87
63
  if len(polyline) < 2:
88
64
  return []
@@ -91,14 +67,16 @@ def _label_positions(polyline, spacing, max_labels=2):
91
67
  if arc < spacing:
92
68
  return []
93
69
 
94
- num_labels = min(max_labels, max(1, int(arc / spacing)))
95
- positions = []
96
- for k in range(num_labels):
97
- target = (k + 0.5) * arc / num_labels
98
- index, point = _point_at_arc_length(polyline, target)
99
- positions.append((point, _closest_polyline_index(polyline, point)))
70
+ candidates = []
71
+ for point in resample_polyline(polyline, spacing)[1:-1]:
72
+ index = _closest_polyline_index(polyline, point)
73
+ candidates.append((point, index, arc))
100
74
 
101
- return positions
75
+ if not candidates and arc >= spacing:
76
+ mid_index = len(polyline) // 2
77
+ candidates.append((polyline[mid_index], mid_index, arc))
78
+
79
+ return candidates
102
80
 
103
81
 
104
82
  def _point_in_expanded_bbox(x, y, bbox, padding):
@@ -132,7 +110,15 @@ class Contour:
132
110
  label : bool, optional
133
111
  Draw inline level labels on contour lines (default is True).
134
112
  labelSpacing : int, optional
135
- Minimum pixel spacing between labels along a contour (default is 100).
113
+ Minimum pixel spacing between label candidates along a contour (default is 100).
114
+ labelCollisionPadding : int, optional
115
+ Extra pixel gap required between placed label bounding boxes (default is 4).
116
+ labelMinArc : int, optional
117
+ Minimum polyline arc length to consider for labels (default is labelSpacing).
118
+ labelMaxBranches : int, optional
119
+ Maximum number of polylines per level to label (default is 1).
120
+ labelMaxPerLevel : int, optional
121
+ Hard cap on labels placed per level; unlimited when None (default is None).
136
122
  labelColor : tuple, optional
137
123
  Color of inline contour labels (default is black).
138
124
 
@@ -156,6 +142,10 @@ class Contour:
156
142
  computePadding: int = 50,
157
143
  label: bool = True,
158
144
  labelSpacing: int = 100,
145
+ labelCollisionPadding: int = 4,
146
+ labelMinArc: Optional[int] = None,
147
+ labelMaxBranches: int = 1,
148
+ labelMaxPerLevel: Optional[int] = None,
159
149
  labelColor=(0, 0, 0, 255),
160
150
  ):
161
151
  self.batch = shapes.Batch()
@@ -168,6 +158,10 @@ class Contour:
168
158
  self.lineThickness = lineThickness
169
159
  self.label = label
170
160
  self.labelSpacing = labelSpacing
161
+ self.labelCollisionPadding = labelCollisionPadding
162
+ self.labelMinArc = labelSpacing if labelMinArc is None else labelMinArc
163
+ self.labelMaxBranches = labelMaxBranches
164
+ self.labelMaxPerLevel = labelMaxPerLevel
171
165
  self.labelColor = to_rgba(labelColor)
172
166
 
173
167
  # color
@@ -208,12 +202,10 @@ class Contour:
208
202
  if self.label and label_parent == identities.XYPLOT:
209
203
  self.__finalizeLabels__(label_parent)
210
204
 
211
- def __finalizeLabels__(self, parent):
212
- fontSize = parent.getAttr('fontSize')
213
- padding = max(2, fontSize // 8)
214
- label_bboxes = []
205
+ def __collectCandidates__(self, parent):
206
+ candidates = []
215
207
 
216
- for z, eq in self.__equations:
208
+ for level_index, (z, eq) in enumerate(self.__equations):
217
209
  label_text = _format_contour_level(z, self.a, self.b, self.steps)
218
210
  polylines = trace_contour_polylines(eq.dotsPosAbstract, parent)
219
211
  polylines = sorted(
@@ -224,36 +216,68 @@ class Contour:
224
216
  polylines = [
225
217
  polyline
226
218
  for polyline in polylines
227
- if _polyline_arc_length(_simplify_polyline(polyline)) >= self.labelSpacing
228
- ][:3]
229
-
230
- for polyline in polylines:
231
- for point, index in _label_positions(polyline, self.labelSpacing):
232
- angle = contour_label_angle(polyline, index)
233
- text = Text(
234
- label_text,
235
- int(point[0]),
236
- int(point[1]),
237
- fontSize=fontSize,
238
- color=self.labelColor,
239
- rotate=int(angle),
240
- anchor_x='center',
241
- anchor_y='center',
242
- )
243
-
244
- left, top = text.getLeftTopPos()
245
- shapes.Rectangle(
246
- left - padding,
247
- top - padding,
248
- text.width + 2 * padding,
249
- text.height + 2 * padding,
250
- color=WHITE,
251
- batch=self.batch,
252
- )
253
- text.batch = self.batch
254
- self.batch.add(text)
255
-
256
- label_bboxes.append(text.getBoundingBox())
219
+ if _polyline_arc_length(_simplify_polyline(polyline)) >= self.labelMinArc
220
+ ][: self.labelMaxBranches]
221
+
222
+ for branch_index, polyline in enumerate(polylines):
223
+ simplified = _simplify_polyline(polyline)
224
+ arc = _polyline_arc_length(simplified)
225
+ for candidate_index, (point, index, _) in enumerate(
226
+ _label_candidates(polyline, self.labelSpacing)
227
+ ):
228
+ candidates.append({
229
+ "z": z,
230
+ "level_index": level_index,
231
+ "branch_index": branch_index,
232
+ "candidate_index": candidate_index,
233
+ "arc": arc,
234
+ "polyline": polyline,
235
+ "point": point,
236
+ "index": index,
237
+ "label_text": label_text,
238
+ })
239
+
240
+ candidates.sort(
241
+ key=lambda item: (-item["arc"], item["level_index"], item["branch_index"], item["candidate_index"])
242
+ )
243
+ return candidates
244
+
245
+ def __finalizeLabels__(self, parent):
246
+ fontSize = parent.getAttr('fontSize')
247
+ placed_bboxes = []
248
+ label_bboxes = []
249
+ placed_per_level = {}
250
+
251
+ for candidate in self.__collectCandidates__(parent):
252
+ level_index = candidate["level_index"]
253
+ if self.labelMaxPerLevel is not None:
254
+ if placed_per_level.get(level_index, 0) >= self.labelMaxPerLevel:
255
+ continue
256
+
257
+ angle = contour_label_angle(candidate["polyline"], candidate["index"])
258
+ text = Text(
259
+ candidate["label_text"],
260
+ int(candidate["point"][0]),
261
+ int(candidate["point"][1]),
262
+ fontSize=fontSize,
263
+ color=self.labelColor,
264
+ rotate=int(angle),
265
+ anchor_x='center',
266
+ anchor_y='center',
267
+ )
268
+ bbox = text.getBoundingBox()
269
+
270
+ if any(
271
+ bbox_overlaps(bbox, placed_bbox, self.labelCollisionPadding)
272
+ for placed_bbox in placed_bboxes
273
+ ):
274
+ continue
275
+
276
+ text.batch = self.batch
277
+ self.batch.add(text)
278
+ placed_bboxes.append(bbox)
279
+ label_bboxes.append(bbox)
280
+ placed_per_level[level_index] = placed_per_level.get(level_index, 0) + 1
257
281
 
258
282
  if not label_bboxes:
259
283
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaxe
3
- Version: 1.5.5.dev0
3
+ Version: 1.5.6
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
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
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
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
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes