kopia lustrzana https://gitlab.com/gerbolyze/gerbonara
Add some documentation
rodzic
c3ca4f95bd
commit
7473e471dc
|
@ -2,3 +2,4 @@ gerbonara_test_failures
|
|||
*.egg-info
|
||||
__pycache__
|
||||
.tox
|
||||
docs/_build/
|
||||
|
|
16
Makefile
16
Makefile
|
@ -1,13 +1,21 @@
|
|||
|
||||
PYTHON ?= python
|
||||
PYTEST ?= pytest
|
||||
PYTHON ?= python
|
||||
PYTEST ?= pytest
|
||||
SPHINX_BUILD ?= sphinx-build
|
||||
|
||||
all: docs sdist bdist_wheel
|
||||
|
||||
.PHONY: clean
|
||||
clean: doc-clean
|
||||
clean:
|
||||
find . -name '*.pyc' -delete
|
||||
rm -rf *.egg-info
|
||||
rm -f .coverage
|
||||
rm -f coverage.xml
|
||||
rm -rf docs/_build
|
||||
|
||||
.PHONY: docs
|
||||
docs:
|
||||
sphinx-build -E docs docs/_build
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
@ -30,7 +38,7 @@ bdist_wheel:
|
|||
python3 setup.py bdist_wheel
|
||||
|
||||
upload: sdist bdist_wheel
|
||||
twine upload -s -i contact@gerbonara.io --config-file ~/.pypirc --skip-existing --repository pypi dist/*
|
||||
twine upload -s -i gerbonara@jaseg.de --config-file ~/.pypirc --skip-existing --repository pypi dist/*
|
||||
|
||||
testupload: sdist bdist_wheel
|
||||
twine upload --config-file ~/.pypirc --skip-existing --repository testpypi dist/*
|
||||
|
|
177
doc/Makefile
177
doc/Makefile
|
@ -1,177 +0,0 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GerberTools.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GerberTools.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/GerberTools"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GerberTools"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
242
doc/make.bat
242
doc/make.bat
|
@ -1,242 +0,0 @@
|
|||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% source
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. xml to make Docutils-native XML files
|
||||
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
|
||||
%SPHINXBUILD% 2> nul
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\GerberTools.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\GerberTools.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdf" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf
|
||||
cd %BUILDDIR%/..
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdfja" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf-ja
|
||||
cd %BUILDDIR%/..
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "xml" (
|
||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pseudoxml" (
|
||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
|
@ -1,40 +0,0 @@
|
|||
About PCB Tools
|
||||
===============
|
||||
|
||||
|
||||
PCB Tools provides a set of utilities for visualizing and working with PCB
|
||||
design files in a variety of formats. The design files are generally referred
|
||||
to as Gerber files. This is a generic term that may refer to
|
||||
`RS-274X (Gerber) <http://en.wikipedia.org/wiki/Gerber_format>`_,
|
||||
`ODB++ <http://en.wikipedia.org/wiki/ODB%2B%2B>`_ ,
|
||||
or `Excellon <http://en.wikipedia.org/wiki/Excellon_format>`_ files. These
|
||||
file formats are used by the CNC equipment used to manufacutre PCBs.
|
||||
|
||||
PCB Tools currently supports the following file formats:
|
||||
|
||||
- Gerber (RS-274X)
|
||||
- Excellon
|
||||
|
||||
with planned support for IPC-2581, ODB++ and more.
|
||||
|
||||
Image Rendering
|
||||
~~~~~~~~~~~~~~~
|
||||
.. image:: ../../examples/cairo_example.png
|
||||
:alt: Rendering Example
|
||||
|
||||
The PCB Tools module provides tools to visualize PCBs and export images in a
|
||||
variety of formats, including SVG and PNG.
|
||||
|
||||
|
||||
|
||||
|
||||
Future Plans
|
||||
~~~~~~~~~~~~
|
||||
We are working on adding the following features to PCB Tools:
|
||||
|
||||
- Design Rules Checking
|
||||
- Editing
|
||||
- Panelization
|
||||
|
||||
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Gerber Tools documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sun Sep 28 18:16:46 2014.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.insert(0, os.path.abspath('../../'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.autosummary',
|
||||
'numpydoc',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'PCB Tools'
|
||||
copyright = u'2014 Paulo Henrique Silva <ph.silva@gmail.com>, Hamilton Kibbe <ham@hamiltonkib.be>'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = False
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'PCBToolsdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'PCBTools.tex', u'PCB Tools Documentation',
|
||||
u'Hamilton Kibbe', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'pcbtools', u'PCB Tools Documentation',
|
||||
[u'Hamilton Kibbe'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'PCBTools', u'PCB Tools Documentation',
|
||||
u'Hamilton Kibbe', 'PCBTools', 'Tools for working with PCB CAM files.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
|
@ -1,42 +0,0 @@
|
|||
:mod:`excellon` --- Excellon file handling
|
||||
==============================================
|
||||
|
||||
.. module:: excellon
|
||||
:synopsis: Functions and classes for handling Excellon files
|
||||
.. sectionauthor:: Hamilton Kibbe <ham@hamiltonkib.be>
|
||||
|
||||
|
||||
The Excellon format is the most common format for exporting PCB drill
|
||||
information. The Excellon format is used to program CNC drilling macines for
|
||||
drilling holes in PCBs. As such, excellon files are sometimes refererred to as
|
||||
NC-drill files. The Excellon format reference is available
|
||||
`here <http://www.excellon.com/manuals/program.htm>`_. The :mod:`excellon`
|
||||
submodule implements classes to read and write excellon files without having
|
||||
to know the precise details of the format.
|
||||
|
||||
The :mod:`excellon` submodule's :func:`read` function serves as a
|
||||
simple interface for parsing excellon files. The :class:`ExcellonFile` class
|
||||
stores all the information contained in an Excellon file allowing the file to
|
||||
be analyzed, modified, and updated. The :class:`ExcellonParser` class is used
|
||||
in the background for parsing RS-274X files.
|
||||
|
||||
.. _excellon-contents:
|
||||
|
||||
Functions
|
||||
---------
|
||||
The :mod:`excellon` module defines the following functions:
|
||||
|
||||
.. autofunction:: gerber.excellon.read
|
||||
|
||||
|
||||
Classes
|
||||
-------
|
||||
The :mod:`excellon` module defines the following classes:
|
||||
|
||||
.. autoclass:: gerber.excellon.ExcellonFile
|
||||
:members:
|
||||
|
||||
|
||||
.. autoclass:: gerber.excellon.ExcellonParser
|
||||
:members:
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
PCB Tools Reference
|
||||
======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Gerber (RS-274X) Files <rs274x>
|
||||
Excellon Files <excellon>
|
||||
Operations <operations>
|
||||
Rendering <render>
|
|
@ -1,24 +0,0 @@
|
|||
:mod:`operations` --- Cam File operations
|
||||
=========================================
|
||||
|
||||
.. module:: operations
|
||||
:synopsis: Functions for modifying CAM files
|
||||
.. sectionauthor:: Hamilton Kibbe <ham@hamiltonkib.be>
|
||||
|
||||
|
||||
The :mod:`operations` module provides functions which modify
|
||||
:class:`gerber.cam.CamFile` objects. All of the functions in this module
|
||||
return a modified copy of the supplied file.
|
||||
|
||||
.. _operations-contents:
|
||||
|
||||
Functions
|
||||
---------
|
||||
The :mod:`operations` module defines the following functions:
|
||||
|
||||
.. autofunction:: gerber.operations.to_inch
|
||||
.. autofunction:: gerber.operations.to_metric
|
||||
.. autofunction:: gerber.operations.offset
|
||||
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
:mod:`render` --- Gerber file Rendering
|
||||
==============================================
|
||||
|
||||
.. module:: render
|
||||
:synopsis: Functions and classes for handling Excellon files
|
||||
.. sectionauthor:: Hamilton Kibbe <ham@hamiltonkib.be>
|
||||
|
||||
Render Module
|
||||
-------------
|
||||
.. automodule:: gerber.render.render
|
||||
:members:
|
|
@ -1,37 +0,0 @@
|
|||
:mod:`rs274x` --- RS-274X file handling
|
||||
==============================================
|
||||
|
||||
.. module:: rs274x
|
||||
:synopsis: Functions and classes for handling RS-274X files
|
||||
.. sectionauthor:: Hamilton Kibbe <ham@hamiltonkib.be>
|
||||
|
||||
|
||||
The RS-274X (Gerber) format is the most common format for exporting PCB
|
||||
artwork. The Specification is published by Ucamco and is available
|
||||
`here <http://www.ucamco.com/files/downloads/file/81/the_gerber_file_format_specification.pdf>`_.
|
||||
The :mod:`rs274x` submodule implements classes to read and write
|
||||
RS-274X files without having to know the precise details of the format.
|
||||
|
||||
The :mod:`rs274x` submodule's :func:`read` function serves as a
|
||||
simple interface for parsing gerber files. The :class:`GerberFile` class
|
||||
stores all the information contained in a gerber file allowing the file to be
|
||||
analyzed, modified, and updated. The :class:`GerberParser` class is used in
|
||||
the background for parsing RS-274X files.
|
||||
|
||||
.. _gerber-contents:
|
||||
|
||||
Functions
|
||||
---------
|
||||
The :mod:`rs274x` module defines the following functions:
|
||||
|
||||
.. autofunction:: gerber.rs274x.read
|
||||
|
||||
Classes
|
||||
-------
|
||||
The :mod:`rs274x` module defines the following classes:
|
||||
|
||||
.. autoclass:: gerber.rs274x.GerberFile
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerber.rs274x.GerberParser
|
||||
:members:
|
|
@ -1,14 +0,0 @@
|
|||
Feature Suppport
|
||||
================
|
||||
|
||||
Currently supported features are as follows:
|
||||
|
||||
============ ======== =========== ================ ====== ======= =======
|
||||
File Format Parsing Rendering Unit Conversion Scale Offset Rotate
|
||||
============ ======== =========== ================ ====== ======= =======
|
||||
RS274-X Yes Yes Yes No Yes No
|
||||
Excellon Yes Yes Yes No Yes No
|
||||
ODB++ No No No No No No
|
||||
============ ======== =========== ================ ====== ======= =======
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
.. PCB-tools documentation master file, created by
|
||||
sphinx-quickstart on Sun Sep 28 18:16:46 2014.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
PCB-Tools
|
||||
========================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
about
|
||||
features
|
||||
documentation/index
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
Aperture Macros
|
||||
===============
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.parse.ApertureMacro
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.parse.GenericMacros
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.expression.Expression
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.expression.UnitExpression
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.expression.ConstantExpression
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.expression.VariableExpression
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.expression.OperatorExpression
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.primitive.Primitive
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.primitive.Circle
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.primitive.VectorLine
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.primitive.CenterLine
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.primitive.Polygon
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.primitive.Thermal
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.primitive.Outline
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.aperture_macros.primitive.Comment
|
||||
:members:
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
Apertures in Gerbonara
|
||||
======================
|
||||
|
||||
Gerbonara maps all standard Gerber apertures to subclasses of the Aperture_ class. These subclasses: CircleAperture_,
|
||||
RectangleAperture_, ObroundAperture_ and PolygonAperture_. Aperture macro instantiations get mapped to
|
||||
ApertureMacroInstance_ (also an Aperture_ subclass).
|
||||
|
||||
All Aperture_ subclasses have these common attributes:
|
||||
|
||||
|
||||
`hole_dia`
|
||||
float with diameter of hole. 0 for no hole.
|
||||
|
||||
`hole_rect_h`
|
||||
float or None. If not None, specifies a rectangular hole of size `hole_dia * hole_rect_h` instead of a round hole.
|
||||
|
||||
`unit`
|
||||
LengthUnit_ for all of this aperture's fields
|
||||
|
||||
`attrs`
|
||||
GerberX2 attributes of this aperture. Note that this will only contain aperture attributes, not file attributes.
|
||||
File attributes are stored in the `attrs` of GerberFile_.
|
||||
|
||||
`original_number`
|
||||
int of aperture index this aperture had when it was read from the Gerber file. This field is purely informational
|
||||
since apertures are de-duplicated and re-numbered when writing a Gerber file. For `D10`, this field would be `10`.
|
||||
If you programmatically create a new aperture, you do not have to set this.
|
||||
|
||||
`rotation`
|
||||
Aperture rotation in radians counter-clockwise. This field is not part of the Gerber standard. Standard rectangle
|
||||
and obround apertures do not support rotation. Gerbonara converts rotated apertures into aperture macros during
|
||||
Gerber export as necessary.
|
||||
|
||||
CircleAperture
|
||||
--------------
|
||||
|
||||
This is the only one valid for use in Line_ or Arc_.
|
||||
|
||||
Attributes:
|
||||
|
||||
Common attributes:
|
||||
`hole_dia`, `hole_rect_h`, `unit`, `attrs`, and `original_number`. `rotation` is present but has no effect in
|
||||
CircleAperture_.
|
||||
|
||||
`diameter`
|
||||
float with diameter of aperture in the unit from the aperture's `unit` field.
|
||||
|
||||
RectangleAperture
|
||||
-----------------
|
||||
|
||||
Common attributes:
|
||||
`hole_dia`, `hole_rect_h`, `unit`, `attrs`, `original_number`, and `rotation`
|
||||
|
||||
`w`, `h`
|
||||
floats with width or height of rectangle in units from the aperture's `unit` field.
|
||||
|
||||
ObroundAperture
|
||||
---------------
|
||||
|
||||
Aperture whose shape is the convex hull of two circles of equal radii.
|
||||
|
||||
Common attributes:
|
||||
`hole_dia`, `hole_rect_h`, `unit`, `attrs`, `original_number`, and `rotation`
|
||||
|
||||
`w`, `h`
|
||||
floats with width and height of bounding box of obround. The smaller one of these will be the diameter of the
|
||||
obround's ends. If `w` is larger, the result will be a landscape obround. If `h` is larger, it will be a portrait
|
||||
obround.
|
||||
|
||||
PolygonAperture
|
||||
---------------
|
||||
|
||||
Aperture whose shape is a regular n-sided polygon (e.g. pentagon, hexagon etc.).
|
||||
|
||||
|
||||
Common attributes:
|
||||
`hole_dia`, `unit`, `attrs`, `original_number`, and `rotation`. `hole_rect_h` is not supported in PolygonAperture_
|
||||
since the Gerber spec does not list it.
|
||||
|
||||
`diameter`
|
||||
float with diameter of circumscribing circle, i.e. the circle that all the polygon's corners lie on.
|
||||
|
||||
`n_vertices`
|
||||
int with number of corners of this polygon. Three for a triangle, four for a square, five for a pentagon etc.
|
||||
|
||||
ApertureMacroInstance
|
||||
---------------------
|
||||
|
||||
One instance of an aperture macro. An aperture macro defined with an `AM` statement can be instantiated by multiple `AD`
|
||||
aperture definition statements using different parameters. An ApertureMacroInstance_ is one such binding of a macro to a
|
||||
particular set of parameters. Note that you still need an ApertureMacroInstance_ even if your ApertureMacro_ has no
|
||||
parameters since an ApertureMacro_ is not an Aperture_ by itself.
|
||||
|
||||
Attributes:
|
||||
|
||||
Common attributes:
|
||||
`unit`, `attrs`, `original_number`, and `rotation`. ApertureMacroInstance_ does not support `hole_dia` or
|
||||
`hole_rect_h`. `rotation` is handled by re-writing the ApertureMacro_ during export.
|
||||
|
||||
`macro`
|
||||
The ApertureMacro_ that is bound here
|
||||
|
||||
`parameters`
|
||||
list of ints or floats with the parameters for this macro. The first element is `$1`, the second is `$2` etc.
|
||||
|
||||
ExcellonTool
|
||||
------------
|
||||
|
||||
Special Aperture_ subclass for use in ExcellonFile_. Similar to CircleAperture_, but does not have `hole_dia` or
|
||||
`hole_rect_h`, and has additional `plated` and `depth_offset` attributes.
|
||||
|
||||
|
||||
Common attributes:
|
||||
`unit`, `original_number`
|
||||
|
||||
`plated`
|
||||
bool or None. True if this hole/slot is copper-plated, False if not, and None if it is undefined or unknown.
|
||||
|
||||
`depth_offset`
|
||||
float with Excellon depth offset for this hole or slot. If the fab supports this, this can be used to create
|
||||
features that do not go all the way through the board.
|
||||
|
||||
Aperture generalization
|
||||
-----------------------
|
||||
|
||||
Gerbonara supports rotating both individual graphic objects and whole files. Alas, this was not a use case that was
|
||||
intended when the Gerber format was developed. We can rotate lines, arcs, and regions alright by simply rotatint all of
|
||||
their points. Flashes are where things get tricky: Individual flashes cannot be rotated at all in any widely supported
|
||||
way. There are some newer additions to the standard, but I would be surprised if any of the cheap board houses
|
||||
understand those. The only way to rotate a flash is to rotate the aperture, not the flash. For cirlces, this is a no-op.
|
||||
For polygons, we simply change the angle parameter. However, for rectangles and obrounds this gets tricky: Neither one
|
||||
supports a rotation parameter. The only way to rotate these is to convert them to an aperture macro, then rotate that.
|
||||
|
||||
This behavior of using aperture macros for general rotated rectangles is common behavior among CAD tools. Gerbonara adds
|
||||
a non-standard `rotation` attribute to all apertures except CircleAperture_ and transparently converts rotated instances
|
||||
to the appropriate ApertureMacroInstance_ objects while it writes out the file. Be aware that this may mean that an
|
||||
object that in memory has a RectangleAperture_ might end up with an aperture macro instance in the output Gerber file.
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
Gerbonara API concepts
|
||||
======================
|
||||
|
||||
High-level overview
|
||||
-------------------
|
||||
|
||||
Gerbonara's API is split into three larger sub-areas:
|
||||
|
||||
**File API**
|
||||
This is where the main user interface classes live: :py:class:`.LayerStack` (for opening a directory/zip full of
|
||||
files, and automatically matching file roles based on filenames), :py:class:`.GerberFile` (for opening an individual
|
||||
RS-274X file), :py:class:`.ExcellonFile` (for Excellon drill files) and :py:class:`.Netlist` (for IPC-356 netlist
|
||||
files).
|
||||
|
||||
**Graphic Object API**
|
||||
This is where the nuts and bolts inside a :py:class:`.GerberFile` or :py:class:`.ExcellonFile` such as
|
||||
:py:class:`~.graphic_objects.Line`, :py:class:`~.graphic_objects.Arc`, :py:class:`.Region` and :py:class:`.Flash`
|
||||
live. Everything in here has explicit unit support. A part of the Graphic object API is the :doc:`Aperture
|
||||
API<apertures>`.
|
||||
|
||||
**Graphic Primitive API**
|
||||
This is a rendering abstraction layer. Graphic objects can be converted into graphic primitives for rendering.
|
||||
Graphic primitives are unit-less. Units are converted during :py:class:`.GraphicObject` to
|
||||
:py:class:`.GraphicPrimitive` rendering.
|
||||
|
||||
The hierarchy works like: A :py:class:`.LayerStack` contains either a :py:class:`.GerberFile`, an
|
||||
:py:class:`.ExcellonFile` or a :py:class:`.Netlist` for each layer. Each of these file objects contains a number of
|
||||
:py:class:`.GraphicObject` instances such as :py:class:`~.graphic_objects.Line` or :py:class:`.Flash`. These objects can
|
||||
easily be changed or deleted, and new ones can be created programmatically. For rendering, each of these objects as well
|
||||
as file objects can be rendered into :py:class:`.GraphicPrimitive` instances using
|
||||
:py:meth:`.GraphicObject.to_primitives`.
|
||||
|
||||
Apertures
|
||||
---------
|
||||
|
||||
Gerber apertures are represented by subclasses of :py:class:`.Aperture` such as :py:class:`.CircleAperture`. An instance
|
||||
of an aperture class is stored inside the :py:attr:`~.graphic_objects.Line.aperture` field of a
|
||||
:py:class:`.GraphicObject`. :py:class:`.GraphicObject` subclasses that have an aperture are
|
||||
:py:class:`~.graphic_objects.Line`, :py:class:`~.graphic_objects.Arc` and :py:class:`.Flash`. You can create and
|
||||
duplicate :py:class:`.Aperture` objects as needed. They are automatically de-duplicated when a Gerber file is written.
|
||||
|
||||
Gerbonara has full aperture macro support. Each aperture macro is represented by an :py:class:`.parse.ApertureMacro`
|
||||
instance. Like apertures, :py:class:`.parse.ApertureMacro` instances are de-duplicated when writing a file. An aperture
|
||||
macro-based aperture definition is represented by the :py:class:`.ApertureMacroInstance` subclass of
|
||||
:py:class:`.Aperture`. An aperture macro instance basically binds an aperture macro to a given set of macro parameters.
|
||||
Note that even if a macro does not accept any parameters you still cannot directly stick it into the aperture field of a
|
||||
graphic object, and instead need to wrap it inside an :py:class:`.ApertureMacroInstance` first.
|
||||
|
||||
Excellon vs. Gerber
|
||||
-------------------
|
||||
|
||||
Excellon files use the same graphic object classes as Gerber files. Inside an Excellon file, only
|
||||
:py:class:`~.graphic_objects.Line`, :py:class:`~.graphic_objects.Arc` and :py:class:`.Flash` are allowed. Lines and arcs map to milled
|
||||
Excellon slots. Excellon drills are mapped to :py:class:`.Flash` instances.
|
||||
|
||||
Excellon drills are internally handled using a special :py:class:`.ExcellonTool` aperture class. When you put a
|
||||
:py:class:`.GraphicObject` from an Excellon file into a Gerber file, these become circular apertures. You can also take
|
||||
objects from an Excellon file and put them into a Gerber file if they have a simple :py:class:`.CircleAperture`. Copying
|
||||
objects with other apertures into an Excellon file will raise an error when saving.
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.absolute()))
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'gerbonara'
|
||||
copyright = '2022, Jan Götte'
|
||||
author = 'jaseg'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.9.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
]
|
||||
|
||||
autodoc_member_order = 'groupwise'
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = []
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
|
@ -0,0 +1,25 @@
|
|||
Layers and Files
|
||||
================
|
||||
|
||||
Gerbonara currently supports three file types: RS-274-X Gerber as `specified by Ucamco
|
||||
<https://www.ucamco.com/en/gerber>`:py:class:`._` through :py:class:`.GerberFile`, Excellon/XNC through
|
||||
:py:class:`.ExcellonFile`, and IPC-356 netlists through :py:class:`.Netlist`.
|
||||
|
||||
Usually, a PCB is sent to a manufacturer as a bundle of several of these files. Such a bundle of files (each of which is
|
||||
either a :py:class:`.GerberFile` or an :py:class:`.ExcellonFile`) is represented by :py:class:`.LayerStack`.
|
||||
:py:class:`.LayerStack` contains logic to automatcally
|
||||
recognize a wide variety of CAD tools from file name and syntactic hints, and can automatically match all files in a
|
||||
folder to their appropriate layers.
|
||||
|
||||
.. autoclass:: gerbonara.layers.LayerStack
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.rs274x.GerberFile
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.excellon.ExcellonFile
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.ipc356.Netlist
|
||||
:members:
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
Graphic Primitives
|
||||
==================
|
||||
|
||||
.. autoclass:: gerbonara.graphic_primitives.GraphicPrimitive
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_primitives.Circle
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_primitives.Obround
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_primitives.ArcPoly
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_primitives.Line
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_primitives.Arc
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_primitives.Rectangle
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_primitives.RegularPolygon
|
||||
:members:
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
Welcome to gerbonara's documentation!
|
||||
=====================================
|
||||
|
||||
Gerbonara is a library to read, modify and write PCB manufacturing files such as Gerber, Excellon and IPC-356 through a
|
||||
pythonic API. Gerbonara can open a folder of manufacturing files, and parse file names and metadata to figure out which
|
||||
file contains what. Gerbonara is tested using an extensive library of real-world example files from CAD tools including
|
||||
KiCAD, Altium, Eagle, Allegro, gEDA, Fritzing, Siemens/Mentor Graphics PADS, and Target3001!.
|
||||
|
||||
Gerbonara's API is built on two principles:
|
||||
|
||||
**Meaningful, object-oriented API**
|
||||
Gerbonara abstracts away the details of the underlying file format such as tool indices, coordinate notation and
|
||||
graphical state, and presents meaningful "graphical objects" such as a :py:class:`~primitives.Line`,
|
||||
:py:class:`~primitives.Arc`, or :py:class:`.Region` through its API. These objects can be easily created,
|
||||
manipulated or deleted from code without breaking anything else. You can even copy graphical objects between files,
|
||||
and Gerbonara will automatically convert coordinate format, units etc. for you. :py:class:`.GerberFile` and
|
||||
:py:class:`.ExcellonFile` use the same types of :doc:`graphic objects <object-api>`, so objects can be directly
|
||||
copied between file types without conversion.
|
||||
|
||||
**Unit-safety**
|
||||
Gerbonara embeds physical :py:class:`.LengthUnit` information in all objects. The high-level API such as
|
||||
:py:meth:`.LayerStack.merge` or :py:meth:`.GerberFile.offset` accepts arguments with an explicitly given unit and
|
||||
automatically converts them as needed. Objects can be copied between :py:class:`.GerberFile` instances and unit
|
||||
conversion will be handled transparently in the background.
|
||||
|
||||
Gerbonara was started as an extensive refactoring of the pcb-tools_ and pcb-tools-extension_ packages. Both of these
|
||||
have statement-based APIs, that is, they parse input files into one python object for every line in the file. This means
|
||||
that when saving files they can recreate the input file almost byte by byte, but manipulating a file by changing
|
||||
statements without breaking things is *hard*.
|
||||
|
||||
Gerbonara powers gerbolyze_, a tool for converting SVG_ vector graphics files into Gerber, and embedding SVG_ into
|
||||
existing Gerber files exported from a normal PCB tool for artistic purposes.
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
* File I/O
|
||||
* Gerber, Excellon (drill file), IPC-356 (netlist) read and write
|
||||
* supports file-level operations: offset, rotate, merge for all file types
|
||||
* Modification API (:py:class:`GraphicObject`)
|
||||
* Rendering API (:py:class:`GraphicPrimitive`)
|
||||
* SVG export
|
||||
* Full aperture macro support, including transformations (offset, rotation)
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
api-concepts
|
||||
file-api
|
||||
object-api
|
||||
apertures
|
||||
aperture-macros
|
||||
graphic-primitive-api
|
||||
utilities
|
||||
|
||||
Quick Start
|
||||
===========
|
||||
|
||||
|
||||
|
||||
Development
|
||||
===========
|
||||
|
||||
Gerbonara is developed on Gitlab under the gerbolyze org:
|
||||
|
||||
https://gitlab.com/gerbolyze/gerbonara/
|
||||
|
||||
A mirror of the repository can be found at:
|
||||
|
||||
https://git.jaseg.de/gerbonara
|
||||
|
||||
Our issue tracker is also on Gitlab:
|
||||
|
||||
https://gitlab.com/gerbolyze/gerbonara/-/issues
|
||||
|
||||
With Gebronara, we aim to support as many different format variants as possible. If you have a file that Gerbonara can't
|
||||
open, please file an issue on our issue tracker. Even if Gerbonara can open all your files, for regression testing we
|
||||
are very interested in example files generated by any CAD or CAM tool that is not already on the list of supported
|
||||
tools.
|
||||
|
||||
Supported CAD Tools
|
||||
===================
|
||||
|
||||
Compatibility with the output of these CAD tools is tested as part of our test suite using example files generated by
|
||||
these tools. Note that not all of these tools come with default Gerber file naming rules, so YMMV if your Gerbers use
|
||||
some non-standard naming convention.
|
||||
|
||||
* Allegro
|
||||
* Altium
|
||||
* Diptrace
|
||||
* Eagle
|
||||
* EasyEDA
|
||||
* Fritzing
|
||||
* gEDA
|
||||
* KiCAD
|
||||
* pcb-rnd
|
||||
* Siemens / Mentor Graphics Xpedition
|
||||
* Siemens / Mentor Graphics PADS
|
||||
* Target 3001!
|
||||
* Upverter
|
||||
* Soon: Zuken CADSTAR and CR-8000
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
.. _pcb-tools: https://github.com/opiopan/pcb-tools-extension
|
||||
.. _pcb-tools-extension: https://github.com/curtacircuitos/pcb-tools/issues
|
||||
.. _gerbolyze: https://github.com/jaseg/gerbolyze
|
||||
.. _SVG: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
Graphic Objects
|
||||
===============
|
||||
|
||||
Graphic objects are the lego blocks a gerbonara :py:class:`gerbonara.rs274x.GerberFile` or
|
||||
:py:class:`gerbonara.excellon.ExcellonFile` is built from. They are stored in the file's
|
||||
:py:attr:`gerbonara.rs274x.GerberFile.objects` list. You can directly manipulate that list from code.
|
||||
|
||||
There are four graphic object types: :py:class:`gerbonara.graphic_objects.Flash`,
|
||||
:py:class:`gerbonara.graphic_objects.Line`, :py:class:`gerbonara.graphic_objects.Arc`, and
|
||||
:py:class:`gerbonara.graphic_objects.Region` . All of them are derived from
|
||||
:py:class:`gerbonara.graphic_objects.GraphicObject`.
|
||||
|
||||
.. autoclass:: gerbonara.graphic_objects.GraphicObject
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_objects.Flash
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_objects.Line
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_objects.Arc
|
||||
:members:
|
||||
|
||||
.. autoclass:: gerbonara.graphic_objects.Region
|
||||
:members:
|
||||
|
||||
.. _pcb-tools: https://github.com/opiopan/pcb-tools-extension
|
||||
.. _gerbolyze: https://github.com/jaseg/gerbolyze
|
||||
.. _svg-flatten: https://github.com/jaseg/gerbolyze/tree/main/svg-flatten
|
|
@ -0,0 +1,3 @@
|
|||
Utilities
|
||||
=========
|
||||
|
|
@ -95,6 +95,7 @@ class Aperture:
|
|||
|
||||
@dataclass(unsafe_hash=True)
|
||||
class ExcellonTool(Aperture):
|
||||
gerber_shape_code = 'C'
|
||||
human_readable_shape = 'drill'
|
||||
diameter : Length(float)
|
||||
plated : bool = None
|
||||
|
|
|
@ -197,6 +197,9 @@ class ExcellonFile(CamFile):
|
|||
else:
|
||||
self.objects.append(obj_or_comment)
|
||||
|
||||
def to_excellon(self):
|
||||
return self
|
||||
|
||||
def to_gerber(self):
|
||||
apertures = {}
|
||||
out = GerberFile()
|
||||
|
@ -292,7 +295,7 @@ class ExcellonFile(CamFile):
|
|||
|
||||
yield 'M30'
|
||||
|
||||
def to_excellon(self, settings=None, drop_comments=True):
|
||||
def generate_excellon(self, settings=None, drop_comments=True):
|
||||
''' Export to Excellon format. This function always generates XNC, which is a well-defined subset of Excellon.
|
||||
'''
|
||||
if settings is None:
|
||||
|
@ -306,10 +309,11 @@ class ExcellonFile(CamFile):
|
|||
|
||||
def save(self, filename, settings=None, drop_comments=True):
|
||||
with open(filename, 'w') as f:
|
||||
f.write(self.to_excellon(settings, drop_comments=drop_comments))
|
||||
f.write(self.generate_excellon(settings, drop_comments=drop_comments))
|
||||
|
||||
def offset(self, x=0, y=0, unit=MM):
|
||||
self.objects = [ obj.with_offset(x, y, unit) for obj in self.objects ]
|
||||
for obj in self.objects:
|
||||
obj.offset(x, y, unit)
|
||||
|
||||
def rotate(self, angle, cx=0, cy=0, unit=MM):
|
||||
if math.isclose(angle % (2*math.pi), 0):
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
|
||||
import math
|
||||
import copy
|
||||
from dataclasses import dataclass, KW_ONLY, astuple, replace, field, fields
|
||||
|
||||
from .utils import MM, InterpMode
|
||||
from .utils import MM, InterpMode, to_unit
|
||||
from . import graphic_primitives as gp
|
||||
|
||||
|
||||
|
@ -18,27 +19,94 @@ class Length:
|
|||
def __init__(self, obj_type):
|
||||
self.type = obj_type
|
||||
|
||||
def __repr__(self):
|
||||
# This makes the automatically generated method signatures in the Sphinx docs look nice
|
||||
return 'float'
|
||||
|
||||
@dataclass
|
||||
class GerberObject:
|
||||
class GraphicObject:
|
||||
""" Base class for the graphic objects that make up a :py:class:`gerbonara.rs274x.GerberFile` or
|
||||
:py:class:`gerbonara.excellon.ExcellonFile`. """
|
||||
_ : KW_ONLY
|
||||
|
||||
#: bool representing the *color* of this feature: whether this is a *dark* or *clear* feature. Clear and dark are
|
||||
#: meant in the sense that they are used in the Gerber spec and refer to whether the transparency film that this
|
||||
#: file describes ends up black or clear at this spot. In a standard green PCB, a *polarity_dark=True* line will
|
||||
#: show up as copper on the copper layer, white ink on the silkscreen layer, or an opening on the soldermask layer.
|
||||
#: Clear features erase dark features, they are not transparent in the colloquial meaning. This property is ignored
|
||||
#: for features of an :py:class:`gerbonara.excellon.ExcellonFile`.
|
||||
polarity_dark : bool = True
|
||||
|
||||
#: :py:class:`gerbonara.utils.LengthUnit` used for all coordinate fields of this feature (such as `x` or `y`).
|
||||
unit : str = None
|
||||
|
||||
|
||||
#: `dict` containing GerberX2 attributes attached to this feature. Note that this does not include file attributes,
|
||||
#: which are stored in the :py:class:`gerbonara.rs274x.GerberFile` object instead.
|
||||
attrs : dict = field(default_factory=dict)
|
||||
|
||||
def converted(self, unit):
|
||||
return replace(self,
|
||||
**{ f.name: self.unit.convert_to(unit, getattr(self, f.name))
|
||||
for f in fields(self) if type(f.type) is Length })
|
||||
""" Convert this gerber object to another :py:class:`gerbonara.utils.LengthUnit`.
|
||||
|
||||
:param unit: Either a :py:class:`gerbonara.utils.LengthUnit` instance or one of the strings ``'mm'`` or ``'inch'``.
|
||||
|
||||
:returns: A copy of this object using the new unit.
|
||||
"""
|
||||
copy = copy.copy(self)
|
||||
copy.convert_to(unit)
|
||||
|
||||
def convert_to(self, unit):
|
||||
""" Convert this gerber object to another :py:class:`gerbonara.utils.LengthUnit` in-place.
|
||||
|
||||
:param unit: Either a :py:class:`gerbonara.utils.LengthUnit` instance or one of the strings ``'mm'`` or ``'inch'``.
|
||||
"""
|
||||
|
||||
for f in fields(self):
|
||||
if type(f.type) is Length:
|
||||
setattr(self, f.name, self.unit.convert_to(unit, getattr(self, f.name)))
|
||||
|
||||
self.unit = to_unit(unit)
|
||||
|
||||
def offset(self, dx, dy, unit=MM):
|
||||
""" Add an offset to the location of this feature. The location can be given in either unit, and is
|
||||
automatically converted into this object's local unit.
|
||||
|
||||
:param float dx: X offset, positive values move the object right.
|
||||
:param float dy: Y offset, positive values move the object up. This is the opposite of the normal screen
|
||||
coordinate system used in SVG and other computer graphics APIs.
|
||||
"""
|
||||
|
||||
def with_offset(self, dx, dy, unit=MM):
|
||||
dx, dy = self.unit(dx, unit), self.unit(dy, unit)
|
||||
return self._with_offset(dx, dy)
|
||||
self._offset(dx, dy)
|
||||
|
||||
def rotate(self, rotation, cx=0, cy=0, unit=MM):
|
||||
""" Rotate this object. The center of rotation can be given in either unit, and is automatically converted into
|
||||
this object's local unit.
|
||||
|
||||
.. note:: The center's Y coordinate as well as the angle's polarity are flipped compared to computer graphics
|
||||
convention since Gerber uses a bottom-to-top Y axis.
|
||||
|
||||
:param float rotation: rotation in radians clockwise.
|
||||
:param float cx: X coordinate of center of rotation in *unit* units.
|
||||
:param float cy: Y coordinate of center of rotation. (0,0) is at the bottom left of the image.
|
||||
:param unit: :py:class:`gerbonara.utils.LengthUnit` or str with unit for *cx* and *cy*
|
||||
"""
|
||||
|
||||
cx, cy = self.unit(cx, unit), self.unit(cy, unit)
|
||||
self._rotate(rotation, cx, cy)
|
||||
|
||||
def bounding_box(self, unit=None):
|
||||
""" Return axis-aligned bounding box of this object in given unit. If no unit is given, return the bounding box
|
||||
in the object's local unit (``self.unit``).
|
||||
|
||||
.. note:: This method returns bounding boxes in a different format than legacy pcb-tools_, which used
|
||||
``(min_x, max_x), (min_y, max_y)``
|
||||
|
||||
:param unit: :py:class:`gerbonara.utils.LengthUnit` or str with unit for return value.
|
||||
|
||||
:returns: tuple of tuples of floats: ``(min_x, min_y), (max_x, max_y)``
|
||||
"""
|
||||
|
||||
bboxes = [ p.bounding_box() for p in self.to_primitives(unit) ]
|
||||
min_x = min(min_x for (min_x, _min_y), _ in bboxes)
|
||||
min_y = min(min_y for (_min_x, min_y), _ in bboxes)
|
||||
|
@ -47,16 +115,62 @@ class GerberObject:
|
|||
return ((min_x, min_y), (max_x, max_y))
|
||||
|
||||
def to_primitives(self, unit=None):
|
||||
raise NotImplementedError()
|
||||
""" Render this object into low-level graphical primitives (subclasses of :py:class:`GraphicPrimitive`). This
|
||||
computes out all coordinates in case aperture macros are involved, and resolves units. The output primitives are
|
||||
converted into the given unit, and will be stripped of unit information. If no unit is given, use this object's
|
||||
native unit (``self.unit``).
|
||||
|
||||
:param unit: :py:class:`gerbonara.utils.LengthUnit` or str with unit for return value.
|
||||
|
||||
:rtype: Iterator[:py:class:`GraphicPrimitive`]
|
||||
"""
|
||||
return self._to_primitives(unit)
|
||||
|
||||
def _to_statements(self, gs):
|
||||
""" Serialize this object into Gerber statements.
|
||||
|
||||
:param gs: :py:class:`rs274x.GraphicsState` object containing current Gerber state (polarity, selected aperture,
|
||||
interpolation mode etc.).
|
||||
|
||||
:returns: Iterator yielding one string per line of output Gerber
|
||||
:rtype: Iterator[str]
|
||||
"""
|
||||
self._to_statements(gs)
|
||||
|
||||
def _to_xnc(self, ctx):
|
||||
""" Serialize this object into XNC Excellon statements.
|
||||
|
||||
:param ctx: :py:class:`excellon.ExcellonContext` object containing current Excellon state (selected tool,
|
||||
interpolation mode etc.).
|
||||
|
||||
:returns: Iterator yielding one string per line of output XNC code
|
||||
:rtype: Iterator[str]
|
||||
"""
|
||||
self._to_xnc(ctx)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Flash(GerberObject):
|
||||
class Flash(GraphicObject):
|
||||
""" A flash is what happens when you "stamp" a Gerber aperture at some location. The :py:attr:`polarity_dark`
|
||||
attribute that Flash inherits from :py:class:`GraphicObject` is ``True`` for normal flashes. If you set a Flash's
|
||||
``polarity_dark`` to ``False``, you invert the polarity of all of its features.
|
||||
|
||||
Flashes are also used to represent drilled holes in an :py:class:`gerbonara.excellon.ExcellonFile`. In this case,
|
||||
:py:attr:`aperture` should be an instance of :py:class:`ExcellonTool`.
|
||||
"""
|
||||
|
||||
#: float with X coordinate of the center of this flash.
|
||||
x : Length(float)
|
||||
|
||||
#: float with Y coordinate of the center of this flash.
|
||||
y : Length(float)
|
||||
|
||||
#: Flashed Aperture. must be a subclass of :py:class:`Aperture`.
|
||||
aperture : object
|
||||
|
||||
@property
|
||||
def tool(self):
|
||||
""" Alias for :py:attr:`aperture` for use inside an :py:class:`gerbonara.excellon.ExcellonFile`. """
|
||||
return self.aperture
|
||||
|
||||
@tool.setter
|
||||
|
@ -65,19 +179,23 @@ class Flash(GerberObject):
|
|||
|
||||
@property
|
||||
def plated(self):
|
||||
return self.tool.plated
|
||||
""" (Excellon only) Returns if this is a plated hole. ``True`` (plated), ``False`` (non-plated) or ``None``
|
||||
(plating undefined)
|
||||
"""
|
||||
return getattr(self.tool, 'plated', None)
|
||||
|
||||
def _with_offset(self, dx, dy):
|
||||
return replace(self, x=self.x+dx, y=self.y+dy)
|
||||
def __offset(self, dx, dy):
|
||||
self.x += dx
|
||||
self.y += dy
|
||||
|
||||
def _rotate(self, rotation, cx=0, cy=0):
|
||||
self.x, self.y = gp.rotate_point(self.x, self.y, rotation, cx, cy)
|
||||
|
||||
def to_primitives(self, unit=None):
|
||||
def _to_primitives(self, unit=None):
|
||||
conv = self.converted(unit)
|
||||
yield from self.aperture.flash(conv.x, conv.y, unit, self.polarity_dark)
|
||||
|
||||
def to_statements(self, gs):
|
||||
def _to_statements(self, gs):
|
||||
yield from gs.set_polarity(self.polarity_dark)
|
||||
yield from gs.set_aperture(self.aperture)
|
||||
|
||||
|
@ -87,7 +205,7 @@ class Flash(GerberObject):
|
|||
|
||||
gs.update_point(self.x, self.y, unit=self.unit)
|
||||
|
||||
def to_xnc(self, ctx):
|
||||
def _to_xnc(self, ctx):
|
||||
yield from ctx.select_tool(self.tool)
|
||||
yield from ctx.drill_mode()
|
||||
|
||||
|
@ -97,11 +215,31 @@ class Flash(GerberObject):
|
|||
|
||||
ctx.set_current_point(self.unit, self.x, self.y)
|
||||
|
||||
# internally used to compute Excellon file path length
|
||||
def curve_length(self, unit=MM):
|
||||
return 0
|
||||
|
||||
|
||||
class Region(GerberObject):
|
||||
class Region(GraphicObject):
|
||||
""" Gerber "region", roughly equivalent to what in computer graphics you would call a polygon. A region is a single
|
||||
filled area defined by a list of coordinates on its contour. A region's polarity is its "fill". A region does not
|
||||
have a "stroke", and thus does not have an `aperture` field. Note that regions are a strict subset of what modern
|
||||
computer graphics considers a polygon or path. Be careful when converting shapes from somewhere else into Gerber
|
||||
regions. For arbitrary shapes (e.g. SVG paths) this is non-trivial, and I recommend you hava look at Gerbolyze_ /
|
||||
svg-flatten_. Here's a list of special features of Gerber regions:
|
||||
|
||||
* A region's outline consists of straigt line segments and circular arcs and must always be closed.
|
||||
* A region is always exactly one connected component.
|
||||
* A region must not overlap itself anywhere.
|
||||
* A region cannot have holes.
|
||||
|
||||
There is one exception from the last two rules: To emulate a region with a hole in it, *cut-ins* are allowed. At a
|
||||
cut-in, the region is allowed to touch (but never overlap!) itself.
|
||||
|
||||
:attr poly: :py:class:`graphic_primitives.ArcPoly` describing the actual outline of this Region. The coordinates of
|
||||
this poly are in the unit of this instance's :py:attr:`unit` field.
|
||||
"""
|
||||
|
||||
def __init__(self, outline=None, arc_centers=None, *, unit, polarity_dark):
|
||||
super().__init__(unit=unit, polarity_dark=polarity_dark)
|
||||
outline = [] if outline is None else outline
|
||||
|
@ -114,11 +252,8 @@ class Region(GerberObject):
|
|||
def __bool__(self):
|
||||
return bool(self.poly)
|
||||
|
||||
def _with_offset(self, dx, dy):
|
||||
return Region([ (x+dx, y+dy) for x, y in self.poly.outline ],
|
||||
self.poly.arc_centers,
|
||||
polarity_dark=self.polarity_dark,
|
||||
unit=self.unit)
|
||||
def _offset(self, dx, dy):
|
||||
self.poly.outline = [ (x+dx, y+dy) for x, y in self.poly.outline ]
|
||||
|
||||
def _rotate(self, angle, cx=0, cy=0):
|
||||
self.poly.outline = [ gp.rotate_point(x, y, angle, cx, cy) for x, y in self.poly.outline ]
|
||||
|
@ -138,7 +273,7 @@ class Region(GerberObject):
|
|||
else:
|
||||
self.poly.arc_centers.append(None)
|
||||
|
||||
def to_primitives(self, unit=None):
|
||||
def _to_primitives(self, unit=None):
|
||||
self.poly.polarity_dark = self.polarity_dark # FIXME: is this the right spot to do this?
|
||||
if unit == self.unit:
|
||||
yield self.poly
|
||||
|
@ -188,17 +323,37 @@ class Region(GerberObject):
|
|||
yield 'G37*'
|
||||
|
||||
@dataclass
|
||||
class Line(GerberObject):
|
||||
# Line with *round* end caps.
|
||||
class Line(GraphicObject):
|
||||
""" A line is what happens when you "drag" a Gerber :py:class:`Aperture` from one point to another. Note that Gerber
|
||||
lines are substantially funkier than normal lines as we know them from modern computer graphics such as SVG. A
|
||||
Gerber line is defined as the area that is covered when you drag its aperture along. This means that for a
|
||||
rectangular aperture, a horizontal line and a vertical line using the same aperture will have different widths.
|
||||
|
||||
.. warning:: Try to only ever use :py:class:`CircleAperture` with :py:class:`Line` and :py:class:`Arc` since other
|
||||
aperture types are not widely supported by renderers / photoplotters even though they are part of the
|
||||
spec.
|
||||
|
||||
.. note:: If you manipulate a :py:class:`Line`, it is okay to assume that it has round end caps and a defined width
|
||||
as exceptions are really rare.
|
||||
"""
|
||||
|
||||
#: X coordinate of start point
|
||||
x1 : Length(float)
|
||||
#: Y coordinate of start point
|
||||
y1 : Length(float)
|
||||
#: X coordinate of end point
|
||||
x2 : Length(float)
|
||||
#: Y coordinate of end point
|
||||
y2 : Length(float)
|
||||
#: Aperture for this line. Should be a subclass of :py:class:`CircleAperture`, whose diameter determines the line
|
||||
#: width.
|
||||
aperture : object
|
||||
|
||||
def _with_offset(self, dx, dy):
|
||||
return replace(self, x1=self.x1+dx, y1=self.y1+dy, x2=self.x2+dx, y2=self.y2+dy)
|
||||
def _offset(self, dx, dy):
|
||||
self.x1 += dx
|
||||
self.y1 += dy
|
||||
self.x2 += dx
|
||||
self.y2 += dy
|
||||
|
||||
def _rotate(self, rotation, cx=0, cy=0):
|
||||
self.x1, self.y1 = gp.rotate_point(self.x1, self.y1, rotation, cx, cy)
|
||||
|
@ -206,18 +361,17 @@ class Line(GerberObject):
|
|||
|
||||
@property
|
||||
def p1(self):
|
||||
""" Convenience alias for ``(self.x1, self.y1)`` returning start point of the line. """
|
||||
return self.x1, self.y1
|
||||
|
||||
@property
|
||||
def p2(self):
|
||||
""" Convenience alias for ``(self.x2, self.y2)`` returning end point of the line. """
|
||||
return self.x2, self.y2
|
||||
|
||||
@property
|
||||
def end_point(self):
|
||||
return self.p2
|
||||
|
||||
@property
|
||||
def tool(self):
|
||||
""" Alias for :py:attr:`aperture` for use inside an :py:class:`gerbonara.excellon.ExcellonFile`. """
|
||||
return self.aperture
|
||||
|
||||
@tool.setter
|
||||
|
@ -226,14 +380,17 @@ class Line(GerberObject):
|
|||
|
||||
@property
|
||||
def plated(self):
|
||||
""" (Excellon only) Returns if this is a plated hole. ``True`` (plated), ``False`` (non-plated) or ``None``
|
||||
(plating undefined)
|
||||
"""
|
||||
return self.tool.plated
|
||||
|
||||
def to_primitives(self, unit=None):
|
||||
def _to_primitives(self, unit=None):
|
||||
conv = self.converted(unit)
|
||||
w = self.aperture.equivalent_width(unit) if self.aperture else 0.1 # for debugging
|
||||
yield gp.Line(*conv.p1, *conv.p2, w, polarity_dark=self.polarity_dark)
|
||||
|
||||
def to_statements(self, gs):
|
||||
def _to_statements(self, gs):
|
||||
yield from gs.set_polarity(self.polarity_dark)
|
||||
yield from gs.set_aperture(self.aperture)
|
||||
yield from gs.set_interpolation_mode(InterpMode.LINEAR)
|
||||
|
@ -245,7 +402,7 @@ class Line(GerberObject):
|
|||
|
||||
gs.update_point(*self.p2, unit=self.unit)
|
||||
|
||||
def to_xnc(self, ctx):
|
||||
def _to_xnc(self, ctx):
|
||||
yield from ctx.select_tool(self.tool)
|
||||
yield from ctx.route_mode(self.unit, *self.p1)
|
||||
|
||||
|
@ -255,26 +412,61 @@ class Line(GerberObject):
|
|||
|
||||
ctx.set_current_point(self.unit, *self.p2)
|
||||
|
||||
# internally used to compute Excellon file path length
|
||||
def curve_length(self, unit=MM):
|
||||
return self.unit.convert_to(unit, math.dist(self.p1, self.p2))
|
||||
|
||||
|
||||
@dataclass
|
||||
class Arc(GerberObject):
|
||||
class Arc(GraphicObject):
|
||||
""" Like :py:class:`Line`, but a circular arc. Has start ``(x1, y1)`` and end ``(x2, y2)`` attributes like a
|
||||
:py:class:`Line`, but additionally has a center ``(cx, cy)`` specified relative to the start point ``(x1, y1)``, as
|
||||
well as a ``clockwise`` attribute indicating the arc's direction.
|
||||
|
||||
.. note:: The same warning on apertures that applies to :py:class:`Line` applies to :py:class:`Arc`, too.
|
||||
|
||||
.. warning:: When creating your own circles, you have to take care yourself that the center is actually the center
|
||||
of a circle that goes through both (x1,y1) and (x2,y2). Elliptical arcs are *not* supported by either
|
||||
us or the Gerber standard.
|
||||
"""
|
||||
#: X coordinate of start point
|
||||
x1 : Length(float)
|
||||
#: Y coordinate of start point
|
||||
y1 : Length(float)
|
||||
#: X coordinate of end point
|
||||
x2 : Length(float)
|
||||
#: Y coordinate of end point
|
||||
y2 : Length(float)
|
||||
# relative to (x1, x2)
|
||||
#: X coordinate of arc center relative to ``x1``
|
||||
cx : Length(float)
|
||||
#: Y coordinate of arc center relative to ``x1``
|
||||
cy : Length(float)
|
||||
#: Direction of arc. ``True`` means clockwise. For a given center coordinate and endpoints there are always two
|
||||
#: possible arcs, the large one and the small one. Flipping this switches between them.
|
||||
clockwise : bool
|
||||
#: Aperture for this arc. Should be a subclass of :py:class:`CircleAperture`, whose diameter determines the line
|
||||
#: width.
|
||||
aperture : object
|
||||
|
||||
def _with_offset(self, dx, dy):
|
||||
return replace(self, x1=self.x1+dx, y1=self.y1+dy, x2=self.x2+dx, y2=self.y2+dy)
|
||||
def _offset(self, dx, dy):
|
||||
self.x1 += dx
|
||||
self.y1 += dy
|
||||
self.x2 += dx
|
||||
self.y2 += dy
|
||||
|
||||
def numeric_error(self, unit=None):
|
||||
""" Gerber arcs are sligtly over-determined. Since we have not just a radius, but center X and Y coordinates, an
|
||||
"impossible" arc can be specified, where the start and end points do not lie on a circle around its center. This
|
||||
function returns the absolute difference between the two radii (start - center) and (end - center) as an
|
||||
indication on how bad this arc is.
|
||||
|
||||
.. note:: For arcs read from a Gerber file, this value can easily be in the order of magnitude of 1e-4. Gerber
|
||||
files have very limited numerical resolution, and rounding errors will necessarily lead to numerical
|
||||
accuracy issues with arcs.
|
||||
|
||||
:rtype: float
|
||||
"""
|
||||
# This function is used internally to determine the right arc in multi-quadrant mode
|
||||
conv = self.converted(unit)
|
||||
cx, cy = conv.cx + conv.x1, conv.cy + conv.y1
|
||||
r1 = math.dist((cx, cy), conv.p1)
|
||||
|
@ -282,6 +474,11 @@ class Arc(GerberObject):
|
|||
return abs(r1 - r2)
|
||||
|
||||
def sweep_angle(self):
|
||||
""" Calculate absolute sweep angle of arc. This is always a positive number.
|
||||
|
||||
:returns: Angle in clockwise radian between ``0`` and ``2*math.pi``
|
||||
:rtype: float
|
||||
"""
|
||||
cx, cy = self.cx + self.x1, self.cy + self.y1
|
||||
x1, y1 = self.x1 - cx, self.y1 - cy
|
||||
x2, y2 = self.x2 - cx, self.y2 - cy
|
||||
|
@ -301,26 +498,35 @@ class Arc(GerberObject):
|
|||
|
||||
@property
|
||||
def p1(self):
|
||||
""" Convenience alias for ``(self.x1, self.y1)`` returning start point of the arc. """
|
||||
return self.x1, self.y1
|
||||
|
||||
@property
|
||||
def p2(self):
|
||||
""" Convenience alias for ``(self.x2, self.y2)`` returning end point of the arc. """
|
||||
return self.x2, self.y2
|
||||
|
||||
@property
|
||||
def center(self):
|
||||
""" Returns the center of the arc in **absolute** coordinates.
|
||||
|
||||
:returns: ``(self.x1 + self.cx, self.y1 + self.cy)``
|
||||
:rtype: tuple(float)
|
||||
"""
|
||||
return self.cx + self.x1, self.cy + self.y1
|
||||
|
||||
@property
|
||||
def center_relative(self):
|
||||
""" Returns the center of the arc in relative coordinates.
|
||||
|
||||
:returns: ``(self.cx, self.cy)``
|
||||
:rtype: tuple(float)
|
||||
"""
|
||||
return self.cx, self.cy
|
||||
|
||||
@property
|
||||
def end_point(self):
|
||||
return self.p2
|
||||
|
||||
@property
|
||||
def tool(self):
|
||||
""" Alias for :py:attr:`aperture` for use inside an :py:class:`gerbonara.excellon.ExcellonFile`. """
|
||||
return self.aperture
|
||||
|
||||
@tool.setter
|
||||
|
@ -329,6 +535,9 @@ class Arc(GerberObject):
|
|||
|
||||
@property
|
||||
def plated(self):
|
||||
""" (Excellon only) Returns if this is a plated hole. ``True`` (plated), ``False`` (non-plated) or ``None``
|
||||
(plating undefined)
|
||||
"""
|
||||
return self.tool.plated
|
||||
|
||||
def _rotate(self, rotation, cx=0, cy=0):
|
||||
|
@ -338,7 +547,7 @@ class Arc(GerberObject):
|
|||
self.x2, self.y2 = gp.rotate_point(self.x2, self.y2, rotation, cx, cy)
|
||||
self.cx, self.cy = new_cx - self.x1, new_cy - self.y1
|
||||
|
||||
def to_primitives(self, unit=None):
|
||||
def _to_primitives(self, unit=None):
|
||||
conv = self.converted(unit)
|
||||
w = self.aperture.equivalent_width(unit) if self.aperture else 0.1 # for debugging
|
||||
yield gp.Arc(x1=conv.x1, y1=conv.y1,
|
||||
|
@ -348,7 +557,7 @@ class Arc(GerberObject):
|
|||
width=w,
|
||||
polarity_dark=self.polarity_dark)
|
||||
|
||||
def to_statements(self, gs):
|
||||
def _to_statements(self, gs):
|
||||
yield from gs.set_polarity(self.polarity_dark)
|
||||
yield from gs.set_aperture(self.aperture)
|
||||
# TODO is the following line correct?
|
||||
|
@ -363,7 +572,7 @@ class Arc(GerberObject):
|
|||
|
||||
gs.update_point(*self.p2, unit=self.unit)
|
||||
|
||||
def to_xnc(self, ctx):
|
||||
def _to_xnc(self, ctx):
|
||||
yield from ctx.select_tool(self.tool)
|
||||
yield from ctx.route_mode(self.unit, self.x1, self.y1)
|
||||
code = 'G02' if self.clockwise else 'G03'
|
||||
|
@ -376,6 +585,7 @@ class Arc(GerberObject):
|
|||
|
||||
ctx.set_current_point(self.unit, self.x2, self.y2)
|
||||
|
||||
# internally used to compute Excellon file path length
|
||||
def curve_length(self, unit=MM):
|
||||
return self.unit.convert_to(unit, math.hypot(self.cx, self.cy) * self.sweep_angle)
|
||||
|
||||
|
|
|
@ -201,10 +201,10 @@ class ArcPoly(GraphicPrimitive):
|
|||
|
||||
# list of (x : float, y : float) tuples. Describes closed outline, i.e. first and last point are considered
|
||||
# connected.
|
||||
outline : [(float,)]
|
||||
outline : list
|
||||
# must be either None (all segments are straight lines) or same length as outline.
|
||||
# Straight line segments have None entry.
|
||||
arc_centers : [(float,)] = None
|
||||
arc_centers : list = None
|
||||
|
||||
@property
|
||||
def segments(self):
|
||||
|
|
|
@ -79,6 +79,9 @@ class GerberFile(CamFile):
|
|||
|
||||
return ExcellonFile(objects=new_objs, comments=self.comments)
|
||||
|
||||
def to_gerber(self):
|
||||
return
|
||||
|
||||
def merge(self, other):
|
||||
""" Merge other GerberFile into this one """
|
||||
if other is None:
|
||||
|
@ -222,9 +225,9 @@ class GerberFile(CamFile):
|
|||
|
||||
def save(self, filename, settings=None, drop_comments=True):
|
||||
with open(filename, 'w', encoding='utf-8') as f: # Encoding is specified as UTF-8 by spec.
|
||||
f.write(self.to_gerber(settings, drop_comments=drop_comments))
|
||||
f.write(self.generate_gerber(settings, drop_comments=drop_comments))
|
||||
|
||||
def to_gerber(self, settings=None, drop_comments=True):
|
||||
def generate_gerber(self, settings=None, drop_comments=True):
|
||||
# Use given settings, or use same settings as original file if not given, or use defaults if not imported from a
|
||||
# file
|
||||
if settings is None:
|
||||
|
@ -245,8 +248,8 @@ class GerberFile(CamFile):
|
|||
|
||||
def offset(self, dx=0, dy=0, unit=MM):
|
||||
# TODO round offset to file resolution
|
||||
|
||||
self.objects = [ obj.with_offset(dx, dy, unit) for obj in self.objects ]
|
||||
for obj in self.objects:
|
||||
obj.with_offset(dx, dy, unit)
|
||||
|
||||
def rotate(self, angle:'radian', center=(0,0), unit=MM):
|
||||
""" Rotate file contents around given point.
|
||||
|
|
2
setup.py
2
setup.py
|
@ -30,7 +30,7 @@ setup(
|
|||
name='gerbonara',
|
||||
version=version(),
|
||||
author='jaseg, XenGi',
|
||||
author_email='contact@gerbonara.jaseg.de',
|
||||
author_email='gerbonara@jaseg.de',
|
||||
description='Tools to handle Gerber and Excellon files in Python',
|
||||
long_description=long_description(),
|
||||
long_description_content_type='text/markdown',
|
||||
|
|
Ładowanie…
Reference in New Issue