icplot 0.0.9__tar.gz → 0.0.11__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.
- {icplot-0.0.9/src/icplot.egg-info → icplot-0.0.11}/PKG-INFO +8 -2
- {icplot-0.0.9 → icplot-0.0.11}/README.md +6 -0
- {icplot-0.0.9 → icplot-0.0.11}/pyproject.toml +2 -2
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot/cairo_interface.py +4 -24
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot/color.py +16 -17
- icplot-0.0.11/src/icplot/gantt/__init__.py +1 -0
- icplot-0.0.11/src/icplot/gantt/gantt.py +19 -0
- icplot-0.0.11/src/icplot/gantt/gantt_renderer.py +59 -0
- icplot-0.0.11/src/icplot/gantt/pgfgantt_interface.py +26 -0
- icplot-0.0.11/src/icplot/graph/axis.py +33 -0
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot/graph/matplotlib.py +5 -5
- icplot-0.0.11/src/icplot/graph/plot.py +64 -0
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot/graph/plotter.py +6 -5
- icplot-0.0.11/src/icplot/graph/series.py +43 -0
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot/image_utils.py +19 -4
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot/main_cli.py +48 -8
- icplot-0.0.11/src/icplot/py.typed +0 -0
- icplot-0.0.11/src/icplot/tex_interface.py +83 -0
- {icplot-0.0.9 → icplot-0.0.11/src/icplot.egg-info}/PKG-INFO +8 -2
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot.egg-info/SOURCES.txt +5 -2
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot.egg-info/requires.txt +1 -1
- icplot-0.0.11/test/test_cairo_interface.py +29 -0
- {icplot-0.0.9 → icplot-0.0.11}/test/test_gantt_chart.py +0 -5
- icplot-0.0.11/test/test_line_plots.py +32 -0
- icplot-0.0.11/test/test_tex_interface.py +24 -0
- icplot-0.0.9/src/icplot/gantt.py +0 -64
- icplot-0.0.9/src/icplot/graph/axis.py +0 -44
- icplot-0.0.9/src/icplot/graph/plot.py +0 -92
- icplot-0.0.9/src/icplot/graph/series.py +0 -111
- icplot-0.0.9/src/icplot/project_elements.py +0 -4
- icplot-0.0.9/src/icplot/tex_interface.py +0 -92
- icplot-0.0.9/test/test_cairo_interface.py +0 -6
- icplot-0.0.9/test/test_line_plots.py +0 -29
- icplot-0.0.9/test/test_tex_interface.py +0 -18
- {icplot-0.0.9 → icplot-0.0.11}/LICENSE +0 -0
- {icplot-0.0.9 → icplot-0.0.11}/setup.cfg +0 -0
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot/__init__.py +0 -0
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot/geometry.py +0 -0
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot/graph/__init__.py +0 -0
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot.egg-info/dependency_links.txt +0 -0
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot.egg-info/entry_points.txt +0 -0
- {icplot-0.0.9 → icplot-0.0.11}/src/icplot.egg-info/top_level.txt +0 -0
- {icplot-0.0.9 → icplot-0.0.11}/test/test_image_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: icplot
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.11
|
|
4
4
|
Summary: Utilities for generating plots and graphics for technical reports.
|
|
5
5
|
Author-email: "James Grogan, Irish Centre for High End Computing" <james.grogan@ichec.ie>
|
|
6
6
|
Project-URL: Repository, https://git.ichec.ie/performance/toolshed/icplot
|
|
@@ -16,7 +16,7 @@ Description-Content-Type: text/markdown
|
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: wand
|
|
18
18
|
Requires-Dist: matplotlib
|
|
19
|
-
Requires-Dist: iccore==0.
|
|
19
|
+
Requires-Dist: iccore==0.1.1
|
|
20
20
|
Provides-Extra: test
|
|
21
21
|
Requires-Dist: pytest; extra == "test"
|
|
22
22
|
Requires-Dist: pytest-cov; extra == "test"
|
|
@@ -62,6 +62,12 @@ To covert between image formats you can do:
|
|
|
62
62
|
icplot convert --source my_image.svg --target my_image.png
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
+
To render a Tex tikz image as pdf and png you can do:
|
|
66
|
+
|
|
67
|
+
``` shell
|
|
68
|
+
icplot convert --source my_tikz.tex
|
|
69
|
+
```
|
|
70
|
+
|
|
65
71
|
# Copyright #
|
|
66
72
|
|
|
67
73
|
Copyright 2024 Irish Centre for High End Computing
|
|
@@ -31,6 +31,12 @@ To covert between image formats you can do:
|
|
|
31
31
|
icplot convert --source my_image.svg --target my_image.png
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
To render a Tex tikz image as pdf and png you can do:
|
|
35
|
+
|
|
36
|
+
``` shell
|
|
37
|
+
icplot convert --source my_tikz.tex
|
|
38
|
+
```
|
|
39
|
+
|
|
34
40
|
# Copyright #
|
|
35
41
|
|
|
36
42
|
Copyright 2024 Irish Centre for High End Computing
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "icplot"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.11"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="James Grogan, Irish Centre for High End Computing", email="james.grogan@ichec.ie" },
|
|
6
6
|
]
|
|
@@ -18,7 +18,7 @@ keywords = ["Publishing", "Technical Reports", "Graphics"]
|
|
|
18
18
|
|
|
19
19
|
dependencies = ["wand",
|
|
20
20
|
"matplotlib",
|
|
21
|
-
"iccore==0.
|
|
21
|
+
"iccore==0.1.1"]
|
|
22
22
|
|
|
23
23
|
[project.urls]
|
|
24
24
|
Repository = "https://git.ichec.ie/performance/toolshed/icplot"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import os
|
|
2
1
|
from pathlib import Path
|
|
2
|
+
|
|
3
3
|
import cairo
|
|
4
4
|
|
|
5
|
-
from .geometry import Scene, Rectangle
|
|
5
|
+
from .geometry import Scene, Rectangle
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class CairoInterface:
|
|
@@ -52,29 +52,9 @@ class CairoInterface:
|
|
|
52
52
|
if item.item_type == "shape":
|
|
53
53
|
self.draw_shape(cr, item)
|
|
54
54
|
elif item.item_type == "text":
|
|
55
|
-
self.draw_text(cr,
|
|
55
|
+
self.draw_text(cr, item)
|
|
56
56
|
|
|
57
|
-
def draw_svg(self, scene, path):
|
|
57
|
+
def draw_svg(self, scene: Scene, path: Path):
|
|
58
58
|
with cairo.SVGSurface(path, scene.size[0], scene.size[1]) as surface:
|
|
59
59
|
cr = cairo.Context(surface)
|
|
60
60
|
self.draw_scene(cr, scene)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if __name__ == "__main__":
|
|
64
|
-
|
|
65
|
-
cairo_interface = CairoInterface()
|
|
66
|
-
|
|
67
|
-
scene = Scene()
|
|
68
|
-
|
|
69
|
-
rect = Rectangle(20, 20)
|
|
70
|
-
rect.location = (10, 10)
|
|
71
|
-
rect.fill = Color(0.5, 0.5, 1, 0.5)
|
|
72
|
-
rect.stroke = Color(0.5, 0.0, 0.0, 0.5)
|
|
73
|
-
scene.items.append(rect)
|
|
74
|
-
|
|
75
|
-
text = TextPath("Hello World")
|
|
76
|
-
text.location = (5, 5)
|
|
77
|
-
scene.items.append(text)
|
|
78
|
-
|
|
79
|
-
output_path = Path(os.getcwd()) / "output.svg"
|
|
80
|
-
cairo_interface.draw_svg(scene, output_path)
|
|
@@ -4,26 +4,26 @@ This module handles colors
|
|
|
4
4
|
|
|
5
5
|
from typing import Callable
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from pydantic import BaseModel
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class Color(
|
|
10
|
+
class Color(BaseModel):
|
|
11
11
|
"""
|
|
12
12
|
A color class, internal storage is rgba as float.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
self.a = a
|
|
15
|
+
r: float = 0.0
|
|
16
|
+
g: float = 0.0
|
|
17
|
+
b: float = 0.0
|
|
18
|
+
a: float = 1.0
|
|
20
19
|
|
|
21
20
|
@staticmethod
|
|
22
|
-
def
|
|
23
|
-
return Color(
|
|
21
|
+
def from_rgba(r: float = 0.0, b: float = 0.0, g: float = 0.0, a: float = 1.0):
|
|
22
|
+
return Color(**{"r": r, "g": g, "b": b, "a": a})
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
@staticmethod
|
|
25
|
+
def from_list(data: list):
|
|
26
|
+
return Color(**{"r": data[0], "g": data[1], "b": data[2]})
|
|
27
27
|
|
|
28
28
|
def as_list(self) -> list:
|
|
29
29
|
return [self.r, self.g, self.b]
|
|
@@ -31,6 +31,9 @@ class Color(Serializable):
|
|
|
31
31
|
def is_black(self) -> bool:
|
|
32
32
|
return self.r == 0.0 and self.g == 0.0 and self.b == 0.0 and self.a == 1.0
|
|
33
33
|
|
|
34
|
+
def serialize(self) -> str:
|
|
35
|
+
return f"r: {self.r}, g: {self.g}, b: {self.b}, a: {self.a}"
|
|
36
|
+
|
|
34
37
|
|
|
35
38
|
class ColorMap:
|
|
36
39
|
"""
|
|
@@ -40,15 +43,11 @@ class ColorMap:
|
|
|
40
43
|
def __init__(self, label: str, data_func: Callable):
|
|
41
44
|
self.label = label
|
|
42
45
|
self.data_func = data_func
|
|
43
|
-
self.start_offset = 0.25
|
|
44
|
-
self.scale = 2
|
|
45
46
|
|
|
46
47
|
def get_color(self, cursor: int, values: list) -> Color:
|
|
47
48
|
"""
|
|
48
49
|
Returns a colour based on a cmap and how far across
|
|
49
50
|
the datasets you are
|
|
50
51
|
"""
|
|
51
|
-
position = cursor / len(values)
|
|
52
|
-
return Color.from_list(
|
|
53
|
-
self.data_func(self.start_offset + (position / self.scale))
|
|
54
|
-
)
|
|
52
|
+
position = cursor / (len(values) - 1)
|
|
53
|
+
return Color.from_list(self.data_func(position))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .gantt import * # NOQA
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from iccore.project import Milestone
|
|
6
|
+
|
|
7
|
+
from icplot.color import Color
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GanttChart(BaseModel):
|
|
11
|
+
|
|
12
|
+
milestones: list[Milestone] = []
|
|
13
|
+
title: str = ""
|
|
14
|
+
start_date: datetime | None = None
|
|
15
|
+
end_date: datetime | None = None
|
|
16
|
+
bar_max_height: float = 0.1
|
|
17
|
+
height: int = 100
|
|
18
|
+
width: int = 500
|
|
19
|
+
bar_color: Color = Color.from_rgba(0.7, 0, 0)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from iccore.project import Milestone
|
|
2
|
+
from icplot.geometry import Scene, Rectangle
|
|
3
|
+
|
|
4
|
+
from .gantt import GanttChart
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GanttRenderer:
|
|
8
|
+
|
|
9
|
+
def _add_milestone(
|
|
10
|
+
self, gantt: GanttChart, scene: Scene, milestone: Milestone, chart_range, yloc
|
|
11
|
+
):
|
|
12
|
+
# chart_delta = chart_range[1] - chart_range[0]
|
|
13
|
+
chart_delta = 0
|
|
14
|
+
start_delta = milestone.start_date - chart_range[0]
|
|
15
|
+
# milestone_delta = milestone.due_date - milestone.start_date
|
|
16
|
+
milestone_delta = 0
|
|
17
|
+
|
|
18
|
+
start_frac = float(start_delta / chart_delta)
|
|
19
|
+
milestone_frac = float(milestone_delta / chart_delta)
|
|
20
|
+
|
|
21
|
+
w = milestone_frac * gantt.width
|
|
22
|
+
h = gantt.bar_max_height * gantt.height
|
|
23
|
+
x = start_frac * gantt.width
|
|
24
|
+
y = yloc
|
|
25
|
+
|
|
26
|
+
rect = Rectangle(w, h)
|
|
27
|
+
rect.location = (x, y)
|
|
28
|
+
rect.fill = gantt.bar_color
|
|
29
|
+
scene.items.append(rect)
|
|
30
|
+
|
|
31
|
+
def _add_milestones(self, gantt: GanttChart, scene: Scene):
|
|
32
|
+
|
|
33
|
+
if not gantt.milestones:
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
milestones = gantt.milestones
|
|
37
|
+
# milestones.sort(key=lambda x: x.start_date, reverse=True)
|
|
38
|
+
|
|
39
|
+
start_date = gantt.start_date
|
|
40
|
+
end_date = gantt.end_date
|
|
41
|
+
if not start_date:
|
|
42
|
+
start_date = milestones[0].start_date
|
|
43
|
+
|
|
44
|
+
# if not end_date:
|
|
45
|
+
# end_date = max(m.due_date for m in milestones)
|
|
46
|
+
|
|
47
|
+
chart_range = (start_date, end_date)
|
|
48
|
+
yloc = 0.0
|
|
49
|
+
bar_height = gantt.bar_max_height * gantt.height
|
|
50
|
+
for milestone in milestones:
|
|
51
|
+
self._add_milestone(gantt, scene, milestone, chart_range, yloc)
|
|
52
|
+
yloc += bar_height
|
|
53
|
+
|
|
54
|
+
def render(self, gantt: GanttChart) -> Scene:
|
|
55
|
+
scene = Scene()
|
|
56
|
+
|
|
57
|
+
self._add_milestones(gantt, scene)
|
|
58
|
+
|
|
59
|
+
return scene
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from .gantt import GanttChart
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PgfGanttRenderer:
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.template: str = ""
|
|
10
|
+
|
|
11
|
+
def _load_template(self):
|
|
12
|
+
if self.template:
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
with open(Path(__file__).parent / "pgfgantt.tex", "r", encoding="utf-8") as f:
|
|
16
|
+
self.template = f.read()
|
|
17
|
+
|
|
18
|
+
def render(self, gantt: GanttChart) -> str:
|
|
19
|
+
|
|
20
|
+
output = self.template
|
|
21
|
+
|
|
22
|
+
if gantt.title:
|
|
23
|
+
title_str = f"\\title{{{gantt.title}}}"
|
|
24
|
+
output.replace("%%TITLE%%", title_str)
|
|
25
|
+
|
|
26
|
+
return output
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module has a plot's axis
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Range(BaseModel):
|
|
9
|
+
"""
|
|
10
|
+
This is a sequence of numbers specified by lower and upper
|
|
11
|
+
bounds and a step size. Eg (1, 5, 1) gives: [1, 2, 3, 4]
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
lower: int
|
|
15
|
+
upper: int
|
|
16
|
+
step: int
|
|
17
|
+
|
|
18
|
+
def eval(self) -> list:
|
|
19
|
+
return list(range(self.lower, self.upper, self.step))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class PlotAxis(BaseModel):
|
|
23
|
+
"""
|
|
24
|
+
This is a plot's axis
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
label: str = ""
|
|
28
|
+
ticks: Range | None = None
|
|
29
|
+
|
|
30
|
+
def get_resolved_ticks(self) -> list:
|
|
31
|
+
if self.ticks:
|
|
32
|
+
return self.ticks.eval()
|
|
33
|
+
return []
|
|
@@ -29,7 +29,7 @@ class MatplotlibPlotter(Plotter):
|
|
|
29
29
|
A plot backend for matplotlib
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
|
-
def __init__(self, cmap_label="
|
|
32
|
+
def __init__(self, cmap_label="gist_rainbow"):
|
|
33
33
|
super().__init__(MatplotlibColorMap(cmap_label))
|
|
34
34
|
|
|
35
35
|
def set_decorations(self, ax, plot: Plot):
|
|
@@ -50,12 +50,12 @@ class MatplotlibPlotter(Plotter):
|
|
|
50
50
|
series.x,
|
|
51
51
|
series.y,
|
|
52
52
|
label=series.label,
|
|
53
|
-
color=series.
|
|
53
|
+
color=series.color.as_list(),
|
|
54
54
|
marker=series.marker,
|
|
55
55
|
)
|
|
56
56
|
|
|
57
57
|
def plot_scatter(self, ax, series: ScatterPlotSeries):
|
|
58
|
-
ax.scatter(series.data, label=series.label, color=series.
|
|
58
|
+
ax.scatter(series.data, label=series.label, color=series.color.as_list())
|
|
59
59
|
|
|
60
60
|
def plot_image(self, ax, series: ImageSeries):
|
|
61
61
|
ax.imshow(series.data)
|
|
@@ -75,11 +75,11 @@ class MatplotlibPlotter(Plotter):
|
|
|
75
75
|
|
|
76
76
|
fig, ax = plt.subplots()
|
|
77
77
|
|
|
78
|
-
self.set_decorations(ax, plot)
|
|
79
|
-
|
|
80
78
|
for series in plot.series:
|
|
81
79
|
self.plot_series(ax, series)
|
|
82
80
|
|
|
81
|
+
self.set_decorations(ax, plot)
|
|
82
|
+
|
|
83
83
|
self.render(fig, path)
|
|
84
84
|
|
|
85
85
|
def plot_grid(self, plot: GridPlot, path: Path | None = None, num_samples: int = 0):
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module has content for generating plots
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from .axis import PlotAxis
|
|
10
|
+
from .series import PlotSeries
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Plot(BaseModel):
|
|
14
|
+
"""
|
|
15
|
+
A generic plot with optional axis ticks
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
title: str = ""
|
|
19
|
+
legend_label: str = ""
|
|
20
|
+
x_axis: PlotAxis = PlotAxis()
|
|
21
|
+
y_axis: PlotAxis = PlotAxis()
|
|
22
|
+
plot_type: str = ""
|
|
23
|
+
series: list[PlotSeries] = []
|
|
24
|
+
|
|
25
|
+
def add_series(self, series: PlotSeries):
|
|
26
|
+
self.series.append(series)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class GridPlot(Plot):
|
|
30
|
+
"""
|
|
31
|
+
Make a grid of plots
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
stride: int = 4
|
|
35
|
+
size: tuple = (25, 20)
|
|
36
|
+
data: list = []
|
|
37
|
+
|
|
38
|
+
def get_series_indices(self, num_samples: int = 0):
|
|
39
|
+
rows = num_samples // self.stride
|
|
40
|
+
cols = num_samples // rows
|
|
41
|
+
len_data = len(self.data)
|
|
42
|
+
|
|
43
|
+
if num_samples == 0:
|
|
44
|
+
indices = list(range(0, len_data))
|
|
45
|
+
else:
|
|
46
|
+
indices = [random.randint(0, len_data - 1) for _ in range(num_samples)]
|
|
47
|
+
return rows, cols, indices
|
|
48
|
+
|
|
49
|
+
def get_subplots(self, num_samples: int = 0):
|
|
50
|
+
rows, cols, indices = self.get_series_indices(num_samples)
|
|
51
|
+
|
|
52
|
+
subplots = []
|
|
53
|
+
count = 1
|
|
54
|
+
for index in indices:
|
|
55
|
+
if num_samples > 0 and count == num_samples + 1:
|
|
56
|
+
break
|
|
57
|
+
if isinstance(self.data[index], list):
|
|
58
|
+
for series in self.data[index]:
|
|
59
|
+
subplots.append(series)
|
|
60
|
+
count += 1
|
|
61
|
+
else:
|
|
62
|
+
subplots.append(self.data[index])
|
|
63
|
+
count += 1
|
|
64
|
+
return rows, cols, subplots
|
|
@@ -11,12 +11,13 @@ class Plotter:
|
|
|
11
11
|
self.cmap = cmap
|
|
12
12
|
|
|
13
13
|
def apply_cmap_colors(self, plot: Plot):
|
|
14
|
-
|
|
14
|
+
non_highlight = []
|
|
15
15
|
for idx, series in enumerate(plot.series):
|
|
16
|
-
if not series.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
if not series.highlight:
|
|
17
|
+
non_highlight.append(series)
|
|
18
|
+
|
|
19
|
+
for idx, series in enumerate(non_highlight):
|
|
20
|
+
series.color = self.cmap.get_color(idx, non_highlight)
|
|
20
21
|
|
|
21
22
|
def plot(self, plot: Plot, path: Path | None = None):
|
|
22
23
|
pass
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from icplot.color import Color
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PlotSeries(BaseModel):
|
|
9
|
+
"""
|
|
10
|
+
A data series in a plot, such as a single line in a line-plot
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
label: str
|
|
14
|
+
color: Color = Color()
|
|
15
|
+
series_type: str = ""
|
|
16
|
+
highlight: bool = False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ImageSeries(PlotSeries):
|
|
20
|
+
"""
|
|
21
|
+
A plot data series where the elements are images
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
data: typing.Any
|
|
25
|
+
transform: typing.Any
|
|
26
|
+
series_type: str = "image"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class LinePlotSeries(PlotSeries):
|
|
30
|
+
"""
|
|
31
|
+
A plot series for line plots
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
x: list
|
|
35
|
+
y: list
|
|
36
|
+
marker: str = "o"
|
|
37
|
+
series_type: str = "line"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ScatterPlotSeries(PlotSeries):
|
|
41
|
+
|
|
42
|
+
data: typing.Any
|
|
43
|
+
series_type: str = "scatter"
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module has functions for converting between image
|
|
3
|
+
formats
|
|
4
|
+
"""
|
|
5
|
+
|
|
1
6
|
import logging
|
|
2
7
|
from pathlib import Path
|
|
3
8
|
|
|
@@ -12,14 +17,16 @@ logger = logging.getLogger(__name__)
|
|
|
12
17
|
def _get_out_filename(source: Path, target: Path | None, extension: str) -> Path:
|
|
13
18
|
if target:
|
|
14
19
|
return target
|
|
15
|
-
|
|
16
|
-
return source.parent / f"{source.stem}.{extension}"
|
|
20
|
+
return source.parent / f"{source.stem}.{extension}"
|
|
17
21
|
|
|
18
22
|
|
|
19
|
-
def pdf_to_png(source: Path, target: Path | None = None):
|
|
23
|
+
def pdf_to_png(source: Path, target: Path | None = None, resolution: int = 300):
|
|
24
|
+
"""
|
|
25
|
+
Convert a pdf to png with white background
|
|
26
|
+
"""
|
|
20
27
|
|
|
21
28
|
outfile = _get_out_filename(source, target, "png")
|
|
22
|
-
with Image(filename=source, resolution=
|
|
29
|
+
with Image(filename=source, resolution=resolution) as img:
|
|
23
30
|
img.format = "png"
|
|
24
31
|
img.background_color = Color("white")
|
|
25
32
|
img.alpha_channel = "remove"
|
|
@@ -27,10 +34,18 @@ def pdf_to_png(source: Path, target: Path | None = None):
|
|
|
27
34
|
|
|
28
35
|
|
|
29
36
|
def svg_to_png(source: Path, target: Path | None = None):
|
|
37
|
+
"""
|
|
38
|
+
Convert an svg to png
|
|
39
|
+
"""
|
|
40
|
+
|
|
30
41
|
outfile = _get_out_filename(source, target, "png")
|
|
31
42
|
cairosvg.svg2png(url=str(source), write_to=str(outfile))
|
|
32
43
|
|
|
33
44
|
|
|
34
45
|
def svg_to_pdf(source: Path, target: Path | None = None):
|
|
46
|
+
"""
|
|
47
|
+
Convert an svg to pdf
|
|
48
|
+
"""
|
|
49
|
+
|
|
35
50
|
outfile = _get_out_filename(source, target, "pdf")
|
|
36
51
|
cairosvg.svg2pdf(url=str(source), write_to=str(outfile))
|
|
@@ -2,24 +2,23 @@
|
|
|
2
2
|
import argparse
|
|
3
3
|
import logging
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
import os
|
|
5
6
|
|
|
6
|
-
from iccore import
|
|
7
|
-
from iccore import
|
|
7
|
+
from iccore.cli_utils import launch_common
|
|
8
|
+
from iccore.serialization import read_yaml
|
|
8
9
|
|
|
10
|
+
from icplot.graph import Plot
|
|
11
|
+
from icplot.graph.matplotlib import MatplotlibPlotter
|
|
9
12
|
from icplot.image_utils import pdf_to_png, svg_to_png, svg_to_pdf
|
|
13
|
+
from icplot.tex_interface import TexBuildSettings, TexInterface
|
|
10
14
|
|
|
11
15
|
logger = logging.getLogger(__name__)
|
|
12
16
|
|
|
13
17
|
|
|
14
|
-
def launch_common(args):
|
|
15
|
-
runtime.ctx.set_is_dry_run(args.dry_run)
|
|
16
|
-
logging_utils.setup_default_logger()
|
|
17
|
-
|
|
18
|
-
|
|
19
18
|
def convert(args):
|
|
20
19
|
launch_common(args)
|
|
21
20
|
|
|
22
|
-
logger.info("
|
|
21
|
+
logger.info("Startubg conversion between %s and %s", args.source, args.target)
|
|
23
22
|
|
|
24
23
|
if args.target:
|
|
25
24
|
target = Path(args.target).resolve()
|
|
@@ -36,9 +35,35 @@ def convert(args):
|
|
|
36
35
|
svg_to_pdf(args.source.resolve(), target)
|
|
37
36
|
else:
|
|
38
37
|
svg_to_png(args.source)
|
|
38
|
+
elif args.source.suffix == ".tex":
|
|
39
|
+
if args.target:
|
|
40
|
+
target = Path(args.target).resolve()
|
|
41
|
+
else:
|
|
42
|
+
target = Path(os.getcwd())
|
|
43
|
+
|
|
44
|
+
settings = TexBuildSettings(
|
|
45
|
+
args.source.resolve(), args.build_dir.resolve(), args.target
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
tex = TexInterface()
|
|
49
|
+
tex.build(settings)
|
|
50
|
+
|
|
39
51
|
logger.info("Finished conversion")
|
|
40
52
|
|
|
41
53
|
|
|
54
|
+
def plot(args):
|
|
55
|
+
|
|
56
|
+
launch_common(args)
|
|
57
|
+
|
|
58
|
+
config = read_yaml(args.config.resolve())
|
|
59
|
+
|
|
60
|
+
plotter = MatplotlibPlotter()
|
|
61
|
+
if ["plots"] in config:
|
|
62
|
+
for eachplot in config["plots"]:
|
|
63
|
+
plot = Plot(**eachplot)
|
|
64
|
+
plotter.plot(plot, args.output_dir.resolve())
|
|
65
|
+
|
|
66
|
+
|
|
42
67
|
def main_cli():
|
|
43
68
|
parser = argparse.ArgumentParser()
|
|
44
69
|
parser.add_argument(
|
|
@@ -61,8 +86,23 @@ def main_cli():
|
|
|
61
86
|
default="",
|
|
62
87
|
help="Path to file to be converted to",
|
|
63
88
|
)
|
|
89
|
+
convert_parser.add_argument(
|
|
90
|
+
"--build_dir",
|
|
91
|
+
default=Path(os.getcwd()) / "_build/tikz",
|
|
92
|
+
help="Path for build output",
|
|
93
|
+
)
|
|
64
94
|
convert_parser.set_defaults(func=convert)
|
|
65
95
|
|
|
96
|
+
plot_parser = subparsers.add_parser("plot")
|
|
97
|
+
plot_parser.add_argument("--config", type=Path, help="Path to the plot config")
|
|
98
|
+
plot_parser.add_argument(
|
|
99
|
+
"--output_dir",
|
|
100
|
+
type=Path,
|
|
101
|
+
default=Path(os.getcwd()),
|
|
102
|
+
help="Path to the plot output directory",
|
|
103
|
+
)
|
|
104
|
+
plot_parser.set_defaults(func=plot)
|
|
105
|
+
|
|
66
106
|
args = parser.parse_args()
|
|
67
107
|
args.func(args)
|
|
68
108
|
|
|
File without changes
|