summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gerbonara/cad/kicad/base_types.py7
-rw-r--r--gerbonara/cad/kicad/schematic.py54
-rw-r--r--gerbonara/cad/kicad/symbols.py76
-rw-r--r--gerbonara/newstroke.py28
4 files changed, 69 insertions, 96 deletions
diff --git a/gerbonara/cad/kicad/base_types.py b/gerbonara/cad/kicad/base_types.py
index 8243c07..6c0526f 100644
--- a/gerbonara/cad/kicad/base_types.py
+++ b/gerbonara/cad/kicad/base_types.py
@@ -333,6 +333,10 @@ class TextMixin:
def h_align(self):
return 'left' if self.effects.justify.h else 'center'
+ @property
+ def mirrored(self):
+ return False, False
+
def to_svg(self, color='black', variables={}):
if not self.effects or self.effects.hide or not self.effects.font:
return
@@ -344,12 +348,13 @@ class TextMixin:
print(text, self.rotation, self.at, self.effects)
yield font.render_svg(text,
size=self.size or 1.27,
- rotation=self.rotation,
h_align=self.h_align,
v_align=self.effects.justify.v or self.default_v_align,
stroke=color,
stroke_width=f'{self.line_width:.3f}',
scale=(1,1),
+ rotation=self.rotation,
+ mirror=self.mirrored,
transform=f'translate({self.at.x:.3f} {self.at.y:.3f})',
)
diff --git a/gerbonara/cad/kicad/schematic.py b/gerbonara/cad/kicad/schematic.py
index 7809685..0fbc47a 100644
--- a/gerbonara/cad/kicad/schematic.py
+++ b/gerbonara/cad/kicad/schematic.py
@@ -3,6 +3,7 @@ Library for handling KiCad's schematic files (`*.kicad_sch`).
"""
import math
+import string
from pathlib import Path
from dataclasses import field, KW_ONLY
from itertools import chain
@@ -296,7 +297,10 @@ class DrawnProperty(TextMixin):
# Alias value for text mixin
@property
def text(self):
- return self.value
+ if self.key == 'Reference' and self.parent.unit > 0:
+ return f'{self.value}{string.ascii_uppercase[self.parent.unit-1]}'
+ else:
+ return self.value
@text.setter
def text(self, value):
@@ -308,25 +312,20 @@ class DrawnProperty(TextMixin):
@property
def h_align(self):
- j = self.effects.justify.h_str
- if False: #self.at.rotation in (270):
- return {'left': 'right', 'right': 'left'}.get(j, j)
- else:
- return j
+ return self.effects.justify.h_str
@property
def rotation(self):
rot = -self.at.rotation
rot += getattr(self.parent.at, 'rotation', 0)
- if getattr(self.parent, 'reference', None) == 'C13':
- print(self.value, self.at, self.parent.at, self.parent.mirror)
- if hasattr(self.parent, 'mirror'):
- if self.parent.mirror.y and rot in (90, 270):
- rot = (rot+180)%360
- if self.parent.mirror.x and rot in (0, 180):
- rot = (rot+180)%360
return rot%360
+ @property
+ def mirrored(self):
+ if hasattr(self.parent, 'mirror'):
+ return self.parent.mirror.x, self.parent.mirror.y
+ return False, False
+
def to_svg(self, colorscheme=Colorscheme.KiCad):
if not self.hide:
yield from TextMixin.to_svg(self, colorscheme.text)
@@ -396,28 +395,29 @@ class SymbolInstance:
sym = self.schematic.lookup_symbol(self.lib_name, self.lib_id)
- name = f'{sym.name}_0_1'
- if name in sym.global_units.get(1, {}):
- for elem in sym.global_units[1][name].graphical_elements:
- children += elem.to_svg(colorscheme)
+ units = [unit for unit in sym.units if unit.unit_global or unit.unit_index == self.unit]
- name = f'{sym.name}_{self.unit}_1'
- if name in sym.styles.get(1, {}):
- for elem in sym.styles[1][name].graphical_elements:
- children += elem.to_svg(colorscheme)
+ if self.reference in ('U18',):
+ print(self.reference, self.unit, self.at, self.mirror, units)
xform = f'translate({self.at.x:.3f} {self.at.y:.3f})'
- if rot:
- xform += f'rotate({-rot})'
- if self.mirror.x:
+ if self.mirror.y:
+ xform += f'scale(-1 -1)'
+ elif self.mirror.x:
xform += f'scale(-1 1)'
- if not self.mirror.y:
+ else:
xform += f'scale(1 -1)'
+ if rot:
+ xform += f'rotate({rot})'
+ children = [foo for unit in units for elem in unit.graphical_elements for foo in elem.to_svg(colorscheme)]
yield Tag('g', children=children, transform=xform, fill=colorscheme.fill, stroke=colorscheme.lines)
- for prop in self.properties:
- yield from prop.to_svg()
+ children = [foo for unit in units for pin in unit.pins for foo in pin.to_svg(colorscheme, self.mirror, rot)]
+ yield Tag('g', children=children, transform=xform, fill=colorscheme.fill, stroke=colorscheme.lines)
+
+ #for prop in self.properties:
+ # yield from prop.to_svg()
@sexp_type('path')
diff --git a/gerbonara/cad/kicad/symbols.py b/gerbonara/cad/kicad/symbols.py
index 94d30d3..5810d97 100644
--- a/gerbonara/cad/kicad/symbols.py
+++ b/gerbonara/cad/kicad/symbols.py
@@ -100,16 +100,20 @@ class Pin:
return (x1, y1), (x2, y2)
- def to_svg(self, colorscheme=Colorscheme.KiCad):
+ def to_svg(self, colorscheme, p_mirror, p_rotation):
if self.hide:
return
- x1, y1 = 0, 0
- x2, y2 = self.length, 0
+ if self.name.value in ('PA3', 'QA'):
+ print(self.name.value, self.at, p_rotation)
+ psx, psy = (-1 if p_mirror.x else 1), (-1 if p_mirror.y else 1)
+ x1, y1 = self.at.x, self.at.y
+ x2, y2 = self.at.x+self.length, self.at.y
xform = {'transform': f'translate({self.at.x:.3f} {self.at.y:.3f}) rotate({self.at.rotation})'}
style = {'stroke_width': 0.254, 'stroke': colorscheme.lines, 'stroke_linecap': 'round'}
yield Tag('path', **xform, **style, d=f'M 0 0 L {self.length:.3f} 0')
+ return
eps = 1
for tag in {
@@ -403,7 +407,6 @@ class Unit:
pins: List(Pin) = field(default_factory=list)
unit_name: Named(str) = None
_ : SEXP_END = None
- global_units: list = field(default_factory=list)
unit_global: Flag() = False
style_global: Flag() = False
demorgan_style: int = 1
@@ -420,7 +423,7 @@ class Unit:
raise FormatError(f'Unit name "{self.name}" does not match symbol name "{self.symbol.name}"')
self.demorgan_style = int(demorgan_style)
self.unit_index = int(unit_index)
- self.style_global = self._demorgan_style == 0
+ self.style_global = self.demorgan_style == 0
self.unit_global = self.unit_index == 0
@property
@@ -430,16 +433,10 @@ class Unit:
yield from self.polylines
yield from self.rectangles
yield from self.texts
- yield from self.pins
def __before_sexp__(self):
self.name = f'{self.symbol.name}_{self.unit_index}_{self.demorgan_style}'
- def __getattr__(self, name):
- if name.startswith('all_'):
- name = name[4:]
- return itertools.chain(getattr(self.global_units, name, []), getattr(self, name, []))
-
def pin_stacks(self):
stacks = defaultdict(lambda: set())
for pin in self.all_pins():
@@ -457,10 +454,8 @@ class Symbol:
in_bom: Named(YesNoAtom()) = True
on_board: Named(YesNoAtom()) = True
properties: List(Property) = field(default_factory=list)
- raw_units: List(Unit) = field(default_factory=list)
+ units: List(Unit) = field(default_factory=list)
_ : SEXP_END = None
- styles: {str: {str: Unit}} = None
- global_units: {str: {str: Unit}} = None
library = None
name: str = None
library_name: str = None
@@ -469,8 +464,6 @@ class Symbol:
self.library = parent
self.library_name, _, self.name = self.raw_name.rpartition(':')
- self.global_units = {}
- self.styles = {}
if self.extends:
self.in_bom = None
@@ -480,29 +473,7 @@ class Symbol:
if (prop := self.properties.get('ki_fp_filters')):
prop.value = prop.value.split() if prop.value else []
- for unit in self.raw_units:
- if unit.unit_global or unit.style_global:
- d = self.global_units.get(unit.demorgan_style, {})
- d[unit.name] = unit
- self.global_units[unit.demorgan_style] = d
-
- for other in self.raw_units:
- if other.unit_global or other.style_global or other == unit:
- continue
- if not (unit.unit_global or other.name == unit.name):
- continue
- if not (unit.style_global or other.demorgan_style == unit.demorgan_style):
- continue
- other.global_units.append(unit)
-
- else:
- d = self.styles.get(unit.demorgan_style, {})
- d[unit.name] = unit
- self.styles[unit.demorgan_style] = d
-
def __before_sexp__(self):
- self.raw_units = ([unit for style in self.global_units.values() for unit in style.values()] +
- [unit for style in self.styles.values() for unit in style.values()])
if (prop := self.properties.get('ki_fp_filters')):
if not isinstance(prop.value, str):
prop.value = ' '.join(prop.value)
@@ -521,30 +492,11 @@ class Symbol:
]):
self.properties[name] = Property(name=name, value=value, id=i, effects=TextEffect(hide=hide))
- def units(self, demorgan_style=None):
+ def resolve(self):
if self.extends:
- return self.library[self.extends].units(demorgan_style)
+ return self.library[self.extends]
else:
- return self.styles.get(demorgan_style or 'default', {})
-
- def get_center_rectangle(self, units):
- # return a polyline for the requested unit that is a rectangle
- # and is closest to the center
- candidates = {}
- # building a dict with floats as keys.. there needs to be a rule against that^^
- pl_rects = [i.to_polyline() for i in self.rectangles]
- pl_rects.extend(pl for pl in self.polylines if pl.is_rectangle())
- for pl in pl_rects:
- if pl.unit in units:
- # extract the center, calculate the distance to origin
- (x, y) = pl.get_center_of_boundingbox()
- dist = math.sqrt(x * x + y * y)
- candidates[dist] = pl
-
- if candidates:
- # sort the list return the first (smallest) item
- return candidates[sorted(candidates.keys())[0]]
- return None
+ return self
def is_graphic_symbol(self):
return self.extends is None and (
@@ -565,10 +517,6 @@ class Symbol:
pins[pin.number].add(pin)
return pins
- def __getattr__(self, name):
- if name.startswith('all_'):
- return itertools.chain(getattr(unit, name) for unit in self.raw_units)
-
def filter_pins(self, name=None, direction=None, electrical_type=None):
for pin in self.all_pins:
if name and not fnmatch(pin.name, name):
diff --git a/gerbonara/newstroke.py b/gerbonara/newstroke.py
index d0ed951..c2f3d55 100644
--- a/gerbonara/newstroke.py
+++ b/gerbonara/newstroke.py
@@ -31,12 +31,13 @@ class Newstroke:
def load(kls):
return kls()
- def render(self, text, size=1.0, x0=0, y0=0, rotation=0, h_align='left', v_align='bottom', space_width=DEFAULT_SPACE_WIDTH, char_gap=DEFAULT_CHAR_GAP, scale=(1, 1)):
+ def render(self, text, size=1.0, x0=0, y0=0, rotation=0, h_align='left', v_align='bottom', space_width=DEFAULT_SPACE_WIDTH, char_gap=DEFAULT_CHAR_GAP, scale=(1, 1), mirror=(False, False)):
text = unicodedata.normalize('NFC', text)
missing_glyph = self.glyphs['?']
sx, sy = scale
+ mx, my = mirror
x = 0
- if text in ('VDDA', 'PA9', 'VSS'):
+ if text in ('VDDA', 'PA9', 'VSS', 'FB3'):
print(text, x0, y0, rotation, h_align, v_align, scale)
if rotation >= 180:
@@ -44,6 +45,25 @@ class Newstroke:
h_align = {'left': 'right', 'right': 'left'}.get(h_align, h_align)
x0, y0 = -x0, y0
+ if scale == (1, 1) and rotation == 90:
+ rotation = 270
+ h_align = {'left': 'right', 'right': 'left'}.get(h_align, h_align)
+ v_align = {'top': 'bottom', 'bottom': 'top'}.get(v_align, v_align)
+
+ #if mx:
+ # x0 = -x0
+ # if rotation == 90:
+ # v_align = {'top': 'bottom', 'bottom': 'top'}.get(v_align, v_align)
+ # else:
+ # h_align = {'left': 'right', 'right': 'left'}.get(h_align, h_align)
+
+ if my:
+ y0 = -y0
+ if rotation == 0:
+ v_align = {'top': 'bottom', 'bottom': 'top'}.get(v_align, v_align)
+ else:
+ h_align = {'left': 'right', 'right': 'left'}.get(h_align, h_align)
+
x0, y0 = rotate_point(x0, y0, math.radians(-rotation))
alx, aly = 0, 0
@@ -77,7 +97,7 @@ class Newstroke:
x += glyph_w*size
- def render_svg(self, text, size=1.0, x0=0, y0=0, rotation=0, h_align='left', v_align='bottom', space_width=DEFAULT_SPACE_WIDTH, char_gap=DEFAULT_CHAR_GAP, scale=(1, -1), **svg_attrs):
+ def render_svg(self, text, size=1.0, x0=0, y0=0, rotation=0, h_align='left', v_align='bottom', space_width=DEFAULT_SPACE_WIDTH, char_gap=DEFAULT_CHAR_GAP, scale=(1, -1), mirror=(False, False), **svg_attrs):
if 'stroke_linecap' not in svg_attrs:
svg_attrs['stroke_linecap'] = 'round'
if 'stroke_linejoin' not in svg_attrs:
@@ -88,7 +108,7 @@ class Newstroke:
strokes = ['M ' + ' L '.join(f'{x:.3f} {y:.3f}' for x, y in stroke)
for stroke in self.render(text, size=size, x0=x0, y0=y0, rotation=rotation, h_align=h_align,
- v_align=v_align, space_width=space_width, char_gap=char_gap,
+ v_align=v_align, mirror=mirror, space_width=space_width, char_gap=char_gap,
scale=scale)]
return Tag('path', d=' '.join(strokes), **svg_attrs)