From 6f876edd09d9b81649691e529f85653f14b8fd1c Mon Sep 17 00:00:00 2001 From: Hamilton Kibbe Date: Tue, 22 Dec 2015 02:45:48 -0500 Subject: Add PCB interface this incorporates some of @chintal's layers.py changes PCB.from_directory() simplifies loading of multiple gerbers the PCB() class should be pretty helpful going forward... the context classes could use some cleaning up, although I'd like to wait until the freecad stuff gets merged, that way we can try to refactor the context base to support more use cases --- gerber/pcb.py | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 gerber/pcb.py (limited to 'gerber/pcb.py') diff --git a/gerber/pcb.py b/gerber/pcb.py new file mode 100644 index 0000000..990a05c --- /dev/null +++ b/gerber/pcb.py @@ -0,0 +1,107 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# copyright 2015 Hamilton Kibbe +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +from .exceptions import ParseError +from .layers import PCBLayer, LayerSet, sort_layers +from .common import read as gerber_read +from .utils import listdir +from .render import theme + + +class PCB(object): + + @classmethod + def from_directory(cls, directory, board_name=None, verbose=False): + layers = [] + names = set() + # Validate + directory = os.path.abspath(directory) + if not os.path.isdir(directory): + raise TypeError('{} is not a directory.'.format(directory)) + # Load gerber files + for filename in listdir(directory, True, True): + try: + camfile = gerber_read(os.path.join(directory, filename)) + layer = PCBLayer.from_gerber(camfile) + layers.append(layer) + names.add(os.path.splitext(filename)[0]) + if verbose: + print('Added {} layer <{}>'.format(layer.layer_class, filename)) + except ParseError: + if verbose: + print('Skipping file {}'.format(filename)) + # Try to guess board name + if board_name is None: + if len(names) == 1: + board_name = names.pop() + else: + board_name = os.path.basename(directory) + # Return PCB + return cls(layers, board_name) + + def __init__(self, layers, name=None): + self.layers = sort_layers(layers) + self.name = name + self._theme = theme.THEMES['Default'] + self.theme = self._theme + + def __len__(self): + return len(self.layers) + + @property + def theme(self): + return self._theme + + @theme.setter + def theme(self, theme): + self._theme = theme + for layer in self.layers: + layer.settings = theme[layer.layer_class] + + @property + def top_layers(self): + board_layers = [l for l in reversed(self.layers) if l.layer_class in ('topsilk', 'topmask', 'top')] + drill_layers = [l for l in self.drill_layers if 'top' in l.layers] + return board_layers + drill_layers + + @property + def bottom_layers(self): + board_layers = [l for l in self.layers if l.layer_class in ('bottomsilk', 'bottommask', 'bottom')] + drill_layers = [l for l in self.drill_layers if 'bottom' in l.layers] + return board_layers + drill_layers + + @property + def drill_layers(self): + return [l for l in self.layers if l.layer_class == 'drill'] + + @property + def layer_count(self): + """ Number of *COPPER* layers + """ + return len([l for l in self.layers if l.layer_class in ('top', 'bottom', 'internal')]) + + @property + def board_bounds(self): + for layer in self.layers: + if layer.layer_class == 'outline': + return layer.bounds + for layer in self.layers: + if layer.layer_class == 'top': + return layer.bounds + -- cgit From 6a005436b475e3517fd6a583473b60e601bcc661 Mon Sep 17 00:00:00 2001 From: Hamilton Kibbe Date: Fri, 1 Jan 2016 12:25:38 -0500 Subject: Refactor a little pulled all rendering stuff out of the pcb/layer objects --- gerber/pcb.py | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'gerber/pcb.py') diff --git a/gerber/pcb.py b/gerber/pcb.py index 990a05c..0518dd4 100644 --- a/gerber/pcb.py +++ b/gerber/pcb.py @@ -21,7 +21,6 @@ from .exceptions import ParseError from .layers import PCBLayer, LayerSet, sort_layers from .common import read as gerber_read from .utils import listdir -from .render import theme class PCB(object): @@ -58,22 +57,10 @@ class PCB(object): def __init__(self, layers, name=None): self.layers = sort_layers(layers) self.name = name - self._theme = theme.THEMES['Default'] - self.theme = self._theme def __len__(self): return len(self.layers) - @property - def theme(self): - return self._theme - - @theme.setter - def theme(self, theme): - self._theme = theme - for layer in self.layers: - layer.settings = theme[layer.layer_class] - @property def top_layers(self): board_layers = [l for l in reversed(self.layers) if l.layer_class in ('topsilk', 'topmask', 'top')] -- cgit From 5476da8aa3f4ee424f56f4f2491e7af1c4b7b758 Mon Sep 17 00:00:00 2001 From: Hamilton Kibbe Date: Thu, 21 Jan 2016 03:57:44 -0500 Subject: Fix a bunch of rendering bugs. - 'clear' polarity primitives no longer erase background - Added aperture macro support for polygons - Added aperture macro rendring support - Renderer now creates a new surface for each layer and merges them instead of working directly on a single surface - Updated examples accordingly --- gerber/pcb.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'gerber/pcb.py') diff --git a/gerber/pcb.py b/gerber/pcb.py index 0518dd4..92a1f28 100644 --- a/gerber/pcb.py +++ b/gerber/pcb.py @@ -63,13 +63,15 @@ class PCB(object): @property def top_layers(self): - board_layers = [l for l in reversed(self.layers) if l.layer_class in ('topsilk', 'topmask', 'top')] + board_layers = [l for l in reversed(self.layers) if l.layer_class in + ('topsilk', 'topmask', 'top')] drill_layers = [l for l in self.drill_layers if 'top' in l.layers] return board_layers + drill_layers @property def bottom_layers(self): - board_layers = [l for l in self.layers if l.layer_class in ('bottomsilk', 'bottommask', 'bottom')] + board_layers = [l for l in self.layers if l.layer_class in + ('bottomsilk', 'bottommask', 'bottom')] drill_layers = [l for l in self.drill_layers if 'bottom' in l.layers] return board_layers + drill_layers @@ -77,11 +79,17 @@ class PCB(object): def drill_layers(self): return [l for l in self.layers if l.layer_class == 'drill'] + @property + def copper_layers(self): + return [layer for layer in self.layers if layer.layer_class in + ('top', 'bottom', 'internal')] + @property def layer_count(self): """ Number of *COPPER* layers """ - return len([l for l in self.layers if l.layer_class in ('top', 'bottom', 'internal')]) + return len([l for l in self.layers if l.layer_class in + ('top', 'bottom', 'internal')]) @property def board_bounds(self): @@ -91,4 +99,3 @@ class PCB(object): for layer in self.layers: if layer.layer_class == 'top': return layer.bounds - -- cgit From 5df38c014fd09792995b2b12b1982c535c962c9a Mon Sep 17 00:00:00 2001 From: Hamilton Kibbe Date: Thu, 28 Jan 2016 12:19:03 -0500 Subject: Cleanup, rendering fixes. fixed rendering of tented vias fixed rendering of semi-transparent layers fixed file type detection issues added some examples --- gerber/pcb.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'gerber/pcb.py') diff --git a/gerber/pcb.py b/gerber/pcb.py index 92a1f28..a213fb3 100644 --- a/gerber/pcb.py +++ b/gerber/pcb.py @@ -18,7 +18,7 @@ import os from .exceptions import ParseError -from .layers import PCBLayer, LayerSet, sort_layers +from .layers import PCBLayer, sort_layers from .common import read as gerber_read from .utils import listdir @@ -29,22 +29,26 @@ class PCB(object): def from_directory(cls, directory, board_name=None, verbose=False): layers = [] names = set() + # Validate directory = os.path.abspath(directory) if not os.path.isdir(directory): raise TypeError('{} is not a directory.'.format(directory)) + # Load gerber files for filename in listdir(directory, True, True): try: camfile = gerber_read(os.path.join(directory, filename)) - layer = PCBLayer.from_gerber(camfile) + layer = PCBLayer.from_cam(camfile) layers.append(layer) names.add(os.path.splitext(filename)[0]) if verbose: - print('Added {} layer <{}>'.format(layer.layer_class, filename)) + print('[PCB]: Added {} layer <{}>'.format(layer.layer_class, + filename)) except ParseError: if verbose: - print('Skipping file {}'.format(filename)) + print('[PCB]: Skipping file {}'.format(filename)) + # Try to guess board name if board_name is None: if len(names) == 1: @@ -66,14 +70,16 @@ class PCB(object): board_layers = [l for l in reversed(self.layers) if l.layer_class in ('topsilk', 'topmask', 'top')] drill_layers = [l for l in self.drill_layers if 'top' in l.layers] - return board_layers + drill_layers + # Drill layer goes under soldermask for proper rendering of tented vias + return [board_layers[0]] + drill_layers + board_layers[1:] @property def bottom_layers(self): board_layers = [l for l in self.layers if l.layer_class in ('bottomsilk', 'bottommask', 'bottom')] drill_layers = [l for l in self.drill_layers if 'bottom' in l.layers] - return board_layers + drill_layers + # Drill layer goes under soldermask for proper rendering of tented vias + return [board_layers[0]] + drill_layers + board_layers[1:] @property def drill_layers(self): @@ -81,8 +87,9 @@ class PCB(object): @property def copper_layers(self): - return [layer for layer in self.layers if layer.layer_class in - ('top', 'bottom', 'internal')] + return list(reversed([layer for layer in self.layers if + layer.layer_class in + ('top', 'bottom', 'internal')])) @property def layer_count(self): -- cgit From 7cd3d53252181d1837b6a17961c420a4511b2326 Mon Sep 17 00:00:00 2001 From: ju5t Date: Mon, 25 Jun 2018 09:43:23 +0200 Subject: Skip subdirectories during import If a directory contains subdirectories from_directory throws an exception. --- gerber/pcb.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'gerber/pcb.py') diff --git a/gerber/pcb.py b/gerber/pcb.py index a213fb3..ba15161 100644 --- a/gerber/pcb.py +++ b/gerber/pcb.py @@ -48,6 +48,9 @@ class PCB(object): except ParseError: if verbose: print('[PCB]: Skipping file {}'.format(filename)) + except IOError: + if verbose: + print('[PCB]: Skipping file {}'.format(filename)) # Try to guess board name if board_name is None: -- cgit From 4aca5b8a02be8d648b7a2d5f462c6b80c6a6edda Mon Sep 17 00:00:00 2001 From: Chintalagiri Shashank Date: Fri, 10 May 2019 23:40:56 +0530 Subject: Correctly recognize gEDA pcb generated gerber filenames --- gerber/pcb.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'gerber/pcb.py') diff --git a/gerber/pcb.py b/gerber/pcb.py index ba15161..69f071e 100644 --- a/gerber/pcb.py +++ b/gerber/pcb.py @@ -18,7 +18,7 @@ import os from .exceptions import ParseError -from .layers import PCBLayer, sort_layers +from .layers import PCBLayer, sort_layers, layer_signatures from .common import read as gerber_read from .utils import listdir @@ -41,7 +41,14 @@ class PCB(object): camfile = gerber_read(os.path.join(directory, filename)) layer = PCBLayer.from_cam(camfile) layers.append(layer) - names.add(os.path.splitext(filename)[0]) + name = os.path.splitext(filename)[0] + if len(os.path.splitext(filename)) > 1: + _name, ext = os.path.splitext(name) + if ext[1:] in layer_signatures(layer.layer_class): + name = _name + if layer.layer_class == 'drill' and 'drill' in ext: + name = _name + names.add(name) if verbose: print('[PCB]: Added {} layer <{}>'.format(layer.layer_class, filename)) -- cgit From 37dfd86368d080534eeefec9f726cae9e5f03e7e Mon Sep 17 00:00:00 2001 From: Chintalagiri Shashank Date: Sat, 11 May 2019 04:03:20 +0530 Subject: Add hook for outline layer to PCB class --- gerber/pcb.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'gerber/pcb.py') diff --git a/gerber/pcb.py b/gerber/pcb.py index ba15161..56deaa3 100644 --- a/gerber/pcb.py +++ b/gerber/pcb.py @@ -94,6 +94,12 @@ class PCB(object): layer.layer_class in ('top', 'bottom', 'internal')])) + @property + def outline_layer(self): + for layer in self.layers: + if layer.layer_class == 'outline': + return layer + @property def layer_count(self): """ Number of *COPPER* layers -- cgit