diff options
Diffstat (limited to 'gerber/utils.py')
-rw-r--r-- | gerber/utils.py | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/gerber/utils.py b/gerber/utils.py new file mode 100644 index 0000000..02a8a14 --- /dev/null +++ b/gerber/utils.py @@ -0,0 +1,130 @@ +#!/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: MIT + +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. + + """ + # 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(' +') + negative = '-' in value + if negative: + value = value.strip(' -') + + # Handle excellon edge case with explicit decimal. "That was easy!" + if '.' in value: + return float(value) + + digits = [digit for digit in '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 -1.0 * 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') + + # 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) + |