QuickGraphLib 0.1.0a0__py3-none-any.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.
@@ -0,0 +1,79 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 Matthew Joyce and other QuickGraphLib contributors
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ from typing import Sequence
5
+
6
+ from PySide6 import QtCore, QtQml
7
+
8
+ from .consts import ( # pylint: disable=unused-import
9
+ QML_IMPORT_MAJOR_VERSION,
10
+ QML_IMPORT_MINOR_VERSION,
11
+ QML_IMPORT_NAME,
12
+ )
13
+
14
+ try:
15
+ import contourpy
16
+ import numpy as np
17
+ except ModuleNotFoundError:
18
+ CONTOURPY_AVAILABLE = False
19
+ else:
20
+ CONTOURPY_AVAILABLE = True
21
+
22
+
23
+ def contour_line(
24
+ x: Sequence[Sequence[float]],
25
+ y: Sequence[Sequence[float]],
26
+ z: Sequence[Sequence[float]],
27
+ height: float,
28
+ ) -> Sequence[Sequence[QtCore.QPointF]]:
29
+ gen = contourpy.contour_generator(
30
+ x=x, y=y, z=z, line_type=contourpy.LineType.Separate
31
+ )
32
+ result = []
33
+ for loop in gen.lines(height):
34
+ assert isinstance(loop, np.ndarray), "Loop is not an array" # For mypy
35
+ points = [QtCore.QPointF(x, y) for x, y in loop]
36
+ result.append(points)
37
+ return result
38
+
39
+
40
+ def contour_fill(
41
+ x: Sequence[Sequence[float]],
42
+ y: Sequence[Sequence[float]],
43
+ z: Sequence[Sequence[float]],
44
+ heights: tuple[float, float],
45
+ ) -> Sequence[Sequence[QtCore.QPointF]]:
46
+ gen = contourpy.contour_generator(
47
+ x=x, y=y, z=z, fill_type=contourpy.FillType.OuterOffset
48
+ )
49
+ result = []
50
+ for loop, offsets in zip(*gen.filled(*heights)):
51
+ for start, end in zip(offsets, offsets[1:]):
52
+ points = [QtCore.QPointF(x, y) for x, y in loop[start:end]]
53
+ result.append(points)
54
+ return result
55
+
56
+
57
+ @QtQml.QmlElement
58
+ @QtQml.QmlSingleton
59
+ class ContourHelper(QtCore.QObject):
60
+ @QtCore.Slot(list, list, list, float, result=list)
61
+ def contourLine(
62
+ self,
63
+ x: Sequence[Sequence[float]],
64
+ y: Sequence[Sequence[float]],
65
+ z: Sequence[Sequence[float]],
66
+ height: float,
67
+ ) -> Sequence[Sequence[QtCore.QPointF]]:
68
+ return contour_line(x, y, z, height)
69
+
70
+ @QtCore.Slot(list, list, list, float, float, result=list)
71
+ def contourFill(
72
+ self,
73
+ x: Sequence[Sequence[float]],
74
+ y: Sequence[Sequence[float]],
75
+ z: Sequence[Sequence[float]],
76
+ h_min: float,
77
+ h_max: float,
78
+ ) -> Sequence[Sequence[QtCore.QPointF]]:
79
+ return contour_fill(x, y, z, (h_min, h_max))
@@ -0,0 +1,167 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 Matthew Joyce and other QuickGraphLib contributors
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ import math
5
+ import pathlib
6
+ from typing import Any, Mapping
7
+
8
+ from PySide6 import QtCore, QtGui, QtQml, QtSvg
9
+
10
+ from .consts import ( # pylint: disable=unused-import
11
+ QML_IMPORT_MAJOR_VERSION,
12
+ QML_IMPORT_MINOR_VERSION,
13
+ QML_IMPORT_NAME,
14
+ )
15
+
16
+
17
+ def _export_child_to_painter(data: Mapping[str, Any], painter: QtGui.QPainter) -> None:
18
+ if "x" in data: # An Item
19
+ if not data["visible"]:
20
+ return
21
+ painter.save()
22
+ painter.translate(data["x"], data["y"])
23
+ painter.setOpacity(data["opacity"])
24
+ rect = QtCore.QRectF(0, 0, data["width"], data["height"])
25
+ if data["clip"]:
26
+ # Note: doesn't work for SVGs until 6.7
27
+ painter.setClipRect(rect)
28
+
29
+ childrenZSorted = sorted(data["children"], key=lambda c: c.get("z", 0))
30
+
31
+ for tr in data["transform"][::-1]:
32
+ if tr is None:
33
+ pass
34
+ elif tr["type"] == "translate":
35
+ painter.translate(tr["x"], tr["y"])
36
+ elif tr["type"] == "rotation":
37
+ painter.translate(tr["origin"].x(), tr["origin"].y())
38
+ painter.rotate(tr["angle"])
39
+ painter.translate(-tr["origin"].x(), -tr["origin"].y())
40
+ elif tr["type"] == "scale":
41
+ painter.translate(tr["origin"].x(), tr["origin"].y())
42
+ painter.scale(tr["xScale"], tr["yScale"])
43
+ painter.translate(-tr["origin"].x(), -tr["origin"].y())
44
+ elif tr["type"] == "matrix4x4":
45
+ painter.setTransform(tr["matrix"].toTransform())
46
+
47
+ for c in childrenZSorted:
48
+ if c.get("z", 0) < 0:
49
+ _export_child_to_painter(c, painter)
50
+
51
+ if data["type"] == "text":
52
+ painter.save()
53
+ font = QtGui.QFont(data["fontFamily"], 1, data["fontWeight"])
54
+ font.setPixelSize(data["fontSize"])
55
+ painter.setFont(font)
56
+ painter.setPen(data["color"])
57
+ painter.drawText(rect, data["text"])
58
+ painter.restore()
59
+ if data["type"] == "rectangle":
60
+ painter.save()
61
+ if data["border_width"] > 0:
62
+ pen = QtGui.QPen(data["border_color"])
63
+ pen.setWidthF(data["border_width"])
64
+ else:
65
+ pen = QtGui.QPen(QtCore.Qt.PenStyle.NoPen)
66
+ painter.setPen(pen)
67
+ painter.setBrush(data["color"])
68
+ painter.drawRoundedRect(rect, data["radius"], data["radius"])
69
+ painter.restore()
70
+
71
+ for c in childrenZSorted:
72
+ if c.get("z", 0) >= 0:
73
+ _export_child_to_painter(c, painter)
74
+ painter.restore()
75
+
76
+ elif data["type"] == "shape_path":
77
+ painter.save()
78
+ pen = QtGui.QPen(data["strokeColor"])
79
+ pen.setWidth(data["strokeWidth"])
80
+ painter.setPen(pen)
81
+ if data["fillGradient"] is None:
82
+ brush = QtGui.QBrush(data["fillColor"])
83
+ elif data["fillGradient"]["type"] == "lineargradient":
84
+ lingrad = QtGui.QLinearGradient(
85
+ data["fillGradient"]["x1"],
86
+ data["fillGradient"]["y1"],
87
+ data["fillGradient"]["x2"],
88
+ data["fillGradient"]["y2"],
89
+ )
90
+ lingrad.setStops(
91
+ [(s["position"], s["color"]) for s in data["fillGradient"]["stops"]]
92
+ )
93
+ brush = QtGui.QBrush(lingrad)
94
+ painter.setBrush(brush)
95
+ for c in data["elements"]:
96
+ _export_child_to_painter(c, painter)
97
+ painter.restore()
98
+
99
+ elif data["type"] == "polyline":
100
+ brush = painter.brush()
101
+ pen = painter.pen()
102
+ painter.setPen(QtCore.Qt.PenStyle.NoPen)
103
+ painter.drawPolygon(data["path"])
104
+ painter.setPen(pen)
105
+ painter.setBrush(QtCore.Qt.BrushStyle.NoBrush)
106
+ painter.drawPolyline(data["path"])
107
+ painter.setBrush(brush)
108
+
109
+ elif data["type"] == "multiline":
110
+ brush = painter.brush()
111
+ pen = painter.pen()
112
+ fill_path = QtGui.QPainterPath()
113
+ for path in data["paths"]:
114
+ fill_path.addPolygon(QtGui.QPolygonF(path))
115
+ fill_path.closeSubpath()
116
+
117
+ painter.setPen(QtCore.Qt.PenStyle.NoPen)
118
+ painter.drawPath(fill_path)
119
+ painter.setPen(pen)
120
+ painter.setBrush(QtCore.Qt.BrushStyle.NoBrush)
121
+ for path in data["paths"]:
122
+ painter.drawPolyline(path)
123
+ painter.setBrush(brush)
124
+
125
+
126
+ def export_to_painter(data: Mapping[str, Any], painter: QtGui.QPainter) -> None:
127
+ painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
128
+ painter.setRenderHint(QtGui.QPainter.RenderHint.TextAntialiasing, True)
129
+ _export_child_to_painter(data, painter)
130
+
131
+
132
+ def export_to_paintdevice(data: Mapping[str, Any], device: QtGui.QPaintDevice) -> None:
133
+ painter = QtGui.QPainter()
134
+ painter.begin(device)
135
+ try:
136
+ export_to_painter(data, painter)
137
+ finally:
138
+ painter.end()
139
+
140
+
141
+ def export_to_svg(data: Mapping[str, Any], path: pathlib.Path) -> None:
142
+ device = QtSvg.QSvgGenerator()
143
+ device.setFileName(str(path))
144
+ export_to_paintdevice(data, device)
145
+
146
+
147
+ def export_to_png(data: Mapping[str, Any], path: pathlib.Path, dpi: int = 96) -> None:
148
+ device = QtGui.QPixmap(
149
+ math.ceil((data["width"] + data["x"]) * dpi / 96),
150
+ math.ceil((data["height"] + data["y"]) * dpi / 96),
151
+ )
152
+ device.fill(QtCore.Qt.GlobalColor.transparent)
153
+ device.setDevicePixelRatio(dpi / 96)
154
+ export_to_paintdevice(data, device)
155
+ device.save(str(path))
156
+
157
+
158
+ @QtQml.QmlElement
159
+ @QtQml.QmlSingleton
160
+ class ExportHelper(QtCore.QObject):
161
+ @QtCore.Slot(dict, QtCore.QUrl)
162
+ def exportToSvg(self, data: Mapping[str, Any], path: QtCore.QUrl) -> None:
163
+ export_to_svg(data, pathlib.Path(path.toLocalFile()))
164
+
165
+ @QtCore.Slot(dict, QtCore.QUrl)
166
+ def exportToPng(self, data: Mapping[str, Any], path: QtCore.QUrl) -> None:
167
+ export_to_png(data, pathlib.Path(path.toLocalFile()))
File without changes