kaxe 1.4.4.dev12__tar.gz → 1.4.4.dev14__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 (131) hide show
  1. {kaxe-1.4.4.dev12/src/kaxe.egg-info → kaxe-1.4.4.dev14}/PKG-INFO +1 -1
  2. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/pyproject.toml +1 -1
  3. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/axis.py +1 -0
  4. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/helper.py +31 -0
  5. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/svg.py +74 -14
  6. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/window.py +36 -3
  7. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/function.py +2 -0
  8. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/point.py +21 -12
  9. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/grid.py +25 -2
  10. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14/src/kaxe.egg-info}/PKG-INFO +1 -1
  11. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/LICENSE +0 -0
  12. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/MANIFEST.in +0 -0
  13. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/README.md +0 -0
  14. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/setup.cfg +0 -0
  15. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/__init__.py +0 -0
  16. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/chart/__init__.py +0 -0
  17. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/chart/bar.py +0 -0
  18. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/chart/box.py +0 -0
  19. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/chart/pie.py +0 -0
  20. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/chart/qqplot.py +0 -0
  21. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/__init__.py +0 -0
  22. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/color.py +0 -0
  23. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/backend.py +0 -0
  24. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/camera.py +0 -0
  25. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/helper.py +0 -0
  26. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/hud.py +0 -0
  27. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/objects/__init__.py +0 -0
  28. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/objects/color.py +0 -0
  29. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/objects/line.py +0 -0
  30. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/objects/point.py +0 -0
  31. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/objects/pointer.py +0 -0
  32. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/objects/triangle.py +0 -0
  33. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/openglrender.py +0 -0
  34. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/d3/translator.py +0 -0
  35. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/draw.py +0 -0
  36. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/fileloader.py +0 -0
  37. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/legend.py +0 -0
  38. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/line.py +0 -0
  39. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/marker.py +0 -0
  40. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/profiler.py +0 -0
  41. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/round.py +0 -0
  42. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/shapes.py +0 -0
  43. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/styles.py +0 -0
  44. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/svg_pdf.py +0 -0
  45. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/symbol.py +0 -0
  46. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/core/text.py +0 -0
  47. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/data/__init__.py +0 -0
  48. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/data/excel.py +0 -0
  49. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/__init__.py +0 -0
  50. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/_lazy.py +0 -0
  51. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/__init__.py +0 -0
  52. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/arrow.py +0 -0
  53. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/bubble.py +0 -0
  54. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/contour.py +0 -0
  55. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/equation.py +0 -0
  56. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/fill.py +0 -0
  57. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/map.py +0 -0
  58. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/parameter.py +0 -0
  59. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d2/pillar.py +0 -0
  60. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d3/__init__.py +0 -0
  61. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d3/base.py +0 -0
  62. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d3/function.py +0 -0
  63. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d3/mesh.py +0 -0
  64. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d3/point.py +0 -0
  65. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/d3/potato.py +0 -0
  66. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/function.py +0 -0
  67. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/legend.py +0 -0
  68. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/mapdata.py +0 -0
  69. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/point.py +0 -0
  70. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/objects/text.py +0 -0
  71. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/__init__.py +0 -0
  72. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/_lazy.py +0 -0
  73. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/box.py +0 -0
  74. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/constants.py +0 -0
  75. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/d3/__init__.py +0 -0
  76. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/d3/axes.py +0 -0
  77. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/d3/geometry.py +0 -0
  78. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/d3/plot3d.py +0 -0
  79. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/d3/variants.py +0 -0
  80. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/double.py +0 -0
  81. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/empty.py +0 -0
  82. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/log.py +0 -0
  83. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/polar.py +0 -0
  84. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/standard.py +0 -0
  85. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/themes.py +0 -0
  86. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/zoom.py +0 -0
  87. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/plot/zoom_connector.py +0 -0
  88. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/__init__.py +0 -0
  89. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/__init__.py +0 -0
  90. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.bright-oblique.ttf +0 -0
  91. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.bright-roman.ttf +0 -0
  92. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.bright-semibold.ttf +0 -0
  93. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.bright-semiboldoblique.ttf +0 -0
  94. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.classical-serif-italic.ttf +0 -0
  95. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.concrete-bold.ttf +0 -0
  96. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.concrete-bolditalic.ttf +0 -0
  97. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.concrete-italic.ttf +0 -0
  98. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.concrete-roman.ttf +0 -0
  99. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-bold.ttf +0 -0
  100. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-boldoblique.ttf +0 -0
  101. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-demi-condensed-demicondensed.ttf +0 -0
  102. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-medium.ttf +0 -0
  103. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-oblique.ttf +0 -0
  104. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.serif-bold.ttf +0 -0
  105. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.serif-bolditalic.ttf +0 -0
  106. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-boldslanted.ttf +0 -0
  107. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-romanslanted.ttf +0 -0
  108. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.serif-italic.ttf +0 -0
  109. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.serif-roman.ttf +0 -0
  110. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.serif-upright-italic-uprightitalic.ttf +0 -0
  111. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bold.ttf +0 -0
  112. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bolditalic.ttf +0 -0
  113. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-italic.ttf +0 -0
  114. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-light.ttf +0 -0
  115. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-lightoblique.ttf +0 -0
  116. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-regular.ttf +0 -0
  117. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-italic.ttf +0 -0
  118. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-medium.ttf +0 -0
  119. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/computer-modern-family/readme.txt +0 -0
  120. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/logo-small.png +0 -0
  121. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/symbolcross.png +0 -0
  122. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/symboldonut.png +0 -0
  123. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/symbollollipop.png +0 -0
  124. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe/resources/symboltriangle.png +0 -0
  125. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe.egg-info/SOURCES.txt +0 -0
  126. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe.egg-info/dependency_links.txt +0 -0
  127. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe.egg-info/requires.txt +0 -0
  128. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/src/kaxe.egg-info/top_level.txt +0 -0
  129. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/tests/test.py +0 -0
  130. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/tests/test2.py +0 -0
  131. {kaxe-1.4.4.dev12 → kaxe-1.4.4.dev14}/tests/test3.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaxe
3
- Version: 1.4.4.dev12
3
+ Version: 1.4.4.dev14
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.4.4.dev12"
3
+ version = "1.4.4.dev14"
4
4
  authors = [
5
5
  { name="Valter Yde Daugberg", email="valteryde@hotmail.com" },
6
6
  ]
@@ -368,6 +368,7 @@ class Axis(AttrObject):
368
368
 
369
369
  def autoAddMarkers(self, parent):
370
370
  self.setAttrMap(parent.attrmap)
371
+ self.markers = []
371
372
 
372
373
  markers = self.computeMarkersAutomatic(parent)
373
374
  self.addMarkersToAxis(markers, parent)
@@ -127,6 +127,37 @@ def insideBox(box, point):
127
127
  return True
128
128
 
129
129
 
130
+ def segment_break_threshold(parent) -> float:
131
+ """Pixel y delta above which a new polyline segment should start."""
132
+ box = parent.windowBox
133
+ return (box[3] - box[1]) * 0.45
134
+
135
+
136
+ def should_break_polyline_segment(
137
+ p1: tuple[float, float],
138
+ p2: tuple[float, float],
139
+ parent,
140
+ ) -> bool:
141
+ """Break when a segment looks like a wrap-around, not a normal diagonal."""
142
+ dx = abs(p2[0] - p1[0])
143
+ dy = abs(p2[1] - p1[1])
144
+ if dy <= segment_break_threshold(parent):
145
+ return False
146
+ return dy > dx * 1.5
147
+
148
+
149
+ def plot_window_clip_rect_svg(plot) -> tuple[float, float, float, float]:
150
+ """Return plot windowBox as an SVG clip rect (x, y, width, height), y-down."""
151
+ box = plot.windowBox
152
+ canvas_h = plot.getSize()[1]
153
+ return (
154
+ float(box[0]),
155
+ float(canvas_h - box[3]),
156
+ float(box[2] - box[0]),
157
+ float(box[3] - box[1]),
158
+ )
159
+
160
+
130
161
  def shell(a):
131
162
  def __shell__():
132
163
  return a
@@ -57,6 +57,9 @@ class SvgDocument:
57
57
  self._width, self._height = int(size[0]), int(size[1])
58
58
  self._elements: list[ET.Element] = []
59
59
  self._fondi_font_css: Optional[str] = None
60
+ self._clip_defs: list[ET.Element] = []
61
+ self._clip_counter = 0
62
+ self._active_group: Optional[ET.Element] = None
60
63
 
61
64
  @property
62
65
  def height(self) -> int:
@@ -69,12 +72,48 @@ class SvgDocument:
69
72
  def flip_y(self, y: float) -> float:
70
73
  return flip_y(y, self._height)
71
74
 
75
+ def _register_clip_rect(self, x: float, y: float, width: float, height: float) -> str:
76
+ clip_id = f"kaxe-clip-{self._clip_counter}"
77
+ self._clip_counter += 1
78
+ clip_path = ET.Element(
79
+ f"{{{SVG_NS}}}clipPath",
80
+ {"id": clip_id},
81
+ )
82
+ ET.SubElement(
83
+ clip_path,
84
+ f"{{{SVG_NS}}}rect",
85
+ {
86
+ "x": str(round(x, 3)),
87
+ "y": str(round(y, 3)),
88
+ "width": str(round(width, 3)),
89
+ "height": str(round(height, 3)),
90
+ },
91
+ )
92
+ self._clip_defs.append(clip_path)
93
+ return clip_id
94
+
95
+ def push_clip_layer(self, x: float, y: float, width: float, height: float) -> None:
96
+ """Clip subsequent elements to a rectangle in SVG top-down coordinates."""
97
+ clip_id = self._register_clip_rect(x, y, width, height)
98
+ group = ET.Element(
99
+ f"{{{SVG_NS}}}g",
100
+ {"clip-path": f"url(#{clip_id})"},
101
+ )
102
+ self._elements.append(group)
103
+ self._active_group = group
104
+
105
+ def pop_clip_layer(self) -> None:
106
+ self._active_group = None
107
+
72
108
  def add_element(self, tag: str, attribs: dict[str, Any]) -> ET.Element:
73
109
  el = ET.Element(
74
110
  f"{{{SVG_NS}}}{tag}",
75
111
  {k: str(v) for k, v in attribs.items() if v is not None},
76
112
  )
77
- self._elements.append(el)
113
+ if self._active_group is not None:
114
+ self._active_group.append(el)
115
+ else:
116
+ self._elements.append(el)
78
117
  return el
79
118
 
80
119
  def add_rect(
@@ -285,9 +324,11 @@ class SvgDocument:
285
324
  rotate: float = 0,
286
325
  rotate_center: Optional[tuple[float, float]] = None,
287
326
  ) -> None:
288
- """Place an image; y is in kaxe coords (y-up). y_coord='bottom' or 'top'."""
327
+ """Place an image; y is in kaxe coords (y-up) unless y_coord='svg_top'."""
289
328
  href = pil_to_data_uri(img)
290
- if y_coord == "top":
329
+ if y_coord == "svg_top":
330
+ svg_top = y
331
+ elif y_coord == "top":
291
332
  svg_top = self.flip_y(y)
292
333
  else:
293
334
  svg_top = self.flip_y(y) - img.height
@@ -347,7 +388,10 @@ class SvgDocument:
347
388
  group = ET.Element(f"{{{SVG_NS}}}g", {"transform": transform})
348
389
  for child in list(inner):
349
390
  group.append(copy.deepcopy(child))
350
- self._elements.append(group)
391
+ if self._active_group is not None:
392
+ self._active_group.append(group)
393
+ else:
394
+ self._elements.append(group)
351
395
 
352
396
  def serialize(self) -> str:
353
397
  root = ET.Element(
@@ -359,15 +403,22 @@ class SvgDocument:
359
403
  "viewBox": f"0 0 {self._width} {self._height}",
360
404
  },
361
405
  )
362
- if self._fondi_font_css is not None:
406
+ if self._fondi_font_css is not None or self._clip_defs:
363
407
  defs = ET.SubElement(root, f"{{{SVG_NS}}}defs")
364
- style = ET.SubElement(defs, f"{{{SVG_NS}}}style")
365
- style.text = self._fondi_font_css
408
+ if self._fondi_font_css is not None:
409
+ style = ET.SubElement(defs, f"{{{SVG_NS}}}style")
410
+ style.text = self._fondi_font_css
411
+ for clip_el in self._clip_defs:
412
+ defs.append(clip_el)
366
413
  for el in self._elements:
367
414
  root.append(el)
368
415
  body = ET.tostring(root, encoding="unicode")
369
- from fondi.backends.svg import _ascii_safe_svg_markup
370
- body = _ascii_safe_svg_markup(body)
416
+ try:
417
+ from fondi.backends.svg import _ascii_safe_svg_markup
418
+
419
+ body = _ascii_safe_svg_markup(body)
420
+ except ImportError:
421
+ pass
371
422
  return '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n' + body
372
423
 
373
424
  def to_pdf(self, fname: Optional[Union[str, BytesIO]] = None) -> bytes:
@@ -408,14 +459,23 @@ def embed_svg_children(
408
459
  children: list[ET.Element],
409
460
  tx: float,
410
461
  ty: float,
462
+ *,
463
+ clip_rect: Optional[tuple[float, float, float, float]] = None,
411
464
  ) -> None:
412
- """Embed copied SVG children at top-left offset (SVG y-down coordinates)."""
465
+ """Embed copied SVG children at top-left offset (SVG y-down coordinates).
466
+
467
+ clip_rect is optional (x, y, width, height) in the embedded cell's local SVG space.
468
+ """
413
469
  if not children:
414
470
  return
415
- group = ET.Element(
416
- f"{{{SVG_NS}}}g",
417
- {"transform": f"translate({round(tx, 3)},{round(ty, 3)})"},
418
- )
471
+ attribs: dict[str, str] = {
472
+ "transform": f"translate({round(tx, 3)},{round(ty, 3)})",
473
+ }
474
+ if clip_rect is not None:
475
+ cx, cy, cw, ch = clip_rect
476
+ clip_id = doc._register_clip_rect(cx, cy, cw, ch)
477
+ attribs["clip-path"] = f"url(#{clip_id})"
478
+ group = ET.Element(f"{{{SVG_NS}}}g", attribs)
419
479
  for child in children:
420
480
  group.append(copy.deepcopy(child))
421
481
  doc._elements.append(group)
@@ -418,7 +418,8 @@ class Window(AttrObject):
418
418
  self.__includeAllAgain__()
419
419
 
420
420
 
421
- self.shapes = [i[0] for i in sorted(self.shapes, key=lambda x: x[1])]
421
+ self.shape_layers = sorted(self.shapes, key=lambda x: x[1])
422
+ self.shapes = [i[0] for i in self.shape_layers]
422
423
 
423
424
  # add style padding
424
425
  self.addPaddingCondition(*self.getAttr('outerPadding'))
@@ -494,7 +495,23 @@ class Window(AttrObject):
494
495
  background = shapes.Rectangle(0, 0, winSize[0], winSize[1], color=self.getAttr('backgroundColor'))
495
496
  background.draw(doc)
496
497
 
497
- for shape in self.shapes:
498
+ box = self.windowBox
499
+ doc.push_clip_layer(
500
+ box[0],
501
+ winSize[1] - box[3],
502
+ box[2] - box[0],
503
+ box[3] - box[1],
504
+ )
505
+ for shape, z in getattr(self, "shape_layers", [(s, 0) for s in self.shapes]):
506
+ if z >= 2:
507
+ continue
508
+ shape.draw(doc)
509
+ if self.showProgressBar: pbar.update()
510
+ doc.pop_clip_layer()
511
+
512
+ for shape, z in getattr(self, "shape_layers", [(s, 0) for s in self.shapes]):
513
+ if z < 2:
514
+ continue
498
515
  shape.draw(doc)
499
516
  if self.showProgressBar: pbar.update()
500
517
 
@@ -524,7 +541,23 @@ class Window(AttrObject):
524
541
  background = shapes.Rectangle(0, 0, winSize[0], winSize[1], color=self.getAttr('backgroundColor'))
525
542
  background.draw(doc)
526
543
 
527
- for shape in self.shapes:
544
+ box = self.windowBox
545
+ doc.push_clip_layer(
546
+ box[0],
547
+ winSize[1] - box[3],
548
+ box[2] - box[0],
549
+ box[3] - box[1],
550
+ )
551
+ for shape, z in getattr(self, "shape_layers", [(s, 0) for s in self.shapes]):
552
+ if z >= 2:
553
+ continue
554
+ shape.draw(doc)
555
+ if self.showProgressBar: pbar.update()
556
+ doc.pop_clip_layer()
557
+
558
+ for shape, z in getattr(self, "shape_layers", [(s, 0) for s in self.shapes]):
559
+ if z < 2:
560
+ continue
528
561
  shape.draw(doc)
529
562
  if self.showProgressBar: pbar.update()
530
563
 
@@ -130,6 +130,8 @@ class Function2D:
130
130
  )
131
131
 
132
132
  if parent.inside(px, py) or parent.inside(*self.__lastPoint__):
133
+ if should_break_polyline_segment(self.__lastPoint__, (px, py), parent):
134
+ self.lineSegments.append([])
133
135
  self.lineSegments[-1].append(parent.clamp(self.__lastPoint__[0], self.__lastPoint__[1]))
134
136
  self.lineSegments[-1].append(parent.clamp(px, py))
135
137
  else:
@@ -136,25 +136,34 @@ class Points2D:
136
136
 
