yaplotlib 0.1.3__py3-none-any.whl → 1.0.0__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.
- yaplotlib/__init__.py +61 -0
- yaplotlib/_writer.py +356 -0
- yaplotlib/core.py +106 -0
- yaplotlib/document.py +162 -0
- yaplotlib/elements.py +366 -0
- yaplotlib/frame.py +365 -0
- yaplotlib/globals.py +62 -0
- yaplotlib/palettes.py +87 -0
- yaplotlib/spec.py +81 -0
- yaplotlib-1.0.0.dist-info/METADATA +129 -0
- yaplotlib-1.0.0.dist-info/RECORD +12 -0
- {yaplotlib-0.1.3.dist-info → yaplotlib-1.0.0.dist-info}/WHEEL +1 -1
- yaplotlib-0.1.3.dist-info/METADATA +0 -15
- yaplotlib-0.1.3.dist-info/RECORD +0 -4
- yaplotlib.py +0 -171
yaplotlib/__init__.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Yaplot 形式の描画データを作る。"""
|
|
2
|
+
|
|
3
|
+
# even: stable; odd: develop
|
|
4
|
+
__version__ = "1.0.0"
|
|
5
|
+
|
|
6
|
+
from .core import (
|
|
7
|
+
Arrow,
|
|
8
|
+
ArrowType,
|
|
9
|
+
Circle,
|
|
10
|
+
Color,
|
|
11
|
+
Layer,
|
|
12
|
+
Line,
|
|
13
|
+
NewPage,
|
|
14
|
+
Polygon,
|
|
15
|
+
SetPalette,
|
|
16
|
+
Size,
|
|
17
|
+
Text,
|
|
18
|
+
)
|
|
19
|
+
from .document import YaplotDocument
|
|
20
|
+
from .elements import Batch, PaletteDef, Primitive, Svg
|
|
21
|
+
from .frame import Yaplot, YaplotFrame
|
|
22
|
+
from .globals import Globals
|
|
23
|
+
from .palettes import (
|
|
24
|
+
GradationPalettes,
|
|
25
|
+
RainbowPalettes,
|
|
26
|
+
RandomPalettes,
|
|
27
|
+
gradation_colors,
|
|
28
|
+
rainbow_colors,
|
|
29
|
+
random_colors,
|
|
30
|
+
)
|
|
31
|
+
from .spec import Kind
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"__version__",
|
|
35
|
+
"Kind",
|
|
36
|
+
"Primitive",
|
|
37
|
+
"Batch",
|
|
38
|
+
"PaletteDef",
|
|
39
|
+
"Svg",
|
|
40
|
+
"Globals",
|
|
41
|
+
"YaplotFrame",
|
|
42
|
+
"Yaplot",
|
|
43
|
+
"YaplotDocument",
|
|
44
|
+
"random_colors",
|
|
45
|
+
"rainbow_colors",
|
|
46
|
+
"gradation_colors",
|
|
47
|
+
"Line",
|
|
48
|
+
"Text",
|
|
49
|
+
"Circle",
|
|
50
|
+
"Arrow",
|
|
51
|
+
"Polygon",
|
|
52
|
+
"Color",
|
|
53
|
+
"SetPalette",
|
|
54
|
+
"Size",
|
|
55
|
+
"Layer",
|
|
56
|
+
"ArrowType",
|
|
57
|
+
"NewPage",
|
|
58
|
+
"RandomPalettes",
|
|
59
|
+
"RainbowPalettes",
|
|
60
|
+
"GradationPalettes",
|
|
61
|
+
]
|
yaplotlib/_writer.py
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""要素の列を Yaplot のテキストに変換する。
|
|
2
|
+
|
|
3
|
+
状態(レイヤ・色・半径・矢印)の差分はここでだけ計算する。
|
|
4
|
+
フレームは状態を持たない。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import operator
|
|
8
|
+
from typing import Optional, Sequence, Union
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from .elements import (
|
|
13
|
+
Batch,
|
|
14
|
+
FrameBreak,
|
|
15
|
+
PaletteDef,
|
|
16
|
+
Primitive,
|
|
17
|
+
Svg,
|
|
18
|
+
_as_int,
|
|
19
|
+
_as_radius,
|
|
20
|
+
)
|
|
21
|
+
from .globals import Globals
|
|
22
|
+
from .spec import KINDS
|
|
23
|
+
|
|
24
|
+
# Yaplot 本体の初期値。色だけは線が 0、塗りが 2 と分かれているため未確定にする。
|
|
25
|
+
_DEFAULT_LAYER = 1
|
|
26
|
+
_DEFAULT_RADIUS = 1.0
|
|
27
|
+
_DEFAULT_ARROW = 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def quote_text(text: str) -> str:
|
|
31
|
+
"""空白や引用符を含む文字列を Yaplot の ``"..."`` にする。"""
|
|
32
|
+
if "\n" in text or "\r" in text:
|
|
33
|
+
raise ValueError("改行を含む文字列は出力できません")
|
|
34
|
+
if text == "" or any(char.isspace() or char in '"\\' for char in text):
|
|
35
|
+
escaped = text.replace("\\", "\\\\").replace('"', '\\"')
|
|
36
|
+
return f'"{escaped}"'
|
|
37
|
+
return text
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def format_command(
|
|
41
|
+
command: str,
|
|
42
|
+
points: Sequence[Sequence[float]],
|
|
43
|
+
*,
|
|
44
|
+
count: bool = False,
|
|
45
|
+
text: Optional[str] = None,
|
|
46
|
+
) -> str:
|
|
47
|
+
"""描画コマンド 1 行を作る。座標の末尾には従来どおり空白を付ける。"""
|
|
48
|
+
parts = [command]
|
|
49
|
+
if count:
|
|
50
|
+
parts.append(str(len(points)))
|
|
51
|
+
for point in points:
|
|
52
|
+
for coord in point:
|
|
53
|
+
parts.append(f"{float(coord):.4f}")
|
|
54
|
+
line = " ".join(parts) + " "
|
|
55
|
+
if text is not None:
|
|
56
|
+
line += " " + quote_text(text)
|
|
57
|
+
return line + "\n"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def format_palette(index: int, rgb: Sequence[int]) -> str:
|
|
61
|
+
"""``@ n r g b`` の 1 行を作る。"""
|
|
62
|
+
red, green, blue = rgb
|
|
63
|
+
return f"@ {int(index)} {int(red)} {int(green)} {int(blue)}\n"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class _State:
|
|
67
|
+
"""書き出し中の、Yaplot 側の現在の状態。"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, defaults: Optional[Globals]) -> None:
|
|
70
|
+
self.layer = _DEFAULT_LAYER
|
|
71
|
+
self.radius = _DEFAULT_RADIUS
|
|
72
|
+
self.arrow = _DEFAULT_ARROW
|
|
73
|
+
self.color: Optional[int] = None
|
|
74
|
+
if defaults is not None:
|
|
75
|
+
if defaults.layer is not None:
|
|
76
|
+
self.layer = _as_int("layer", defaults.layer, 1)
|
|
77
|
+
if defaults.radius is not None:
|
|
78
|
+
self.radius = _as_radius(defaults.radius)
|
|
79
|
+
if defaults.arrow is not None:
|
|
80
|
+
self.arrow = _as_int("arrow", defaults.arrow, 0)
|
|
81
|
+
self.color = _opening_color(defaults)
|
|
82
|
+
self._initial = (self.layer, self.radius, self.arrow, self.color)
|
|
83
|
+
|
|
84
|
+
def reset(self) -> None:
|
|
85
|
+
self.layer, self.radius, self.arrow, self.color = self._initial
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _opening_color(defaults: Globals) -> Optional[int]:
|
|
89
|
+
"""global ブロックを読み終えた時点で選ばれている色。"""
|
|
90
|
+
if defaults.color is not None:
|
|
91
|
+
return _as_int("color", defaults.color, 0)
|
|
92
|
+
if not defaults.palettes:
|
|
93
|
+
return None
|
|
94
|
+
last = sorted(defaults.palettes)[-1]
|
|
95
|
+
return _as_int("palette", last, 0)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _lowering(kind: str, lower: bool) -> bool:
|
|
99
|
+
spec = KINDS[kind]
|
|
100
|
+
if lower and spec.lower_to:
|
|
101
|
+
return True
|
|
102
|
+
if spec.command is None:
|
|
103
|
+
raise ValueError(
|
|
104
|
+
f"{kind!r} にはまだ Yaplot のコマンドがありません。"
|
|
105
|
+
" dumps(lower=True) で線分に分解して出力できます。"
|
|
106
|
+
)
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _emit_style(state: _State, item: Union[Primitive, Batch], out: list[str]) -> None:
|
|
111
|
+
if item.layer is not None and item.layer != state.layer:
|
|
112
|
+
state.layer = item.layer
|
|
113
|
+
out.append(f"y {state.layer}\n")
|
|
114
|
+
if item.color is not None and item.color != state.color:
|
|
115
|
+
state.color = item.color
|
|
116
|
+
out.append(f"@ {state.color}\n")
|
|
117
|
+
if item.radius is not None and item.radius != state.radius:
|
|
118
|
+
state.radius = item.radius
|
|
119
|
+
out.append(f"r {state.radius}\n")
|
|
120
|
+
if item.arrow is not None and item.arrow != state.arrow:
|
|
121
|
+
state.arrow = item.arrow
|
|
122
|
+
out.append(f"a {state.arrow}\n")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _emit_primitive(state: _State, prim: Primitive, out: list[str], lower: bool) -> None:
|
|
126
|
+
spec = KINDS[prim.kind]
|
|
127
|
+
_emit_style(state, prim, out)
|
|
128
|
+
if _lowering(prim.kind, lower):
|
|
129
|
+
command = KINDS[spec.lower_to].command # type: ignore[index]
|
|
130
|
+
points = prim.points
|
|
131
|
+
for start, end in zip(points, points[1:]):
|
|
132
|
+
out.append(format_command(command or "", (start, end)))
|
|
133
|
+
return
|
|
134
|
+
out.append(
|
|
135
|
+
format_command(
|
|
136
|
+
spec.command or "",
|
|
137
|
+
prim.points,
|
|
138
|
+
count=spec.npoints is None,
|
|
139
|
+
text=prim.text,
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _emit_batch(state: _State, batch: Batch, out: list[str], lower: bool) -> None:
|
|
145
|
+
spec = KINDS[batch.kind]
|
|
146
|
+
lowering = _lowering(batch.kind, lower)
|
|
147
|
+
_emit_style(state, batch, out)
|
|
148
|
+
if lowering:
|
|
149
|
+
command = KINDS[spec.lower_to].command or "" # type: ignore[union-attr]
|
|
150
|
+
if isinstance(batch.coords, np.ndarray):
|
|
151
|
+
out.append(_format_block(command, _chains_to_lines(batch.coords), None, False))
|
|
152
|
+
else:
|
|
153
|
+
for array in batch.coords:
|
|
154
|
+
for index in range(array.shape[0] - 1):
|
|
155
|
+
out.append(format_command(command, (array[index], array[index + 1])))
|
|
156
|
+
return
|
|
157
|
+
if isinstance(batch.coords, np.ndarray):
|
|
158
|
+
out.append(
|
|
159
|
+
_format_block(spec.command or "", batch.coords, batch.texts, spec.npoints is None)
|
|
160
|
+
)
|
|
161
|
+
return
|
|
162
|
+
for index, array in enumerate(batch.coords):
|
|
163
|
+
text = None if batch.texts is None else batch.texts[index]
|
|
164
|
+
out.append(format_command(spec.command or "", array, count=True, text=text))
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _emit_palette(state: _State, item: PaletteDef, out: list[str]) -> None:
|
|
168
|
+
out.append(format_palette(item.index, item.rgb))
|
|
169
|
+
# @ n r g b はパレット n を現在の色としても選ぶ。
|
|
170
|
+
state.color = item.index
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _chains_to_lines(coords: np.ndarray) -> np.ndarray:
|
|
174
|
+
"""(N, m, 3) の折れ線を、隣り合う頂点を結ぶ (N*(m-1), 2, 3) の線分にする。"""
|
|
175
|
+
if coords.shape[0] == 0 or coords.shape[1] < 2:
|
|
176
|
+
return np.zeros((0, 2, 3), dtype=float)
|
|
177
|
+
paired = np.stack((coords[:, :-1, :], coords[:, 1:, :]), axis=2)
|
|
178
|
+
return paired.reshape(-1, 2, 3)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _format_block(
|
|
182
|
+
command: str,
|
|
183
|
+
coords: np.ndarray,
|
|
184
|
+
texts: Optional[tuple[str, ...]],
|
|
185
|
+
with_count: bool,
|
|
186
|
+
) -> str:
|
|
187
|
+
"""(N, k, 3) をコマンドの行にまとめて整形する。"""
|
|
188
|
+
count = int(coords.shape[0])
|
|
189
|
+
if count == 0:
|
|
190
|
+
return ""
|
|
191
|
+
width = int(coords.shape[1])
|
|
192
|
+
formatted = np.char.mod("%.4f", coords.reshape(count, width * 3))
|
|
193
|
+
prefix = f"{command} {width} " if with_count else f"{command} "
|
|
194
|
+
lines: list[str] = []
|
|
195
|
+
for index in range(count):
|
|
196
|
+
line = prefix + " ".join(formatted[index].tolist()) + " "
|
|
197
|
+
if texts is not None:
|
|
198
|
+
line += " " + quote_text(texts[index])
|
|
199
|
+
lines.append(line + "\n")
|
|
200
|
+
return "".join(lines)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _svg_text(svg: Svg) -> str:
|
|
204
|
+
text = svg.text
|
|
205
|
+
if not text.endswith("\n"):
|
|
206
|
+
return text + "\n"
|
|
207
|
+
return text
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def render_frame(
|
|
211
|
+
items: Sequence[object],
|
|
212
|
+
svgs: Sequence[Svg],
|
|
213
|
+
defaults: Optional[Globals],
|
|
214
|
+
lower: bool = True,
|
|
215
|
+
) -> str:
|
|
216
|
+
"""1 フレーム分をテキストにする。末尾にフレーム区切りの空行は付けない。"""
|
|
217
|
+
state = _State(defaults)
|
|
218
|
+
out: list[str] = []
|
|
219
|
+
for item in items:
|
|
220
|
+
if isinstance(item, FrameBreak):
|
|
221
|
+
out.append("\n")
|
|
222
|
+
state.reset()
|
|
223
|
+
continue
|
|
224
|
+
if isinstance(item, PaletteDef):
|
|
225
|
+
_emit_palette(state, item, out)
|
|
226
|
+
elif isinstance(item, Batch):
|
|
227
|
+
_emit_batch(state, item, out, lower)
|
|
228
|
+
elif isinstance(item, Primitive):
|
|
229
|
+
_emit_primitive(state, item, out, lower)
|
|
230
|
+
else:
|
|
231
|
+
raise TypeError(f"フレームに置けない要素です: {type(item).__name__}")
|
|
232
|
+
for svg in svgs:
|
|
233
|
+
out.append(_svg_text(svg))
|
|
234
|
+
return "".join(out)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _join_frames(texts: Sequence[str]) -> str:
|
|
238
|
+
out: list[str] = []
|
|
239
|
+
last = len(texts) - 1
|
|
240
|
+
for index, text in enumerate(texts):
|
|
241
|
+
if text and not text.endswith("\n"):
|
|
242
|
+
text += "\n"
|
|
243
|
+
out.append(text)
|
|
244
|
+
if index != last:
|
|
245
|
+
out.append("\n")
|
|
246
|
+
return "".join(out)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def render_document(defaults: Globals, frames: Sequence[object], lower: bool = True) -> str:
|
|
250
|
+
"""global ブロックと全フレームを 1 つのテキストにする。"""
|
|
251
|
+
parts: list[str] = []
|
|
252
|
+
if not defaults.is_empty():
|
|
253
|
+
parts.append(render_globals(defaults))
|
|
254
|
+
rendered = [
|
|
255
|
+
render_frame(frame.items, frame.svgs, defaults, lower) # type: ignore[attr-defined]
|
|
256
|
+
for frame in frames
|
|
257
|
+
]
|
|
258
|
+
parts.append(_join_frames(rendered))
|
|
259
|
+
return "".join(parts)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def render_globals(defaults: Globals) -> str:
|
|
263
|
+
"""global ブロックを、終端の空行まで含めて作る。"""
|
|
264
|
+
lines = ["global\n"]
|
|
265
|
+
if defaults.reality is not None:
|
|
266
|
+
lines.append(f"reality {_int_between('reality', defaults.reality, 0, 4)}\n")
|
|
267
|
+
if defaults.splat is not None:
|
|
268
|
+
lines.append(f"splat {_int_between('splat', defaults.splat, 0, 3)}\n")
|
|
269
|
+
if defaults.fov is not None:
|
|
270
|
+
lines.append(f"fov {_float_token(_as_float('fov', defaults.fov))}\n")
|
|
271
|
+
if defaults.verbose is not None:
|
|
272
|
+
lines.append(f"verbose {_int_between('verbose', defaults.verbose, 0, 3)}\n")
|
|
273
|
+
if defaults.thickness is not None:
|
|
274
|
+
lines.append(f"thickness {_plain_int('thickness', defaults.thickness)}\n")
|
|
275
|
+
if defaults.rotation is not None:
|
|
276
|
+
lines.append("rotation " + _rotation(defaults.rotation) + "\n")
|
|
277
|
+
if defaults.fps is not None:
|
|
278
|
+
lines.append(f"fps {_float_token(_as_float('fps', defaults.fps))}\n")
|
|
279
|
+
if defaults.hide:
|
|
280
|
+
lines.append("hide " + _layers("hide", defaults.hide) + "\n")
|
|
281
|
+
if defaults.show:
|
|
282
|
+
lines.append("show " + _layers("show", defaults.show) + "\n")
|
|
283
|
+
for message in defaults.messages:
|
|
284
|
+
if not isinstance(message, str):
|
|
285
|
+
raise TypeError("message は文字列にしてください")
|
|
286
|
+
lines.append("message " + quote_text(message) + "\n")
|
|
287
|
+
if defaults.radius is not None:
|
|
288
|
+
lines.append(f"r {_as_radius(defaults.radius)}\n")
|
|
289
|
+
if defaults.layer is not None:
|
|
290
|
+
lines.append(f"y {_as_int('layer', defaults.layer, 1)}\n")
|
|
291
|
+
if defaults.arrow is not None:
|
|
292
|
+
lines.append(f"a {_as_int('arrow', defaults.arrow, 0)}\n")
|
|
293
|
+
for index in sorted(defaults.palettes):
|
|
294
|
+
item = PaletteDef(index, defaults.palettes[index])
|
|
295
|
+
lines.append(format_palette(item.index, item.rgb))
|
|
296
|
+
if defaults.color is not None:
|
|
297
|
+
color = _as_int("color", defaults.color, 0)
|
|
298
|
+
last: Optional[int] = None
|
|
299
|
+
if defaults.palettes:
|
|
300
|
+
last = _as_int("palette", sorted(defaults.palettes)[-1], 0)
|
|
301
|
+
# 直前の @ n r g b がすでにこの色を選んでいれば、繰り返し出力しない。
|
|
302
|
+
if last != color:
|
|
303
|
+
lines.append(f"@ {color}\n")
|
|
304
|
+
for svg in defaults.svgs:
|
|
305
|
+
lines.append(_svg_text(svg))
|
|
306
|
+
lines.append("\n")
|
|
307
|
+
return "".join(lines)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _plain_int(name: str, value: object) -> int:
|
|
311
|
+
if isinstance(value, bool):
|
|
312
|
+
raise TypeError(f"{name} は整数で指定してください")
|
|
313
|
+
try:
|
|
314
|
+
return operator.index(value) # type: ignore[arg-type]
|
|
315
|
+
except TypeError:
|
|
316
|
+
raise TypeError(f"{name} は整数で指定してください") from None
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _int_between(name: str, value: object, lo: int, hi: int) -> int:
|
|
320
|
+
number = _plain_int(name, value)
|
|
321
|
+
if not lo <= number <= hi:
|
|
322
|
+
raise ValueError(f"{name} は {lo}..{hi} の範囲です: {number}")
|
|
323
|
+
return number
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _as_float(name: str, value: object) -> float:
|
|
327
|
+
if isinstance(value, bool):
|
|
328
|
+
raise TypeError(f"{name} は数値で指定してください")
|
|
329
|
+
try:
|
|
330
|
+
return float(value) # type: ignore[arg-type]
|
|
331
|
+
except (TypeError, ValueError):
|
|
332
|
+
raise TypeError(f"{name} は数値で指定してください") from None
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _float_token(value: float) -> str:
|
|
336
|
+
if value == int(value) and abs(value) < 1e15:
|
|
337
|
+
return str(int(value))
|
|
338
|
+
return format(value, ".6g")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _rotation(value: object) -> str:
|
|
342
|
+
try:
|
|
343
|
+
numbers = tuple(_as_float("rotation", item) for item in value) # type: ignore[union-attr]
|
|
344
|
+
except TypeError:
|
|
345
|
+
raise TypeError("rotation は 1〜3 個の数値にしてください") from None
|
|
346
|
+
if not 1 <= len(numbers) <= 3:
|
|
347
|
+
raise ValueError(f"rotation は 1〜3 個の数値にしてください({len(numbers)} 個渡されました)")
|
|
348
|
+
return " ".join(_float_token(number) for number in numbers)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _layers(name: str, values: object) -> str:
|
|
352
|
+
try:
|
|
353
|
+
numbers = [_as_int(name, item, 1) for item in values] # type: ignore[union-attr]
|
|
354
|
+
except TypeError:
|
|
355
|
+
raise TypeError(f"{name} はレイヤ番号の列にしてください") from None
|
|
356
|
+
return " ".join(str(number) for number in numbers)
|
yaplotlib/core.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""低レベル API。1 コマンドぶんの文字列を返す。
|
|
2
|
+
|
|
3
|
+
新しいコードでは :class:`yaplotlib.YaplotDocument` を使う。
|
|
4
|
+
これらの関数は互換のために残してある。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import warnings
|
|
8
|
+
from typing import Iterable, Optional, Sequence, Union
|
|
9
|
+
|
|
10
|
+
from ._writer import format_command, format_palette
|
|
11
|
+
from .elements import Primitive
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _deprecated(name: str, replacement: str) -> None:
|
|
15
|
+
warnings.warn(
|
|
16
|
+
f"yaplotlib.{name}() は非推奨です。{replacement} を使ってください。",
|
|
17
|
+
DeprecationWarning,
|
|
18
|
+
stacklevel=3,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def Line(v0: Sequence[float], v1: Sequence[float]) -> str:
|
|
23
|
+
"""2 点を結ぶ線分。非推奨。"""
|
|
24
|
+
_deprecated("Line", "フレームの add('line', v0, v1)")
|
|
25
|
+
prim = Primitive("line", (v0, v1))
|
|
26
|
+
return format_command("l", prim.points)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def Text(v: Sequence[float], txt: str) -> str:
|
|
30
|
+
"""位置 v に文字列を描く。非推奨。"""
|
|
31
|
+
_deprecated("Text", "フレームの add('text', v, text=...)")
|
|
32
|
+
prim = Primitive("text", (v,), text=txt)
|
|
33
|
+
return format_command("t", prim.points, text=prim.text)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def Circle(v0: Sequence[float]) -> str:
|
|
37
|
+
"""中心 v0 に円を描く。非推奨。"""
|
|
38
|
+
_deprecated("Circle", "フレームの add('circle', v0)")
|
|
39
|
+
prim = Primitive("circle", (v0,))
|
|
40
|
+
return format_command("c", prim.points)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def Arrow(v0: Sequence[float], v1: Sequence[float]) -> str:
|
|
44
|
+
"""始点 v0、終点 v1 の棒(stick)を描く。非推奨。"""
|
|
45
|
+
_deprecated("Arrow", "フレームの add('stick', v0, v1)")
|
|
46
|
+
prim = Primitive("stick", (v0, v1))
|
|
47
|
+
return format_command("s", prim.points)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def Polygon(vertices: Iterable[Sequence[float]]) -> str:
|
|
51
|
+
"""頂点列から多角形を描く。非推奨。"""
|
|
52
|
+
_deprecated("Polygon", "フレームの add('polygon', *vertices)")
|
|
53
|
+
prim = Primitive("polygon", tuple(vertices))
|
|
54
|
+
return format_command("p", prim.points, count=True)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def Color(x: int) -> str:
|
|
58
|
+
"""パレット番号 x を選ぶ。非推奨。"""
|
|
59
|
+
_deprecated("Color", "要素の color=")
|
|
60
|
+
return f"@ {int(x)}\n"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def SetPalette(
|
|
64
|
+
x: int,
|
|
65
|
+
R: Union[int, Sequence[float]],
|
|
66
|
+
G: Optional[float] = None,
|
|
67
|
+
B: Optional[float] = None,
|
|
68
|
+
maxval: float = 255.0,
|
|
69
|
+
) -> str:
|
|
70
|
+
"""パレット x の RGB を設定する。非推奨。
|
|
71
|
+
|
|
72
|
+
R, G, B は 0..maxval の範囲、または (R, G, B) の列を渡す。
|
|
73
|
+
"""
|
|
74
|
+
_deprecated("SetPalette", "フレームの palette(index, rgb)")
|
|
75
|
+
if G is None:
|
|
76
|
+
R, G, B = R # type: ignore[misc]
|
|
77
|
+
rgb = (
|
|
78
|
+
int(R * 255 / maxval), # type: ignore[operator]
|
|
79
|
+
int(G * 255 / maxval), # type: ignore[operator]
|
|
80
|
+
int(B * 255 / maxval), # type: ignore[operator]
|
|
81
|
+
)
|
|
82
|
+
return format_palette(int(x), rgb)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def Size(x: float) -> str:
|
|
86
|
+
"""円や棒の半径を設定する。非推奨。"""
|
|
87
|
+
_deprecated("Size", "要素の radius=")
|
|
88
|
+
return f"r {float(x)}\n"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def Layer(x: int) -> str:
|
|
92
|
+
"""描画レイヤを設定する。非推奨。"""
|
|
93
|
+
_deprecated("Layer", "要素の layer=")
|
|
94
|
+
return f"y {int(x)}\n"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def ArrowType(x: int) -> str:
|
|
98
|
+
"""矢印のスタイルを設定する。非推奨。"""
|
|
99
|
+
_deprecated("ArrowType", "要素の arrow=")
|
|
100
|
+
return f"a {int(x)}\n"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def NewPage() -> str:
|
|
104
|
+
"""フレームの区切り。非推奨。"""
|
|
105
|
+
_deprecated("NewPage", "YaplotDocument.new_frame()")
|
|
106
|
+
return "\n"
|
yaplotlib/document.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""複数フレームと global ブロックからなる Yaplot ドキュメント。"""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Iterable, Iterator, Optional, Sequence, Union
|
|
5
|
+
|
|
6
|
+
from ._writer import render_document
|
|
7
|
+
from .frame import YaplotFrame, _deprecated
|
|
8
|
+
from .globals import Globals
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class YaplotDocument:
|
|
12
|
+
"""Yaplot のファイル 1 つ。
|
|
13
|
+
|
|
14
|
+
:attr:`globals` に全フレーム共通の既定値と SVG を置き、
|
|
15
|
+
:meth:`new_frame` が返すフレームに描画要素を追加する。
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self) -> None:
|
|
19
|
+
self.globals = Globals()
|
|
20
|
+
self.frames: list[YaplotFrame] = []
|
|
21
|
+
|
|
22
|
+
def new_frame(self) -> YaplotFrame:
|
|
23
|
+
"""フレームを 1 つ追加して、それを返す。"""
|
|
24
|
+
frame = YaplotFrame()
|
|
25
|
+
self.frames.append(frame)
|
|
26
|
+
return frame
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def current(self) -> YaplotFrame:
|
|
30
|
+
"""最後に追加したフレームを返す。"""
|
|
31
|
+
if not self.frames:
|
|
32
|
+
raise IndexError("フレームがありません。new_frame() を先に呼んでください。")
|
|
33
|
+
return self.frames[-1]
|
|
34
|
+
|
|
35
|
+
def __iter__(self) -> Iterator[YaplotFrame]:
|
|
36
|
+
return iter(self.frames)
|
|
37
|
+
|
|
38
|
+
def __len__(self) -> int:
|
|
39
|
+
return len(self.frames)
|
|
40
|
+
|
|
41
|
+
def dumps(self, *, lower: bool = True) -> str:
|
|
42
|
+
"""global ブロックと全フレームをテキストにする。
|
|
43
|
+
|
|
44
|
+
``lower`` が真のとき、polyline は線分に分解して出力する。
|
|
45
|
+
"""
|
|
46
|
+
return render_document(self.globals, self.frames, lower)
|
|
47
|
+
|
|
48
|
+
def save(self, path: Union[str, Path], *, lower: bool = True) -> None:
|
|
49
|
+
"""ドキュメント全体をファイルに書く。"""
|
|
50
|
+
Path(path).write_text(self.dumps(lower=lower), encoding="utf-8")
|
|
51
|
+
|
|
52
|
+
def _legacy_frame(self) -> YaplotFrame:
|
|
53
|
+
if not self.frames:
|
|
54
|
+
return self.new_frame()
|
|
55
|
+
return self.frames[-1]
|
|
56
|
+
|
|
57
|
+
# 互換 API(非推奨)。呼ばれた時点でフレームが無ければ 1 つ作る。
|
|
58
|
+
def Layer(self, layer: int) -> None:
|
|
59
|
+
_deprecated("YaplotDocument.Layer()", "要素の layer= か、global の layer")
|
|
60
|
+
self._legacy_frame().Layer(layer, _quiet=True)
|
|
61
|
+
|
|
62
|
+
def Color(self, color: int) -> None:
|
|
63
|
+
_deprecated("YaplotDocument.Color()", "要素の color=")
|
|
64
|
+
self._legacy_frame().Color(color, _quiet=True)
|
|
65
|
+
|
|
66
|
+
def Size(self, size: float) -> None:
|
|
67
|
+
_deprecated("YaplotDocument.Size()", "要素の radius=")
|
|
68
|
+
self._legacy_frame().Size(size, _quiet=True)
|
|
69
|
+
|
|
70
|
+
def ArrowType(self, arrowtype: int) -> None:
|
|
71
|
+
_deprecated("YaplotDocument.ArrowType()", "要素の arrow=")
|
|
72
|
+
self._legacy_frame().ArrowType(arrowtype, _quiet=True)
|
|
73
|
+
|
|
74
|
+
def Line(self, a: Sequence[float], b: Sequence[float], layer: int = -1, color: int = -1) -> None:
|
|
75
|
+
_deprecated("YaplotDocument.Line()", "フレームの add('line', a, b)")
|
|
76
|
+
self._legacy_frame().Line(a, b, layer, color, _quiet=True)
|
|
77
|
+
|
|
78
|
+
def Arrow(
|
|
79
|
+
self,
|
|
80
|
+
a: Sequence[float],
|
|
81
|
+
b: Sequence[float],
|
|
82
|
+
layer: int = -1,
|
|
83
|
+
color: int = -1,
|
|
84
|
+
size: float = -1,
|
|
85
|
+
arrowtype: int = -1,
|
|
86
|
+
) -> None:
|
|
87
|
+
_deprecated("YaplotDocument.Arrow()", "フレームの add('stick', a, b, arrow=...)")
|
|
88
|
+
self._legacy_frame().Arrow(a, b, layer, color, size, arrowtype, _quiet=True)
|
|
89
|
+
|
|
90
|
+
def Polygon(self, p: Iterable[Sequence[float]], layer: int = -1, color: int = -1) -> None:
|
|
91
|
+
_deprecated("YaplotDocument.Polygon()", "フレームの add('polygon', *vertices)")
|
|
92
|
+
self._legacy_frame().Polygon(p, layer, color, _quiet=True)
|
|
93
|
+
|
|
94
|
+
def Circle(
|
|
95
|
+
self,
|
|
96
|
+
c: Sequence[float],
|
|
97
|
+
layer: int = -1,
|
|
98
|
+
color: int = -1,
|
|
99
|
+
size: float = -1,
|
|
100
|
+
) -> None:
|
|
101
|
+
_deprecated("YaplotDocument.Circle()", "フレームの add('circle', center)")
|
|
102
|
+
self._legacy_frame().Circle(c, layer, color, size, _quiet=True)
|
|
103
|
+
|
|
104
|
+
def SetPalette(
|
|
105
|
+
self,
|
|
106
|
+
x: int,
|
|
107
|
+
R: Union[int, Sequence[float]],
|
|
108
|
+
G: Optional[float] = None,
|
|
109
|
+
B: Optional[float] = None,
|
|
110
|
+
maxval: float = 255.0,
|
|
111
|
+
) -> None:
|
|
112
|
+
_deprecated("YaplotDocument.SetPalette()", "フレームの palette(index, rgb)")
|
|
113
|
+
self._legacy_frame().SetPalette(x, R, G, B, maxval, _quiet=True)
|
|
114
|
+
|
|
115
|
+
def GradationPalettes(
|
|
116
|
+
self,
|
|
117
|
+
N: int,
|
|
118
|
+
color: Sequence[float],
|
|
119
|
+
color1: Sequence[float],
|
|
120
|
+
offset: int = 10,
|
|
121
|
+
maxval: float = 255.0,
|
|
122
|
+
) -> None:
|
|
123
|
+
_deprecated("YaplotDocument.GradationPalettes()", "フレームの palettes(gradation_colors(...))")
|
|
124
|
+
self._legacy_frame().GradationPalettes(N, color, color1, offset, maxval, _quiet=True)
|
|
125
|
+
|
|
126
|
+
def RandomPalettes(self, N: int, offset: int = 10) -> None:
|
|
127
|
+
_deprecated("YaplotDocument.RandomPalettes()", "フレームの palettes(random_colors(...))")
|
|
128
|
+
self._legacy_frame().RandomPalettes(N, offset, _quiet=True)
|
|
129
|
+
|
|
130
|
+
def RainbowPalettes(self, N: int, offset: int = 10) -> None:
|
|
131
|
+
_deprecated("YaplotDocument.RainbowPalettes()", "フレームの palettes(rainbow_colors(...))")
|
|
132
|
+
self._legacy_frame().RainbowPalettes(N, offset, _quiet=True)
|
|
133
|
+
|
|
134
|
+
def Points(
|
|
135
|
+
self,
|
|
136
|
+
points: Iterable[Sequence[float]],
|
|
137
|
+
layer: int = -1,
|
|
138
|
+
color: int = -1,
|
|
139
|
+
size: float = -1,
|
|
140
|
+
) -> None:
|
|
141
|
+
_deprecated("YaplotDocument.Points()", "フレームの add_many('circle', points)")
|
|
142
|
+
self._legacy_frame().Points(points, layer, color, size, _quiet=True)
|
|
143
|
+
|
|
144
|
+
def LinesBatch(
|
|
145
|
+
self,
|
|
146
|
+
segments: Iterable[tuple[Sequence[float], Sequence[float]]],
|
|
147
|
+
layer: int = -1,
|
|
148
|
+
color: int = -1,
|
|
149
|
+
) -> None:
|
|
150
|
+
_deprecated("YaplotDocument.LinesBatch()", "フレームの add_many('line', coords)")
|
|
151
|
+
self._legacy_frame().LinesBatch(segments, layer, color, _quiet=True)
|
|
152
|
+
|
|
153
|
+
def ArrowsBatch(
|
|
154
|
+
self,
|
|
155
|
+
segments: Iterable[tuple[Sequence[float], Sequence[float]]],
|
|
156
|
+
layer: int = -1,
|
|
157
|
+
color: int = -1,
|
|
158
|
+
size: float = -1,
|
|
159
|
+
arrowtype: int = -1,
|
|
160
|
+
) -> None:
|
|
161
|
+
_deprecated("YaplotDocument.ArrowsBatch()", "フレームの add_many('stick', coords)")
|
|
162
|
+
self._legacy_frame().ArrowsBatch(segments, layer, color, size, arrowtype, _quiet=True)
|