WIP
This commit is contained in:
parent
09c9d26728
commit
91b99a0452
4 changed files with 69 additions and 96 deletions
|
|
@ -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})',
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue