diff options
Diffstat (limited to 'gerbonara/gerber/utils.py')
-rw-r--r-- | gerbonara/gerber/utils.py | 218 |
1 files changed, 0 insertions, 218 deletions
diff --git a/gerbonara/gerber/utils.py b/gerbonara/gerber/utils.py deleted file mode 100644 index 71251de..0000000 --- a/gerbonara/gerber/utils.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/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. -""" - -import os -import re -import textwrap -from enum import Enum -from math import radians, sin, cos, sqrt, atan2, pi - -class UnknownStatementWarning(Warning): - pass - -class RegexMatcher: - def __init__(self): - self.mapping = {} - - def match(self, regex): - def wrapper(fun): - nonlocal self - self.mapping[regex] = fun - return fun - return wrapper - - def handle(self, inst, line): - for regex, handler in self.mapping.items(): - if (match := re.fullmatch(regex, line)): - handler(inst, match) - return True - else: - return False - -class LengthUnit: - def __init__(self, name, shorthand, this_in_mm): - self.name = name - self.shorthand = shorthand - self.factor = this_in_mm - - def convert_from(self, unit, value): - if isinstance(unit, str): - unit = units[unit] - - if unit == self or unit is None or value is None: - return value - - return value * unit.factor / self.factor - - def convert_to(self, unit, value): - if isinstance(unit, str): - unit = to_unit(unit) - - if unit is None: - return value - - return unit.convert_from(self, value) - - def format(self, value): - return f'{value:.3f}{self.shorthand}' if value is not None else '' - - def __call__(self, value, unit): - return self.convert_from(unit, value) - - def __eq__(self, other): - if isinstance(other, str): - return other.lower() in (self.name, self.shorthand) - else: - return id(self) == id(other) - - # This class is a singleton, we don't want copies around - def __copy__(self): - return self - - def __deepcopy__(self, memo): - return self - - def __str__(self): - return self.shorthand - - def __repr__(self): - return f'<LengthUnit {self.name}>' - - -MILLIMETERS_PER_INCH = 25.4 -Inch = LengthUnit('inch', 'in', MILLIMETERS_PER_INCH) -MM = LengthUnit('millimeter', 'mm', 1) -units = {'inch': Inch, 'mm': MM, None: None} -to_unit = lambda name: units[name.lower() if name else None] - - -class InterpMode(Enum): - LINEAR = 0 - CIRCULAR_CW = 1 - CIRCULAR_CCW = 2 - - -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(',') - else: - integer, decimal = floatstr, "0" - - 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 validate_coordinates(position): - if position is not None: - if len(position) != 2: - raise TypeError('Position must be a tuple (n=2) of coordinates') - else: - for coord in position: - if not (isinstance(coord, int) or isinstance(coord, float)): - raise TypeError('Coordinates must be integers or floats') - -def rotate_point(point, angle, center=(0.0, 0.0)): - """ Rotate a point about another point. - - Parameters - ----------- - point : tuple(<float>, <float>) - Point to rotate about origin or center point - - angle : float - Angle to rotate the point [degrees] - - center : tuple(<float>, <float>) - Coordinates about which the point is rotated. Defaults to the origin. - - Returns - ------- - rotated_point : tuple(<float>, <float>) - `point` rotated about `center` by `angle` degrees. - """ - angle = radians(angle) - - cos_angle = cos(angle) - sin_angle = sin(angle) - - return ( - cos_angle * (point[0] - center[0]) - sin_angle * (point[1] - center[1]) + center[0], - sin_angle * (point[0] - center[0]) + cos_angle * (point[1] - center[1]) + center[1]) - -def nearly_equal(point1, point2, ndigits = 6): - '''Are the points nearly equal''' - - return round(point1[0] - point2[0], ndigits) == 0 and round(point1[1] - point2[1], ndigits) == 0 - - -def sq_distance(point1, point2): - - diff1 = point1[0] - point2[0] - diff2 = point1[1] - point2[1] - return diff1 * diff1 + diff2 * diff2 - -class Tag: - def __init__(self, name, children=None, root=False, **attrs): - self.name, self.attrs = name, attrs - self.children = children or [] - self.root = root - - def __str__(self): - prefix = '<?xml version="1.0" encoding="utf-8"?>\n' if self.root else '' - opening = ' '.join([self.name] + [f'{key.replace("__", ":").replace("_", "-")}="{value}"' for key, value in self.attrs.items()]) - if self.children: - children = '\n'.join(textwrap.indent(str(c), ' ') for c in self.children) - return f'{prefix}<{opening}>\n{children}\n</{self.name}>' - else: - return f'{prefix}<{opening}/>' - - |