137
137
 
138
138
  def __finalizeConnectedLine__(self, parent, line_width):
139
- points = []
139
+ segments = [[]]
140
140
  for x_val, y_val in zip(self.x, self.y):
141
141
  px, py = parent.pixel(x_val, y_val)
142
142
  if px is None or py is None:
143
143
  continue
144
144
  if not parent.inside(px, py):
145
145
  continue
146
- points.append(parent.clamp(px, py))
146
+ point = parent.clamp(px, py)
147
+ if segments[-1] and should_break_polyline_segment(segments[-1][-1], point, parent):
148
+ segments.append([])
149
+ segments[-1].append(point)
147
150
 
148
- if len(points) < 2:
149
- return
150
- if len(points) == 2:
151
- x0, y0 = points[0]
152
- x1, y1 = points[1]
153
- line = shapes.Line(x0, y0, x1, y1, color=self.color, width=line_width, batch=self.batch, center=True)
154
- self.lines.append(line)
155
- return
156
-
157
- shapes.LineSegment(points, color=self.color, width=line_width, batch=self.batch)
151
+ for points in segments:
152
+ if len(points) < 2:
153
+ continue
154
+ if len(points) == 2:
155
+ x0, y0 = points[0]
156
+ x1, y1 = points[1]
157
+ line = shapes.Line(
158
+ x0, y0, x1, y1,
159
+ color=self.color,
160
+ width=line_width,
161
+ batch=self.batch,
162
+ center=True,
163
+ )
164
+ self.lines.append(line)
165
+ continue
166
+ shapes.LineSegment(points, color=self.color, width=line_width, batch=self.batch)
158
167
 
159
168
 
160
169
  def draw(self, *args, **kwargs):
@@ -94,6 +94,7 @@ class Grid(AttrObject):
94
94
  """
95
95
 
96
96
  self.__legends = legends
97
+ self._invalidate_bake_cache()
97
98
 
98
99
 
99
100
  def theme(self, theme):
@@ -110,10 +111,23 @@ class Grid(AttrObject):
110
111
  plot.__included__ = []
111
112
  plot.__baked__ = False
112
113
  plot.__bakedImage__ = False
114
+ if hasattr(plot, "shape_layers"):
115
+ plot.shape_layers = []
116
+ for axis in (getattr(plot, "firstAxis", None), getattr(plot, "secondAxis", None)):
117
+ if axis is not None:
118
+ axis.markers = []
119
+ axis.title = None
120
+
121
+
122
+ def _invalidate_bake_cache(self):
123
+ self.__bakedImage__ = False
124
+ self.__bakedSvg__ = None
125
+ self.__bakedPdf__ = None
113
126
 
114
127
 
115
128
  def _apply_cell_styles(self, plot, cellWidth, cellHeight):
116
129
  self._reset_cell_bake_state(plot)
130
+ plot.showLegend = False
117
131
  plot.style(
118
132
  width=cellWidth,
119
133
  height=cellHeight,
@@ -315,12 +329,18 @@ class Grid(AttrObject):
315
329
  xml = plot.__ioBytes.read().decode("utf-8")
316
330
  root = parse_svg_root(xml)
317
331
  children, fondi_css = extract_svg_children(root)
318
- embed_svg_children(doc, children, px, py)
332
+ embed_svg_children(
333
+ doc,
334
+ children,
335
+ px,
336
+ py,
337
+ clip_rect=plot_window_clip_rect_svg(plot),
338
+ )
319
339
  merge_fondi_css(doc, fondi_css)
320
340
  else:
321
341
  plot.__ioBytes.seek(0)
322
342
  img = Image.open(plot.__ioBytes)
323
- doc.add_image(img, px, py, y_coord="top")
343
+ doc.add_image(img, px, py, y_coord="svg_top")
324
344
 
325
345
  x += cellWidth + gapcol
326
346
 
@@ -364,6 +384,7 @@ class Grid(AttrObject):
364
384
 
365
385
  """
366
386
 
387
+ self._invalidate_bake_cache()
367
388
  self.grid.append(row)
368
389
 
369
390
 
@@ -377,6 +398,8 @@ class Grid(AttrObject):
377
398
  A list of plots to be added as a column in the grid.
378
399
  """
379
400
 
401
+ self._invalidate_bake_cache()
402
+
380
403
  for i, plot in enumerate(column):
381
404
 
382
405
  if i >= len(self.grid):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaxe
3
- Version: 1.4.4.dev12
3
+ Version: 1.4.4.dev14
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