icplot 0.0.11__tar.gz → 0.0.13__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 (43) hide show
  1. {icplot-0.0.11/src/icplot.egg-info → icplot-0.0.13}/PKG-INFO +2 -2
  2. {icplot-0.0.11 → icplot-0.0.13}/pyproject.toml +2 -2
  3. icplot-0.0.13/src/icplot/cairo_interface.py +59 -0
  4. icplot-0.0.13/src/icplot/gantt/gantt_renderer.py +59 -0
  5. icplot-0.0.13/src/icplot/gantt/pgfgantt.py +15 -0
  6. icplot-0.0.13/src/icplot/geometry.py +45 -0
  7. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/graph/axis.py +2 -1
  8. icplot-0.0.13/src/icplot/graph/generator.py +84 -0
  9. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/graph/matplotlib.py +2 -2
  10. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/graph/plot.py +1 -3
  11. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/main_cli.py +2 -3
  12. icplot-0.0.13/src/icplot/tex.py +77 -0
  13. icplot-0.0.13/src/icplot/trace/__init__.py +1 -0
  14. icplot-0.0.13/src/icplot/trace/trace.py +0 -0
  15. {icplot-0.0.11 → icplot-0.0.13/src/icplot.egg-info}/PKG-INFO +2 -2
  16. {icplot-0.0.11 → icplot-0.0.13}/src/icplot.egg-info/SOURCES.txt +5 -2
  17. {icplot-0.0.11 → icplot-0.0.13}/src/icplot.egg-info/requires.txt +1 -1
  18. icplot-0.0.13/test/test_cairo_interface.py +31 -0
  19. {icplot-0.0.11 → icplot-0.0.13}/test/test_tex_interface.py +2 -3
  20. icplot-0.0.11/src/icplot/cairo_interface.py +0 -60
  21. icplot-0.0.11/src/icplot/gantt/gantt_renderer.py +0 -59
  22. icplot-0.0.11/src/icplot/gantt/pgfgantt_interface.py +0 -26
  23. icplot-0.0.11/src/icplot/geometry.py +0 -50
  24. icplot-0.0.11/src/icplot/tex_interface.py +0 -83
  25. icplot-0.0.11/test/test_cairo_interface.py +0 -29
  26. {icplot-0.0.11 → icplot-0.0.13}/LICENSE +0 -0
  27. {icplot-0.0.11 → icplot-0.0.13}/README.md +0 -0
  28. {icplot-0.0.11 → icplot-0.0.13}/setup.cfg +0 -0
  29. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/__init__.py +0 -0
  30. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/color.py +0 -0
  31. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/gantt/__init__.py +0 -0
  32. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/gantt/gantt.py +0 -0
  33. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/graph/__init__.py +0 -0
  34. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/graph/plotter.py +0 -0
  35. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/graph/series.py +0 -0
  36. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/image_utils.py +0 -0
  37. {icplot-0.0.11 → icplot-0.0.13}/src/icplot/py.typed +0 -0
  38. {icplot-0.0.11 → icplot-0.0.13}/src/icplot.egg-info/dependency_links.txt +0 -0
  39. {icplot-0.0.11 → icplot-0.0.13}/src/icplot.egg-info/entry_points.txt +0 -0
  40. {icplot-0.0.11 → icplot-0.0.13}/src/icplot.egg-info/top_level.txt +0 -0
  41. {icplot-0.0.11 → icplot-0.0.13}/test/test_gantt_chart.py +0 -0
  42. {icplot-0.0.11 → icplot-0.0.13}/test/test_image_utils.py +0 -0
  43. {icplot-0.0.11 → icplot-0.0.13}/test/test_line_plots.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: icplot
3
- Version: 0.0.11
3
+ Version: 0.0.13
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.1.1
19
+ Requires-Dist: iccore==0.1.5
20
20
  Provides-Extra: test
21
21
  Requires-Dist: pytest; extra == "test"
22
22
  Requires-Dist: pytest-cov; extra == "test"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "icplot"
3
- version = "0.0.11"
3
+ version = "0.0.13"
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.1.1"]
21
+ "iccore==0.1.5"]
22
22
 
23
23
  [project.urls]
24
24
  Repository = "https://git.ichec.ie/performance/toolshed/icplot"
@@ -0,0 +1,59 @@
1
+ from pathlib import Path
2
+
3
+ import cairo
4
+
5
+ from .geometry import Scene, Rectangle
6
+
7
+
8
+ def draw_rect(cr, rect: Rectangle):
9
+ x0, y0 = rect.location
10
+ x1 = rect.w
11
+ y1 = rect.h
12
+ cr.move_to(x0, y0)
13
+ cr.line_to(x1, y0)
14
+ cr.line_to(x1, y1)
15
+ cr.line_to(x0, y1)
16
+ cr.close_path()
17
+
18
+
19
+ def draw_shape(cr, shape):
20
+ cr.save()
21
+ if shape.shape_type == "rect":
22
+ draw_rect(cr, shape)
23
+ else:
24
+ return
25
+ cr.set_source_rgba(shape.fill.r, shape.fill.g, shape.fill.b, shape.fill.a)
26
+ cr.fill_preserve()
27
+ if shape.stroke is not None:
28
+ cr.set_source_rgba(
29
+ shape.stroke.r, shape.stroke.g, shape.stroke.b, shape.stroke.a
30
+ )
31
+ cr.set_line_width(shape.stroke_thickness)
32
+ cr.stroke()
33
+
34
+ cr.restore()
35
+
36
+
37
+ def draw_text(cr, text):
38
+ cr.save()
39
+
40
+ cr.select_font_face(text.font.family)
41
+ cr.set_font_size(text.font.size)
42
+ cr.move_to(text.location[0], text.location[1])
43
+ cr.show_text(text.content)
44
+
45
+ cr.restore()
46
+
47
+
48
+ def draw_scene(cr, scene: Scene):
49
+ for item in scene.items:
50
+ if item.item_type == "shape":
51
+ draw_shape(cr, item)
52
+ elif item.item_type == "text":
53
+ draw_text(cr, item)
54
+
55
+
56
+ def draw_svg(scene: Scene, path: Path):
57
+ with cairo.SVGSurface(path, scene.size[0], scene.size[1]) as surface:
58
+ cr = cairo.Context(surface)
59
+ draw_scene(cr, scene)
@@ -0,0 +1,59 @@
1
+ from iccore.project import Milestone
2
+ from icplot.geometry import Scene, SceneItem, Rectangle
3
+
4
+ from .gantt import GanttChart
5
+
6
+
7
+ def _render_milestone(
8
+ gantt: GanttChart, milestone: Milestone, chart_range, yloc
9
+ ) -> SceneItem:
10
+ # chart_delta = chart_range[1] - chart_range[0]
11
+ chart_delta = 0
12
+ start_delta = milestone.start_date - chart_range[0]
13
+ # milestone_delta = milestone.due_date - milestone.start_date
14
+ milestone_delta = 0
15
+
16
+ start_frac = float(start_delta / chart_delta)
17
+ milestone_frac = float(milestone_delta / chart_delta)
18
+
19
+ w = milestone_frac * gantt.width
20
+ h = gantt.bar_max_height * gantt.height
21
+ x = start_frac * gantt.width
22
+ y = yloc
23
+
24
+ rect = Rectangle(w=w, h=h, location=(x, y), fill=gantt.bar_color)
25
+ return rect
26
+
27
+
28
+ def _render_milestones(gantt: GanttChart):
29
+
30
+ if not gantt.milestones:
31
+ return
32
+
33
+ milestones = gantt.milestones
34
+ # milestones.sort(key=lambda x: x.start_date, reverse=True)
35
+
36
+ start_date = gantt.start_date
37
+ end_date = gantt.end_date
38
+ if not start_date:
39
+ start_date = milestones[0].start_date
40
+
41
+ # if not end_date:
42
+ # end_date = max(m.due_date for m in milestones)
43
+
44
+ chart_range = (start_date, end_date)
45
+ yloc = 0.0
46
+ bar_height = gantt.bar_max_height * gantt.height
47
+ scene_items = []
48
+ for milestone in milestones:
49
+ item = _render_milestone(gantt, milestone, chart_range, yloc)
50
+ scene_items.append(item)
51
+ yloc += bar_height
52
+ return scene_items
53
+
54
+
55
+ def render(gantt: GanttChart) -> Scene:
56
+ scene = Scene()
57
+ items = _render_milestones(gantt)
58
+ scene.items.extend(items)
59
+ return scene
@@ -0,0 +1,15 @@
1
+ from pathlib import Path
2
+
3
+ from .gantt import GanttChart
4
+
5
+
6
+ def render(gantt: GanttChart) -> str:
7
+
8
+ with open(Path(__file__).parent / "pgfgantt.tex", "r", encoding="utf-8") as f:
9
+ template = f.read()
10
+
11
+ if gantt.title:
12
+ title_str = f"\\title{{{gantt.title}}}"
13
+ template.replace("%%TITLE%%", title_str)
14
+
15
+ return template
@@ -0,0 +1,45 @@
1
+ from pydantic import BaseModel
2
+
3
+ from .color import Color
4
+
5
+
6
+ class Font(BaseModel):
7
+
8
+ family: str = "Sans"
9
+ weight: str = "normal"
10
+ slant: str = "normal"
11
+ size: float = 0.5
12
+
13
+
14
+ class SceneItem(BaseModel):
15
+
16
+ item_type: str
17
+ location: tuple[float, float] = (0.0, 0.0)
18
+
19
+
20
+ class Shape(SceneItem):
21
+
22
+ item_type: str = "shape"
23
+ fill: Color = Color()
24
+ stroke: Color = Color()
25
+ stroke_thickness: float = 0.5
26
+
27
+
28
+ class TextPath(SceneItem):
29
+
30
+ item_type: str = "text"
31
+ content: str
32
+ font: Font = Font()
33
+
34
+
35
+ class Rectangle(Shape):
36
+
37
+ item_type: str = "rect"
38
+ w: float
39
+ h: float
40
+
41
+
42
+ class Scene(BaseModel):
43
+
44
+ items: list[SceneItem] = []
45
+ size: tuple[int, int] = (100, 100)
@@ -27,7 +27,8 @@ class PlotAxis(BaseModel):
27
27
  label: str = ""
