From 4bd1097fc1e161e1c0ecfdfa2be271dfe6f69dfa Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 15 Apr 2023 22:12:45 +0200 Subject: [PATCH 001/248] Bump minimum Python version to 3.10 --- .gitlab-ci.yml | 20 -------------- gerbonara/cad/kicad/base_types.py | 43 ++++++++++++++++++++++++++----- setup.py | 5 ++-- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7a35f14..c47d156 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -58,26 +58,6 @@ test:ubuntu2204: paths: - gerbonara_test_failures/* -test:ubuntu2004: - stage: test - image: "registry.gitlab.com/gerbolyze/build-containers/ubuntu:20.04" - script: - - python3 -m pip install pytest beautifulsoup4 pillow numpy slugify lxml click scipy - - python3 -m pytest -o 'testpaths=gerbonara/tests' -o 'norecursedirs=*' - dependencies: - - build:archlinux - cache: - key: test-image-cache - paths: - - gerbonara/tests/image_cache/*.svg - - gerbonara/tests/image_cache/*.png - artifacts: - name: "gerbolyze-$CI_COMMIT_REF_NAME-gerbonara" - when: on_failure - paths: - - gerbonara_test_failures/* - - docs:archlinux: stage: test image: "registry.gitlab.com/gerbolyze/build-containers/archlinux:latest" diff --git a/gerbonara/cad/kicad/base_types.py b/gerbonara/cad/kicad/base_types.py index 840b9d5..54437f5 100644 --- a/gerbonara/cad/kicad/base_types.py +++ b/gerbonara/cad/kicad/base_types.py @@ -5,6 +5,8 @@ import time from dataclasses import field import math import uuid +from contextlib import contextmanager +from itertools import cycle @sexp_type('color') @@ -20,14 +22,43 @@ class Stroke: width: Named(float) = 0.254 type: Named(AtomChoice(Atom.dash, Atom.dot, Atom.dash_dot_dot, Atom.dash_dot, Atom.default, Atom.solid)) = Atom.default color: Color = None + - @property - def width_mil(self): - return mm_to_mil(self.width) +class Dasher: + def __init__(self, stroke): + self.width = stroke.width + gap = 4*stroke.width + dot = 0 + gap = 11*stroke.width + self.pattern = { + Atom.dash: [dash, gap], + Atom.dot: [dot, gap], + Atom.dash_dot_dot: [dash, gap, dot, gap, dot, gap], + Atom.dash_dot: [dash, gap, dot, gap], + Atom.default: [1e99], + Atom.solid: [1e99]}[stroke.type] + self.start_x, self.start_y = None, None + self.cur_x, self.cur_y = None, None + self.segments = [] + + def move(self, x, y): + self.start_x, self.start_y = x, y + + def line(x, y): + if x is None or y is None: + raise ValueError('line() called before move()') + self.segments.append((self.cur_x, self.cur_y, x, y)) + cur_x, cur_y = x, y + + def close(): + self.segments.append((self.cur_x, self.cur_y, start_x, start_y)) + + def __iter__(self): + offset = 0 + for length, stroked in cycle(zip(self.pattern, cycle([True, False]))): + for x1, y1, x2, y2 in segments: + segment_length = math.dist((x1, y1), (x2, y2)) - @width_mil.setter - def width_mil(self, value): - self.width = mil_to_mm(value) @sexp_type('xy') diff --git a/setup.py b/setup.py index b6e2172..8af6447 100644 --- a/setup.py +++ b/setup.py @@ -49,9 +49,8 @@ setup( 'Operating System :: POSIX :: Linux', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', 'Topic :: Artistic Software', 'Topic :: Multimedia :: Graphics', 'Topic :: Printing', @@ -62,5 +61,5 @@ setup( 'Typing :: Typed', ], keywords='gerber excellon pcb', - python_requires='>=3.8', + python_requires='>=3.10', ) From 5ea1491704ce9fb668f1eefc79fbcbcdcf98e6d0 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 15 Apr 2023 22:14:59 +0200 Subject: [PATCH 002/248] Bump version to v1.0.1 --- gerbonara/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gerbonara/__init__.py b/gerbonara/__init__.py index ea9a053..c4d1b95 100644 --- a/gerbonara/__init__.py +++ b/gerbonara/__init__.py @@ -31,4 +31,4 @@ from .ipc356 import Netlist from .layers import LayerStack from .utils import MM, Inch -__version__ = '1.0.0' +__version__ = '1.0.1' From 5c7bfb2744429a3ea311c68fb9fa269f155c5ed9 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 15 Apr 2023 22:26:38 +0200 Subject: [PATCH 003/248] Fix protoserve bugs in obround pads --- gerbonara/cad/primitives.py | 8 ++++---- gerbonara/cad/protoboard.py | 3 ++- gerbonara/layers.py | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/gerbonara/cad/primitives.py b/gerbonara/cad/primitives.py index abce1a4..80373c6 100644 --- a/gerbonara/cad/primitives.py +++ b/gerbonara/cad/primitives.py @@ -10,7 +10,7 @@ from collections import defaultdict from ..utils import LengthUnit, MM, rotate_point, svg_arc, sum_bounds, bbox_intersect, Tag from ..layers import LayerStack from ..graphic_objects import Line, Arc, Flash -from ..apertures import Aperture, CircleAperture, RectangleAperture, ExcellonTool +from ..apertures import Aperture, CircleAperture, ObroundAperture, RectangleAperture, ExcellonTool from ..newstroke import Newstroke @@ -385,9 +385,9 @@ class THTPad(Pad): @classmethod def obround(kls, x, y, hole_dia, w, h, rotation=0, mask_expansion=0.0, paste_expanson=0.0, paste=True, plated=True, unit=MM): - ap_c = CircleAperture(dia, unit=unit) - ap_m = CircleAperture(dia+2*mask_expansion, unit=unit) - ap_p = CircleAperture(dia+2*paste_expansion, unit=unit) if paste else None + ap_c = ObroundAperture(w, h, unit=unit) + ap_m = ObroundAperture(w+2*mask_expansion, h+2*mask_expansion, unit=unit) + ap_p = ObroundAperture(w, h, unit=unit) if paste else None pad = SMDPad(0, 0, side='top', copper_aperture=ap_c, mask_aperture=ap_m, paste_aperture=ap_p, unit=unit) return kls(x, y, hole_dia, pad, rotation=rotation, plated=plated, unit=unit) diff --git a/gerbonara/cad/protoboard.py b/gerbonara/cad/protoboard.py index b61a0ed..3c523bc 100644 --- a/gerbonara/cad/protoboard.py +++ b/gerbonara/cad/protoboard.py @@ -497,7 +497,8 @@ def eval_value(value, total_length=None): def _demo(): - pattern1 = PatternProtoArea(2.54, obj=THTPad.circle(0, 0, 0.9, 1.8, paste=False)) + #pattern1 = PatternProtoArea(2.54, obj=THTPad.circle(0, 0, 0.9, 1.8, paste=False)) + pattern1 = PatternProtoArea(2.54, 3.84, obj=THTPad.obround(0, 0, 0.9, 1.8, 2.5, paste=False)) pattern2 = PatternProtoArea(1.2, 2.0, obj=SMDPad.rect(0, 0, 1.0, 1.8, paste=False)) pattern3 = PatternProtoArea(2.54, 1.27, obj=SMDPad.rect(0, 0, 2.3, 1.0, paste=False)) #pattern3 = EmptyProtoArea(copper_fill=True) diff --git a/gerbonara/layers.py b/gerbonara/layers.py index 10101dc..bb2d635 100644 --- a/gerbonara/layers.py +++ b/gerbonara/layers.py @@ -816,11 +816,11 @@ class LayerStack: for i, layer in enumerate(self.drill_layers): layers.append(tag('g', list(layer.instance.svg_objects(svg_unit=svg_unit, fg='white', bg='black', tag=Tag)), - id=f'g-drill-{i}', filter=f'url(#f-drill)', **stroke_attrs, **inkscape_attrs(f'drill-{i}'))) + id=f'l-drill-{i}', filter=f'url(#f-drill)', **stroke_attrs, **inkscape_attrs(f'drill-{i}'))) if self.outline: layers.append(tag('g', list(self.outline.instance.svg_objects(svg_unit=svg_unit, fg='white', bg='black', tag=Tag)), - id=f'g-outline-{i}', **stroke_attrs, **inkscape_attrs(f'outline-{i}'))) + id=f'l-mechanical-outline', **stroke_attrs, **inkscape_attrs(f'outline'))) layer_group = tag('g', layers, transform=f'translate(0 {bounds[0][1] + bounds[1][1]}) scale(1 -1)') tags = [tag('defs', filter_defs), layer_group] From 2eefb9cc7dc341cd7c350392a2e5b43f97669969 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 15 Apr 2023 22:31:09 +0200 Subject: [PATCH 004/248] ci: Clone kicad libraries for tests --- .gitlab-ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c47d156..1cb51ee 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,7 +25,9 @@ test:archlinux: stage: test image: "registry.gitlab.com/gerbolyze/build-containers/archlinux:latest" script: - - pytest -o 'testpaths=gerbonara/tests' -o 'norecursedirs=*' + - git clone --depth 1 https://gitlab.com/kicad/libraries/kicad-symbols + - git clone --depth 1 https://gitlab.com/kicad/libraries/kicad-footprints + - env KICAD_SYMBOLS=kicad-symbols KICAD_FOOTPRINTS=kicad-footprints pytest -o 'testpaths=gerbonara/tests' -o 'norecursedirs=*' dependencies: - build:archlinux cache: @@ -44,7 +46,9 @@ test:ubuntu2204: image: "registry.gitlab.com/gerbolyze/build-containers/ubuntu:22.04" script: - python3 -m pip install pytest beautifulsoup4 pillow numpy slugify lxml click scipy - - python3 -m pytest -o 'testpaths=gerbonara/tests' -o 'norecursedirs=*' + - git clone --depth 1 https://gitlab.com/kicad/libraries/kicad-symbols + - git clone --depth 1 https://gitlab.com/kicad/libraries/kicad-footprints + - env KICAD_SYMBOLS=kicad-symbols KICAD_FOOTPRINTS=kicad-footprints python3 -m pytest -o 'testpaths=gerbonara/tests' -o 'norecursedirs=*' dependencies: - build:archlinux cache: From 390579850b169dfdcd8b490d19e1eb3ac64140ac Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 15 Apr 2023 23:23:27 +0200 Subject: [PATCH 005/248] Fix dasher --- gerbonara/cad/kicad/base_types.py | 73 +++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/gerbonara/cad/kicad/base_types.py b/gerbonara/cad/kicad/base_types.py index 54437f5..1b3a327 100644 --- a/gerbonara/cad/kicad/base_types.py +++ b/gerbonara/cad/kicad/base_types.py @@ -29,7 +29,7 @@ class Dasher: self.width = stroke.width gap = 4*stroke.width dot = 0 - gap = 11*stroke.width + dash = 11*stroke.width self.pattern = { Atom.dash: [dash, gap], Atom.dot: [dot, gap], @@ -42,23 +42,63 @@ class Dasher: self.segments = [] def move(self, x, y): - self.start_x, self.start_y = x, y + if self.cur_x is None: + self.start_x, self.start_y = x, y + self.cur_x, self.cur_y = x, y - def line(x, y): + def line(self, x, y): if x is None or y is None: raise ValueError('line() called before move()') self.segments.append((self.cur_x, self.cur_y, x, y)) - cur_x, cur_y = x, y + self.cur_x, self.cur_y = x, y - def close(): - self.segments.append((self.cur_x, self.cur_y, start_x, start_y)) + def close(self): + self.segments.append((self.cur_x, self.cur_y, self.start_x, self.start_y)) + self.cur_x, self.cur_y = None, None + + @staticmethod + def _interpolate(x1, y1, x2, y2, length): + dx, dy = x2-x1, y2-y1 + total = math.hypot(dx, dy) + if total == 0: + return x2, y2 + frac = length / total + return x1 + dx*frac, y1 + dy*frac def __iter__(self): - offset = 0 + it = iter(self.segments) + segment_remaining, segment_pos = 0, 0 for length, stroked in cycle(zip(self.pattern, cycle([True, False]))): - for x1, y1, x2, y2 in segments: - segment_length = math.dist((x1, y1), (x2, y2)) + length = max(1e-12, length) + import sys + print('new dash', length, stroked, file=sys.stderr) + while length > 0: + print(f'{length=} {segment_remaining=}', file=sys.stderr) + if segment_remaining == 0: + try: + x1, y1, x2, y2 = next(it) + except StopIteration: + return + dx, dy = x2-x1, y2-y1 + lx, ly = x1, y1 + segment_remaining = math.hypot(dx, dy) + segment_pos = 0 + print('new segment', x1, y1, x2, y2, segment_remaining, file=sys.stderr) + if segment_remaining > length: + segment_pos += length + ix, iy = self._interpolate(x1, y1, x2, y2, segment_pos) + segment_remaining -= length + if stroked: + yield lx, ly, ix, iy + lx, ly = ix, iy + break + + else: + length -= segment_remaining + segment_remaining = 0 + if stroked: + yield lx, ly, x2, y2 @sexp_type('xy') @@ -151,3 +191,18 @@ class EditTime: def bump(self): self.value = time.time() +if __name__ == '__main__': + d = Dasher(Stroke(0.01, Atom.dash_dot_dot)) + #d = Dasher(Stroke(0.01, Atom.solid)) + d.move(1, 1) + d.line(1, 2) + d.line(3, 2) + d.line(3, 1) + d.close() + + print('') + print('') + print('') + for x1, y1, x2, y2 in d: + print(f'') + print('') From 0421e037177b3695947ac996e95f151dbcf32aa5 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 15 Apr 2023 23:24:11 +0200 Subject: [PATCH 006/248] Bump version to v1.0.2 --- gerbonara/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gerbonara/__init__.py b/gerbonara/__init__.py index c4d1b95..ef055c6 100644 --- a/gerbonara/__init__.py +++ b/gerbonara/__init__.py @@ -31,4 +31,4 @@ from .ipc356 import Netlist from .layers import LayerStack from .utils import MM, Inch -__version__ = '1.0.1' +__version__ = '1.0.2' From 263033c9bdecf8c82027f6475c863d818f499914 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sun, 16 Apr 2023 13:42:14 +0200 Subject: [PATCH 007/248] protoserve: Remove incorrect SMD pad shape entry --- gerbonara/cad/protoserve_data/protoserve.html | 1 - 1 file changed, 1 deletion(-) diff --git a/gerbonara/cad/protoserve_data/protoserve.html b/gerbonara/cad/protoserve_data/protoserve.html index 3afeb5c..215513c 100644 --- a/gerbonara/cad/protoserve_data/protoserve.html +++ b/gerbonara/cad/protoserve_data/protoserve.html @@ -395,7 +395,6 @@ input {