summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rwxr-xr-xgerber/excellon.py15
-rw-r--r--gerber/utils.py406
-rw-r--r--requirements.txt3
-rw-r--r--setup.py61
5 files changed, 282 insertions, 207 deletions
diff --git a/.gitignore b/.gitignore
index abfd2fa..01ba410 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,9 +38,9 @@ nosetests.xml
.idea/misc.xml
.idea
-# Komodo
+# Komodo Files
*.komodoproject
-# OS
+# OS Files
.DS_Store
Thumbs.db \ No newline at end of file
diff --git a/gerber/excellon.py b/gerber/excellon.py
index 780d08f..9d09576 100755
--- a/gerber/excellon.py
+++ b/gerber/excellon.py
@@ -1,8 +1,8 @@
-#! /usr/bin/env python
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# copyright 2014 Hamilton Kibbe <ham@hamiltonkib.be>
-#
+# Copyright 2014 Hamilton Kibbe <ham@hamiltonkib.be>
+
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -13,7 +13,8 @@
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
-# limitations under the License.
+# limitations under the License.
+
"""
Excellon File module
====================
@@ -124,7 +125,7 @@ class ExcellonParser(object):
self.tools = {}
self.hits = []
self.active_tool = None
- self.pos = [0., 0.]
+ self.pos = [0., 0.]
if settings is not None:
self.units = settings.units
self.zero_suppression = settings.zero_suppression
@@ -234,7 +235,7 @@ class ExcellonParser(object):
stmt = CoordinateStmt.from_excellon(line, fmt, zs)
x = stmt.x
y = stmt.y
- self.statements.append(stmt)
+ self.statements.append(stmt)
if self.notation == 'absolute':
if x is not None:
self.pos[0] = x
@@ -245,7 +246,7 @@ class ExcellonParser(object):
self.pos[0] += x
if y is not None:
self.pos[1] += y
- if self.state == 'DRILL':
+ if self.state == 'DRILL':
self.hits.append((self.active_tool, tuple(self.pos)))
self.active_tool._hit()
else:
diff --git a/gerber/utils.py b/gerber/utils.py
index 31ff196..7749e22 100644
--- a/gerber/utils.py
+++ b/gerber/utils.py
@@ -1,196 +1,210 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-gerber.utils
-============
-**Gerber and Excellon file handling utilities**
-
-This module provides utility functions for working with Gerber and Excellon
-files.
-"""
-
-# Author: Hamilton Kibbe <ham@hamiltonkib.be>
-# License:
-
-
-def parse_gerber_value(value, format=(2, 5), zero_suppression='trailing'):
- """ Convert gerber/excellon formatted string to floating-point number
-
- .. note::
- Format and zero suppression are configurable. Note that the Excellon
- and Gerber formats use opposite terminology with respect to leading
- and trailing zeros. The Gerber format specifies which zeros are
- suppressed, while the Excellon format specifies which zeros are
- included. This function uses the Gerber-file convention, so an
- Excellon file in LZ (leading zeros) mode would use
- `zero_suppression='trailing'`
-
-
- Parameters
- ----------
- value : string
- A Gerber/Excellon-formatted string representing a numerical value.
-
- format : tuple (int,int)
- Gerber/Excellon precision format expressed as a tuple containing:
- (number of integer-part digits, number of decimal-part digits)
-
- zero_suppression : string
- Zero-suppression mode. May be 'leading' or 'trailing'
-
- Returns
- -------
- value : float
- The specified value as a floating-point number.
-
- """
- # Handle excellon edge case with explicit decimal. "That was easy!"
- if '.' in value:
- return float(value)
-
- # Format precision
- integer_digits, decimal_digits = format
- MAX_DIGITS = integer_digits + decimal_digits
-
- # Absolute maximum number of digits supported. This will handle up to
- # 6:7 format, which is somewhat supported, even though the gerber spec
- # only allows up to 6:6
- if MAX_DIGITS > 13 or integer_digits > 6 or decimal_digits > 7:
- raise ValueError('Parser only supports precision up to 6:7 format')
-
- # Remove extraneous information
- #value = value.strip()
- value = value.lstrip('+')
- negative = '-' in value
- if negative:
- value = value.lstrip('-')
-
-
- digits = list('0' * MAX_DIGITS)
- offset = 0 if zero_suppression == 'trailing' else (MAX_DIGITS - len(value))
- for i, digit in enumerate(value):
- digits[i + offset] = digit
-
- result = float(''.join(digits[:integer_digits] + ['.'] + digits[integer_digits:]))
- return -result if negative else result
-
-
-def write_gerber_value(value, format=(2, 5), zero_suppression='trailing'):
- """ Convert a floating point number to a Gerber/Excellon-formatted string.
-
- .. note::
- Format and zero suppression are configurable. Note that the Excellon
- and Gerber formats use opposite terminology with respect to leading
- and trailing zeros. The Gerber format specifies which zeros are
- suppressed, while the Excellon format specifies which zeros are
- included. This function uses the Gerber-file convention, so an
- Excellon file in LZ (leading zeros) mode would use
- `zero_suppression='trailing'`
-
- Parameters
- ----------
- value : float
- A floating point value.
-
- format : tuple (n=2)
- Gerber/Excellon precision format expressed as a tuple containing:
- (number of integer-part digits, number of decimal-part digits)
-
- zero_suppression : string
- Zero-suppression mode. May be 'leading' or 'trailing'
-
- Returns
- -------
- value : string
- The specified value as a Gerber/Excellon-formatted string.
- """
- # Format precision
- integer_digits, decimal_digits = format
- MAX_DIGITS = integer_digits + decimal_digits
-
- if MAX_DIGITS > 13 or integer_digits > 6 or decimal_digits > 7:
- raise ValueError('Parser only supports precision up to 6:7 format')
-
- # Edge case...
- if value == 0:
- return '00'
-
- # negative sign affects padding, so deal with it at the end...
- negative = value < 0.0
- if negative:
- value = -1.0 * value
-
- # Format string for padding out in both directions
- fmtstring = '%%0%d.0%df' % (MAX_DIGITS + 1, decimal_digits)
- digits = [val for val in fmtstring % value if val != '.']
-
- # Suppression...
- if zero_suppression == 'trailing':
- while digits[-1] == '0':
- digits.pop()
- else:
- while digits[0] == '0':
- digits.pop(0)
-
- return ''.join(digits) if not negative else ''.join(['-'] + digits)
-
-
-def decimal_string(value, precision=6, padding=False):
- """ Convert float to string with limited precision
-
- Parameters
- ----------
- value : float
- A floating point value.
-
- precision :
- Maximum number of decimal places to print
-
- Returns
- -------
- value : string
- The specified value as a string.
-
- """
- floatstr = '%0.10g' % value
- integer = None
- decimal = None
- if '.' in floatstr:
- integer, decimal = floatstr.split('.')
- elif ',' in floatstr:
- integer, decimal = floatstr.split(',')
- if len(decimal) > precision:
- decimal = decimal[:precision]
- elif padding:
- decimal = decimal + (precision - len(decimal)) * '0'
- if integer or decimal:
- return ''.join([integer, '.', decimal])
- else:
- return int(floatstr)
-
-
-def detect_file_format(filename):
- """ Determine format of a file
-
- Parameters
- ----------
- filename : string
- Filename of the file to read.
-
- Returns
- -------
- format : string
- File format. either 'excellon' or 'rs274x'
- """
-
- # Read the first 20 lines
- with open(filename, 'r') as f:
- lines = [next(f) for x in xrange(20)]
-
- # Look for
- for line in lines:
- if 'M48' in line:
- return 'excellon'
- elif '%FS' in line:
- return'rs274x'
- return 'unknown'
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2014 Hamilton Kibbe <ham@hamiltonkib.be>
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+gerber.utils
+============
+**Gerber and Excellon file handling utilities**
+
+This module provides utility functions for working with Gerber and Excellon
+files.
+"""
+
+# Author: Hamilton Kibbe <ham@hamiltonkib.be>
+# License:
+
+
+def parse_gerber_value(value, format=(2, 5), zero_suppression='trailing'):
+ """ Convert gerber/excellon formatted string to floating-point number
+
+ .. note::
+ Format and zero suppression are configurable. Note that the Excellon
+ and Gerber formats use opposite terminology with respect to leading
+ and trailing zeros. The Gerber format specifies which zeros are
+ suppressed, while the Excellon format specifies which zeros are
+ included. This function uses the Gerber-file convention, so an
+ Excellon file in LZ (leading zeros) mode would use
+ `zero_suppression='trailing'`
+
+
+ Parameters
+ ----------
+ value : string
+ A Gerber/Excellon-formatted string representing a numerical value.
+
+ format : tuple (int,int)
+ Gerber/Excellon precision format expressed as a tuple containing:
+ (number of integer-part digits, number of decimal-part digits)
+
+ zero_suppression : string
+ Zero-suppression mode. May be 'leading' or 'trailing'
+
+ Returns
+ -------
+ value : float
+ The specified value as a floating-point number.
+
+ """
+ # Handle excellon edge case with explicit decimal. "That was easy!"
+ if '.' in value:
+ return float(value)
+
+ # Format precision
+ integer_digits, decimal_digits = format
+ MAX_DIGITS = integer_digits + decimal_digits
+
+ # Absolute maximum number of digits supported. This will handle up to
+ # 6:7 format, which is somewhat supported, even though the gerber spec
+ # only allows up to 6:6
+ if MAX_DIGITS > 13 or integer_digits > 6 or decimal_digits > 7:
+ raise ValueError('Parser only supports precision up to 6:7 format')
+
+ # Remove extraneous information
+ #value = value.strip()
+ value = value.lstrip('+')
+ negative = '-' in value
+ if negative:
+ value = value.lstrip('-')
+
+
+ digits = list('0' * MAX_DIGITS)
+ offset = 0 if zero_suppression == 'trailing' else (MAX_DIGITS - len(value))
+ for i, digit in enumerate(value):
+ digits[i + offset] = digit
+
+ result = float(''.join(digits[:integer_digits] + ['.'] + digits[integer_digits:]))
+ return -result if negative else result
+
+
+def write_gerber_value(value, format=(2, 5), zero_suppression='trailing'):
+ """ Convert a floating point number to a Gerber/Excellon-formatted string.
+
+ .. note::
+ Format and zero suppression are configurable. Note that the Excellon
+ and Gerber formats use opposite terminology with respect to leading
+ and trailing zeros. The Gerber format specifies which zeros are
+ suppressed, while the Excellon format specifies which zeros are
+ included. This function uses the Gerber-file convention, so an
+ Excellon file in LZ (leading zeros) mode would use
+ `zero_suppression='trailing'`
+
+ Parameters
+ ----------
+ value : float
+ A floating point value.
+
+ format : tuple (n=2)
+ Gerber/Excellon precision format expressed as a tuple containing:
+ (number of integer-part digits, number of decimal-part digits)
+
+ zero_suppression : string
+ Zero-suppression mode. May be 'leading' or 'trailing'
+
+ Returns
+ -------
+ value : string
+ The specified value as a Gerber/Excellon-formatted string.
+ """
+ # Format precision
+ integer_digits, decimal_digits = format
+ MAX_DIGITS = integer_digits + decimal_digits
+
+ if MAX_DIGITS > 13 or integer_digits > 6 or decimal_digits > 7:
+ raise ValueError('Parser only supports precision up to 6:7 format')
+
+ # Edge case...
+ if value == 0:
+ return '00'
+
+ # negative sign affects padding, so deal with it at the end...
+ negative = value < 0.0
+ if negative:
+ value = -1.0 * value
+
+ # Format string for padding out in both directions
+ fmtstring = '%%0%d.0%df' % (MAX_DIGITS + 1, decimal_digits)
+ digits = [val for val in fmtstring % value if val != '.']
+
+ # Suppression...
+ if zero_suppression == 'trailing':
+ while digits[-1] == '0':
+ digits.pop()
+ else:
+ while digits[0] == '0':
+ digits.pop(0)
+
+ return ''.join(digits) if not negative else ''.join(['-'] + digits)
+
+
+def decimal_string(value, precision=6, padding=False):
+ """ Convert float to string with limited precision
+
+ Parameters
+ ----------
+ value : float
+ A floating point value.
+
+ precision :
+ Maximum number of decimal places to print
+
+ Returns
+ -------
+ value : string
+ The specified value as a string.
+
+ """
+ floatstr = '%0.10g' % value
+ integer = None
+ decimal = None
+ if '.' in floatstr:
+ integer, decimal = floatstr.split('.')
+ elif ',' in floatstr:
+ integer, decimal = floatstr.split(',')
+ if len(decimal) > precision:
+ decimal = decimal[:precision]
+ elif padding:
+ decimal = decimal + (precision - len(decimal)) * '0'
+ if integer or decimal:
+ return ''.join([integer, '.', decimal])
+ else:
+ return int(floatstr)
+
+
+def detect_file_format(filename):
+ """ Determine format of a file
+
+ Parameters
+ ----------
+ filename : string
+ Filename of the file to read.
+
+ Returns
+ -------
+ format : string
+ File format. either 'excellon' or 'rs274x'
+ """
+
+ # Read the first 20 lines
+ with open(filename, 'r') as f:
+ lines = [next(f) for x in xrange(20)]
+
+ # Look for
+ for line in lines:
+ if 'M48' in line:
+ return 'excellon'
+ elif '%FS' in line:
+ return'rs274x'
+ return 'unknown'
diff --git a/requirements.txt b/requirements.txt
index 4fada51..1605e3d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,8 +6,7 @@ Sphinx==1.2.3
coverage==3.7.1
docutils==0.12
nose==1.3.4
+numpydoc==0.5
pyparsing==2.0.2
svgwrite==1.1.6
wsgiref==0.1.2
-numpydoc==0.5
-
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..02b9df6
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2013-2014 Paulo Henrique Silva <ph.silva@gmail.com>
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import os
+
+def read(fname):
+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+
+METADATA = {
+ 'name': 'gerber-tools',
+ 'version': 0.1,
+ 'author': 'Paulo Henrique Silva <ph.silva@gmail.com>, Hamilton Kibbe <ham@hamiltonkib.be>',
+ 'author_email': "ph.silva@gmail.com, ham@hamiltonkib.be",
+ 'description': ("Utilities to handle Gerber (RS-274X) files."),
+ 'license': "Apache",
+ 'keywords': "gerber tools",
+ 'url': "http://github.com/curtacircuitos/gerber-tools",
+ 'packages': ['gerber'],
+ 'long_description': read('README.md'),
+ 'classifiers':[
+ "Development Status :: 3 - Alpha",
+ "Topic :: Utilities",
+ "License :: OSI Approved :: Apple Public Source License",
+ ],
+}
+
+SETUPTOOLS_METADATA = {
+ 'install_requires': ['svgwrite'],
+}
+
+
+def install():
+ """ Install using setuptools, fallback to distutils
+ """
+ try:
+ from setuptools import setup
+ METADATA.update(SETUPTOOLS_METADATA)
+ setup(**METADATA)
+ except ImportError:
+ from sys import stderr
+ stderr.write('Could not import setuptools, using distutils')
+ stderr.write('NOTE: You will need to install dependencies manualy')
+ from distutils.core import setup
+ setup(**METADATA)
+
+if __name__ == '__main__':
+ install() \ No newline at end of file