summaryrefslogtreecommitdiff
path: root/gerber
diff options
context:
space:
mode:
Diffstat (limited to 'gerber')
-rw-r--r--gerber/layers.py6
-rw-r--r--gerber/pcb.py13
-rw-r--r--gerber/render/cairo_backend.py95
-rw-r--r--gerber/render/render.py10
-rw-r--r--gerber/render/theme.py17
5 files changed, 68 insertions, 73 deletions
diff --git a/gerber/layers.py b/gerber/layers.py
index c6a5bf7..2b73893 100644
--- a/gerber/layers.py
+++ b/gerber/layers.py
@@ -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
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,23 +57,11 @@ 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')]
drill_layers = [l for l in self.drill_layers if 'top' in l.layers]
diff --git a/gerber/render/cairo_backend.py b/gerber/render/cairo_backend.py
index a3ee3fa..0cb230b 100644
--- a/gerber/render/cairo_backend.py
+++ b/gerber/render/cairo_backend.py
@@ -24,7 +24,8 @@ from operator import mul, div
import math
import tempfile
-from .render import GerberContext
+from .render import GerberContext, RenderSettings
+from .theme import THEMES
from ..primitives import *
try:
@@ -44,6 +45,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])
@@ -63,34 +73,55 @@ 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`
+ """
+ if filename and filename.lower().endswith(".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:
+ return 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()
@@ -298,10 +329,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)
@@ -316,8 +348,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()
@@ -326,28 +357,4 @@ class GerberCairoContext(GerberContext):
self.bg = True
self.ctx.set_source_rgba(self.background_color[0], self.background_color[1], self.background_color[2], alpha=1.0)
self.ctx.paint()
-
- def dump(self, filename):
- if filename and filename.lower().endswith(".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:
- return 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()
+
diff --git a/gerber/render/render.py b/gerber/render/render.py
index cb65a8d..f521c44 100644
--- a/gerber/render/render.py
+++ b/gerber/render/render.py
@@ -214,8 +214,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
diff --git a/gerber/render/theme.py b/gerber/render/theme.py
index 5978831..e538df8 100644
--- a/gerber/render/theme.py
+++ b/gerber/render/theme.py
@@ -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']),