diff options
author | jaseg <git@jaseg.de> | 2022-06-20 11:21:42 +0200 |
---|---|---|
committer | jaseg <git@jaseg.de> | 2022-06-20 11:21:42 +0200 |
commit | c1cda48a4ccd8b272b2af9ed1f51d0fb2aa63c51 (patch) | |
tree | 55c9c67c97b99c0a53a994e7c9983a502a08584f /gerbolyze | |
parent | d09cf6ef3b6ea82f7ff67719527cd563569e0893 (diff) | |
download | gerbolyze-c1cda48a4ccd8b272b2af9ed1f51d0fb2aa63c51.tar.gz gerbolyze-c1cda48a4ccd8b272b2af9ed1f51d0fb2aa63c51.tar.bz2 gerbolyze-c1cda48a4ccd8b272b2af9ed1f51d0fb2aa63c51.zip |
protoboard: Add SMD patterns
Diffstat (limited to 'gerbolyze')
-rwxr-xr-x | gerbolyze/__init__.py | 6 | ||||
-rw-r--r-- | gerbolyze/protoboard.py | 83 |
2 files changed, 76 insertions, 13 deletions
diff --git a/gerbolyze/__init__.py b/gerbolyze/__init__.py index 3011833..0ba7b02 100755 --- a/gerbolyze/__init__.py +++ b/gerbolyze/__init__.py @@ -212,8 +212,9 @@ def empty_template(output_svg, size, force, copper_layers, no_default_layers, la @click.option('--vectorizer', help='passed through to svg-flatten') @click.option('--vectorizer-map', help='passed through to svg-flatten') @click.option('--exclude-groups', help='passed through to svg-flatten') +@click.option('--pattern-complete-tiles-only', is_flag=True, help='passed through to svg-flatten') def convert(input_svg, output_gerbers, is_zip, dilate, curve_tolerance, no_subtract, subtract, trace_space, vectorizer, - vectorizer_map, exclude_groups, separate_drill, naming_scheme): + vectorizer_map, exclude_groups, separate_drill, naming_scheme, pattern_complete_tiles_only): ''' Convert SVG file directly to gerbers. Unlike `gerbolyze paste`, this does not add the SVG's contents to existing gerbers. It allows you to directly create @@ -242,6 +243,7 @@ def convert(input_svg, output_gerbers, is_zip, dilate, curve_tolerance, no_subtr grb = svg_to_gerber(input_svg, trace_space=trace_space, vectorizer=vectorizer, vectorizer_map=vectorizer_map, exclude_groups=exclude_groups, curve_tolerance=curve_tolerance, only_groups=group_id, + pattern_complete_tiles_only=pattern_complete_tiles_only, outline_mode=(use == 'outline' or use == 'drill')) grb.original_path = Path() @@ -505,7 +507,7 @@ def svg_to_gerber(infile, outline_mode=False, **kwargs): ] for k, v in kwargs.items(): - if v is not None: + if v: args.append('--' + k.replace('_', '-')) if not isinstance(v, bool): args.append(str(v)) diff --git a/gerbolyze/protoboard.py b/gerbolyze/protoboard.py index 622c0e7..a4b6478 100644 --- a/gerbolyze/protoboard.py +++ b/gerbolyze/protoboard.py @@ -35,6 +35,17 @@ class CirclePattern(Pattern): def content(self): return f'<circle cx="{self.w/2}" cy="{self.h/2}" r="{self.d/2}"/>' +class RectPattern(Pattern): + def __init__(self, rw, rh, w, h): + self.rw, self.rh = rw, rh + self.w, self.h = w, h + + @property + def content(self): + x = (self.w - self.rw) / 2 + y = (self.h - self.rh) / 2 + return f'<rect x="{x}" y="{y}" width="{self.rw}" height="{self.rh}"/>' + make_layer = lambda layer_name, content: \ f'<g id="g-{layer_name.replace(" ", "-")}" inkscape:label="{layer_name}" inkscape:groupmode="layer">{svg_str(content)}</g>' @@ -59,9 +70,13 @@ svg_template = textwrap.dedent(''' ''').strip() class PatternProtoArea: - def __init__(self, pitch_x, pitch_y=None): + def __init__(self, pitch_x, pitch_y=None, border=None): self.pitch_x = pitch_x self.pitch_y = pitch_y or pitch_x + match border: + case None: self.border = (0, 0, 0, 0) + case (t, r, b, l): self.border = border + case _: self.border = (border, border, border, border) @property def pitch(self): @@ -70,6 +85,9 @@ class PatternProtoArea: return self.pitch_x def fit_rect(self, x, y, w, h, center=True): + t, r, b, l = self.border + x, y, w, h = (x+l), (y+t), (w-l-r), (h-t-b) + w_mod, h_mod = round((w + 5e-7) % self.pitch_x, 6), round((h + 5e-7) % self.pitch_y, 6) w_fit, h_fit = round(w - w_mod, 6), round(h - h_mod, 6) @@ -81,9 +99,24 @@ class PatternProtoArea: else: return x, y, w_fit, h_fit + def generate(self, x, y, w, h, defs=None, center=True, clip=''): + return {} + +class EmptyProtoArea: + def __init__(self, copper=False, border=None): + match border: + case None: self.border = (0, 0, 0, 0) + case (t, r, b, l): self.border = border + case _: self.border = (border, border, border, border) + + def generate(self, x, y, w, h, defs=None, center=True, clip=''): + t, r, b, l = self.border + x, y, w, h = x+l, y+t, w-l-r, h-t-b + return { 'top copper': f'<rect x="{x}" y="{y}" width="{w}" height="{h}" {clip} fill="black"/>' } + class THTProtoAreaCircles(PatternProtoArea): - def __init__(self, pad_dia=2.0, drill=1.0, pitch=2.54, sides='both', plated=True): - super().__init__(pitch) + def __init__(self, pad_dia=2.0, drill=1.0, pitch=2.54, sides='both', plated=True, border=None): + super().__init__(pitch, border=border) self.pad_dia = pad_dia self.drill = drill self.drill_pattern = CirclePattern(self.drill, self.pitch) @@ -92,7 +125,7 @@ class THTProtoAreaCircles(PatternProtoArea): self.plated = plated self.sides = sides - def generate(self, x, y, w, h, center=True, clip=''): + def generate(self, x, y, w, h, defs=None, center=True, clip=''): x, y, w, h = self.fit_rect(x, y, w, h, center) drill = 'plated drill' if self.plated else 'nonplated drill' @@ -106,14 +139,32 @@ class THTProtoAreaCircles(PatternProtoArea): if self.sides in ('top', 'both'): d['top copper'] = make_rect(pad_id, x, y, w, h, clip) + d['top mask'] = make_rect(pad_id, x, y, w, h, clip) if self.sides in ('bottom', 'both'): d['bottom copper'] = make_rect(pad_id, x, y, w, h, clip) + d['bottom mask'] = make_rect(pad_id, x, y, w, h, clip) return d def __repr__(self): return f'THTCircles(d={self.pad_dia}, h={self.drill}, p={self.pitch}, sides={self.sides}, plated={self.plated})' +class SMDProtoAreaRectangles(PatternProtoArea): + def __init__(self, pitch_x, pitch_y, w=None, h=None, border=None): + super().__init__(pitch_x, pitch_y, border=border) + w = w or pitch_x - 0.15 + h = h or pitch_y - 0.15 + self.w, self.h = w, h + self.pad_pattern = RectPattern(w, h, pitch_x, pitch_y) + self.patterns = [self.pad_pattern] + + def generate(self, x, y, w, h, defs=None, center=True, clip=''): + x, y, w, h = self.fit_rect(x, y, w, h, center) + pad_id = str(uuid.uuid4()) + return {'defs': [self.pad_pattern.svg_def(pad_id, x, y)], + 'top copper': make_rect(pad_id, x, y, w, h, clip), + 'top mask': make_rect(pad_id, x, y, w, h, clip)} + LAYERS = [ 'top paste', 'top silk', @@ -129,10 +180,15 @@ LAYERS = [ ] class ProtoBoard: - def __init__(self, defs, expr, mounting_holes=None): + def __init__(self, defs, expr, mounting_holes=None, border=None, center=True): self.defs = eval_defs(defs) self.layout = parse_layout(expr) self.mounting_holes = mounting_holes + self.center = center + match border: + case None: self.border = (0, 0, 0, 0) + case (t, r, b, l): self.border = border + case _: self.border = (border, border, border, border) def generate(self, w, h): out = {l: [] for l in LAYERS} @@ -152,7 +208,8 @@ class ProtoBoard: f'<circle cx="{w-o}" cy="{h-o}" r="{d/2}"/>', f'<circle cx="{o}" cy="{h-o}" r="{d/2}"/>' ]) - for layer_dict in self.layout.generate(0, 0, w, h, self.defs, clip): + t, r, b, l = self.border + for layer_dict in self.layout.generate(l, t, w-l-r, h-t-b, self.defs, self.center, clip): for l in LAYERS: if l in layer_dict: out[l].append(layer_dict[l]) @@ -193,13 +250,13 @@ class PropLayout: if len(content) != len(proportions): raise ValueError('proportions and content must have same length') - def generate(self, x, y, w, h, defs, clip=''): + def generate(self, x, y, w, h, defs, center=True, clip=''): for (c_x, c_y, c_w, c_h), child in self.layout_2d(x, y, w, h): if isinstance(child, str): - yield defs[child].generate(c_x, c_y, c_w, c_h, defs, clip) + yield defs[child].generate(c_x, c_y, c_w, c_h, defs, center, clip) else: - yield from child.generate(c_x, c_y, c_w, c_h, defs, clip) + yield from child.generate(c_x, c_y, c_w, c_h, defs, center, clip) def layout_2d(self, x, y, w, h): for l, child in zip(self.layout(w if self.direction == 'h' else h), self.content): @@ -298,7 +355,9 @@ def parse_layout(expr): raise SyntaxError('Invalid layout expression') from e PROTO_AREA_TYPES = { - 'THTCircles': THTProtoAreaCircles + 'THTCircles': THTProtoAreaCircles, + 'SMDPads': SMDProtoAreaRectangles, + 'Empty': EmptyProtoArea, } def eval_defs(defs): @@ -358,5 +417,7 @@ if __name__ == '__main__': # print(line, '->', eval_defs(line)) # print() # print('===== Proto board =====') - b = ProtoBoard('tht = THTCircles(); tht_small = THTCircles(pad_dia=1.0, drill=0.6, pitch=1.27)', 'tht@1in|(tht_small@2/tht@1)', mounting_holes=(3.2, 5.0, 5.0)) + #b = ProtoBoard('tht = THTCircles(); tht_small = THTCircles(pad_dia=1.0, drill=0.6, pitch=1.27)', + # 'tht@1in|(tht_small@2/tht@1)', mounting_holes=(3.2, 5.0, 5.0), border=2, center=False) + b = ProtoBoard('smd = SMDPads(0.8, 1.27)', 'smd', mounting_holes=(3.2, 5.0, 5.0), border=2) print(b.generate(80, 60)) |