diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ec44a8d..3cbd67e31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support for CURRENT_DATE text variable - Populate: - Basic support for regular list items (#480) +- Position: + - Experimental support for gerber position files (#500) - Help for the error levels - Warnings: - Explain about wrong dir/output separation (#493) diff --git a/docs/samples/generic_plot.kibot.yaml b/docs/samples/generic_plot.kibot.yaml index c04bd873a..a10d8de0f 100644 --- a/docs/samples/generic_plot.kibot.yaml +++ b/docs/samples/generic_plot.kibot.yaml @@ -2769,15 +2769,19 @@ outputs: # [string|list(string)='_none'] 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: '_none' - # [string='ASCII'] [ASCII,CSV] Format for the position file + # [string='ASCII'] [ASCII,CSV,GBR] Format for the position file. + # Note that the gerber format (GBR) needs KiCad 7+ and doesn't support most of the options. + # Only the options that explicitly say the format is supported format: 'ASCII' + # [boolean=false] Include the board edge in the gerber output + gerber_board_edge: false # [boolean=false] Include virtual components. For special purposes, not pick & place. # Note that virtual components is a KiCad 5 concept. # For KiCad 6+ we replace this concept by the option to exclude from position file include_virtual: false # [boolean=true] Only include the surface mount components only_smd: true - # [string='%f-%i%I%v.%x'] Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'). + # [string='%f-%i%I%v.%x'] Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'|'gbr'). # Important: when using separate files you must use `%i` to differentiate them. Affected by global options output: '%f-%i%I%v.%x' # [string|list(string)='_none'] Name of the filter to transform fields before applying other filters. @@ -2791,7 +2795,8 @@ outputs: separate_files_for_front_and_back: true # [string='millimeters'] [millimeters,inches,mils] Units used for the positions. Affected by global options units: 'millimeters' - # [boolean=true] Use the auxiliary axis as origin for coordinates (KiCad default) + # [boolean=true] Use the auxiliary axis as origin for coordinates (KiCad default). + # Supported by the gerber format use_aux_axis_as_origin: true # [string=''] Board variant to apply variant: '' @@ -2924,8 +2929,8 @@ outputs: options: # [string='QR'] Short name for the library lib: 'QR' - # [string='%f-%i%I%v.%x'] Filename/dirname for the output (%i=qr, %x=lib/kicad_sym/pretty). - # You must use %x in the name to get a symbols lib and a footprint. Affected by global options + # [string='%f-%i%I%v.%x'] Filename/dirname for the output library (%i=qr, %x=lib/kicad_sym/pretty). + # You must use %x in the name to get a symbols lib and a footprints lib. Affected by global options output: '%f-%i%I%v.%x' # [list(dict)] QR codes to include in the library qrs: diff --git a/docs/source/configuration/outputs/position.rst b/docs/source/configuration/outputs/position.rst index b52d3df13..e13499c04 100644 --- a/docs/source/configuration/outputs/position.rst +++ b/docs/source/configuration/outputs/position.rst @@ -24,9 +24,11 @@ Parameters: - Valid keys: - - **format** :index:`: ` [string='ASCII'] [ASCII,CSV] Format for the position file. + - **format** :index:`: ` [string='ASCII'] [ASCII,CSV,GBR] Format for the position file. + Note that the gerber format (GBR) needs KiCad 7+ and doesn't support most of the options. + Only the options that explicitly say the format is supported. - **only_smd** :index:`: ` [boolean=true] Only include the surface mount components. - - **output** :index:`: ` [string='%f-%i%I%v.%x'] Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'). + - **output** :index:`: ` [string='%f-%i%I%v.%x'] Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'|'gbr'). Important: when using separate files you must use `%i` to differentiate them. Affected by global options. - **separate_files_for_front_and_back** :index:`: ` [boolean=true] Generate two separated files, one for the top and another for the bottom. - **units** :index:`: ` [string='millimeters'] [millimeters,inches,mils] Units used for the positions. Affected by global options. @@ -41,6 +43,7 @@ Parameters: - ``dnf_filter`` :index:`: ` [string|list(string)='_none'] Name of the filter to mark components as not fitted. A short-cut to use for simple cases where a variant is an overkill. + - ``gerber_board_edge`` :index:`: ` [boolean=false] Include the board edge in the gerber output. - ``include_virtual`` :index:`: ` [boolean=false] Include virtual components. For special purposes, not pick & place. Note that virtual components is a KiCad 5 concept. For KiCad 6+ we replace this concept by the option to exclude from position file. @@ -50,6 +53,7 @@ Parameters: - ``quote_all`` :index:`: ` [boolean=false] When generating the CSV quote all values, even numbers. - ``right_digits`` :index:`: ` [number=4] number of digits for mantissa part of coordinates (0 is auto). - ``use_aux_axis_as_origin`` :index:`: ` [boolean=true] Use the auxiliary axis as origin for coordinates (KiCad default). + Supported by the gerber format. - ``variant`` :index:`: ` [string=''] Board variant to apply. - **type** :index:`: ` [string=''] Type of output. diff --git a/docs/source/configuration/outputs/qr_lib.rst b/docs/source/configuration/outputs/qr_lib.rst index 889149ebf..98ef9ce85 100644 --- a/docs/source/configuration/outputs/qr_lib.rst +++ b/docs/source/configuration/outputs/qr_lib.rst @@ -30,8 +30,8 @@ Parameters: - Valid keys: - **lib** :index:`: ` [string='QR'] Short name for the library. - - **output** :index:`: ` [string='%f-%i%I%v.%x'] Filename/dirname for the output (%i=qr, %x=lib/kicad_sym/pretty). - You must use %x in the name to get a symbols lib and a footprint. Affected by global options. + - **output** :index:`: ` [string='%f-%i%I%v.%x'] Filename/dirname for the output library (%i=qr, %x=lib/kicad_sym/pretty). + You must use %x in the name to get a symbols lib and a footprints lib. Affected by global options. - **qrs** :index:`: ` [list(dict)] QR codes to include in the library. - Valid keys: diff --git a/kibot/out_position.py b/kibot/out_position.py index 9a9a9769f..bf034d21d 100644 --- a/kibot/out_position.py +++ b/kibot/out_position.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2020-2022 Salvador E. Tropea -# Copyright (c) 2020-2022 Instituto Nacional de TecnologĂ­a Industrial +# Copyright (c) 2020-2023 Salvador E. Tropea +# Copyright (c) 2020-2023 Instituto Nacional de TecnologĂ­a Industrial # Copyright (c) 2019 Romain Deterre (@rdeterre) # License: GPL-3.0 # Project: KiBot (formerly KiPlot) @@ -10,6 +10,7 @@ from datetime import datetime from collections import OrderedDict from .gs import GS +from .kiplot import run_command from .misc import UI_SMD, UI_VIRTUAL, MOD_THROUGH_HOLE, MOD_SMD, MOD_EXCLUDE_FROM_POS_FILES from .optionable import Optionable from .out_base import VariantOptions @@ -60,13 +61,15 @@ class PositionOptions(VariantOptions): def __init__(self): with document: self.format = 'ASCII' - """ *[ASCII,CSV] Format for the position file """ + """ *[ASCII,CSV,GBR] Format for the position file. + Note that the gerber format (GBR) needs KiCad 7+ and doesn't support most of the options. + Only the options that explicitly say the format is supported """ self.separate_files_for_front_and_back = True """ *Generate two separated files, one for the top and another for the bottom """ self.only_smd = True """ *Only include the surface mount components """ self.output = GS.def_global_output - """ *Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'). + """ *Output file name (%i='top_pos'|'bottom_pos'|'both_pos', %x='pos'|'csv'|'gbr'). Important: when using separate files you must use `%i` to differentiate them """ self.units = 'millimeters' """ *[millimeters,inches,mils] Units used for the positions. Affected by global options """ @@ -78,13 +81,16 @@ def __init__(self): self.bottom_negative_x = False """ Use negative X coordinates for footprints on bottom layer """ self.use_aux_axis_as_origin = True - """ Use the auxiliary axis as origin for coordinates (KiCad default) """ + """ Use the auxiliary axis as origin for coordinates (KiCad default). + Supported by the gerber format """ self.include_virtual = False """ Include virtual components. For special purposes, not pick & place. Note that virtual components is a KiCad 5 concept. For KiCad 6+ we replace this concept by the option to exclude from position file """ self.quote_all = False """ When generating the CSV quote all values, even numbers """ + self.gerber_board_edge = False + """ Include the board edge in the gerber output """ super().__init__() self._expand_id = 'position' @@ -105,7 +111,7 @@ def config(self, parent): new_name = col.name if col.name else new_col new_columns[new_col] = new_name self.columns = new_columns - self._expand_ext = 'pos' if self.format == 'ASCII' else 'csv' + self._expand_ext = 'pos' if self.format == 'ASCII' else self.format.lower() def _do_position_plot_ascii(self, output_dir, columns, modulesStr, maxSizes, modules_side): topf = None @@ -224,10 +230,31 @@ def get_targets(self, out_dir): self.expand_filename(out_dir, self.output, 'bottom_pos', ext)] return [self.expand_filename(out_dir, self.output, 'both_pos', ext)] + def run_gerber(self, output_dir): + if not GS.ki7: + raise KiPlotConfigurationError("Gerber position needs KiCad 7+") + + pcb_name = self.save_tmp_board_if_variant() + cmd_base = ['kicad-cli', 'pcb', 'export', 'pos', '--format', 'gerber'] + if self.use_aux_axis_as_origin: + cmd_base.append('--use-drill-file-origin') + if self.gerber_board_edge: + cmd_base.append('--gerber-board-edge') + cmd_base.append('--side') + + fname = self.expand_filename(output_dir, self.output, 'top_pos', self._expand_ext) + run_command(cmd_base+['front', '-o', fname, pcb_name]) + + fname = self.expand_filename(output_dir, self.output, 'bottom_pos', self._expand_ext) + run_command(cmd_base+['back', '-o', fname, pcb_name]) + def run(self, fname): super().run(fname) - self.filter_pcb_components() output_dir = os.path.dirname(fname) + if self.format == 'GBR': + self.run_gerber(output_dir) + return + self.filter_pcb_components() columns = self.columns.values() conv = GS.unit_name_to_scale_factor(self.units) # Format all strings diff --git a/tests/test_plot/test_position.py b/tests/test_plot/test_position.py index 3ee2711e8..5b96da996 100644 --- a/tests/test_plot/test_position.py +++ b/tests/test_plot/test_position.py @@ -339,3 +339,13 @@ def test_position_flags_3(test_dir): ctx.run() check_comp_list(ctx.load_csv(prj+'-both_pos.csv')[0], {'R1', 'R2', 'R3'}) ctx.clean_up() + + +@pytest.mark.skipif(not context.ki7(), reason="needs kicad-cli") +def test_position_gerber_1(test_dir): + prj = 'light_control' + ctx = context.TestContext(test_dir, prj, 'simple_position_gbr', POS_DIR) + ctx.run() + ctx.expect_out_file_d(prj+'-top_pos.gbr') + ctx.expect_out_file_d(prj+'-bottom_pos.gbr') + ctx.clean_up(keep_project=True) diff --git a/tests/yaml_samples/simple_position_gbr.kibot.yaml b/tests/yaml_samples/simple_position_gbr.kibot.yaml new file mode 100644 index 000000000..d0cc5031f --- /dev/null +++ b/tests/yaml_samples/simple_position_gbr.kibot.yaml @@ -0,0 +1,11 @@ +# Example KiBot config file for a basic 2-layer board +kibot: + version: 1 + +outputs: + + - name: 'position' + type: position + dir: positiondir + options: + format: GBR