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
107 lines
3.5 KiB
Python
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
|
|
|