154 lines
4.5 KiB
Python
154 lines
4.5 KiB
Python
"""
|
|
Main Kiplot code
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
|
|
from . import plot_config as PCfg
|
|
|
|
try:
|
|
import pcbnew
|
|
except ImportError:
|
|
logging.error("Failed to import pcbnew Python module."
|
|
" Do you need to add it to PYTHONPATH?")
|
|
raise
|
|
|
|
|
|
class Plotter(object):
|
|
"""
|
|
Main Plotter class - this is what will perform the plotting
|
|
"""
|
|
|
|
def __init__(self, cfg):
|
|
self.cfg = cfg
|
|
|
|
def plot(self, brd_file):
|
|
logging.debug("Starting plot of board {}".format(brd_file))
|
|
|
|
board = pcbnew.LoadBoard(brd_file)
|
|
|
|
logging.debug("Board loaded")
|
|
|
|
for op in self.cfg.outputs:
|
|
|
|
logging.debug("Processing output: {}".format(op.name))
|
|
|
|
# fresh plot controller
|
|
pc = pcbnew.PLOT_CONTROLLER(board)
|
|
|
|
if self._output_is_layer(op):
|
|
self._do_layer_plot(board, pc, op)
|
|
elif self._output_is_drill(op):
|
|
self._do_drill_plot(board, pc, op)
|
|
else:
|
|
raise ValueError("Don't know how to plot type {}"
|
|
.format(op.options.type))
|
|
|
|
pc.ClosePlot()
|
|
|
|
def _output_is_layer(self, output):
|
|
|
|
return output.options.type in [PCfg.OutputOptions.GERBER]
|
|
|
|
def _output_is_drill(self, output):
|
|
|
|
return output.options.type in [PCfg.OutputOptions.EXCELLON]
|
|
|
|
def _get_plot_format(self, output):
|
|
"""
|
|
Gets the Pcbnew plot format for a given KiPlot output type
|
|
"""
|
|
|
|
if output.options.type == PCfg.OutputOptions.GERBER:
|
|
return pcbnew.PLOT_FORMAT_GERBER
|
|
|
|
raise ValueError("Don't know how to translate plot type: {}"
|
|
.format(output.options.type))
|
|
|
|
def _do_layer_plot(self, board, plot_ctrl, output):
|
|
|
|
self._configure_plot_ctrl(plot_ctrl, output)
|
|
|
|
layer_cnt = board.GetCopperLayerCount()
|
|
|
|
# plot every layer in the output
|
|
for l in output.layers:
|
|
|
|
layer = l.layer
|
|
suffix = l.suffix
|
|
desc = l.desc
|
|
|
|
# for inner layers, we can now check if the layer exists
|
|
if layer.is_inner:
|
|
|
|
if layer.layer < 1 or layer.layer >= layer_cnt - 1:
|
|
raise ValueError(
|
|
"Inner layer {} is not valid for this board"
|
|
.format(layer.layer))
|
|
|
|
# Set current layer
|
|
plot_ctrl.SetLayer(layer.layer)
|
|
|
|
# Plot single layer to file
|
|
plot_format = self._get_plot_format(output)
|
|
plot_ctrl.OpenPlotfile(suffix, plot_format, desc)
|
|
logging.debug("Plotting layer {} to {}".format(
|
|
layer.layer, plot_ctrl.GetPlotFileName()))
|
|
plot_ctrl.PlotLayer()
|
|
|
|
def _do_drill_plot(self, board, plot_ctrl, output):
|
|
|
|
pass
|
|
|
|
def _configure_gerber_opts(self, po, output):
|
|
|
|
# true if gerber
|
|
po.SetUseGerberAttributes(True)
|
|
|
|
assert(output.options.type == PCfg.OutputOptions.GERBER)
|
|
gerb_opts = output.options.type_options
|
|
|
|
po.SetSubtractMaskFromSilk(gerb_opts.subtract_mask_from_silk)
|
|
po.SetUseGerberProtelExtensions(gerb_opts.use_protel_extensions)
|
|
|
|
def _configure_plot_ctrl(self, plot_ctrl, output):
|
|
|
|
logging.debug("Configuring plot controller for output")
|
|
|
|
po = plot_ctrl.GetPlotOptions()
|
|
|
|
opts = output.options.type_options
|
|
|
|
# Set some important plot options:
|
|
po.SetPlotFrameRef(False)
|
|
# Line width for items without one defined
|
|
po.SetLineWidth(opts.line_width)
|
|
|
|
po.SetAutoScale(False) # do not change it
|
|
po.SetScale(1) # do not change it
|
|
po.SetMirror(False)
|
|
|
|
po.SetExcludeEdgeLayer(opts.exclude_edge_layer)
|
|
po.SetPlotPadsOnSilkLayer(not opts.exclude_pads_from_silkscreen)
|
|
po.SetUseAuxOrigin(opts.use_aux_axis_as_origin)
|
|
|
|
po.SetUseGerberAttributes(False)
|
|
|
|
if output.options.type == PCfg.OutputOptions.GERBER:
|
|
self._configure_gerber_opts(po, output)
|
|
|
|
# Disable plot pad holes
|
|
po.SetDrillMarksType(pcbnew.PCB_PLOT_PARAMS.NO_DRILL_SHAPE)
|
|
# Skip plot pad NPTH when possible: when drill size and shape == pad size
|
|
# and shape
|
|
# usually sel to True for copper layers
|
|
po.SetSkipPlotNPTH_Pads(False)
|
|
|
|
# outdir is a combination of the config and output
|
|
outdir = os.path.join(self.cfg.outdir, output.outdir)
|
|
|
|
logging.debug("Output destination: {}".format(outdir))
|
|
|
|
po.SetOutputDirectory(outdir)
|