gerbonara/gerber/pcb.py
Hamilton Kibbe 6f876edd09 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
2015-12-22 02:47:23 -05:00

107 lines
3.5 KiB
Python

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# copyright 2015 Hamilton Kibbe <ham@hamiltonkib.be>
#
# 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