kopia lustrzana https://gitlab.com/gerbolyze/gerbonara
212 wiersze
6.3 KiB
Python
212 wiersze
6.3 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2021 Jan Götte <gerbonara@jaseg.de>
|
|
|
|
import operator
|
|
import re
|
|
import ast
|
|
|
|
from ..utils import MM, Inch, MILLIMETERS_PER_INCH
|
|
|
|
|
|
def expr(obj):
|
|
return obj if isinstance(obj, Expression) else ConstantExpression(obj)
|
|
|
|
|
|
class Expression:
|
|
def optimized(self, variable_binding={}):
|
|
return self
|
|
|
|
def __str__(self):
|
|
return f'<{self.to_gerber()}>'
|
|
|
|
def __repr__(self):
|
|
return f'<E {self.to_gerber()}>'
|
|
|
|
def converted(self, unit):
|
|
return self
|
|
|
|
def calculate(self, variable_binding={}, unit=None):
|
|
expr = self.converted(unit).optimized(variable_binding)
|
|
if not isinstance(expr, ConstantExpression):
|
|
raise IndexError(f'Cannot fully resolve expression due to unresolved variables: {expr} with variables {variable_binding}')
|
|
return expr.value
|
|
|
|
def __add__(self, other):
|
|
return OperatorExpression(operator.add, self, expr(other)).optimized()
|
|
|
|
def __radd__(self, other):
|
|
return expr(other) + self
|
|
|
|
def __sub__(self, other):
|
|
return OperatorExpression(operator.sub, self, expr(other)).optimized()
|
|
|
|
def __rsub__(self, other):
|
|
return expr(other) - self
|
|
|
|
def __mul__(self, other):
|
|
return OperatorExpression(operator.mul, self, expr(other)).optimized()
|
|
|
|
def __rmul__(self, other):
|
|
return expr(other) * self
|
|
|
|
def __truediv__(self, other):
|
|
return OperatorExpression(operator.truediv, self, expr(other)).optimized()
|
|
|
|
def __rtruediv__(self, other):
|
|
return expr(other) / self
|
|
|
|
def __neg__(self):
|
|
return 0 - self
|
|
|
|
def __pos__(self):
|
|
return self
|
|
|
|
class UnitExpression(Expression):
|
|
def __init__(self, expr, unit):
|
|
self._expr = expr
|
|
self.unit = unit
|
|
|
|
def to_gerber(self, unit=None):
|
|
return self.converted(unit).optimized().to_gerber()
|
|
|
|
def __eq__(self, other):
|
|
return type(other) == type(self) and \
|
|
self.unit == other.unit and\
|
|
self._expr == other._expr
|
|
|
|
def __str__(self):
|
|
return f'<{self._expr.to_gerber()} {self.unit}>'
|
|
|
|
def __repr__(self):
|
|
return f'<UE {self._expr.to_gerber()} {self.unit}>'
|
|
|
|
def converted(self, unit):
|
|
if self.unit is None or unit is None or self.unit == unit:
|
|
return self._expr
|
|
|
|
elif MM == unit:
|
|
return self._expr * MILLIMETERS_PER_INCH
|
|
|
|
elif Inch == unit:
|
|
return self._expr / MILLIMETERS_PER_INCH
|
|
|
|
else:
|
|
raise ValueError(f'invalid unit {unit}, must be "inch" or "mm".')
|
|
|
|
def __add__(self, other):
|
|
if not isinstance(other, UnitExpression):
|
|
raise ValueError('Unit mismatch: Can only add/subtract UnitExpression from UnitExpression, not scalar.')
|
|
|
|
if self.unit == other.unit or self.unit is None or other.unit is None:
|
|
return UnitExpression(self._expr + other._expr, self.unit)
|
|
|
|
if other.unit == 'mm': # -> and self.unit == 'inch'
|
|
return UnitExpression(self._expr + (other._expr / MILLIMETERS_PER_INCH), self.unit)
|
|
else: # other.unit == 'inch' and self.unit == 'mm'
|
|
return UnitExpression(self._expr + (other._expr * MILLIMETERS_PER_INCH), self.unit)
|
|
|
|
def __radd__(self, other):
|
|
# left hand side cannot have been an UnitExpression or __radd__ would not have been called
|
|
raise ValueError('Unit mismatch: Can only add/subtract UnitExpression from UnitExpression, not scalar.')
|
|
|
|
def __sub__(self, other):
|
|
return (self + (-other)).optimize()
|
|
|
|
def __rsub__(self, other):
|
|
# see __radd__ above
|
|
raise ValueError('Unit mismatch: Can only add/subtract UnitExpression from UnitExpression, not scalar.')
|
|
|
|
def __mul__(self, other):
|
|
return UnitExpression(self._expr * other, self.unit)
|
|
|
|
def __rmul__(self, other):
|
|
return UnitExpression(other * self._expr, self.unit)
|
|
|
|
def __truediv__(self, other):
|
|
return UnitExpression(self._expr / other, self.unit)
|
|
|
|
def __rtruediv__(self, other):
|
|
return UnitExpression(other / self._expr, self.unit)
|
|
|
|
def __neg__(self):
|
|
return UnitExpression(-self._expr, self.unit)
|
|
|
|
def __pos__(self):
|
|
return self
|
|
|
|
|
|
class ConstantExpression(Expression):
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __float__(self):
|
|
return float(self.value)
|
|
|
|
def __eq__(self, other):
|
|
return type(self) == type(other) and self.value == other.value
|
|
|
|
def to_gerber(self, _unit=None):
|
|
return f'{self.value:.6f}'.rstrip('0').rstrip('.')
|
|
|
|
|
|
class VariableExpression(Expression):
|
|
def __init__(self, number):
|
|
self.number = number
|
|
|
|
def optimized(self, variable_binding={}):
|
|
if self.number in variable_binding:
|
|
return ConstantExpression(variable_binding[self.number])
|
|
return self
|
|
|
|
def __eq__(self, other):
|
|
return type(self) == type(other) and \
|
|
self.number == other.number
|
|
|
|
def to_gerber(self, _unit=None):
|
|
return f'${self.number}'
|
|
|
|
|
|
class OperatorExpression(Expression):
|
|
def __init__(self, op, l, r):
|
|
self.op = op
|
|
self.l = ConstantExpression(l) if isinstance(l, (int, float)) else l
|
|
self.r = ConstantExpression(r) if isinstance(r, (int, float)) else r
|
|
|
|
def __eq__(self, other):
|
|
return type(self) == type(other) and \
|
|
self.op == other.op and \
|
|
self.l == other.l and \
|
|
self.r == other.r
|
|
|
|
def optimized(self, variable_binding={}):
|
|
l = self.l.optimized(variable_binding)
|
|
r = self.r.optimized(variable_binding)
|
|
|
|
if self.op in (operator.add, operator.mul):
|
|
if id(r) < id(l):
|
|
l, r = r, l
|
|
|
|
if isinstance(l, ConstantExpression) and isinstance(r, ConstantExpression):
|
|
return ConstantExpression(self.op(float(l), float(r)))
|
|
|
|
return OperatorExpression(self.op, l, r)
|
|
|
|
def to_gerber(self, unit=None):
|
|
lval = self.l.to_gerber(unit)
|
|
rval = self.r.to_gerber(unit)
|
|
|
|
if isinstance(self.l, OperatorExpression):
|
|
lval = f'({lval})'
|
|
if isinstance(self.r, OperatorExpression):
|
|
rval = f'({rval})'
|
|
|
|
op = {operator.add: '+',
|
|
operator.sub: '-',
|
|
operator.mul: 'X',
|
|
operator.truediv: '/'} [self.op]
|
|
|
|
return f'{lval}{op}{rval}'
|
|
|