diff --git a/CHANGELOG.md b/CHANGELOG.md index 1db8ba9c..445ddd29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - FusionPCB: gerber, drill and compress - Elecrow: gerber, drill and compress - JLCPCB: gerber, drill, position, BoM and compress + - MacroFab_XYRS: XYRS position file compatible with MacroFab - P-Ban: gerber, drill and compress + - PCB2Blender_2_1: generates a pcb2blender 2.1 file to import on Blender - PCBWay: gerber, drill and compress - Plot related outputs and PCB_Print: - Added support for the KiCad 6 "sketch_pads_on_fab_layers" option. (#356) diff --git a/README.md b/README.md index 9b9b4a52..827038a0 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ For example, it's common that you might want for each board rev: * Gerbers, drills and drill maps for a fab in their favourite format * Fab docs for the assembler, including the BoM (Bill of Materials), costs spreadsheet and board view * Pick and place files -* PCB 3D model in STEP and VRML format +* PCB 3D model in STEP, VRML and PCB3D formats * PCB 3D render in PNG format * Compare PCB/SCHs * Panelization @@ -1371,7 +1371,7 @@ The available values for *type* are: - 3D model: - `step` *Standard for the Exchange of Product Data* for the PCB - `vrml` *Virtual Reality Modeling Language* for the PCB - - `render_3d` PCB render, from the KiCad's 3D Viewer (broken in KiCad 6.0.0) + - `render_3d` PCB render, from the KiCad's 3D Viewer - Web pages: - `populate` To create step-by-step assembly instructions. - `kikit_present` To create a project presentation web page. @@ -3104,6 +3104,47 @@ Notes: Internally we use 10 for low priority, 90 for high priority and 50 for most outputs. - `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested. +* PCB2Blender Tools + * Type: `pcb2blender_tools` + * Description: A bunch of tools used to generate PCB3D files used to export PCBs to Blender. + Blender is the most important free software 3D render package. + The PCB3D file format is used by the PCB2Blender project (https://github.com/30350n/pcb2blender) + to import KiCad PCBs in Blender. + You need to install a Blender plug-in to load PCB3D files. + The tools in this output are used by internal templates used to generate PCB3D files. + * Valid keys: + - **`comment`**: [string=''] A comment for documentation purposes. It helps to identify the output. + - **`dir`**: [string='./'] Output directory for the generated files. + If it starts with `+` the rest is concatenated to the default dir. + - **`name`**: [string=''] Used to identify this particular output definition. + Avoid using `_` as first character. These names are reserved for KiBot. + - **`options`**: [dict] Options for the `pcb2blender_tools` output. + * Valid keys: + - **`output`**: [string='%f-%i%I%v.%x'] Filename for the output (%i=pcb2blender, %x=pcb3d). Affected by global options. + - `board_bounds_create`: [boolean=true] Create the file that informs the size of the used PCB area. + This is the bounding box reported by KiCad for the PCB edge with 1 mm of margin. + - `board_bounds_dir`: [string='layers'] Sub-directory where the bounds file is stored. + - `board_bounds_file`: [string='bounds'] Name of the bounds file. + - `dnf_filter`: [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. + - `pads_info_create`: [boolean=true] Create the files containing the PCB pads information. + - `pads_info_dir`: [string='pads'] Sub-directory where the pads info files are stored. + - `pre_transform`: [string|list(string)='_none'] Name of the filter to transform fields before applying other filters. + A short-cut to use for simple cases where a variant is an overkill. + - `variant`: [string=''] Board variant to apply. + - `category`: [string|list(string)=''] The category for this output. If not specified an internally defined category is used. + Categories looks like file system paths, i.e. **PCB/fabrication/gerber**. + The categories are currently used for `navigate_results`. + - `disable_run_by_default`: [string|boolean] Use it to disable the `run_by_default` status of other output. + Useful when this output extends another and you don't want to generate the original. + Use the boolean true value to disable the output you are extending. + - `extends`: [string=''] Copy the `options` section from the indicated output. + Used to inherit options from another output of the same type. + - `output_id`: [string=''] Text to use for the %I expansion content. To differentiate variations of this output. + - `priority`: [number=50] [0,100] Priority for this output. High priority outputs are created first. + Internally we use 10 for low priority, 90 for high priority and 50 for most outputs. + - `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested. + * PCB Print * Type: `pcb_print` * Description: Prints the PCB using a mechanism that is more flexible than `pdf_pcb_print` and `svg_pcb_print`. @@ -4747,6 +4788,12 @@ They include support for: - _P-Ban_gerbers: Gerbers. You need to define the layers for more than 8. - _P-Ban_drill: Drill files - _P-Ban: _P-Ban_gerbers+_P-Ban_drill +- [PCB2Blender_2_1](https://github.com/30350n/pcb2blender) + - _PCB2Blender_layers_2_1: The layers in SVG format. Disabled by default. + - _PCB2Blender_vrml_2_1: The VRML for the board. Disabled by default. + - _PCB2Blender_tools_2_1: Pads and bounds information. Disabled by default. + - _PCB2Blender_2_1: The PCB3D file. Is enabled and creates the other files. + - _PCB2Blender_elements_2_1: _PCB2Blender_tools_2_1+_PCB2Blender_layers_2_1+_PCB2Blender_vrml_2_1 - [PCBWay](https://www.pcbway.com): contain fabrication outputs compatible with PCBWay - _PCBWay_gerbers: Gerbers - _PCBWay_drill: Drill files diff --git a/docs/README.in b/docs/README.in index 7bce8d1d..afb3fab6 100644 --- a/docs/README.in +++ b/docs/README.in @@ -96,7 +96,7 @@ For example, it's common that you might want for each board rev: * Gerbers, drills and drill maps for a fab in their favourite format * Fab docs for the assembler, including the BoM (Bill of Materials), costs spreadsheet and board view * Pick and place files -* PCB 3D model in STEP and VRML format +* PCB 3D model in STEP, VRML and PCB3D formats * PCB 3D render in PNG format * Compare PCB/SCHs * Panelization @@ -748,7 +748,7 @@ The available values for *type* are: - 3D model: - `step` *Standard for the Exchange of Product Data* for the PCB - `vrml` *Virtual Reality Modeling Language* for the PCB - - `render_3d` PCB render, from the KiCad's 3D Viewer (broken in KiCad 6.0.0) + - `render_3d` PCB render, from the KiCad's 3D Viewer - Web pages: - `populate` To create step-by-step assembly instructions. - `kikit_present` To create a project presentation web page. @@ -1311,6 +1311,12 @@ They include support for: - _P-Ban_gerbers: Gerbers. You need to define the layers for more than 8. - _P-Ban_drill: Drill files - _P-Ban: _P-Ban_gerbers+_P-Ban_drill +- [PCB2Blender_2_1](https://github.com/30350n/pcb2blender) + - _PCB2Blender_layers_2_1: The layers in SVG format. Disabled by default. + - _PCB2Blender_vrml_2_1: The VRML for the board. Disabled by default. + - _PCB2Blender_tools_2_1: Pads and bounds information. Disabled by default. + - _PCB2Blender_2_1: The PCB3D file. Is enabled and creates the other files. + - _PCB2Blender_elements_2_1: _PCB2Blender_tools_2_1+_PCB2Blender_layers_2_1+_PCB2Blender_vrml_2_1 - [PCBWay](https://www.pcbway.com): contain fabrication outputs compatible with PCBWay - _PCBWay_gerbers: Gerbers - _PCBWay_drill: Drill files diff --git a/docs/samples/generic_plot.kibot.yaml b/docs/samples/generic_plot.kibot.yaml index d0d04568..b27759d4 100644 --- a/docs/samples/generic_plot.kibot.yaml +++ b/docs/samples/generic_plot.kibot.yaml @@ -1833,6 +1833,38 @@ outputs: units: 'mm' # [string=''] Board variant to apply variant: '' + # PCB2Blender Tools: + # Blender is the most important free software 3D render package. + # The PCB3D file format is used by the PCB2Blender project (https://github.com/30350n/pcb2blender) + # to import KiCad PCBs in Blender. + # You need to install a Blender plug-in to load PCB3D files. + # The tools in this output are used by internal templates used to generate PCB3D files. + - name: 'pcb2blender_tools_example' + comment: 'A bunch of tools used to generate PCB3D files used to export PCBs to Blender.' + type: 'pcb2blender_tools' + dir: 'Example/pcb2blender_tools_dir' + options: + # [boolean=true] Create the file that informs the size of the used PCB area. + # This is the bounding box reported by KiCad for the PCB edge with 1 mm of margin + board_bounds_create: true + # [string='layers'] Sub-directory where the bounds file is stored + board_bounds_dir: 'layers' + # [string='bounds'] Name of the bounds file + board_bounds_file: 'bounds' + # [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='%f-%i%I%v.%x'] Filename for the output (%i=pcb2blender, %x=pcb3d). Affected by global options + output: '%f-%i%I%v.%x' + # [boolean=true] Create the files containing the PCB pads information + pads_info_create: true + # [string='pads'] Sub-directory where the pads info files are stored + pads_info_dir: 'pads' + # [string|list(string)='_none'] Name of the filter to transform fields before applying other filters. + # A short-cut to use for simple cases where a variant is an overkill + pre_transform: '_none' + # [string=''] Board variant to apply + variant: '' # PCB Print: # Supports PDF, SVG, PNG, EPS and PS formats. # KiCad 5: including the frame is slow. diff --git a/kibot/out_pcb2blender_tools.py b/kibot/out_pcb2blender_tools.py new file mode 100644 index 00000000..bf6b429f --- /dev/null +++ b/kibot/out_pcb2blender_tools.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2023 Salvador E. Tropea +# Copyright (c) 2023 Instituto Nacional de TecnologĂ­a Industrial +# License: GPL-3.0 +# Project: KiBot (formerly KiPlot) +# Some code is adapted from: https://github.com/30350n/pcb2blender +import os +import struct +from pcbnew import B_Paste, F_Paste +from .gs import GS +from .misc import MOD_THROUGH_HOLE, MOD_SMD, UI_VIRTUAL +from .out_base import VariantOptions +from .macros import macros, document, output_class # noqa: F401 +from . import log + +logger = log.get_logger() + + +class PCB2Blender_ToolsOptions(VariantOptions): + def __init__(self): + with document: + self.output = GS.def_global_output + """ *Filename for the output (%i=pcb2blender, %x=pcb3d) """ + self.board_bounds_create = True + """ Create the file that informs the size of the used PCB area. + This is the bounding box reported by KiCad for the PCB edge with 1 mm of margin """ + self.board_bounds_dir = 'layers' + """ Sub-directory where the bounds file is stored """ + self.board_bounds_file = 'bounds' + """ Name of the bounds file """ + self.pads_info_create = True + """ Create the files containing the PCB pads information """ + self.pads_info_dir = 'pads' + """ Sub-directory where the pads info files are stored """ + super().__init__() + self._expand_id = 'pcb2blender' + self._expand_ext = 'pcb3d' + + def do_board_bounds(self, dir_name): + if not self.board_bounds_create: + return + dir_name = os.path.join(dir_name, self.board_bounds_dir) + os.makedirs(dir_name, exist_ok=True) + fname = os.path.join(dir_name, self.board_bounds_file) + # PCB bounding box using the PCB edge, converted to mm + bounds = tuple(map(GS.to_mm, GS.board.ComputeBoundingBox(aBoardEdgesOnly=True).getWxRect())) + # Apply 1 mm margin (x, y, w, h) + bounds = (bounds[0]-1, bounds[1]-1, bounds[2]+2, bounds[3]+2) + with open(fname, 'wb') as f: + # Four big endian float numbers + f.write(struct.pack("!ffff", *bounds)) + + @staticmethod + def is_not_virtual_ki6(m): + return bool(m.GetAttributes() & (MOD_THROUGH_HOLE | MOD_SMD)) + + @staticmethod + def is_not_virtual_ki5(m): + return bool(m.GetAttributes() != UI_VIRTUAL) + + def do_pads_info(self, dir_name): + if not self.pads_info_create: + return + dir_name = os.path.join(dir_name, self.pads_info_dir) + os.makedirs(dir_name, exist_ok=True) + is_not_virtual = self.is_not_virtual_ki5 if GS.ki5 else self.is_not_virtual_ki6 + for i, footprint in enumerate(GS.get_modules()): + has_model = len(footprint.Models()) > 0 + is_tht_or_smd = is_not_virtual(footprint) + value = footprint.GetValue() + reference = footprint.GetReference() + for j, pad in enumerate(footprint.Pads()): + name = os.path.join(dir_name, "{}_{}_{}_{}".format(value, reference, i, j)) + is_flipped = pad.IsFlipped() + has_paste = pad.IsOnLayer(B_Paste if is_flipped else F_Paste) + with open(name, 'wb') as f: + f.write(struct.pack("!ff????BBffffBff", + *map(GS.to_mm, pad.GetPosition()), + is_flipped, + has_model, + is_tht_or_smd, + has_paste, + pad.GetAttribute(), + pad.GetShape(), + *map(GS.to_mm, pad.GetSize()), + pad.GetOrientationRadians(), + pad.GetRoundRectRadiusRatio(), + pad.GetDrillShape(), + *map(GS.to_mm, pad.GetDrillSize()))) + + def run(self, output): + super().run(output) + dir_name = os.path.dirname(output) + self.filter_pcb_components(do_3D=True) + self.do_board_bounds(dir_name) + self.do_pads_info(dir_name) + self.unfilter_pcb_components(do_3D=True) + + def get_targets(self, out_dir): + files = [] + if self.board_bounds_create: + files.append(os.path.join(out_dir, self.board_bounds_dir, self.board_bounds_file)) + if self.pads_info_create: + dir_name = os.path.join(out_dir, self.pads_info_dir) + for i, footprint in enumerate(GS.get_modules()): + value = footprint.GetValue() + reference = footprint.GetReference() + for j in range(len(footprint.Pads())): + files.append(os.path.join(dir_name, "{}_{}_{}_{}".format(value, reference, i, j))) + return files + + +@output_class +class PCB2Blender_Tools(BaseOutput): # noqa: F821 + """ PCB2Blender Tools + A bunch of tools used to generate PCB3D files used to export PCBs to Blender. + Blender is the most important free software 3D render package. + The PCB3D file format is used by the PCB2Blender project (https://github.com/30350n/pcb2blender) + to import KiCad PCBs in Blender. + You need to install a Blender plug-in to load PCB3D files. + The tools in this output are used by internal templates used to generate PCB3D files. """ + def __init__(self): + super().__init__() + self._category = 'PCB/3D' + with document: + self.options = PCB2Blender_ToolsOptions + """ *[dict] Options for the `pcb2blender_tools` output """ diff --git a/kibot/resources/config_templates/PCB2Blender_2_1.kibot.yaml b/kibot/resources/config_templates/PCB2Blender_2_1.kibot.yaml new file mode 100644 index 00000000..05204531 --- /dev/null +++ b/kibot/resources/config_templates/PCB2Blender_2_1.kibot.yaml @@ -0,0 +1,55 @@ +# PCB2Blender (PCB3D) format for plug-in v2.1 +# URL: https://github.com/30350n/pcb2blender +kibot: + version: 1 + +groups: + - name: _PCB2Blender_elements_2_1 + outputs: + - _PCB2Blender_tools_2_1 + - _PCB2Blender_layers_2_1 + - _PCB2Blender_vrml_2_1 + +outputs: + - name: _PCB2Blender_tools_2_1 + comment: Pads information and board bounds for PCB3D 2.1 + type: pcb2blender_tools + dir: '%f%v_pcb3d' + run_by_default: false + + - name: _PCB2Blender_layers_2_1 + comment: SVG files for the layers for PCB3D 2.1 + type: svg + dir: '%f%v_pcb3d/layers' + run_by_default: false + options: + output: "%i.%x" + margin: 1 + limit_viewbox: true + svg_precision: 6 + layers: ["F.Cu", "B.Cu", "F.Paste", "B.Paste", "F.SilkS", "B.SilkS", "F.Mask", "B.Mask"] + + - name: _PCB2Blender_vrml_2_1 + comment: VRML model for PCB3D 2.1 + type: vrml + dir: '%f%v_pcb3d' + run_by_default: false + options: + output: pcb.wrl + dir_models: components + use_pcb_center_as_ref: false + model_units: meters + + - name: _PCB2Blender_2_1 + comment: PCB3D model for pcb2blender plug-in + type: compress + options: + output: '%f%v.pcb3d' + format: ZIP + files: + - from_output: _PCB2Blender_tools_2_1 + from_output_dir: true + - from_output: _PCB2Blender_layers_2_1 + dest: layers + - from_output: _PCB2Blender_vrml_2_1 + from_output_dir: true diff --git a/tests/yaml_samples/pcb2blender_tools_1.kibot.yaml b/tests/yaml_samples/pcb2blender_tools_1.kibot.yaml new file mode 100644 index 00000000..b1c29c8b --- /dev/null +++ b/tests/yaml_samples/pcb2blender_tools_1.kibot.yaml @@ -0,0 +1,9 @@ +# Example KiBot config file +kibot: + version: 1 + +outputs: + - name: pcb2blender + comment: "PCB to Blender tools test" + dir: pcb2blender + type: pcb2blender_tools diff --git a/tests/yaml_samples/pcb2blender_tools_2.kibot.yaml b/tests/yaml_samples/pcb2blender_tools_2.kibot.yaml new file mode 100644 index 00000000..4a978268 --- /dev/null +++ b/tests/yaml_samples/pcb2blender_tools_2.kibot.yaml @@ -0,0 +1,6 @@ +# Example KiBot config file +kibot: + version: 1 + +import: + - file: PCB2Blender_2_1