summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/cairo_example.py3
-rw-r--r--examples/pcb_example.py3
-rw-r--r--gerber/cam.py10
-rw-r--r--gerber/render/__init__.py2
-rw-r--r--gerber/render/cairo_backend.py13
-rw-r--r--gerber/render/excellon_backend.py91
-rw-r--r--gerber/render/render.py27
-rw-r--r--gerber/render/rs274x_backend.py10
8 files changed, 90 insertions, 69 deletions
diff --git a/examples/cairo_example.py b/examples/cairo_example.py
index fcd7a44..ecfed4d 100644
--- a/examples/cairo_example.py
+++ b/examples/cairo_example.py
@@ -25,7 +25,8 @@ a .png file.
import os
from gerber import load_layer
-from gerber.render import GerberCairoContext, RenderSettings, theme
+from gerber.render import RenderSettings, theme
+from gerber.render.cairo_backend import GerberCairoContext
GERBER_FOLDER = os.path.abspath(os.path.join(os.path.dirname(__file__), 'gerbers'))
diff --git a/examples/pcb_example.py b/examples/pcb_example.py
index 9e066a8..34afee6 100644
--- a/examples/pcb_example.py
+++ b/examples/pcb_example.py
@@ -22,7 +22,8 @@ images using the PCB interface
import os
from gerber import PCB
-from gerber.render import GerberCairoContext, theme
+from gerber.render import theme
+from gerber.render.cairo_backend import GerberCairoContext
GERBER_FOLDER = os.path.abspath(os.path.join(os.path.dirname(__file__), 'gerbers'))
diff --git a/gerber/cam.py b/gerber/cam.py
index 15b801a..4f20283 100644
--- a/gerber/cam.py
+++ b/gerber/cam.py
@@ -250,6 +250,10 @@ class CamFile(object):
"""
pass
+ @property
+ def bounding_box(self):
+ pass
+
def to_inch(self):
pass
@@ -271,12 +275,12 @@ class CamFile(object):
from .render import GerberCairoContext
ctx = GerberCairoContext()
ctx.set_bounds(self.bounding_box)
- ctx._paint_background()
+ ctx.paint_background()
ctx.invert = invert
- ctx._new_render_layer()
+ ctx.new_render_layer()
for p in self.primitives:
ctx.render(p)
- ctx._flatten()
+ ctx.flatten()
if filename is not None:
ctx.dump(filename)
diff --git a/gerber/render/__init__.py b/gerber/render/__init__.py
index 3598c4d..fe08d50 100644
--- a/gerber/render/__init__.py
+++ b/gerber/render/__init__.py
@@ -23,6 +23,4 @@ This module provides contexts for rendering images of gerber layers. Currently
SVG is the only supported format.
"""
-
-from .cairo_backend import GerberCairoContext
from .render import RenderSettings
diff --git a/gerber/render/cairo_backend.py b/gerber/render/cairo_backend.py
index 0e3a721..e1d1408 100644
--- a/gerber/render/cairo_backend.py
+++ b/gerber/render/cairo_backend.py
@@ -91,7 +91,7 @@ class GerberCairoContext(GerberContext):
self.set_bounds(bounds)
else:
self.set_bounds(layer.bounds)
- self._paint_background(bgsettings)
+ self.paint_background(bgsettings)
if verbose:
print('[Render]: Rendering {} Layer.'.format(layer.layer_class))
self._render_count += 1
@@ -193,11 +193,11 @@ class GerberCairoContext(GerberContext):
def _render_layer(self, layer, settings):
self.invert = settings.invert
# Get a new clean layer to render on
- self._new_render_layer(mirror=settings.mirror)
+ self.new_render_layer(mirror=settings.mirror)
for prim in layer.primitives:
self.render(prim)
# Add layer to image
- self._flatten(settings.color, settings.alpha)
+ self.flatten(settings.color, settings.alpha)
def _render_line(self, line, color):
start = self.scale_point(line.start)
@@ -530,7 +530,7 @@ class GerberCairoContext(GerberContext):
self.ctx.show_text(primitive.net_name)
self.ctx.scale(1, -1)
- def _new_render_layer(self, color=None, mirror=False):
+ def new_render_layer(self, color=None, mirror=False):
size_in_pixels = self.scale_point(self.size_in_inch)
matrix = copy.copy(self._xform_matrix)
layer = cairo.SVGSurface(None, size_in_pixels[0], size_in_pixels[1])
@@ -548,8 +548,7 @@ class GerberCairoContext(GerberContext):
self.active_layer = layer
self.active_matrix = matrix
-
- def _flatten(self, color=None, alpha=None):
+ def flatten(self, color=None, alpha=None):
color = color if color is not None else self.color
alpha = alpha if alpha is not None else self.alpha
self.output_ctx.set_source_rgba(color[0], color[1], color[2], alpha)
@@ -558,7 +557,7 @@ class GerberCairoContext(GerberContext):
self.active_layer = None
self.active_matrix = None
- def _paint_background(self, settings=None):
+ def paint_background(self, settings=None):
color = settings.color if settings is not None else self.background_color
alpha = settings.alpha if settings is not None else 1.0
if not self.has_bg:
diff --git a/gerber/render/excellon_backend.py b/gerber/render/excellon_backend.py
index da5b22b..765d68c 100644
--- a/gerber/render/excellon_backend.py
+++ b/gerber/render/excellon_backend.py
@@ -4,13 +4,13 @@ from ..excellon import DrillSlot
from ..excellon_statements import *
class ExcellonContext(GerberContext):
-
+
MODE_DRILL = 1
- MODE_SLOT =2
-
+ MODE_SLOT =2
+
def __init__(self, settings):
GerberContext.__init__(self)
-
+
# Statements that we write
self.comments = []
self.header = []
@@ -18,57 +18,57 @@ class ExcellonContext(GerberContext):
self.body_start = [RewindStopStmt()]
self.body = []
self.start = [HeaderBeginStmt()]
-
+
# Current tool and position
self.handled_tools = set()
self.cur_tool = None
self.drill_mode = ExcellonContext.MODE_DRILL
self.drill_down = False
self._pos = (None, None)
-
+
self.settings = settings
self._start_header()
self._start_comments()
-
+
def _start_header(self):
"""Create the header from the settings"""
-
+
self.header.append(UnitStmt.from_settings(self.settings))
-
+
if self.settings.notation == 'incremental':
raise NotImplementedError('Incremental mode is not implemented')
else:
self.body.append(AbsoluteModeStmt())
-
+
def _start_comments(self):
-
+
# Write the digits used - this isn't valid Excellon statement, so we write as a comment
self.comments.append(CommentStmt('FILE_FORMAT=%d:%d' % (self.settings.format[0], self.settings.format[1])))
-
+
def _get_end(self):
"""How we end depends on our mode"""
-
+
end = []
-
+
if self.drill_down:
end.append(RetractWithClampingStmt())
end.append(RetractWithoutClampingStmt())
-
+
end.append(EndOfProgramStmt())
-
+
return end
-
+
@property
def statements(self):
return self.start + self.comments + self.header + self.body_start + self.body + self._get_end()
-
- def set_bounds(self, bounds):
+
+ def set_bounds(self, bounds, *args, **kwargs):
pass
-
- def _paint_background(self):
+
+ def paint_background(self):
pass
-
+
def _render_line(self, line, color):
raise ValueError('Invalid Excellon object')
def _render_arc(self, arc, color):
@@ -76,7 +76,7 @@ class ExcellonContext(GerberContext):
def _render_region(self, region, color):
raise ValueError('Invalid Excellon object')
-
+
def _render_level_polarity(self, region):
raise ValueError('Invalid Excellon object')
@@ -85,105 +85,104 @@ class ExcellonContext(GerberContext):
def _render_rectangle(self, rectangle, color):
raise ValueError('Invalid Excellon object')
-
+
def _render_obround(self, obround, color):
raise ValueError('Invalid Excellon object')
-
+
def _render_polygon(self, polygon, color):
raise ValueError('Invalid Excellon object')
-
+
def _simplify_point(self, point):
return (point[0] if point[0] != self._pos[0] else None, point[1] if point[1] != self._pos[1] else None)
def _render_drill(self, drill, color):
-
+
if self.drill_mode != ExcellonContext.MODE_DRILL:
self._start_drill_mode()
-
+
tool = drill.hit.tool
if not tool in self.handled_tools:
self.handled_tools.add(tool)
self.header.append(ExcellonTool.from_tool(tool))
-
+
if tool != self.cur_tool:
self.body.append(ToolSelectionStmt(tool.number))
self.cur_tool = tool
-
+
point = self._simplify_point(drill.position)
self._pos = drill.position
self.body.append(CoordinateStmt.from_point(point))
-
+
def _start_drill_mode(self):
"""
If we are not in drill mode, then end the ROUT so we can do basic drilling
"""
-
+
if self.drill_mode == ExcellonContext.MODE_SLOT:
-
+
# Make sure we are retracted before changing modes
last_cmd = self.body[-1]
if self.drill_down:
self.body.append(RetractWithClampingStmt())
self.body.append(RetractWithoutClampingStmt())
self.drill_down = False
-
+
# Switch to drill mode
self.body.append(DrillModeStmt())
self.drill_mode = ExcellonContext.MODE_DRILL
-
+
else:
raise ValueError('Should be in slot mode')
-
+
def _render_slot(self, slot, color):
-
+
# Set the tool first, before we might go into drill mode
tool = slot.hit.tool
if not tool in self.handled_tools:
self.handled_tools.add(tool)
self.header.append(ExcellonTool.from_tool(tool))
-
+
if tool != self.cur_tool:
self.body.append(ToolSelectionStmt(tool.number))
self.cur_tool = tool
-
+
# Two types of drilling - normal drill and slots
if slot.hit.slot_type == DrillSlot.TYPE_ROUT:
# For ROUT, setting the mode is part of the actual command.
-
+
# Are we in the right position?
if slot.start != self._pos:
if self.drill_down:
# We need to move into the right position, so retract
self.body.append(RetractWithClampingStmt())
self.drill_down = False
-
+
# Move to the right spot
point = self._simplify_point(slot.start)
self._pos = slot.start
self.body.append(CoordinateStmt.from_point(point, mode="ROUT"))
-
+
# Now we are in the right spot, so drill down
if not self.drill_down:
self.body.append(ZAxisRoutPositionStmt())
self.drill_down = True
-
+
# Do a linear move from our current position to the end position
point = self._simplify_point(slot.end)
self._pos = slot.end
self.body.append(CoordinateStmt.from_point(point, mode="LINEAR"))
self.drill_mode = ExcellonContext.MODE_SLOT
-
+
else:
# This is a G85 slot, so do this in normally drilling mode
if self.drill_mode != ExcellonContext.MODE_DRILL:
self._start_drill_mode()
-
+
# Slots don't use simplified points
self._pos = slot.end
self.body.append(SlotStmt.from_points(slot.start, slot.end))
def _render_inverted_layer(self):
pass
- \ No newline at end of file
diff --git a/gerber/render/render.py b/gerber/render/render.py
index 79f43d6..580a7ea 100644
--- a/gerber/render/render.py
+++ b/gerber/render/render.py
@@ -139,7 +139,7 @@ class GerberContext(object):
if not primitive:
return
- self._pre_render_primitive(primitive)
+ self.pre_render_primitive(primitive)
color = self.color
if isinstance(primitive, Line):
@@ -167,16 +167,35 @@ class GerberContext(object):
elif isinstance(primitive, TestRecord):
self._render_test_record(primitive, color)
- self._post_render_primitive(primitive)
+ self.post_render_primitive(primitive)
- def _pre_render_primitive(self, primitive):
+ def set_bounds(self, bounds, *args, **kwargs):
+ """Called by the renderer to set the extents of the file to render.
+
+ Parameters
+ ----------
+ bounds: Tuple[Tuple[float, float], Tuple[float, float]]
+ ( (x_min, x_max), (y_min, y_max)
+ """
+ pass
+
+ def paint_background(self):
+ pass
+
+ def new_render_layer(self):
+ pass
+
+ def flatten(self):
+ pass
+
+ def pre_render_primitive(self, primitive):
"""
Called before rendering a primitive. Use the callback to perform some action before rendering
a primitive, for example adding a comment.
"""
return
- def _post_render_primitive(self, primitive):
+ def post_render_primitive(self, primitive):
"""
Called after rendering a primitive. Use the callback to perform some action after rendering
a primitive
diff --git a/gerber/render/rs274x_backend.py b/gerber/render/rs274x_backend.py
index d32602a..30048c4 100644
--- a/gerber/render/rs274x_backend.py
+++ b/gerber/render/rs274x_backend.py
@@ -148,10 +148,10 @@ class Rs274xContext(GerberContext):
def statements(self):
return self.comments + self.header + self.body + self.end
- def set_bounds(self, bounds):
+ def set_bounds(self, bounds, *args, **kwargs):
pass
- def _paint_background(self):
+ def paint_background(self):
pass
def _select_aperture(self, aperture):
@@ -173,7 +173,7 @@ class Rs274xContext(GerberContext):
self.body.append(ApertureStmt(aper.d))
self._dcode = aper.d
- def _pre_render_primitive(self, primitive):
+ def pre_render_primitive(self, primitive):
if hasattr(primitive, 'comment'):
self.body.append(CommentStmt(primitive.comment))
@@ -489,11 +489,11 @@ class Rs274xContext(GerberContext):
def _render_inverted_layer(self):
pass
- def _new_render_layer(self):
+ def new_render_layer(self):
# TODO Might need to implement this
pass
- def _flatten(self):
+ def flatten(self):
# TODO Might need to implement this
pass