Added variants support to the PCB print (PDF)
Needs some adjustement, but is working.
This commit is contained in:
parent
0e394b468b
commit
23e46df1c5
|
|
@ -18,8 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Marking components as "Do Not Fit"
|
||||
- Marking components as "Do Not Change"
|
||||
- The internal BoM format supports KiBoM and IBoM style variants
|
||||
- Schematic print to PDF/SVG support for variants. Not fitted components are
|
||||
crossed.
|
||||
- Schematic/PCB print to PDF/SVG support for variants. Not fitted components
|
||||
are crossed.
|
||||
- Position (Pick & Place) support for variants.
|
||||
- All plot formats (gerber, pdf, svg, etc.) support for variants:
|
||||
- Pads removed from *.Paste
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -115,6 +115,7 @@ gen_ref:
|
|||
src/kibot -b tests/board_samples/kibom-variant_4.kicad_pcb -c tests/yaml_samples/pdf_variant_1.kibot.yaml -d $(REFDIR)
|
||||
src/kibot -b tests/board_samples/kibom-variant_3.kicad_pcb -c tests/yaml_samples/pcbdraw_variant_1.kibot.yaml -d $(REFDIR)
|
||||
src/kibot -b tests/board_samples/kibom-variant_3.kicad_pcb -c tests/yaml_samples/pcbdraw_variant_2.kibot.yaml -d $(REFDIR)
|
||||
src/kibot -b tests/board_samples/kibom-variant_3.kicad_pcb -c tests/yaml_samples/print_pcb_variant_1.kibot.yaml -d $(REFDIR)
|
||||
cp -a $(REFILL).ok $(REFILL)
|
||||
|
||||
doc:
|
||||
|
|
|
|||
|
|
@ -831,8 +831,11 @@ Next time you need this list just use an alias, like this:
|
|||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `pdf_pcb_print` output.
|
||||
* Valid keys:
|
||||
- `dnf_filter`: [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
- `output`: [string='%f-%i%v.%x'] filename for the output PDF (%i=layers, %x=pdf). Affected by global options.
|
||||
- *output_name*: Alias for output.
|
||||
- `variant`: [string=''] Board variant to apply.
|
||||
|
||||
* PDF Schematic Print (Portable Document Format)
|
||||
* Type: `pdf_sch_print`
|
||||
|
|
|
|||
|
|
@ -648,9 +648,14 @@ outputs:
|
|||
type: 'pdf_pcb_print'
|
||||
dir: 'Example/pdf_pcb_print_dir'
|
||||
options:
|
||||
# [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
# A short-cut to use for simple cases where a variant is an overkill
|
||||
dnf_filter: ''
|
||||
# [string='%f-%i%v.%x'] filename for the output PDF (%i=layers, %x=pdf). Affected by global options
|
||||
output: '%f-%i%v.%x'
|
||||
# `output_name` is an alias for `output`
|
||||
# [string=''] Board variant to apply
|
||||
variant: ''
|
||||
layers: all
|
||||
|
||||
# PDF Schematic Print (Portable Document Format):
|
||||
|
|
|
|||
|
|
@ -77,3 +77,25 @@ DNC = {
|
|||
"no change": 1,
|
||||
"fixed": 1
|
||||
}
|
||||
|
||||
|
||||
class Rect(object):
|
||||
""" What KiCad returns isn't a real wxWidget's wxRect.
|
||||
Here I add what I really need """
|
||||
def __init__(self):
|
||||
self.x1 = None
|
||||
self.y1 = None
|
||||
self.x2 = None
|
||||
self.y2 = None
|
||||
|
||||
def Union(self, wxRect):
|
||||
if self.x1 is None:
|
||||
self.x1 = wxRect.x
|
||||
self.y1 = wxRect.y
|
||||
self.x2 = wxRect.x+wxRect.width
|
||||
self.y2 = wxRect.y+wxRect.height
|
||||
else:
|
||||
self.x1 = min(self.x1, wxRect.x)
|
||||
self.y1 = min(self.y1, wxRect.y)
|
||||
self.x2 = max(self.x2, wxRect.x+wxRect.width)
|
||||
self.y2 = max(self.y2, wxRect.y+wxRect.height)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from .out_base import (BaseOutput)
|
|||
from .error import (PlotError, KiPlotConfigurationError)
|
||||
from .layer import Layer
|
||||
from .gs import GS
|
||||
from .misc import UI_VIRTUAL
|
||||
from .misc import UI_VIRTUAL, Rect
|
||||
from .out_base import VariantOptions
|
||||
from .macros import macros, document # noqa: F401
|
||||
from . import log
|
||||
|
|
@ -19,28 +19,6 @@ from . import log
|
|||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
class Rect(object):
|
||||
""" What KiCad returns isn't a real wxWidget's wxRect.
|
||||
Here I add what I really need """
|
||||
def __init__(self):
|
||||
self.x1 = None
|
||||
self.y1 = None
|
||||
self.x2 = None
|
||||
self.y2 = None
|
||||
|
||||
def Union(self, wxRect):
|
||||
if self.x1 is None:
|
||||
self.x1 = wxRect.x
|
||||
self.y1 = wxRect.y
|
||||
self.x2 = wxRect.x+wxRect.width
|
||||
self.y2 = wxRect.y+wxRect.height
|
||||
else:
|
||||
self.x1 = min(self.x1, wxRect.x)
|
||||
self.y1 = min(self.y1, wxRect.y)
|
||||
self.x2 = max(self.x2, wxRect.x+wxRect.width)
|
||||
self.y2 = max(self.y2, wxRect.y+wxRect.height)
|
||||
|
||||
|
||||
class AnyLayerOptions(VariantOptions):
|
||||
""" Base class for: DXF, Gerber, HPGL, PDF, PS and SVG """
|
||||
def __init__(self):
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@
|
|||
# Copyright (c) 2020 Instituto Nacional de Tecnología Industrial
|
||||
# License: GPL-3.0
|
||||
# Project: KiBot (formerly KiPlot)
|
||||
import os
|
||||
from tempfile import NamedTemporaryFile
|
||||
from pcbnew import EDGE_MODULE, wxPoint
|
||||
from .pre_base import BasePreFlight
|
||||
from .error import (KiPlotConfigurationError)
|
||||
from .gs import (GS)
|
||||
from .kiplot import check_script, exec_with_retry
|
||||
from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT)
|
||||
from .optionable import BaseOptions
|
||||
from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT, Rect, UI_VIRTUAL)
|
||||
from .out_base import VariantOptions
|
||||
from .macros import macros, document, output_class # noqa: F401
|
||||
from .layer import Layer
|
||||
from . import log
|
||||
|
|
@ -16,7 +19,7 @@ from . import log
|
|||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
class PDF_Pcb_PrintOptions(BaseOptions):
|
||||
class PDF_Pcb_PrintOptions(VariantOptions):
|
||||
def __init__(self):
|
||||
with document:
|
||||
self.output = GS.def_global_output
|
||||
|
|
@ -25,7 +28,79 @@ class PDF_Pcb_PrintOptions(BaseOptions):
|
|||
""" {output} """
|
||||
super().__init__()
|
||||
|
||||
@staticmethod
|
||||
def cross_module(m, rect, layer):
|
||||
seg1 = EDGE_MODULE(m)
|
||||
seg1.SetWidth(120000)
|
||||
seg1.SetStart(wxPoint(rect.x1, rect.y1))
|
||||
seg1.SetEnd(wxPoint(rect.x2, rect.y2))
|
||||
seg1.SetLayer(layer)
|
||||
seg1.SetLocalCoord() # Update the local coordinates
|
||||
m.Add(seg1)
|
||||
seg2 = EDGE_MODULE(m)
|
||||
seg2.SetWidth(120000)
|
||||
seg2.SetStart(wxPoint(rect.x1, rect.y2))
|
||||
seg2.SetEnd(wxPoint(rect.x2, rect.y1))
|
||||
seg2.SetLayer(layer)
|
||||
seg2.SetLocalCoord() # Update the local coordinates
|
||||
m.Add(seg2)
|
||||
return [seg1, seg2]
|
||||
|
||||
def filter_components(self, board):
|
||||
if not self._comps:
|
||||
return GS.pcb_file
|
||||
comps_hash = self.get_refs_hash()
|
||||
# Cross the affected components
|
||||
ffab = board.GetLayerID('F.Fab')
|
||||
bfab = board.GetLayerID('B.Fab')
|
||||
extra_ffab_lines = []
|
||||
extra_bfab_lines = []
|
||||
for m in board.GetModules():
|
||||
ref = m.GetReference()
|
||||
# Rectangle containing the drawings, no text
|
||||
frect = Rect()
|
||||
brect = Rect()
|
||||
c = comps_hash.get(ref, None)
|
||||
if (c and not c.fitted) and m.GetAttributes() != UI_VIRTUAL:
|
||||
# Meassure the component BBox (only graphics)
|
||||
for gi in m.GraphicalItems():
|
||||
if gi.GetClass() == 'MGRAPHIC':
|
||||
l_gi = gi.GetLayer()
|
||||
if l_gi == ffab:
|
||||
frect.Union(gi.GetBoundingBox().getWxRect())
|
||||
if l_gi == bfab:
|
||||
brect.Union(gi.GetBoundingBox().getWxRect())
|
||||
# Cross the graphics in *.Fab
|
||||
if frect.x1 is not None:
|
||||
extra_ffab_lines.append(self.cross_module(m, frect, ffab))
|
||||
else:
|
||||
extra_ffab_lines.append(None)
|
||||
if brect.x1 is not None:
|
||||
extra_bfab_lines.append(self.cross_module(m, brect, bfab))
|
||||
else:
|
||||
extra_bfab_lines.append(None)
|
||||
# Save the PCB to a temporal file
|
||||
with NamedTemporaryFile(mode='w', suffix='.kicad_pcb', delete=False) as f:
|
||||
fname = f.name
|
||||
logger.debug('Storing filtered PCB to `{}`'.format(fname))
|
||||
GS.board.Save(fname)
|
||||
# Undo the drawings
|
||||
for m in GS.board.GetModules():
|
||||
ref = m.GetReference()
|
||||
c = comps_hash.get(ref, None)
|
||||
if (c and not c.fitted) and m.GetAttributes() != UI_VIRTUAL:
|
||||
restore = extra_ffab_lines.pop(0)
|
||||
if restore:
|
||||
for line in restore:
|
||||
m.Remove(line)
|
||||
restore = extra_bfab_lines.pop(0)
|
||||
if restore:
|
||||
for line in restore:
|
||||
m.Remove(line)
|
||||
return fname
|
||||
|
||||
def run(self, output_dir, board, layers):
|
||||
super().run(board, layers)
|
||||
check_script(CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, '1.4.1')
|
||||
layers = Layer.solve(layers)
|
||||
# Output file name
|
||||
|
|
@ -34,7 +109,8 @@ class PDF_Pcb_PrintOptions(BaseOptions):
|
|||
cmd = [CMD_PCBNEW_PRINT_LAYERS, 'export', '--output_name', output]
|
||||
if BasePreFlight.get_option('check_zone_fills'):
|
||||
cmd.append('-f')
|
||||
cmd.extend([GS.pcb_file, output_dir])
|
||||
board_name = self.filter_components(board)
|
||||
cmd.extend([board_name, output_dir])
|
||||
if GS.debug_enabled:
|
||||
cmd.insert(1, '-vv')
|
||||
cmd.insert(1, '-r')
|
||||
|
|
@ -42,6 +118,9 @@ class PDF_Pcb_PrintOptions(BaseOptions):
|
|||
cmd.extend([la.layer for la in layers])
|
||||
# Execute it
|
||||
ret = exec_with_retry(cmd)
|
||||
# Remove the temporal PCB
|
||||
if board_name != GS.pcb_file:
|
||||
os.remove(board_name)
|
||||
if ret: # pragma: no cover
|
||||
# We check all the arguments, we even load the PCB
|
||||
# A fail here isn't easy to reproduce
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -39,3 +39,14 @@ def test_print_pcb_refill():
|
|||
|
||||
ctx.expect_out_file(PDF_FILE_B)
|
||||
ctx.compare_image(PDF_FILE_B)
|
||||
|
||||
|
||||
def test_print_variant_1():
|
||||
prj = 'kibom-variant_3'
|
||||
ctx = context.TestContext('test_print_variant_1', prj, 'print_pcb_variant_1', '')
|
||||
ctx.run()
|
||||
# Check all outputs are there
|
||||
fname = prj+'-F_Fab.pdf'
|
||||
ctx.expect_out_file(fname)
|
||||
ctx.compare_pdf(fname)
|
||||
ctx.clean_up()
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ class TestContext(object):
|
|||
image,
|
||||
reference,
|
||||
# Avoid the part where KiCad version is printed
|
||||
'-crop', '100%x92%+0+0', '+repage',
|
||||
'-crop', '100%x88%+0+0', '+repage',
|
||||
'-colorspace', 'RGB',
|
||||
self.get_out_path(diff)]
|
||||
logging.debug('Comparing images with: '+str(cmd))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# Example KiBot config file
|
||||
kibot:
|
||||
version: 1
|
||||
|
||||
variants:
|
||||
- name: 'default'
|
||||
comment: 'Default variant'
|
||||
type: ibom
|
||||
variants_blacklist: T2,T3
|
||||
|
||||
outputs:
|
||||
- name: 'pdf_default'
|
||||
comment: "PCB print w/variant"
|
||||
type: pdf_pcb_print
|
||||
options:
|
||||
variant: default
|
||||
layers: F.Fab
|
||||
Loading…
Reference in New Issue