Refactor a little

pulled all rendering stuff out of the pcb/layer objects
This commit is contained in:
Hamilton Kibbe 2016-01-01 12:25:38 -05:00
parent 5430fa6738
commit 6a005436b4
5 changed files with 68 additions and 74 deletions

View file

@ -21,7 +21,7 @@ from collections import namedtuple
from .excellon import ExcellonFile
from .ipc356 import IPC_D_356
from .render.render import Renderable
Hint = namedtuple('Hint', 'layer ext name')
@ -109,7 +109,7 @@ def sort_layers(layers):
return output
class PCBLayer(Renderable):
class PCBLayer(object):
""" Base class for PCB Layers
Parameters
@ -207,7 +207,7 @@ class InternalLayer(PCBLayer):
return (self.order <= other.order)
class LayerSet(Renderable):
class LayerSet(object):
def __init__(self, name, layers, **kwargs):
super(LayerSet, self).__init__(**kwargs)
self.name = name

View file

@ -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')]

View file

@ -21,7 +21,8 @@ from operator import mul
import math
import tempfile
from .render import GerberContext
from .render import GerberContext, RenderSettings
from .theme import THEMES
from ..primitives import *
try:
@ -41,6 +42,15 @@ class GerberCairoContext(GerberContext):
self.mask_ctx = None
self.origin_in_inch = None
self.size_in_inch = None
self._xform_matrix = None
@property
def origin_in_pixels(self):
return tuple(map(mul, self.origin_in_inch, self.scale)) if self.origin_in_inch is not None else (0.0, 0.0)
@property
def size_in_pixels(self):
return tuple(map(mul, self.size_in_inch, self.scale)) if self.size_in_inch is not None else (0.0, 0.0)
def set_bounds(self, bounds, new_surface=False):
origin_in_inch = (bounds[0][0], bounds[1][0])
@ -60,34 +70,56 @@ class GerberCairoContext(GerberContext):
self.mask_ctx.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
self.mask_ctx.scale(1, -1)
self.mask_ctx.translate(-(origin_in_inch[0] * self.scale[0]), (-origin_in_inch[1]*self.scale[0]) - size_in_pixels[1])
self._xform_matrix = cairo.Matrix(xx=1.0, yy=-1.0, x0=-self.origin_in_pixels[0], y0=self.size_in_pixels[1] + self.origin_in_pixels[1])
def render_layers(self, layers, filename):
def render_layers(self, layers, filename, theme=THEMES['default']):
""" Render a set of layers
"""
self.set_bounds(layers[0].bounds, True)
self._paint_background(True)
for layer in layers:
self._render_layer(layer)
self._render_layer(layer, theme)
self.dump(filename)
@property
def origin_in_pixels(self):
return tuple(map(mul, self.origin_in_inch, self.scale)) if self.origin_in_inch is not None else (0.0, 0.0)
def dump(self, filename):
""" Save image as `filename`
"""
is_svg = filename.lower().endswith(".svg")
if is_svg:
self.surface.finish()
self.surface_buffer.flush()
with open(filename, "w") as f:
self.surface_buffer.seek(0)
f.write(self.surface_buffer.read())
f.flush()
else:
self.surface.write_to_png(filename)
@property
def size_in_pixels(self):
return tuple(map(mul, self.size_in_inch, self.scale)) if self.size_in_inch is not None else (0.0, 0.0)
def dump_str(self):
""" Return a string containing the rendered image.
"""
fobj = StringIO()
self.surface.write_to_png(fobj)
return fobj.getvalue()
def _render_layer(self, layer):
self.color = layer.settings.color
self.alpha = layer.settings.alpha
self.invert = layer.settings.invert
if layer.settings.mirror:
def dump_svg_str(self):
""" Return a string containg the rendered SVG.
"""
self.surface.finish()
self.surface_buffer.flush()
return self.surface_buffer.read()
def _render_layer(self, layer, theme=THEMES['default']):
settings = theme.get(layer.layer_class, RenderSettings())
self.color = settings.color
self.alpha = settings.alpha
self.invert = settings.invert
if settings.mirror:
raise Warning('mirrored layers aren\'t supported yet...')
if self.invert:
self._clear_mask()
for p in layer.primitives:
self.render(p)
for prim in layer.primitives:
self.render(prim)
if self.invert:
self._render_mask()
@ -209,10 +241,11 @@ class GerberCairoContext(GerberContext):
def _render_test_record(self, primitive, color):
position = tuple(map(add, primitive.position, self.origin_in_inch))
self.ctx.set_operator(cairo.OPERATOR_OVER)
self.ctx.select_font_face('monospace', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
self.ctx.set_font_size(13)
self._render_circle(Circle(position, 0.015), color)
self.ctx.set_source_rgb(*color)
self.ctx.set_source_rgba(*color, alpha=self.alpha)
self.ctx.set_operator(cairo.OPERATOR_OVER if primitive.level_polarity == "dark" else cairo.OPERATOR_CLEAR)
self.ctx.move_to(*[self.scale[0] * (coord + 0.015) for coord in position])
self.ctx.scale(1, -1)
@ -227,8 +260,7 @@ class GerberCairoContext(GerberContext):
def _render_mask(self):
self.ctx.set_operator(cairo.OPERATOR_OVER)
ptn = cairo.SurfacePattern(self.mask)
ptn.set_matrix(cairo.Matrix(xx=1.0, yy=-1.0, x0=-self.origin_in_pixels[0],
y0=self.size_in_pixels[1] + self.origin_in_pixels[1]))
ptn.set_matrix(self._xform_matrix)
self.ctx.set_source(ptn)
self.ctx.paint()
@ -237,29 +269,3 @@ class GerberCairoContext(GerberContext):
self.bg = True
self.ctx.set_source_rgba(*self.background_color, alpha=1.0)
self.ctx.paint()
def dump(self, filename):
is_svg = filename.lower().endswith(".svg")
if is_svg:
self.surface.finish()
self.surface_buffer.flush()
with open(filename, "w") as f:
self.surface_buffer.seek(0)
f.write(self.surface_buffer.read())
f.flush()
else:
self.surface.write_to_png(filename)
def dump_str(self):
""" Return a string containing the rendered image.
"""
fobj = StringIO()
self.surface.write_to_png(fobj)
return fobj.getvalue()
def dump_svg_str(self):
""" Return a string containg the rendered SVG.
"""
self.surface.finish()
self.surface_buffer.flush()
return self.surface_buffer.read()

View file

@ -183,8 +183,10 @@ class GerberContext(object):
pass
class Renderable(object):
def __init__(self, settings=None):
self.settings = settings
self.primitives = []
class RenderSettings(object):
def __init__(self, color=(0.0, 0.0, 0.0), alpha=1.0, invert=False, mirror=False):
self.color = color
self.alpha = alpha
self.invert = invert
self.mirror = mirror

View file

@ -16,6 +16,8 @@
# limitations under the License.
from .render import RenderSettings
COLORS = {
'black': (0.0, 0.0, 0.0),
'white': (1.0, 1.0, 1.0),
@ -33,14 +35,6 @@ COLORS = {
}
class RenderSettings(object):
def __init__(self, color, alpha=1.0, invert=False, mirror=False):
self.color = color
self.alpha = alpha
self.invert = invert
self.mirror = mirror
class Theme(object):
def __init__(self, name=None, **kwargs):
self.name = 'Default' if name is None else name
@ -57,8 +51,13 @@ class Theme(object):
def __getitem__(self, key):
return getattr(self, key)
def get(self, key, noneval=None):
val = getattr(self, key)
return val if val is not None else noneval
THEMES = {
'Default': Theme(),
'default': Theme(),
'OSH Park': Theme(name='OSH Park',
top=RenderSettings(COLORS['enig copper']),
bottom=RenderSettings(COLORS['enig copper']),