Schematics WIP
This commit is contained in:
parent
b69e9fded4
commit
a39af853c8
3 changed files with 125 additions and 56 deletions
|
|
@ -270,6 +270,20 @@ class Justify:
|
|||
v: AtomChoice(Atom.top, Atom.bottom) = None
|
||||
mirror: Flag() = False
|
||||
|
||||
@property
|
||||
def h_str(self):
|
||||
if self.h is None:
|
||||
return 'center'
|
||||
else:
|
||||
return str(self.h)
|
||||
|
||||
@property
|
||||
def v_str(self):
|
||||
if self.v is None:
|
||||
return 'middle'
|
||||
else:
|
||||
return str(self.v)
|
||||
|
||||
|
||||
@sexp_type('effects')
|
||||
class TextEffect:
|
||||
|
|
@ -277,6 +291,7 @@ class TextEffect:
|
|||
hide: Flag() = False
|
||||
justify: OmitDefault(Justify) = field(default_factory=Justify)
|
||||
|
||||
|
||||
class TextMixin:
|
||||
@property
|
||||
def size(self):
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ class Pin:
|
|||
name: Rename(StyledText) = field(default_factory=StyledText)
|
||||
number: Rename(StyledText) = field(default_factory=StyledText)
|
||||
alternates: List(AltFunction) = field(default_factory=list)
|
||||
_: SEXP_END = None
|
||||
unit: object = None
|
||||
|
||||
def __after_parse__(self, parent=None):
|
||||
self.unit = parent
|
||||
|
||||
@property
|
||||
def direction(self):
|
||||
|
|
@ -96,6 +101,9 @@ class Pin:
|
|||
return (x1, y1), (x2, y2)
|
||||
|
||||
def to_svg(self, colorscheme=Colorscheme.KiCad):
|
||||
if self.hide:
|
||||
return
|
||||
|
||||
x1, y1 = 0, 0
|
||||
x2, y2 = self.length, 0
|
||||
xform = {'transform': f'translate({self.at.x:.3f} {self.at.y:.3f}) rotate({self.at.rotation})'}
|
||||
|
|
@ -109,68 +117,52 @@ class Pin:
|
|||
'inverted': [
|
||||
Tag('circle', **xform, **style, cx=x2-eps/3-0.2, cy=y2, r=eps/3)],
|
||||
'clock': [
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps/2} L {x2+eps/2} {y2} L {x2} {y2+eps/2}')], # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps/2} L {x2+eps/2} {y2} L {x2} {y2+eps/2}')],
|
||||
'inverted_clock': [
|
||||
Tag('circle', **xform, **style, cx=x2-eps/3-0.2, cy=y2, r=eps/3),
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps/2} L {x2+eps/2} {y2} L {x2} {y2+eps/2}')], # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps/2} L {x2+eps/2} {y2} L {x2} {y2+eps/2}')],
|
||||
'input_low': [
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2} L {x2-eps} {y2-eps} L {x2-eps} {y2}')], # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2} L {x2-eps} {y2-eps} L {x2-eps} {y2}')],
|
||||
'clock_low': [
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2} L {x2-eps} {y2-eps} L {x2-eps} {y2}'), # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps/2} L {x2+eps/2} {y2} L {x2} {y2+eps/2}')], # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2} L {x2-eps} {y2-eps} L {x2-eps} {y2}'),
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps/2} L {x2+eps/2} {y2} L {x2} {y2+eps/2}')],
|
||||
'output_low': [
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps} L {x2-eps} {y2}')], # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps} L {x2-eps} {y2}')],
|
||||
'edge_clock_high': [
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2} L {x2-eps} {y2-eps} L {x2-eps} {y2}'), # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps/2} L {x2+eps/2} {y2} L {x2} {y2+eps/2}')], # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2} L {x2-eps} {y2-eps} L {x2-eps} {y2}'),
|
||||
Tag('path', **xform, **style, d=f'M {x2} {y2-eps/2} L {x2+eps/2} {y2} L {x2} {y2+eps/2}')],
|
||||
'non_logic': [
|
||||
Tag('path', **xform, **style, d=f'M {x2-eps/2} {y2-eps/2} L {x2+eps/2} {y2+eps/2}'), # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2-eps/2} {y2+eps/2} L {x2+eps/2} {y2-eps/2}')], # NOQA: E501
|
||||
Tag('path', **xform, **style, d=f'M {x2-eps/2} {y2-eps/2} L {x2+eps/2} {y2+eps/2}'),
|
||||
Tag('path', **xform, **style, d=f'M {x2-eps/2} {y2+eps/2} L {x2+eps/2} {y2-eps/2}')],
|
||||
# FIXME...
|
||||
}.get(self.style, []):
|
||||
yield tag
|
||||
|
||||
if self.at.rotation in (90, 270):
|
||||
t_rot = 90
|
||||
else:
|
||||
t_rot = 0
|
||||
|
||||
size = self.name.effects.font.size.y or 1.27
|
||||
font = Newstroke.load()
|
||||
strokes = list(font.render(self.name.value, size=size))
|
||||
min_x = min(x for st in strokes for x, y in st) if strokes else 0
|
||||
min_y = min(y for st in strokes for x, y in st) if strokes else 0
|
||||
max_x = max(x for st in strokes for x, y in st) if strokes else 0
|
||||
max_y = max(y for st in strokes for x, y in st) if strokes else 0
|
||||
w = max_x - min_x
|
||||
h = max_y - min_y
|
||||
if self.name.value != '~' and not self.unit.symbol.pin_names.hide:
|
||||
yield font.render_svg(self.name.value,
|
||||
size=self.name.effects.font.size.y or 1.27,
|
||||
x0=self.length + 0.2,
|
||||
y0=0,
|
||||
h_align='left',
|
||||
v_align='middle',
|
||||
rotation=self.at.rotation,
|
||||
stroke=colorscheme.text,
|
||||
transform=f'translate({self.at.x:.3f} {self.at.y:.3f})',
|
||||
)
|
||||
|
||||
if self.at.rotation == 0:
|
||||
offx = -min_x + self.length + 0.2
|
||||
offy = h/2
|
||||
elif self.at.rotation == 180:
|
||||
offx = min_x - self.length - 0.2 - w
|
||||
offy = h/2
|
||||
elif self.at.rotation == 90:
|
||||
offx = -h/2
|
||||
offy = min_x - self.length - 0.2 - w
|
||||
elif self.at.rotation == 270:
|
||||
offx = -h/2
|
||||
offy = -min_x + self.length + 0.2
|
||||
else:
|
||||
raise ValueError(f'Invalid pin rotation {self.at.rotation}')
|
||||
if self.number.value != '~' and not self.unit.symbol.pin_numbers.hide:
|
||||
yield font.render_svg(self.number.value,
|
||||
size=self.number.effects.font.size.y or 1.27,
|
||||
x0=self.length-0.2,
|
||||
y0=0.4,
|
||||
h_align='right',
|
||||
v_align='bottom',
|
||||
rotation=self.at.rotation,
|
||||
stroke=colorscheme.text,
|
||||
transform=f'translate({self.at.x:.3f} {self.at.y:.3f})',
|
||||
)
|
||||
|
||||
d = []
|
||||
for stroke in strokes:
|
||||
points = []
|
||||
for x, y in stroke:
|
||||
x, y = x+offx, y+offy
|
||||
x, y = rotate_point(x, y, math.radians(self.at.rotation or 0))
|
||||
x, y = x+self.at.x, y+self.at.y
|
||||
points.append(f'{x:.3f} {y:.3f}')
|
||||
d.append('M '+ ' L '.join(points) + ' ')
|
||||
yield Tag('path', d=' '.join(d), fill='none', stroke=colorscheme.text, stroke_width='0.254', stroke_linecap='round', stroke_linejoin='round')
|
||||
print('name', self.name.value)
|
||||
|
||||
|
||||
@sexp_type('fill')
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ import unicodedata
|
|||
import re
|
||||
import ast
|
||||
from functools import lru_cache
|
||||
import math
|
||||
from importlib.resources import files
|
||||
|
||||
from . import data
|
||||
from .utils import rotate_point, Tag
|
||||
|
||||
|
||||
STROKE_FONT_SCALE = 1/21
|
||||
|
|
@ -29,7 +31,68 @@ class Newstroke:
|
|||
def load(kls):
|
||||
return kls()
|
||||
|
||||
def render(self, text, size=1.0, space_width=DEFAULT_SPACE_WIDTH, char_gap=DEFAULT_CHAR_GAP):
|
||||
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)):
|
||||
text = unicodedata.normalize('NFC', text)
|
||||
missing_glyph = self.glyphs['?']
|
||||
sx, sy = scale
|
||||
x = 0
|
||||
if text in ('VDDA', 'PA9', 'VSS'):
|
||||
print(text, x0, y0, rotation, h_align, v_align, scale)
|
||||
|
||||
if rotation >= 180:
|
||||
rotation -= 180
|
||||
h_align = {'left': 'right', 'right': 'left'}.get(h_align, h_align)
|
||||
x0, y0 = -x0, y0
|
||||
|
||||
x0, y0 = rotate_point(x0, y0, math.radians(-rotation))
|
||||
|
||||
alx, aly = 0, 0
|
||||
if h_align != 'left':
|
||||
(minx, miny), (maxx, maxy) = bbox = self.bounding_box(text, size, space_width, char_gap)
|
||||
w = maxx - minx
|
||||
if h_align == 'right':
|
||||
alx = -w
|
||||
elif h_align == 'center':
|
||||
alx = -w/2
|
||||
else:
|
||||
raise ValueError(f'Invalid h_align value "{h_align}"')
|
||||
|
||||
if v_align == 'top':
|
||||
aly = -1.2*size
|
||||
elif v_align == 'middle':
|
||||
aly = -1.2*size/2
|
||||
elif v_align != 'bottom':
|
||||
raise ValueError(f'Invalid v_align value "{v_align}"')
|
||||
|
||||
for c in text:
|
||||
if c == ' ':
|
||||
x += space_width*size
|
||||
continue
|
||||
|
||||
width, strokes = self.glyphs.get(c, missing_glyph)
|
||||
glyph_w = max(width, max(x for st in strokes for x, _y in st))
|
||||
|
||||
for st in strokes:
|
||||
yield self.transform_stroke(st, translate=(x0, y0), offset=(x+alx, aly), rotation=math.radians(-rotation), scale=(sx*size, sy*size))
|
||||
|
||||
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, **svg_attrs):
|
||||
if 'stroke_linecap' not in svg_attrs:
|
||||
svg_attrs['stroke_linecap'] = 'round'
|
||||
if 'stroke_linejoin' not in svg_attrs:
|
||||
svg_attrs['stroke_linejoin'] = 'round'
|
||||
if 'stroke_width' not in svg_attrs:
|
||||
svg_attrs['stroke_width'] = f'{0.2*size:.3f}'
|
||||
svg_attrs['fill'] = 'none'
|
||||
|
||||
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,
|
||||
scale=(1, -1))]
|
||||
return Tag('path', d=' '.join(strokes), **svg_attrs)
|
||||
|
||||
def bounding_box(self, text, size=1.0, space_width=DEFAULT_SPACE_WIDTH, char_gap=DEFAULT_CHAR_GAP):
|
||||
text = unicodedata.normalize('NFC', text)
|
||||
missing_glyph = self.glyphs['?']
|
||||
x = 0
|
||||
|
|
@ -40,17 +103,16 @@ class Newstroke:
|
|||
|
||||
width, strokes = self.glyphs.get(c, missing_glyph)
|
||||
glyph_w = max(width, max(x for st in strokes for x, _y in st))
|
||||
|
||||
for st in strokes:
|
||||
yield self.transform_stroke(st, translate=(x, 0), scale=(size, size))
|
||||
|
||||
x += glyph_w*size
|
||||
|
||||
return (0, -0.2*size), (x, 1.2*size)
|
||||
|
||||
@classmethod
|
||||
def transform_stroke(kls, stroke, translate, scale):
|
||||
dx, dy = translate
|
||||
def transform_stroke(kls, stroke, translate, offset, scale, rotation=0):
|
||||
x0, y0 = translate
|
||||
sx, sy = scale
|
||||
return [(x*sx+dx, y*sy+dy) for x, y in stroke]
|
||||
dx, dy = offset
|
||||
return [rotate_point(x*sx+dx+x0, y*sy+dy+y0, rotation, x0, y0) for x, y in stroke]
|
||||
|
||||
|
||||
def load_font(self, newstroke_cpp):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue