Skip to content

Commit

Permalink
Add Single file guide
Browse files Browse the repository at this point in the history
  • Loading branch information
Argmaster committed Sep 27, 2024
1 parent 7c2b768 commit a4896fe
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 3 deletions.
6 changes: 3 additions & 3 deletions docs/70_gerber/20_quick_start/00_introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type (eg. silk screen, copper, drill etc.). If second argument is not provided,
behavior is to try to guess file type based on file extension or file attributes. Method
returns `GerberFile` instance.

{{ include_code("test/examples/gerberx3/api/_90_quick_start_from_str.py", "python", title="example_from_str.py", linenums="1", hl_lines="12") }}
{{ include_code("test/examples/gerberx3/api/_60_quick_start_from_str.quickstart.py", "python", title="example_from_str.py", linenums="1", hl_lines="12") }}

---

Expand All @@ -61,7 +61,7 @@ and can be used to manually set file type (eg. silk screen, copper, drill etc.).
second argument is not provided, default behavior is to try to guess file type based on
file extension or file attributes. Method returns `GerberFile` instance.

{{ include_code("test/examples/gerberx3/api/_91_quick_start_from_file.py", "python", title="example_from_file.py", linenums="1", hl_lines="6") }}
{{ include_code("test/examples/gerberx3/api/_61_quick_start_from_file.quickstart.py", "python", title="example_from_file.py", linenums="1", hl_lines="6") }}

---

Expand All @@ -77,7 +77,7 @@ to manually set file type (eg. silk screen, copper, drill etc.). If second argum
not provided, default behavior is to try to guess file type based on file extension or
file attributes. Method returns `GerberFile` instance.

{{ include_code("test/examples/gerberx3/api/_92_quick_start_from_buffer.py", "python", title="example_from_buffer.py", linenums="1", hl_lines="5") }}
{{ include_code("test/examples/gerberx3/api/_62_quick_start_from_buffer.quickstart.py", "python", title="example_from_buffer.py", linenums="1", hl_lines="5") }}

---

Expand Down
49 changes: 49 additions & 0 deletions docs/70_gerber/20_quick_start/01_single_file.md
Original file line number Diff line number Diff line change
@@ -1 +1,50 @@
# Single file guide

This guide shows how to use `GerberFile` object from `pygerber.gerber.api` to to render
and format Gerber individual files. For overview of `pygerber.gerber.api` module,
including `GerberFile` construction options check out
[Introduction](./00_introduction.md). For guide on how to arrange multiple files into
single image using `Project` class check out
[Multi file project](./02_multi_file_project.md).

## Rendering Gerber file

`GerberFile` object exposes `render_with_pillow` method which renders Gerber file into
Pillow image object.

{{ include_code("test/examples/gerberx3/api/_00_single_file_render_with_pillow_defaults_str.example.py", "python", title="render_with_pillow.py", linenums="1") }}

`render_with_pillow()` method accepts `dpmm` parameter which can be used to set custom
dots-per-millimeter value, hence increase and decrease image resolution. By default this
value is set to 20, which is a safe default, but quite low for small PCBs.

`render_with_pillow()` returns `PillowImage` object which wraps actual image
(`PIL.Image.Image` object) and additional information about image coordinate space.

To retrieve image object, you can use `get_image()` method, then you can save it with
[`save()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.save)
method offered by `PIL.Image.Image` object or transform with other methods. To find out
more please refer to [Pillow documentation](https://pillow.readthedocs.io/en/stable/).

To retrieve information about image space you can use `get_image_space()` method. This
method returns `ImageSpace` object which contains information about image coordinates,
image size, etc, as presented below:

{{ include_code("test/examples/gerberx3/api/_50_show_image_info.singlefile.py", "python", title="show_image_space.py", linenums="1") }}

{{ run_capture_stdout("python test/examples/gerberx3/api/_50_show_image_info.singlefile.py", "python show_image_space.py") }}

## Formatting Gerber file

`GerberFile` object exposes `format()` and `formats()` methods which generate Gerber
code formatted according to the specified configuration. For detailed documentation of
formatting options, please refer to
[Formatter -> Configuration](../60_formatter/05_configuration.md).

The difference between `format()` and `formats()` methods is that `format()` method
writes formatted code to `TextIO`-like object while `formats()` returns it as a `str`
object.

{{ include_code("test/examples/gerberx3/api/_51_single_file_format.singlefile.py", "python", title="format_file.py", linenums="1") }}

{{ run_capture_stdout("python test/examples/gerberx3/api/_51_single_file_format.singlefile.py", "python format_file.py") }}
1 change: 1 addition & 0 deletions docs/70_gerber/60_formatter/00_introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Introduction
1 change: 1 addition & 0 deletions docs/70_gerber/60_formatter/05_configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Configuration
11 changes: 11 additions & 0 deletions docs/macros/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,14 @@ def include_definition(symbol: str, **options: str) -> str:
{options_string}
"""

@env.macro
def run_capture_stdout(command: str, title: str, language: str = "log") -> str:
"""Run a command and capture its stdout."""
from subprocess import check_output

return (
f'```{language} title="$ {title}"\n'
+ check_output(command, shell=True).decode(encoding="utf-8")
+ "```"
)
2 changes: 2 additions & 0 deletions src/pygerber/gerber/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Units,
)
from pygerber.gerber.api._project import Project
from pygerber.gerber.formatter.options import Options

__all__ = [
"FileTypeEnum",
Expand All @@ -28,4 +29,5 @@
"PillowImage",
"DEFAULT_COLOR_MAP",
"DEFAULT_ALPHA_COLOR_MAP",
"Options",
]
12 changes: 12 additions & 0 deletions src/pygerber/gerber/api/_gerber_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ def max_y_pixels(self) -> int:
"""Maximum Y coordinate of image in pixels."""
return int(self.max_y_mm * self.dpmm)

def __str__(self) -> str:
return f"""
ImageSpace(
units = {self.units},
min_x_mm = {self.min_x_mm},
min_y_mm = {self.min_y_mm},
max_x_mm = {self.max_x_mm},
max_y_mm = {self.max_y_mm},
dpmm = {self.dpmm},
)
"""


class Image:
"""The `Image` class is a base class for all rendered images returned by
Expand Down
8 changes: 8 additions & 0 deletions test/examples/gerberx3/api/_50_show_image_info.singlefile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pygerber.gerber.api import GerberFile

from pygerber.examples import ExamplesEnum, load_example

gerber_source_code = load_example(ExamplesEnum.UCAMCO_2_11_2)

image = GerberFile.from_str(gerber_source_code).render_with_pillow()
print(image.get_image_space())
10 changes: 10 additions & 0 deletions test/examples/gerberx3/api/_51_single_file_format.singlefile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from pygerber.gerber.api import GerberFile, Options

source_code = """
%FSLAX26Y26*%%MOMM*%%ADD100C,1.5*%D100*X0Y0D03*M02*
"""

gerber_file = GerberFile.from_str(source_code)
formatted_code = gerber_file.formats(Options(d03_indent=2))

print(formatted_code)
29 changes: 29 additions & 0 deletions test/examples/gerberx3/api/test_examples.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from __future__ import annotations

from pathlib import Path
import shutil

import pytest

from pygerber.examples import ExamplesEnum, get_example_path
from test.conftest import cd_to_tempdir

THIS_FILE = Path(__file__)
Expand All @@ -21,3 +23,30 @@ def test_examples(example_path: Path) -> None:
with cd_to_tempdir():
exec(example_path.read_text(encoding="utf-8")) # noqa: S102
assert (Path.cwd() / "output.png").exists()


@pytest.mark.parametrize(
"example_path",
[
*THIS_DIRECTORY.glob("*.quickstart.py"),
],
ids=lambda path: path.name,
)
def test_quickstart_examples(example_path: Path) -> None:
with cd_to_tempdir():
shutil.copy(
get_example_path(ExamplesEnum.UCAMCO_2_11_2).as_posix(), "example.grb"
)
exec(example_path.read_text(encoding="utf-8")) # noqa: S102


@pytest.mark.parametrize(
"example_path",
[
*THIS_DIRECTORY.glob("*.singlefile.py"),
],
ids=lambda path: path.name,
)
def test_single_file_examples(example_path: Path) -> None:
with cd_to_tempdir():
exec(example_path.read_text(encoding="utf-8")) # noqa: S102

0 comments on commit a4896fe

Please sign in to comment.