Refactor a little
pulled all rendering stuff out of the pcb/layer objects
This commit is contained in:
parent
5430fa6738
commit
6a005436b4
5 changed files with 68 additions and 74 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')]
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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']),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue