rgrid-python 4.5.3__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.
- grid_py/__init__.py +340 -0
- grid_py/_arrow.py +331 -0
- grid_py/_clippath.py +170 -0
- grid_py/_colour.py +815 -0
- grid_py/_coords.py +1534 -0
- grid_py/_curve.py +1668 -0
- grid_py/_display_list.py +507 -0
- grid_py/_draw.py +1397 -0
- grid_py/_edit.py +756 -0
- grid_py/_font_metrics.py +319 -0
- grid_py/_gpar.py +572 -0
- grid_py/_grab.py +501 -0
- grid_py/_grob.py +1377 -0
- grid_py/_group.py +798 -0
- grid_py/_highlevel.py +2176 -0
- grid_py/_just.py +361 -0
- grid_py/_layout.py +593 -0
- grid_py/_ls.py +895 -0
- grid_py/_mask.py +196 -0
- grid_py/_path.py +414 -0
- grid_py/_patterns.py +1049 -0
- grid_py/_primitives.py +2198 -0
- grid_py/_renderer_base.py +1184 -0
- grid_py/_scene_graph.py +248 -0
- grid_py/_size.py +1352 -0
- grid_py/_state.py +683 -0
- grid_py/_transforms.py +448 -0
- grid_py/_typeset.py +384 -0
- grid_py/_units.py +1924 -0
- grid_py/_utils.py +310 -0
- grid_py/_viewport.py +1649 -0
- grid_py/_vp_calc.py +970 -0
- grid_py/py.typed +0 -0
- grid_py/renderer.py +1762 -0
- grid_py/renderer_web.py +764 -0
- grid_py/resources/d3.v7.min.js +2 -0
- grid_py/resources/gridpy.css +80 -0
- grid_py/resources/gridpy.js +813 -0
- rgrid_python-4.5.3.dist-info/METADATA +489 -0
- rgrid_python-4.5.3.dist-info/RECORD +42 -0
- rgrid_python-4.5.3.dist-info/WHEEL +4 -0
- rgrid_python-4.5.3.dist-info/licenses/LICENSE +3 -0
grid_py/_scene_graph.py
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Scene graph data model for the WebRenderer.
|
|
2
|
+
|
|
3
|
+
Provides a JSON-serializable tree representation of the entire grid scene,
|
|
4
|
+
including viewports, grobs, and shared definitions (gradients, patterns,
|
|
5
|
+
clip paths, masks).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import threading
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"SceneNode",
|
|
17
|
+
"ViewportNode",
|
|
18
|
+
"GrobNode",
|
|
19
|
+
"DefsCollection",
|
|
20
|
+
"SceneGraph",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Auto-incrementing ID generator
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
class _IdGenerator:
|
|
29
|
+
"""Thread-safe auto-incrementing ID generator.
|
|
30
|
+
|
|
31
|
+
Each call to ``next(prefix)`` returns ``"{prefix}-{counter}"`` where
|
|
32
|
+
*counter* is a monotonically increasing integer starting at 0.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self) -> None:
|
|
36
|
+
self._counter: int = 0
|
|
37
|
+
self._lock = threading.Lock()
|
|
38
|
+
|
|
39
|
+
def next(self, prefix: str = "node") -> str:
|
|
40
|
+
"""Return the next unique ID string with the given *prefix*."""
|
|
41
|
+
with self._lock:
|
|
42
|
+
node_id = f"{prefix}-{self._counter}"
|
|
43
|
+
self._counter += 1
|
|
44
|
+
return node_id
|
|
45
|
+
|
|
46
|
+
def reset(self) -> None:
|
|
47
|
+
"""Reset the counter to zero."""
|
|
48
|
+
with self._lock:
|
|
49
|
+
self._counter = 0
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Module-level singleton shared by factory helpers.
|
|
53
|
+
_id_gen = _IdGenerator()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
# Scene nodes
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class SceneNode:
|
|
62
|
+
"""Base node in the scene graph tree.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
node_id : str
|
|
67
|
+
Unique identifier for this node.
|
|
68
|
+
node_type : str
|
|
69
|
+
Discriminator tag persisted in the serialized form (e.g.
|
|
70
|
+
``"viewport"``, ``"rect"``, ``"text"``).
|
|
71
|
+
children : list[SceneNode]
|
|
72
|
+
Ordered child nodes.
|
|
73
|
+
metadata : dict
|
|
74
|
+
Arbitrary key/value pairs attached to this node.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
node_id: str
|
|
78
|
+
node_type: str
|
|
79
|
+
children: list = field(default_factory=list)
|
|
80
|
+
metadata: dict = field(default_factory=dict)
|
|
81
|
+
|
|
82
|
+
def to_dict(self) -> dict:
|
|
83
|
+
"""Recursively convert the node tree to a plain dict.
|
|
84
|
+
|
|
85
|
+
The returned structure is directly suitable for
|
|
86
|
+
``json.dumps(node.to_dict())``. Keys use the short names
|
|
87
|
+
expected by the JavaScript runtime (``id``, ``type``).
|
|
88
|
+
"""
|
|
89
|
+
d: dict = {
|
|
90
|
+
"id": self.node_id,
|
|
91
|
+
"type": self.node_type,
|
|
92
|
+
"children": [child.to_dict() for child in self.children],
|
|
93
|
+
}
|
|
94
|
+
if self.metadata:
|
|
95
|
+
d["metadata"] = self.metadata
|
|
96
|
+
return d
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class ViewportNode(SceneNode):
|
|
101
|
+
"""A viewport (coordinate-system container) in the scene tree.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
name : str
|
|
106
|
+
The viewport name (mirrors the grid viewport name).
|
|
107
|
+
transform : dict
|
|
108
|
+
Viewport placement expressed as ``{"x0": ..., "y0": ...,
|
|
109
|
+
"w": ..., "h": ...}`` in the parent coordinate system.
|
|
110
|
+
clip : bool
|
|
111
|
+
Whether drawing is clipped to the viewport boundary.
|
|
112
|
+
clip_id : str or None
|
|
113
|
+
Reference to a ``<clipPath>`` element in *DefsCollection*.
|
|
114
|
+
mask_id : str or None
|
|
115
|
+
Reference to a ``<mask>`` element in *DefsCollection*.
|
|
116
|
+
mask_type : str or None
|
|
117
|
+
Mask compositing type (e.g. ``"luminance"``, ``"alpha"``).
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
name: str = ""
|
|
121
|
+
transform: dict = field(default_factory=lambda: {"x0": 0.0, "y0": 0.0, "w": 1.0, "h": 1.0})
|
|
122
|
+
clip: bool = False
|
|
123
|
+
clip_id: Optional[str] = None
|
|
124
|
+
mask_id: Optional[str] = None
|
|
125
|
+
mask_type: Optional[str] = None
|
|
126
|
+
|
|
127
|
+
def to_dict(self) -> dict:
|
|
128
|
+
d = super().to_dict()
|
|
129
|
+
d["name"] = self.name
|
|
130
|
+
d["transform"] = self.transform
|
|
131
|
+
d["clip"] = self.clip
|
|
132
|
+
if self.clip_id is not None:
|
|
133
|
+
d["clip_id"] = self.clip_id
|
|
134
|
+
if self.mask_id is not None:
|
|
135
|
+
d["mask_id"] = self.mask_id
|
|
136
|
+
if self.mask_type is not None:
|
|
137
|
+
d["mask_type"] = self.mask_type
|
|
138
|
+
return d
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class GrobNode(SceneNode):
|
|
143
|
+
"""A graphical object (grob) leaf or branch in the scene tree.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
props : dict
|
|
148
|
+
Grob-specific geometric properties (e.g. ``x``, ``y``,
|
|
149
|
+
``width``, ``height`` for a rect grob).
|
|
150
|
+
gpar : dict
|
|
151
|
+
Serialized graphical parameters (fill, colour, lwd, ...).
|
|
152
|
+
render_hint : str
|
|
153
|
+
Hint for the web renderer: ``"auto"``, ``"svg"``, or
|
|
154
|
+
``"canvas"``.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
props: dict = field(default_factory=dict)
|
|
158
|
+
gpar: dict = field(default_factory=dict)
|
|
159
|
+
render_hint: str = "auto"
|
|
160
|
+
|
|
161
|
+
def to_dict(self) -> dict:
|
|
162
|
+
d = super().to_dict()
|
|
163
|
+
d["props"] = self.props
|
|
164
|
+
d["gpar"] = self.gpar
|
|
165
|
+
d["render_hint"] = self.render_hint
|
|
166
|
+
# Pass through metadata as "data" for JS tooltip consumption
|
|
167
|
+
if self.metadata:
|
|
168
|
+
d["data"] = self.metadata
|
|
169
|
+
return d
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# ---------------------------------------------------------------------------
|
|
173
|
+
# Shared definitions (gradients, patterns, clip paths, masks)
|
|
174
|
+
# ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class DefsCollection:
|
|
178
|
+
"""Container for shared definitions referenced by ID from scene nodes.
|
|
179
|
+
|
|
180
|
+
Each list stores plain dicts that must contain at least an ``"id"``
|
|
181
|
+
key together with type-specific fields.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
gradients: list = field(default_factory=list)
|
|
185
|
+
patterns: list = field(default_factory=list)
|
|
186
|
+
clip_paths: list = field(default_factory=list)
|
|
187
|
+
masks: list = field(default_factory=list)
|
|
188
|
+
|
|
189
|
+
def to_dict(self) -> dict:
|
|
190
|
+
return {
|
|
191
|
+
"gradients": list(self.gradients),
|
|
192
|
+
"patterns": list(self.patterns),
|
|
193
|
+
"clip_paths": list(self.clip_paths),
|
|
194
|
+
"masks": list(self.masks),
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# ---------------------------------------------------------------------------
|
|
199
|
+
# Top-level scene graph
|
|
200
|
+
# ---------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
@dataclass
|
|
203
|
+
class SceneGraph:
|
|
204
|
+
"""Top-level scene graph container.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
version : int
|
|
209
|
+
Schema version (currently ``1``).
|
|
210
|
+
width : float
|
|
211
|
+
Device width in user units.
|
|
212
|
+
height : float
|
|
213
|
+
Device height in user units.
|
|
214
|
+
dpi : float
|
|
215
|
+
Dots-per-inch used for unit conversion.
|
|
216
|
+
root : ViewportNode
|
|
217
|
+
The root viewport of the scene tree.
|
|
218
|
+
defs : DefsCollection
|
|
219
|
+
Shared definitions (gradients, patterns, clips, masks).
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
width: float
|
|
223
|
+
height: float
|
|
224
|
+
dpi: float
|
|
225
|
+
root: ViewportNode
|
|
226
|
+
version: int = 1
|
|
227
|
+
defs: DefsCollection = field(default_factory=DefsCollection)
|
|
228
|
+
|
|
229
|
+
def to_dict(self) -> dict:
|
|
230
|
+
"""Return the full scene graph as a plain nested dict."""
|
|
231
|
+
return {
|
|
232
|
+
"version": self.version,
|
|
233
|
+
"width": self.width,
|
|
234
|
+
"height": self.height,
|
|
235
|
+
"dpi": self.dpi,
|
|
236
|
+
"root": self.root.to_dict(),
|
|
237
|
+
"defs": self.defs.to_dict(),
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
def to_json(self, *, indent: int | None = 2) -> str:
|
|
241
|
+
"""Serialize the scene graph to a JSON string.
|
|
242
|
+
|
|
243
|
+
Parameters
|
|
244
|
+
----------
|
|
245
|
+
indent : int or None
|
|
246
|
+
JSON indentation level. Pass ``None`` for compact output.
|
|
247
|
+
"""
|
|
248
|
+
return json.dumps(self.to_dict(), indent=indent)
|