From d09400d49ddc66eec81216d583c725501d999362 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Fri, 4 Sep 2020 18:08:22 -0300 Subject: [PATCH] Added support for variants to the PcbDraw output. --- CHANGELOG.md | 1 + Makefile | 2 + README.md | 5 +- docs/samples/generic_plot.kibot.yaml | 7 +- kibot/out_pcbdraw.py | 69 +++++++++++++----- tests/reference/kibom-variant_3-top-C1.png | Bin 0 -> 4314 bytes tests/reference/kibom-variant_3-top.png | Bin 0 -> 4311 bytes tests/test_plot/test_pcbdraw.py | 36 +++++++++ tests/yaml_samples/pcbdraw_simple.kibot.yaml | 1 + .../yaml_samples/pcbdraw_variant_1.kibot.yaml | 18 +++++ .../yaml_samples/pcbdraw_variant_2.kibot.yaml | 22 ++++++ .../yaml_samples/pcbdraw_variant_3.kibot.yaml | 19 +++++ 12 files changed, 161 insertions(+), 19 deletions(-) create mode 100644 tests/reference/kibom-variant_3-top-C1.png create mode 100644 tests/reference/kibom-variant_3-top.png create mode 100644 tests/yaml_samples/pcbdraw_variant_1.kibot.yaml create mode 100644 tests/yaml_samples/pcbdraw_variant_2.kibot.yaml create mode 100644 tests/yaml_samples/pcbdraw_variant_3.kibot.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index ba00b85b..05c48325 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Drawings removed from *.Adhes - Components crossed in *.Fab - STEP (3D) support for variants. +- PcbDraw support for variants. ### Fixed - Virtual components are always excluded from position files. diff --git a/Makefile b/Makefile index 0e0fb40c..1c1102b1 100644 --- a/Makefile +++ b/Makefile @@ -110,6 +110,8 @@ gen_ref: mv "$(REFDIR)no_inductor/test_v5-schematic_(no_L).pdf" $(REFDIR) rmdir $(REFDIR)no_inductor/ 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) cp -a $(REFILL).ok $(REFILL) doc: diff --git a/README.md b/README.md index 7efee06a..f1f9f4fb 100644 --- a/README.md +++ b/README.md @@ -725,6 +725,8 @@ Next time you need this list just use an alias, like this: - `options`: [dict] Options for the `pcbdraw` output. * Valid keys: - `bottom`: [boolean=false] render the bottom side of the board (default is top side). + - `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. - `dpi`: [number=300] [10,1200] dots per inch (resolution) of the generated image. - `format`: [string='svg'] [svg,png,jpg] output format. Only used if no `output` is specified. - `highlight`: [list(string)=[]] list of components to highlight. @@ -734,7 +736,7 @@ Next time you need this list just use an alias, like this: - `output`: [string='%f-%i%v.%x'] name for the generated file. Affected by global options. - `placeholder`: [boolean=false] show placeholder for missing components. - `remap`: [dict|None] replacements for PCB references using components (lib:component). - - `show_components`: [string|list(string)=none] [none,all] list of components to draw, can be also a string for none or all. + - `show_components`: [list(string)|string=none] [none,all] list of components to draw, can be also a string for none or all. The default is none. - `style`: [string|dict] PCB style (colors). An internal name, the name of a JSON file or the style options. * Valid keys: @@ -748,6 +750,7 @@ Next time you need this list just use an alias, like this: - `pads`: [string='#b5ae30'] color for the exposed pads (metal finish). - `silk`: [string='#f0f0f0'] color for the silk screen. - `vcut`: [string='#bf2600'] color for the V-CUTS. + - `variant`: [string=''] Board variant to apply. - `vcuts`: [boolean=false] render V-CUTS on the Cmts.User layer. - `warnings`: [string='visible'] [visible,all,none] using visible only the warnings about components in the visible side are generated. diff --git a/docs/samples/generic_plot.kibot.yaml b/docs/samples/generic_plot.kibot.yaml index e6a8d6a8..0bd0aa91 100644 --- a/docs/samples/generic_plot.kibot.yaml +++ b/docs/samples/generic_plot.kibot.yaml @@ -531,6 +531,9 @@ outputs: options: # [boolean=false] render the bottom side of the board (default is top side) bottom: false + # [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: '' # [number=300] [10,1200] dots per inch (resolution) of the generated image dpi: 300 # [string='svg'] [svg,png,jpg] output format. Only used if no `output` is specified @@ -549,7 +552,7 @@ outputs: placeholder: false # [dict|None] replacements for PCB references using components (lib:component) remap: - # [string|list(string)=none] [none,all] list of components to draw, can be also a string for none or all. + # [list(string)|string=none] [none,all] list of components to draw, can be also a string for none or all. # The default is none show_components: none # [string|dict] PCB style (colors). An internal name, the name of a JSON file or the style options @@ -574,6 +577,8 @@ outputs: silk: '#f0f0f0' # [string='#bf2600'] color for the V-CUTS vcut: '#bf2600' + # [string=''] Board variant to apply + variant: '' # [boolean=false] render V-CUTS on the Cmts.User layer vcuts: false # [string='visible'] [visible,all,none] using visible only the warnings about components in the visible side are generated diff --git a/kibot/out_pcbdraw.py b/kibot/out_pcbdraw.py index 96369194..f68dcec8 100644 --- a/kibot/out_pcbdraw.py +++ b/kibot/out_pcbdraw.py @@ -13,7 +13,10 @@ from .misc import PCBDRAW, PCBDRAW_ERR, URL_PCBDRAW from .kiplot import check_script from .error import KiPlotConfigurationError from .gs import (GS) -from .optionable import (BaseOptions, Optionable) +from .optionable import BaseOptions, Optionable +from .registrable import RegOutput +from .kiplot import load_sch +from .fil_base import BaseFilter, apply_fitted_filter from .macros import macros, document, output_class # noqa: F401 from . import log @@ -136,6 +139,11 @@ class PcbDrawOptions(BaseOptions): """ [svg,png,jpg] output format. Only used if no `output` is specified """ self.output = GS.def_global_output """ name for the generated file """ + self.variant = '' + """ Board variant to apply """ + self.dnf_filter = Optionable + """ [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 """ super().__init__() def config(self): @@ -150,10 +158,15 @@ class PcbDrawOptions(BaseOptions): self.highlight = None else: self.highlight = ','.join(self.highlight) + # Variants + self.variant = RegOutput.check_variant(self.variant) + self.dnf_filter = BaseFilter.solve_filter(self.dnf_filter, 'dnf_filter') # Filter if isinstance(self.show_components, type): self.show_components = '' elif isinstance(self.show_components, str): + if self.variant or self.dnf_filter: + logger.warning('Ambiguous list of components to show `{}` vs variant/filter'.format(self.show_components)) if self.show_components == 'none': self.show_components = '' else: @@ -204,6 +217,39 @@ class PcbDrawOptions(BaseOptions): f.close() return f.name + def get_filtered_refs(self): + if not self.dnf_filter and not self.variant: + return [] + load_sch() + # Get the components list from the schematic + comps = GS.sch.get_components() + # Apply the filter + apply_fitted_filter(comps, self.dnf_filter) + # Apply the variant + if self.variant: + # Apply the variant + self.variant.filter(comps) + return [c.ref for c in comps if c.fitted] + + def _append_output(self, cmd, output): + svg = None + if self.format == 'svg': + cmd.append(output) + else: + # PNG and JPG outputs are unreliable + if shutil.which(SVG2PNG) is None: + logger.warning('`{}` not installed, using unreliable PNG/JPG conversion'.format(SVG2PNG)) + logger.warning('If you experiment problems install `librsvg2-bin` or equivalent') + cmd.append(output) + elif shutil.which(CONVERT) is None: + logger.warning('`{}` not installed, using unreliable PNG/JPG conversion'.format(CONVERT)) + logger.warning('If you experiment problems install `imagemagick` or equivalent') + cmd.append(output) + else: + svg = _get_tmp_name('.svg') + cmd.append(svg) + return svg + def run(self, output_dir, board): check_script(PCBDRAW, URL_PCBDRAW, '0.6.0') # Output file name @@ -231,6 +277,10 @@ class PcbDrawOptions(BaseOptions): if self.highlight: cmd.extend(['-a', self.highlight]) if self.show_components is not None: + to_add = ','.join(self.get_filtered_refs()) + if self.show_components and to_add: + self.show_components += ',' + self.show_components += to_add cmd.extend(['-f', self.show_components]) if self.vcuts: cmd.append('-v') @@ -247,22 +297,7 @@ class PcbDrawOptions(BaseOptions): tmp_remap = None # The board & output cmd.append(GS.pcb_file) - svg = None - if self.format == 'svg': - cmd.append(output) - else: - # PNG and JPG outputs are unreliable - if shutil.which(SVG2PNG) is None: - logger.warning('`{}` not installed, using unreliable PNG/JPG conversion'.format(SVG2PNG)) - logger.warning('If you experiment problems install `librsvg2-bin` or equivalent') - cmd.append(output) - elif shutil.which(CONVERT) is None: - logger.warning('`{}` not installed, using unreliable PNG/JPG conversion'.format(CONVERT)) - logger.warning('If you experiment problems install `imagemagick` or equivalent') - cmd.append(output) - else: - svg = _get_tmp_name('.svg') - cmd.append(svg) + svg = self._append_output(cmd, output) # Execute and inform is successful _run_command(cmd, tmp_remap, tmp_style) if svg is not None: diff --git a/tests/reference/kibom-variant_3-top-C1.png b/tests/reference/kibom-variant_3-top-C1.png new file mode 100644 index 0000000000000000000000000000000000000000..167932a577680e444ae0380efb27c8e772e2423d GIT binary patch literal 4314 zcmbVQ2T+qsyAGg-DjcNt&P8%9U@hvh> z9;Ut=WaABlLo4aTb42g5cnpeG{=A%+<0Y<09bL=!n(`XVVZN(&sg&-{2XWENB28Y@ z0Cuubw%4QErS@pOJ1e|7dS@#scfx)PW>1c8w^=`l883<6F5I8^eYhP{5`w<)nr=>4 zNKck{C36QYbA4uyNR2}1yicsA=C`KX1HKgbOSDeCLd0G0W#+8W-?`27Rql@5V=t!m z3q`_wi-crr_CF5g1hMNshH%%2_DA?yAN$3K3GeCM=iij68E^+N2-UJ2Fr=OB z5gd-as}>H)7QvhvGp9@eDlu68{PSwWQ%Q}kV!gMctJjIU$xx*HY;kL4ZKShv9?N#m^CR?!us26lJXNA)?zqLmJpBVJ?67iTT zY!VY6lDjRJ@^(FU#84UsYRNT#AH@S1s+N)brSKSqgA~QRh^RS{HUYWr&K^l==^drG zYZhOXW`;7>E?C5k?!300gAMO_5X4SN^n)R(ud+T7) zQ7VC;g1v@5R+d})8~@mcTMl#FV))kLlI`=-o1eBiUW~{5yeq2%>#cPPksoVnlR{>{ zDu#Z9W<$tC@38{~SVp1a{rgqUE6oNwYZNLs@+In$rY-W7r(#H?b2S8uziP7*`SlT7 z*y5Bb!s^TFhF(o9Rg?38!3bd4X7F?ScNW*{6<38qGs|ae#o3=)D2#vaZezyORv3$4 z=_+N^YVOF*0TZ+EUM=*%W|(~U)jrX}WAX&*#a|j@P9yp1`9HT&Uc4}Lq3L2z1|Ee* z*qA3L<7oI~rV|WBGCKMQ2yl#j8A3Ixu8u;OD6)kOe*sOUCFAmDSh_p2%q^{!G>oYn z8Yiofk4W&G4weGuixW+@uE2KjgxD_5Cw@37k?2c~$?_g*T;HB`h*VdfEuoVFZLN3I z>pXxtjsU^%66}MPAKd~RIy=J*1!9~W{yixVYtQz&g4(hX4#t@~gypgJ>|f2v<-_fO z+v}?R+w-!Ul2e3{oDQr6DObLm?d}uQBPYPhv$=ez0jziXzpfdc*fQexVUeN89c-A_+7lP~UkCSmNlW zy*&58WqEAt$cz?b4ZB{B_%bNo+sPY_!feTRKVz(6|G4Q=xK$2VI5-`suN z#yBDjCdeE2e$besa-G6jbe_H4^~=Cp;}4$^WPUVIYXk zEEnH!2#d5%_-?#<(U(y;UsI8dicx*p$A4%sk27i8T1Y1~r5&tN`niJwJTIXLA4fPc z5HBn5w;Aln%>p}ytkyT&N<-LExcIE$Ej&WRPB(QZPeosr?%&%0A(vsHkgursvWUUQ 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 literal 0 HcmV?d00001 diff --git a/tests/reference/kibom-variant_3-top.png b/tests/reference/kibom-variant_3-top.png new file mode 100644 index 0000000000000000000000000000000000000000..2197a984b0634628a230a17cc3214b9ccf7c4434 GIT binary patch literal 4311 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 e(ZS<|qpLIQfA0Vl