28
28
  ticks: Range | None = None
29
29
 
30
- def get_resolved_ticks(self) -> list:
30
+ @property
31
+ def resolved_ticks(self) -> list:
31
32
  if self.ticks:
32
33
  return self.ticks.eval()
33
34
  return []
@@ -0,0 +1,84 @@
1
+ from typing import Any, Callable
2
+ import copy
3
+ import os
4
+ from pathlib import Path
5
+
6
+ from pydantic import BaseModel
7
+
8
+ from .plot import Plot
9
+ from .series import LinePlotSeries
10
+ from .matplotlib import MatplotlibPlotter
11
+
12
+
13
+ class PlotConfig(BaseModel):
14
+
15
+ series_attr: str
16
+ x_attr: str
17
+ plots: dict[str, Plot]
18
+ colormap: Any
19
+
20
+
21
+ def make_plots(
22
+ results: list[Any], config: PlotConfig, point_func: Callable
23
+ ) -> list[Plot]:
24
+ """
25
+ For a set of results and a given plot config generate a list of
26
+ plots.
27
+ """
28
+
29
+ # The instance attribute value for 'series_attr' is used to generate
30
+ # a set of labels, one per plot series
31
+ series_labels = set(getattr(r, config.series_attr) for r in results)
32
+
33
+ plots: list[Plot] = []
34
+ # Each 'y attribute' in the config corresponds to a 'plot' of this
35
+ # attribute's values.
36
+ for y_attr, plot_template in config.plots.items():
37
+ new_plot = copy.deepcopy(plot_template)
38
+ new_plot.name = y_attr
39
+ series = []
40
+
41
+ for label in series_labels:
42
+ # Get results for this series
43
+ label_results = [
44
+ r for r in results if getattr(r, config.series_attr) == label
45
+ ]
46
+
47
+ # Use a user-provided 'point_func' to get the series 'x, y' points
48
+ # from the results, given a 'x_attr, y_attr' pair
49
+ points = [point_func(r, config.x_attr, y_attr) for r in label_results].sort(
50
+ key=lambda x: x[0]
51
+ )
52
+ x, y = zip(*points)
53
+
54
+ if label in config.colormap:
55
+ series.append(
56
+ LinePlotSeries(
57
+ x=x,
58
+ y=y,
59
+ label=label,
60
+ highlight=True,
61
+ color=config.colormap[label],
62
+ )
63
+ )
64
+ else:
65
+ series.append(LinePlotSeries(x=x, y=y, label=label))
66
+ new_plot.series.extend(series)
67
+ plots.append(new_plot)
68
+ return plots
69
+
70
+
71
+ def plot(
72
+ results: list[Any],
73
+ config: PlotConfig,
74
+ point_func: Callable,
75
+ output_dir: Path = Path(os.getcwd()),
76
+ plotter=MatplotlibPlotter(),
77
+ ):
78
+ """
79
+ For a given set of results and plot config generate the
80
+ corresponding plots and render them to files.
81
+ """
82
+
83
+ for p in make_plots(results, config, point_func):
84
+ plotter.plot(p, output_dir / f"{p.name}.svg")
@@ -39,9 +39,9 @@ class MatplotlibPlotter(Plotter):
39
39
  if plot.y_axis.label:
40
40
  ax.set_ylabel(plot.y_axis.label)
41
41
  if plot.x_axis.ticks:
42
- ax.set_xticks(plot.x_axis.get_resolved_ticks())
42
+ ax.set_xticks(plot.x_axis.resolved_ticks)
43
43
  if plot.y_axis.ticks:
44
- ax.set_yticks(plot.y_axis.get_resolved_ticks())
44
+ ax.set_yticks(plot.y_axis.resolved_ticks)
45
45
  if plot.title:
46
46
  ax.set_title(plot.title)
47
47
 
@@ -17,14 +17,12 @@ class Plot(BaseModel):
17
17
 
18
18
  title: str = ""
19
19
  legend_label: str = ""
20
+ name: str = ""
20
21
  x_axis: PlotAxis = PlotAxis()
21
22
  y_axis: PlotAxis = PlotAxis()
22
23
  plot_type: str = ""
23
24
  series: list[PlotSeries] = []
24
25
 
25
- def add_series(self, series: PlotSeries):
26
- self.series.append(series)
27
-
28
26
 
29
27
  class GridPlot(Plot):
30
28
  """
@@ -10,7 +10,8 @@ from iccore.serialization import read_yaml
10
10
  from icplot.graph import Plot
11
11
  from icplot.graph.matplotlib import MatplotlibPlotter
12
12
  from icplot.image_utils import pdf_to_png, svg_to_png, svg_to_pdf
13
- from icplot.tex_interface import TexBuildSettings, TexInterface
13
+ from icplot import tex
14
+ from icplot.tex import TexBuildSettings
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
@@ -44,8 +45,6 @@ def convert(args):
44
45
  settings = TexBuildSettings(
45
46
  args.source.resolve(), args.build_dir.resolve(), args.target
46
47
  )
47
-
48
- tex = TexInterface()
49
48
  tex.build(settings)
50
49
 
51
50
  logger.info("Finished conversion")
@@ -0,0 +1,77 @@
1
+ """
2
+ This module has functionality for publishing Tex documents
3
+ """
4
+
5
+ import subprocess
6
+ from typing import NamedTuple
7
+ import shutil
8
+ import os
9
+ from pathlib import Path
10
+ import logging
11
+
12
+ from .image_utils import pdf_to_png
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class TexBuildSettings(NamedTuple):
18
+ """
19
+ This holds settings for the Tex build
20
+ """
21
+
22
+ source: Path
23
+ build_dir: Path
24
+ output_dir: Path | None = None
25
+ build_engine: str = "pdflatex"
26
+ output_png: bool = True
27
+ clear_workdir: bool = True
28
+
29
+
30
+ def build_pdf(settings: TexBuildSettings, work_dir: Path):
31
+
32
+ cmd = f"{settings.build_engine} {settings.source}"
33
+ with open(work_dir / "stdout.txt", "w", encoding="utf-8") as f:
34
+ subprocess.run(cmd, shell=True, check=True, cwd=work_dir, stdout=f, stderr=f)
35
+
36
+
37
+ def build_single(settings: TexBuildSettings):
38
+ logger.info("Building source: %s", settings.source)
39
+
40
+ # Make a working dir
41
+ work_dir = settings.build_dir / settings.source.stem
42
+ os.makedirs(work_dir, exist_ok=True)
43
+
44
+ build_pdf(settings, work_dir)
45
+
46
+ tex_path = work_dir / settings.source.name
47
+ pdf_path = tex_path.parent / f"{tex_path.stem}.pdf"
48
+ if settings.output_png:
49
+ pdf_to_png(pdf_path)
50
+
51
+ # If output dir is different to build dir copy final content there
52
+ if settings.output_dir and settings.output_dir != work_dir:
53
+ os.makedirs(settings.output_dir, exist_ok=True)
54
+ shutil.copy(pdf_path, settings.output_dir)
55
+
56
+ if settings.output_png:
57
+ png_path = pdf_path.parent / f"{tex_path.stem}.png"
58
+ shutil.copy(png_path, settings.output_dir)
59
+
60
+ if settings.clear_workdir:
61
+ shutil.rmtree(work_dir)
62
+
63
+
64
+ def build(settings: TexBuildSettings):
65
+ if settings.source.is_dir():
66
+ for tex_file in settings.source.glob("*.tex"):
67
+ single_build = TexBuildSettings(
68
+ tex_file,
69
+ settings.build_dir,
70
+ settings.output_dir,
71
+ settings.build_engine,
72
+ )
73
+ build_single(single_build)
74
+ else:
75
+ build_single(settings)
76
+
77
+ logger.info("Finished building sources")
@@ -0,0 +1 @@
1
+ from .trace import * # NOQA
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: icplot
3
- Version: 0.0.11
3
+ Version: 0.0.13
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.1.1
19
+ Requires-Dist: iccore==0.1.5
20
20
  Provides-Extra: test
21
21
  Requires-Dist: pytest; extra == "test"
22
22
  Requires-Dist: pytest-cov; extra == "test"
@@ -9,7 +9,7 @@ src/icplot/geometry.py
9
9
  src/icplot/image_utils.py
10
10
  src/icplot/main_cli.py
11
11
  src/icplot/py.typed
12
- src/icplot/tex_interface.py
12
+ src/icplot/tex.py
13
13
  src/icplot.egg-info/PKG-INFO
14
14
  src/icplot.egg-info/SOURCES.txt
15
15
  src/icplot.egg-info/dependency_links.txt
@@ -19,13 +19,16 @@ src/icplot.egg-info/top_level.txt
19
19
  src/icplot/gantt/__init__.py
20
20
  src/icplot/gantt/gantt.py
21
21
  src/icplot/gantt/gantt_renderer.py
22
- src/icplot/gantt/pgfgantt_interface.py
22
+ src/icplot/gantt/pgfgantt.py
23
23
  src/icplot/graph/__init__.py
24
24
  src/icplot/graph/axis.py
25
+ src/icplot/graph/generator.py
25
26
  src/icplot/graph/matplotlib.py
26
27
  src/icplot/graph/plot.py
27
28
  src/icplot/graph/plotter.py
28
29
  src/icplot/graph/series.py
30
+ src/icplot/trace/__init__.py
31
+ src/icplot/trace/trace.py
29
32
  test/test_cairo_interface.py
30
33
  test/test_gantt_chart.py
31
34
  test/test_image_utils.py
@@ -1,6 +1,6 @@
1
1
  wand
2
2
  matplotlib
3
- iccore==0.1.1
3
+ iccore==0.1.5
4
4
 
5
5
  [cairo]
6
6
  pycairo
@@ -0,0 +1,31 @@
1
+ import os
2
+ import shutil
3
+
4
+ from iccore.test_utils import get_test_output_dir
5
+
6
+ from icplot import cairo_interface
7
+ from icplot.geometry import Scene, Rectangle, TextPath
8
+ from icplot.color import Color
9
+
10
+
11
+ def test_cairo_interface():
12
+
13
+ scene_items = []
14
+ rect = Rectangle(
15
+ w=20, h=20, location= (10, 10),
16
+ fill = Color.from_rgba(0.5, 0.5, 1, 0.5),
17
+ stroke = Color.from_rgba(0.5, 0.0, 0.0, 0.5)
18
+ )
19
+ scene_items.append(rect)
20
+
21
+ text = TextPath(content="Hello World",
22
+ location=(5,5))
23
+ scene_items.append(text)
24
+
25
+ output_dir = get_test_output_dir()
26
+ os.makedirs(output_dir, exist_ok=True)
27
+
28
+ scene = Scene(items=scene_items)
29
+ cairo_interface.draw_svg(scene, output_dir / "output.svg")
30
+ shutil.rmtree(output_dir)
31
+
@@ -3,7 +3,8 @@ from pathlib import Path
3
3
 
4
4
  from iccore.test_utils import get_test_data_dir, get_test_output_dir
5
5
 
6
- from icplot.tex_interface import TexBuildSettings, TexInterface
6
+ from icplot import tex
7
+ from icplot.tex import TexBuildSettings
7
8
 
8
9
 
9
10
  def test_tex_interface():
@@ -14,8 +15,6 @@ def test_tex_interface():
14
15
 
15
16
  settings = TexBuildSettings(source, build_dir, build_dir)
16
17
 
17
- tex = TexInterface()
18
-
19
18
  tex.build(settings)
20
19
 
21
20
  assert Path(build_dir / "test.pdf").exists()
@@ -1,60 +0,0 @@
1
- from pathlib import Path
2
-
3
- import cairo
4
-
5
- from .geometry import Scene, Rectangle
6
-
7
-
8
- class CairoInterface:
9
-
10
- def __init__(self):
11
- pass
12
-
13
- def draw_rect(self, cr, rect: Rectangle):
14
- x0, y0 = rect.location
15
- x1 = rect.w
16
- y1 = rect.h
17
- cr.move_to(x0, y0)
18
- cr.line_to(x1, y0)
19
- cr.line_to(x1, y1)
20
- cr.line_to(x0, y1)
21
- cr.close_path()
22
-
23
- def draw_shape(self, cr, shape):
24
- cr.save()
25
- if shape.shape_type == "rect":
26
- self.draw_rect(cr, shape)
27
- else:
28
- return
29
- cr.set_source_rgba(shape.fill.r, shape.fill.g, shape.fill.b, shape.fill.a)
30
- cr.fill_preserve()
31
- if shape.stroke is not None:
32
- cr.set_source_rgba(
33
- shape.stroke.r, shape.stroke.g, shape.stroke.b, shape.stroke.a
34
- )
35
- cr.set_line_width(shape.stroke_thickness)
36
- cr.stroke()
37
-
38
- cr.restore()
39
-
40
- def draw_text(self, cr, text):
41
- cr.save()
42
-
43
- cr.select_font_face(text.font.family)
44
- cr.set_font_size(text.font.size)
45
- cr.move_to(text.location[0], text.location[1])
46
- cr.show_text(text.content)
47
-
48
- cr.restore()
49
-
50
- def draw_scene(self, cr, scene: Scene):
51
- for item in scene.items:
52
- if item.item_type == "shape":
53
- self.draw_shape(cr, item)
54
- elif item.item_type == "text":
55
- self.draw_text(cr, item)
56
-
57
- def draw_svg(self, scene: Scene, path: Path):
58
- with cairo.SVGSurface(path, scene.size[0], scene.size[1]) as surface:
59
- cr = cairo.Context(surface)
60
- self.draw_scene(cr, scene)
@@ -1,59 +0,0 @@
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
@@ -1,26 +0,0 @@
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
@@ -1,50 +0,0 @@
1
- from .color import Color
2
-
3
-
4
- class Font:
5
-
6
- def __init__(self, family="Sans"):
7
- self.family = family
8
- self.weight = "normal"
9
- self.slant = "normal"
10
- self.size = 0.5
11
-
12
-
13
- class SceneItem:
14
-
15
- def __init__(self, item_type):
16
- self.location = (0, 0)
17
- self.item_type = item_type
18
-
19
-
20
- class Shape(SceneItem):
21
-
22
- def __init__(self, shape_type):
23
- super().__init__("shape")
24
- self.shape_type = shape_type
25
- self.fill = Color()
26
- self.stroke = None
27
- self.stroke_thickness = 0.5
28
-
29
-
30
- class TextPath(SceneItem):
31
-
32
- def __init__(self, content):
33
- super().__init__("text")
34
- self.content = content
35
- self.font = Font()
36
-
37
-
38
- class Rectangle(Shape):
39
-
40
- def __init__(self, w=1.0, h=1.0):
41
- super().__init__("rect")
42
- self.w = w
43
- self.h = h
44
-
45
-
46
- class Scene:
47
-
48
- def __init__(self):
49
- self.items = []
50
- self.size = (100, 100)
@@ -1,83 +0,0 @@
1
- """
2
- This module has functionality for publishing Tex documents
3
- """
4
-
5
- import subprocess
6
- from typing import NamedTuple
7
- import shutil
8
- import os
9
- from pathlib import Path
10
- import logging
11
-
12
- from .image_utils import pdf_to_png
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- class TexBuildSettings(NamedTuple):
18
- """
19
- This holds settings for the Tex build
20
- """
21
-
22
- source: Path
23
- build_dir: Path
24
- output_dir: Path | None = None
25
- build_engine: str = "pdflatex"
26
- output_png: bool = True
27
- clear_workdir: bool = True
28
-
29
-
30
- class TexInterface:
31
- """
32
- This is an interface to Tex for building Tex doucments.
33
- """
34
-
35
- def build_pdf(self, settings: TexBuildSettings, work_dir: Path):
36
-
37
- cmd = f"{settings.build_engine} {settings.source}"
38
-
39
- with open(work_dir / "stdout.txt", "w", encoding="utf-8") as f:
40
- subprocess.run(
41
- cmd, shell=True, check=True, cwd=work_dir, stdout=f, stderr=f
42
- )
43
-
44
- def build_single(self, settings: TexBuildSettings):
45
- logger.info("Building source: %s", settings.source)
46
-
47
- # Make a working dir
48
- work_dir = settings.build_dir / settings.source.stem
49
- os.makedirs(work_dir, exist_ok=True)
50
-
51
- self.build_pdf(settings, work_dir)
52
-
53
- tex_path = work_dir / settings.source.name
54
- pdf_path = tex_path.parent / f"{tex_path.stem}.pdf"
55
- if settings.output_png:
56
- pdf_to_png(pdf_path)
57
-
58
- # If output dir is different to build dir copy final content there
59
- if settings.output_dir and settings.output_dir != work_dir:
60
- os.makedirs(settings.output_dir, exist_ok=True)
61
- shutil.copy(pdf_path, settings.output_dir)
62
-
63
- if settings.output_png:
64
- png_path = pdf_path.parent / f"{tex_path.stem}.png"
65
- shutil.copy(png_path, settings.output_dir)
66
-
67
- if settings.clear_workdir:
68
- shutil.rmtree(work_dir)
69
-
70
- def build(self, settings: TexBuildSettings):
71
- if settings.source.is_dir():
72
- for tex_file in settings.source.glob("*.tex"):
73
- single_build = TexBuildSettings(
74
- tex_file,
75
- settings.build_dir,
76
- settings.output_dir,
77
- settings.build_engine,
78
- )
79
- self.build_single(single_build)
80
- else:
81
- self.build_single(settings)
82
-
83
- logger.info("Finished building sources")
@@ -1,29 +0,0 @@
1
- from pathlib import Path
2
- import os
3
- import shutil
4
-
5
- from icplot.cairo_interface import CairoInterface
6
- from icplot.geometry import Scene, Rectangle, TextPath
7
- from icplot.color import Color
8
-
9
-
10
- def test_cairo_interface():
11
-
12
- cairo_interface = CairoInterface()
13
- scene = Scene()
14
-
15
- rect = Rectangle(20, 20)
16
- rect.location = (10, 10)
17
- rect.fill = Color.from_rgba(0.5, 0.5, 1, 0.5)
18
- rect.stroke = Color.from_rgba(0.5, 0.0, 0.0, 0.5)
19
- scene.items.append(rect)
20
-
21
- text = TextPath("Hello World")
22
- text.location = (5, 5)
23
- scene.items.append(text)
24
-
25
- output_dir = Path(os.getcwd()) / "test_cairo_interface"
26
- os.makedirs(output_dir, exist_ok=True)
27
- cairo_interface.draw_svg(scene, output_dir / "output.svg")
28
- shutil.rmtree(output_dir)
29
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes