├── .gitignore ├── LICENSE ├── README.md ├── __init__.py ├── cfgman.py ├── examples ├── FlatStructure │ ├── FlatStructure.kicad_pcb │ ├── FlatStructure.kicad_pcb.hierpcb.json │ ├── FlatStructure.kicad_prl │ ├── FlatStructure.kicad_pro │ ├── FlatStructure.kicad_sch │ ├── SubPcb.kicad_pcb │ ├── SubPcb.kicad_pcb.hierpcb.json │ ├── SubPcb.kicad_prl │ ├── SubPcb.kicad_pro │ ├── SubPcb.kicad_sch │ └── fp-info-cache ├── FolderStructure │ ├── FolderStructure.kicad_pcb │ ├── FolderStructure.kicad_pcb.hierpcb.json │ ├── FolderStructure.kicad_prl │ ├── FolderStructure.kicad_pro │ ├── FolderStructure.kicad_sch │ ├── SubPcb │ │ ├── SubPcb.kicad_pcb │ │ ├── SubPcb.kicad_prl │ │ ├── SubPcb.kicad_pro │ │ ├── SubPcb.kicad_sch │ │ └── fp-info-cache │ └── fp-info-cache └── TripleRecursion │ ├── FolderStructure.kicad_pcb │ ├── FolderStructure.kicad_pcb.hierpcb.json │ ├── FolderStructure.kicad_prl │ ├── FolderStructure.kicad_pro │ ├── FolderStructure.kicad_sch │ ├── SubPcb │ ├── SubPcb.kicad_pcb │ ├── SubPcb.kicad_pcb.hierpcb.json │ ├── SubPcb.kicad_prl │ ├── SubPcb.kicad_pro │ ├── SubPcb.kicad_sch │ ├── fp-info-cache │ ├── nType │ │ ├── fp-info-cache │ │ ├── nType.kicad_pcb │ │ ├── nType.kicad_pcb.hierpcb.json │ │ ├── nType.kicad_prl │ │ ├── nType.kicad_pro │ │ └── nType.kicad_sch │ └── pType │ │ ├── fp-info-cache │ │ ├── pType.kicad_pcb │ │ ├── pType.kicad_pcb.hierpcb.json │ │ ├── pType.kicad_prl │ │ ├── pType.kicad_pro │ │ └── pType.kicad_sch │ └── fp-info-cache ├── hdata.py ├── hplugin.py ├── icon.png ├── images ├── 03_kle_placement.png ├── 04_input_hierarchical_pcb.png ├── 05_hierarchical_pcb.png ├── 06_menubutton.png ├── 11_even_with_rotations.png ├── 12_menu.png ├── 14_subpcb.png ├── 14_subsch.png └── icon-orig.png ├── interface ├── DlgHPCBRun.py ├── DlgHPCBRun_Base.py ├── HierarchicalPCB.fbp └── __init__.py └── placement.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Gaurav Manek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HierarchicalPcb 2 | 3 | This provides a true hierarchical PCB layout engine in KiCad, mirroring its hierarchical schematic capabilities. 4 | 5 | Instead of laying out everything in your final file, you can define a sub-PCB next to your sub-schematic file, and HierarchicalPCB will automatically force the sub-PCB to be laid out exactly like that in the final PCB. There's no depth limitation to the nesting -- in fact, we encourage organizing your PCB layouts for maximum reusability. 6 | 7 | This is inspired by the venerable [`ReplicateLayout`](https://github.com/MitjaNemec/ReplicateLayout) plugin, and is intended to be a more modular, powerful, and flexible replacement. 8 | 9 | **Installation**: 10 | 11 | This requires KiCad 8 to run. For now, you have to manually download or clone this repository to the plugin directory. On Windows, this is `C:\Users\\Documents\KiCad\8.0\3rdparty\plugins`. 12 | 13 | I'm working on getting this deployed to the official repository or on hosting my own. 14 | 15 | ## How To Use 16 | 17 | In summary: 18 | 19 | 1. Organize your schematic into hierarchical sheets, as you would normally do. 20 | 2. Create a `.kicad_pcb` file next to a hierarchical sheet and set up the placement of the sub-PCB there. 21 | 22 | a. You should create a project file to help with the placement. 23 | b. Any footprints missing from the sub-PCB (but present in the schematic) will be ignored during automatic placement. 24 | c. Any footprints present in the sub-PCB but missing from the main PCB will be ignored. 25 | d. Any footprints placed off to the left or above the origin will be ignored during automatic placement. 26 | 27 | 3. Open the main PCB and run the plugin. 28 | 4. Select which hierarchical sheets you want to enforce the layout of, and configure which footprint to use as the anchor for each sub-PCB. 29 | 5. Click OK and watch the magic happen. 30 | 31 | If you wish to see a project that uses this, check out [AngloDox, my keyboard project](https://github.com/gauravmm/AngloDox/). We use HierarchicalPCB to provide for rich per-key circuitry. 32 | 33 | ### In Detail 34 | 35 | Begin by preparing your hierarchical schematic as per normal. In this example, we have a simple circuit for a single key in a keyboard, including an LED and an RC debouncing circuit: 36 | 37 | ![Schematic for one key.](images/14_subsch.png) 38 | 39 | This would be an absolute nightmare to layout ~90 different times; that's where HierarchicalPCB comes in: after creating the schematic inside the main project, we: 40 | 41 | 1. create a new project with the same name as the `sch` file, 42 | 2. open that project in KiCAD, taking care not to change the schematic while the main project is open 43 | 3. create a PCB inside the project, 44 | 4. update the PCB from the schematic, 45 | 5. layout the PCB for the schematic. 46 | 47 | When complete, this should look like: 48 | 49 | ![Layout for one key](images/14_subpcb.png) 50 | 51 | Great! Now we're ready to propagate this layout to every key on the main PCB. To do that, create a new PCB in the **main** project (i.e., at the root level), and lay out the "anchor" component in each. The anchor component is the component around which all other components will be moved into position and all traces will be drawn. Here we choose the switch (`SW???01`) to be the anchor component and lay it out in the shape of our keyboard: 52 | 53 | ![Position anchor components](images/03_kle_placement.png) 54 | 55 | This layout was done using my [KLEPlacement plugin](https://github.com/gauravmm/KLEPlacement) from this [KLE file](https://github.com/gauravmm/AngloDox/blob/master/keyboard-layout-left.json). Note that only the switch components are moved into position, all other components are unmoved. 56 | 57 | To enforce our desired layout around each key we invoke the plugin. Click on the button in the top bar to start: 58 | 59 | ![Menu button](images/06_menubutton.png) 60 | 61 | This opens up the menu where you configure the plugin. The window is split into two panes: the upper lets you pick which sub-schematics to enforce the layout for; the lower lets you pick the anchor for each sub-PCB you define. The plugin remembers the last selections you made (writing it to a file next to the `.pcb` file). 62 | 63 | ![Main Menu](images/12_menu.png) 64 | 65 | First, we select which sub-schematics to enforce the layout for. The plugin allows you to arbitrarily nest PCBs, so one sub-PCB can itself have sub-sub-PCBs, but at any one time the contents of each schematic may only be arranged according to one sub-PCB. You can select which sub-schematics to arrange by double-clicking each element. If you click "Reset to Default", it automatically selects the outermost PCB for each possible schematic. 66 | 67 | Next, you select the anchor element for each PCB. You do this by clicking on the row and picking a new anchor in the drop-down box below. 68 | 69 | Once you've done both, click "Apply" and watch the magic happen: 70 | 71 | ![Arranged PCB](images/05_hierarchical_pcb.png) 72 | 73 | Ta-da! 74 | 75 | And since the plugin remembers your settings you can propagate any change in just two clicks. 76 | 77 | ![Rotations](images/11_even_with_rotations.png) 78 | 79 | ## Notes and Details 80 | 81 | 1. Each footprint may only be positioned following one sub-PCB. If you have a sub-PCB with its own sub-sub-PCBs, you can only enforce the layout of one of them. This is enforced during the selection process. 82 | 2. If you want multiple variations of the same schematic, you can nest them (i.e. LayoutVariant includes LayoutCommon and nothing else, each has a different sub-PCB, and you can enforce the layout of either one). 83 | 3. Currently, it does not support Zones, but I'll happily add that if someone needs it. Open an issue if you're interested and have some time to help me test it. 84 | 85 | ### Details of the placement algorithm 86 | 87 | The algorithm works as follows: 88 | 89 | 1. For each selected hierarchical sheet, find the sub-PCB file and load it. 90 | 2. Match the anchor footprint in the sub-PCB to the anchor footprint in the main PCB. 91 | a. Copy some properties of the anchor footprint from the sub-PCB to the main PCB. In particular, move it to the same layer as the anchor footprint. 92 | b. Move the anchor footprint in the main PCB into an automatically-named group (or create it if it doesn't exist). 93 | 3. For each footprint in the sub-PCB, find the corresponding footprint in the main PCB. 94 | a. Match the footprint by internal ID, not the reference designator. 95 | b. Copy some properties. 96 | c. Place and rotate the main footprint w.r.t. the main anchor so that it matches the sub-PCB. 97 | 4. For all traces in the sub-PCB: 98 | a. Clear the traces in the main PCB in the same group as the anchor. 99 | b. Recreate all traces, arcs, and vias. 100 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .hplugin import HierarchicalPCBPlugin 2 | 3 | HierarchicalPCBPlugin().register() 4 | -------------------------------------------------------------------------------- /cfgman.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import logging 3 | from pathlib import Path 4 | from types import TracebackType 5 | from typing import Any 6 | import json 7 | 8 | logger = logging.getLogger("hierpcb") 9 | 10 | 11 | class ConfigMan(contextlib.AbstractContextManager): 12 | def __init__(self, path: Path): 13 | self.path = path 14 | 15 | def __enter__(self) -> "ConfigMan": 16 | try: 17 | with self.path.open("r") as fp: 18 | self.config = json.load(fp) 19 | except FileNotFoundError: 20 | logger.warning(f"Config file {self.path} not found. Creating new one.") 21 | self.config = {} 22 | return self 23 | 24 | def __exit__(self, *args): 25 | with self.path.open("w") as fp: 26 | json.dump(self.config, fp, indent=2) 27 | return super().__exit__(*args) 28 | 29 | def get(self, *key: str, default=None): 30 | node = self.config 31 | for k in key: 32 | node = node.get(k) 33 | if node is None: 34 | return default 35 | return node 36 | 37 | def clear(self, *key: str): 38 | node = self.config 39 | for k in key[:-1]: 40 | node = node.get(k) 41 | if node is None: 42 | return 43 | node.pop(key[-1], None) 44 | 45 | def set(self, *key: str, value, create_missing=True): 46 | node = self.config 47 | terminal = key[-1] 48 | prefix = [] 49 | for k in key[:-1]: 50 | prefix.append(k) 51 | node_next = node.get(k) 52 | if node_next is None: 53 | if create_missing: 54 | node[k] = node_next = {} 55 | else: 56 | raise KeyError(f"Key {'.'.join(prefix)} not found in config.") 57 | node = node_next 58 | node[terminal] = value 59 | -------------------------------------------------------------------------------- /examples/FlatStructure/FlatStructure.kicad_pcb.hierpcb.json: -------------------------------------------------------------------------------- 1 | { 2 | "subpcb": { 3 | "FlatStructure.kicad_pcb": { 4 | "anchor": "U1" 5 | }, 6 | "SubPcb.kicad_pcb": { 7 | "anchor": "Q1" 8 | } 9 | }, 10 | "sheet": { 11 | "/6d991a4b-c375-434a-9bb2-d732e04d9857": { 12 | "checked": true 13 | }, 14 | "/c6c75fec-0368-4360-ab22-fe9626928152": { 15 | "checked": true 16 | }, 17 | "/600193de-35c9-4fdb-a9dc-528e28be004e": { 18 | "checked": true 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /examples/FlatStructure/FlatStructure.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "All Layers", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "fffffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "FlatStructure.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/FlatStructure/FlatStructure.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.5 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "ignore", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.0, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.5, 121 | "min_hole_clearance": 0.25, 122 | "min_hole_to_hole": 0.25, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 2, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.8, 128 | "min_text_thickness": 0.08, 129 | "min_through_hole_diameter": 0.3, 130 | "min_track_width": 0.0, 131 | "min_via_annular_width": 0.1, 132 | "min_via_diameter": 0.5, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.4, 182 | 0.6 183 | ], 184 | "tuning_pattern_settings": { 185 | "diff_pair_defaults": { 186 | "corner_radius_percentage": 80, 187 | "corner_style": 1, 188 | "max_amplitude": 1.0, 189 | "min_amplitude": 0.2, 190 | "single_sided": false, 191 | "spacing": 1.0 192 | }, 193 | "diff_pair_skew_defaults": { 194 | "corner_radius_percentage": 80, 195 | "corner_style": 1, 196 | "max_amplitude": 1.0, 197 | "min_amplitude": 0.2, 198 | "single_sided": false, 199 | "spacing": 0.6 200 | }, 201 | "single_track_defaults": { 202 | "corner_radius_percentage": 80, 203 | "corner_style": 1, 204 | "max_amplitude": 1.0, 205 | "min_amplitude": 0.2, 206 | "single_sided": false, 207 | "spacing": 0.6 208 | } 209 | }, 210 | "via_dimensions": [ 211 | { 212 | "diameter": 0.0, 213 | "drill": 0.0 214 | } 215 | ], 216 | "zones_allow_external_fillets": false 217 | }, 218 | "ipc2581": { 219 | "dist": "", 220 | "distpn": "", 221 | "internal_id": "", 222 | "mfg": "", 223 | "mpn": "" 224 | }, 225 | "layer_presets": [], 226 | "viewports": [] 227 | }, 228 | "boards": [], 229 | "cvpcb": { 230 | "equivalence_files": [] 231 | }, 232 | "erc": { 233 | "erc_exclusions": [], 234 | "meta": { 235 | "version": 0 236 | }, 237 | "pin_map": [ 238 | [ 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 0, 245 | 1, 246 | 0, 247 | 0, 248 | 0, 249 | 0, 250 | 2 251 | ], 252 | [ 253 | 0, 254 | 2, 255 | 0, 256 | 1, 257 | 0, 258 | 0, 259 | 1, 260 | 0, 261 | 2, 262 | 2, 263 | 2, 264 | 2 265 | ], 266 | [ 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 0, 273 | 1, 274 | 0, 275 | 1, 276 | 0, 277 | 1, 278 | 2 279 | ], 280 | [ 281 | 0, 282 | 1, 283 | 0, 284 | 0, 285 | 0, 286 | 0, 287 | 1, 288 | 1, 289 | 2, 290 | 1, 291 | 1, 292 | 2 293 | ], 294 | [ 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 0, 301 | 1, 302 | 0, 303 | 0, 304 | 0, 305 | 0, 306 | 2 307 | ], 308 | [ 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 2 321 | ], 322 | [ 323 | 1, 324 | 1, 325 | 1, 326 | 1, 327 | 1, 328 | 0, 329 | 1, 330 | 1, 331 | 1, 332 | 1, 333 | 1, 334 | 2 335 | ], 336 | [ 337 | 0, 338 | 0, 339 | 0, 340 | 1, 341 | 0, 342 | 0, 343 | 1, 344 | 0, 345 | 0, 346 | 0, 347 | 0, 348 | 2 349 | ], 350 | [ 351 | 0, 352 | 2, 353 | 1, 354 | 2, 355 | 0, 356 | 0, 357 | 1, 358 | 0, 359 | 2, 360 | 2, 361 | 2, 362 | 2 363 | ], 364 | [ 365 | 0, 366 | 2, 367 | 0, 368 | 1, 369 | 0, 370 | 0, 371 | 1, 372 | 0, 373 | 2, 374 | 0, 375 | 0, 376 | 2 377 | ], 378 | [ 379 | 0, 380 | 2, 381 | 1, 382 | 1, 383 | 0, 384 | 0, 385 | 1, 386 | 0, 387 | 2, 388 | 0, 389 | 0, 390 | 2 391 | ], 392 | [ 393 | 2, 394 | 2, 395 | 2, 396 | 2, 397 | 2, 398 | 2, 399 | 2, 400 | 2, 401 | 2, 402 | 2, 403 | 2, 404 | 2 405 | ] 406 | ], 407 | "rule_severities": { 408 | "bus_definition_conflict": "error", 409 | "bus_entry_needed": "error", 410 | "bus_to_bus_conflict": "error", 411 | "bus_to_net_conflict": "error", 412 | "conflicting_netclasses": "error", 413 | "different_unit_footprint": "error", 414 | "different_unit_net": "error", 415 | "duplicate_reference": "error", 416 | "duplicate_sheet_names": "error", 417 | "endpoint_off_grid": "warning", 418 | "extra_units": "error", 419 | "global_label_dangling": "warning", 420 | "hier_label_mismatch": "error", 421 | "label_dangling": "error", 422 | "lib_symbol_issues": "warning", 423 | "missing_bidi_pin": "warning", 424 | "missing_input_pin": "warning", 425 | "missing_power_pin": "error", 426 | "missing_unit": "warning", 427 | "multiple_net_names": "warning", 428 | "net_not_bus_member": "warning", 429 | "no_connect_connected": "warning", 430 | "no_connect_dangling": "warning", 431 | "pin_not_connected": "error", 432 | "pin_not_driven": "error", 433 | "pin_to_pin": "warning", 434 | "power_pin_not_driven": "error", 435 | "similar_labels": "warning", 436 | "simulation_model_issue": "ignore", 437 | "unannotated": "error", 438 | "unit_value_mismatch": "error", 439 | "unresolved_variable": "error", 440 | "wire_dangling": "error" 441 | } 442 | }, 443 | "libraries": { 444 | "pinned_footprint_libs": [], 445 | "pinned_symbol_libs": [] 446 | }, 447 | "meta": { 448 | "filename": "FlatStructure.kicad_pro", 449 | "version": 1 450 | }, 451 | "net_settings": { 452 | "classes": [ 453 | { 454 | "bus_width": 12, 455 | "clearance": 0.2, 456 | "diff_pair_gap": 0.25, 457 | "diff_pair_via_gap": 0.25, 458 | "diff_pair_width": 0.2, 459 | "line_style": 0, 460 | "microvia_diameter": 0.3, 461 | "microvia_drill": 0.1, 462 | "name": "Default", 463 | "pcb_color": "rgba(0, 0, 0, 0.000)", 464 | "schematic_color": "rgba(0, 0, 0, 0.000)", 465 | "track_width": 0.2, 466 | "via_diameter": 0.6, 467 | "via_drill": 0.3, 468 | "wire_width": 6 469 | } 470 | ], 471 | "meta": { 472 | "version": 3 473 | }, 474 | "net_colors": null, 475 | "netclass_assignments": null, 476 | "netclass_patterns": [] 477 | }, 478 | "pcbnew": { 479 | "last_paths": { 480 | "gencad": "", 481 | "idf": "", 482 | "netlist": "", 483 | "plot": "", 484 | "pos_files": "", 485 | "specctra_dsn": "", 486 | "step": "", 487 | "svg": "", 488 | "vrml": "" 489 | }, 490 | "page_layout_descr_file": "" 491 | }, 492 | "schematic": { 493 | "annotate_start_num": 0, 494 | "bom_fmt_presets": [], 495 | "bom_fmt_settings": { 496 | "field_delimiter": ",", 497 | "keep_line_breaks": false, 498 | "keep_tabs": false, 499 | "name": "CSV", 500 | "ref_delimiter": ",", 501 | "ref_range_delimiter": "", 502 | "string_delimiter": "\"" 503 | }, 504 | "bom_presets": [], 505 | "bom_settings": { 506 | "exclude_dnp": false, 507 | "fields_ordered": [ 508 | { 509 | "group_by": false, 510 | "label": "Reference", 511 | "name": "Reference", 512 | "show": true 513 | }, 514 | { 515 | "group_by": true, 516 | "label": "Value", 517 | "name": "Value", 518 | "show": true 519 | }, 520 | { 521 | "group_by": false, 522 | "label": "Datasheet", 523 | "name": "Datasheet", 524 | "show": true 525 | }, 526 | { 527 | "group_by": false, 528 | "label": "Footprint", 529 | "name": "Footprint", 530 | "show": true 531 | }, 532 | { 533 | "group_by": false, 534 | "label": "Qty", 535 | "name": "${QUANTITY}", 536 | "show": true 537 | }, 538 | { 539 | "group_by": true, 540 | "label": "DNP", 541 | "name": "${DNP}", 542 | "show": true 543 | } 544 | ], 545 | "filter_string": "", 546 | "group_symbols": true, 547 | "name": "Grouped By Value", 548 | "sort_asc": true, 549 | "sort_field": "Reference" 550 | }, 551 | "connection_grid_size": 50.0, 552 | "drawing": { 553 | "dashed_lines_dash_length_ratio": 12.0, 554 | "dashed_lines_gap_length_ratio": 3.0, 555 | "default_line_thickness": 6.0, 556 | "default_text_size": 50.0, 557 | "field_names": [], 558 | "intersheets_ref_own_page": false, 559 | "intersheets_ref_prefix": "", 560 | "intersheets_ref_short": false, 561 | "intersheets_ref_show": false, 562 | "intersheets_ref_suffix": "", 563 | "junction_size_choice": 3, 564 | "label_size_ratio": 0.375, 565 | "operating_point_overlay_i_precision": 3, 566 | "operating_point_overlay_i_range": "~A", 567 | "operating_point_overlay_v_precision": 3, 568 | "operating_point_overlay_v_range": "~V", 569 | "overbar_offset_ratio": 1.23, 570 | "pin_symbol_size": 25.0, 571 | "text_offset_ratio": 0.15 572 | }, 573 | "legacy_lib_dir": "", 574 | "legacy_lib_list": [], 575 | "meta": { 576 | "version": 1 577 | }, 578 | "net_format_name": "", 579 | "page_layout_descr_file": "", 580 | "plot_directory": "", 581 | "spice_current_sheet_as_root": false, 582 | "spice_external_command": "spice \"%I\"", 583 | "spice_model_current_sheet_as_root": true, 584 | "spice_save_all_currents": false, 585 | "spice_save_all_dissipations": false, 586 | "spice_save_all_voltages": false, 587 | "subpart_first_id": 65, 588 | "subpart_id_separator": 0 589 | }, 590 | "sheets": [ 591 | [ 592 | "5225e552-cfab-4090-a502-b034bf1f2bf2", 593 | "Root" 594 | ], 595 | [ 596 | "600193de-35c9-4fdb-a9dc-528e28be004e", 597 | "Sinking Digital" 598 | ], 599 | [ 600 | "c6c75fec-0368-4360-ab22-fe9626928152", 601 | "Sinking Digital1" 602 | ], 603 | [ 604 | "6d991a4b-c375-434a-9bb2-d732e04d9857", 605 | "Sinking Digital2" 606 | ] 607 | ], 608 | "text_variables": {} 609 | } 610 | -------------------------------------------------------------------------------- /examples/FlatStructure/SubPcb.kicad_pcb.hierpcb.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /examples/FlatStructure/SubPcb.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "All Layers", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "fffffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "SubPcb.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/FlatStructure/SubPcb.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.0 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "ignore", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.0, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.5, 121 | "min_hole_clearance": 0.25, 122 | "min_hole_to_hole": 0.25, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 2, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.8, 128 | "min_text_thickness": 0.08, 129 | "min_through_hole_diameter": 0.3, 130 | "min_track_width": 0.0, 131 | "min_via_annular_width": 0.1, 132 | "min_via_diameter": 0.5, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.8 182 | ], 183 | "tuning_pattern_settings": { 184 | "diff_pair_defaults": { 185 | "corner_radius_percentage": 80, 186 | "corner_style": 1, 187 | "max_amplitude": 1.0, 188 | "min_amplitude": 0.2, 189 | "single_sided": false, 190 | "spacing": 1.0 191 | }, 192 | "diff_pair_skew_defaults": { 193 | "corner_radius_percentage": 80, 194 | "corner_style": 1, 195 | "max_amplitude": 1.0, 196 | "min_amplitude": 0.2, 197 | "single_sided": false, 198 | "spacing": 0.6 199 | }, 200 | "single_track_defaults": { 201 | "corner_radius_percentage": 80, 202 | "corner_style": 1, 203 | "max_amplitude": 1.0, 204 | "min_amplitude": 0.2, 205 | "single_sided": false, 206 | "spacing": 0.6 207 | } 208 | }, 209 | "via_dimensions": [ 210 | { 211 | "diameter": 0.0, 212 | "drill": 0.0 213 | } 214 | ], 215 | "zones_allow_external_fillets": false 216 | }, 217 | "ipc2581": { 218 | "dist": "", 219 | "distpn": "", 220 | "internal_id": "", 221 | "mfg": "", 222 | "mpn": "" 223 | }, 224 | "layer_presets": [], 225 | "viewports": [] 226 | }, 227 | "boards": [], 228 | "cvpcb": { 229 | "equivalence_files": [] 230 | }, 231 | "erc": { 232 | "erc_exclusions": [], 233 | "meta": { 234 | "version": 0 235 | }, 236 | "pin_map": [ 237 | [ 238 | 0, 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 1, 245 | 0, 246 | 0, 247 | 0, 248 | 0, 249 | 2 250 | ], 251 | [ 252 | 0, 253 | 2, 254 | 0, 255 | 1, 256 | 0, 257 | 0, 258 | 1, 259 | 0, 260 | 2, 261 | 2, 262 | 2, 263 | 2 264 | ], 265 | [ 266 | 0, 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 1, 273 | 0, 274 | 1, 275 | 0, 276 | 1, 277 | 2 278 | ], 279 | [ 280 | 0, 281 | 1, 282 | 0, 283 | 0, 284 | 0, 285 | 0, 286 | 1, 287 | 1, 288 | 2, 289 | 1, 290 | 1, 291 | 2 292 | ], 293 | [ 294 | 0, 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 1, 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 2 306 | ], 307 | [ 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 2 320 | ], 321 | [ 322 | 1, 323 | 1, 324 | 1, 325 | 1, 326 | 1, 327 | 0, 328 | 1, 329 | 1, 330 | 1, 331 | 1, 332 | 1, 333 | 2 334 | ], 335 | [ 336 | 0, 337 | 0, 338 | 0, 339 | 1, 340 | 0, 341 | 0, 342 | 1, 343 | 0, 344 | 0, 345 | 0, 346 | 0, 347 | 2 348 | ], 349 | [ 350 | 0, 351 | 2, 352 | 1, 353 | 2, 354 | 0, 355 | 0, 356 | 1, 357 | 0, 358 | 2, 359 | 2, 360 | 2, 361 | 2 362 | ], 363 | [ 364 | 0, 365 | 2, 366 | 0, 367 | 1, 368 | 0, 369 | 0, 370 | 1, 371 | 0, 372 | 2, 373 | 0, 374 | 0, 375 | 2 376 | ], 377 | [ 378 | 0, 379 | 2, 380 | 1, 381 | 1, 382 | 0, 383 | 0, 384 | 1, 385 | 0, 386 | 2, 387 | 0, 388 | 0, 389 | 2 390 | ], 391 | [ 392 | 2, 393 | 2, 394 | 2, 395 | 2, 396 | 2, 397 | 2, 398 | 2, 399 | 2, 400 | 2, 401 | 2, 402 | 2, 403 | 2 404 | ] 405 | ], 406 | "rule_severities": { 407 | "bus_definition_conflict": "error", 408 | "bus_entry_needed": "error", 409 | "bus_to_bus_conflict": "error", 410 | "bus_to_net_conflict": "error", 411 | "conflicting_netclasses": "error", 412 | "different_unit_footprint": "error", 413 | "different_unit_net": "error", 414 | "duplicate_reference": "error", 415 | "duplicate_sheet_names": "error", 416 | "endpoint_off_grid": "warning", 417 | "extra_units": "error", 418 | "global_label_dangling": "warning", 419 | "hier_label_mismatch": "error", 420 | "label_dangling": "error", 421 | "lib_symbol_issues": "warning", 422 | "missing_bidi_pin": "warning", 423 | "missing_input_pin": "warning", 424 | "missing_power_pin": "error", 425 | "missing_unit": "warning", 426 | "multiple_net_names": "warning", 427 | "net_not_bus_member": "warning", 428 | "no_connect_connected": "warning", 429 | "no_connect_dangling": "warning", 430 | "pin_not_connected": "error", 431 | "pin_not_driven": "error", 432 | "pin_to_pin": "warning", 433 | "power_pin_not_driven": "error", 434 | "similar_labels": "warning", 435 | "simulation_model_issue": "ignore", 436 | "unannotated": "error", 437 | "unit_value_mismatch": "error", 438 | "unresolved_variable": "error", 439 | "wire_dangling": "error" 440 | } 441 | }, 442 | "libraries": { 443 | "pinned_footprint_libs": [], 444 | "pinned_symbol_libs": [] 445 | }, 446 | "meta": { 447 | "filename": "SubPcb.kicad_pro", 448 | "version": 1 449 | }, 450 | "net_settings": { 451 | "classes": [ 452 | { 453 | "bus_width": 12, 454 | "clearance": 0.2, 455 | "diff_pair_gap": 0.25, 456 | "diff_pair_via_gap": 0.25, 457 | "diff_pair_width": 0.2, 458 | "line_style": 0, 459 | "microvia_diameter": 0.3, 460 | "microvia_drill": 0.1, 461 | "name": "Default", 462 | "pcb_color": "rgba(0, 0, 0, 0.000)", 463 | "schematic_color": "rgba(0, 0, 0, 0.000)", 464 | "track_width": 0.2, 465 | "via_diameter": 0.6, 466 | "via_drill": 0.3, 467 | "wire_width": 6 468 | } 469 | ], 470 | "meta": { 471 | "version": 3 472 | }, 473 | "net_colors": null, 474 | "netclass_assignments": null, 475 | "netclass_patterns": [] 476 | }, 477 | "pcbnew": { 478 | "last_paths": { 479 | "gencad": "", 480 | "idf": "", 481 | "netlist": "", 482 | "plot": "", 483 | "pos_files": "", 484 | "specctra_dsn": "", 485 | "step": "", 486 | "svg": "", 487 | "vrml": "" 488 | }, 489 | "page_layout_descr_file": "" 490 | }, 491 | "schematic": { 492 | "annotate_start_num": 0, 493 | "bom_fmt_presets": [], 494 | "bom_fmt_settings": { 495 | "field_delimiter": ",", 496 | "keep_line_breaks": false, 497 | "keep_tabs": false, 498 | "name": "CSV", 499 | "ref_delimiter": ",", 500 | "ref_range_delimiter": "", 501 | "string_delimiter": "\"" 502 | }, 503 | "bom_presets": [], 504 | "bom_settings": { 505 | "exclude_dnp": false, 506 | "fields_ordered": [ 507 | { 508 | "group_by": false, 509 | "label": "Reference", 510 | "name": "Reference", 511 | "show": true 512 | }, 513 | { 514 | "group_by": true, 515 | "label": "Value", 516 | "name": "Value", 517 | "show": true 518 | }, 519 | { 520 | "group_by": false, 521 | "label": "Datasheet", 522 | "name": "Datasheet", 523 | "show": true 524 | }, 525 | { 526 | "group_by": false, 527 | "label": "Footprint", 528 | "name": "Footprint", 529 | "show": true 530 | }, 531 | { 532 | "group_by": false, 533 | "label": "Qty", 534 | "name": "${QUANTITY}", 535 | "show": true 536 | }, 537 | { 538 | "group_by": true, 539 | "label": "DNP", 540 | "name": "${DNP}", 541 | "show": true 542 | } 543 | ], 544 | "filter_string": "", 545 | "group_symbols": true, 546 | "name": "Grouped By Value", 547 | "sort_asc": true, 548 | "sort_field": "Reference" 549 | }, 550 | "connection_grid_size": 50.0, 551 | "drawing": { 552 | "dashed_lines_dash_length_ratio": 12.0, 553 | "dashed_lines_gap_length_ratio": 3.0, 554 | "default_line_thickness": 6.0, 555 | "default_text_size": 50.0, 556 | "field_names": [], 557 | "intersheets_ref_own_page": false, 558 | "intersheets_ref_prefix": "", 559 | "intersheets_ref_short": false, 560 | "intersheets_ref_show": false, 561 | "intersheets_ref_suffix": "", 562 | "junction_size_choice": 3, 563 | "label_size_ratio": 0.375, 564 | "operating_point_overlay_i_precision": 3, 565 | "operating_point_overlay_i_range": "~A", 566 | "operating_point_overlay_v_precision": 3, 567 | "operating_point_overlay_v_range": "~V", 568 | "overbar_offset_ratio": 1.23, 569 | "pin_symbol_size": 25.0, 570 | "text_offset_ratio": 0.15 571 | }, 572 | "legacy_lib_dir": "", 573 | "legacy_lib_list": [], 574 | "meta": { 575 | "version": 1 576 | }, 577 | "net_format_name": "", 578 | "page_layout_descr_file": "", 579 | "plot_directory": "", 580 | "spice_current_sheet_as_root": false, 581 | "spice_external_command": "spice \"%I\"", 582 | "spice_model_current_sheet_as_root": true, 583 | "spice_save_all_currents": false, 584 | "spice_save_all_dissipations": false, 585 | "spice_save_all_voltages": false, 586 | "subpart_first_id": 65, 587 | "subpart_id_separator": 0 588 | }, 589 | "sheets": [ 590 | [ 591 | "e0597968-f11c-42e6-8091-8a167b37e076", 592 | "Root" 593 | ] 594 | ], 595 | "text_variables": {} 596 | } 597 | -------------------------------------------------------------------------------- /examples/FolderStructure/FolderStructure.kicad_pcb.hierpcb.json: -------------------------------------------------------------------------------- 1 | { 2 | "subpcb": { 3 | "FolderStructure.kicad_pcb": { 4 | "anchor": "U1" 5 | }, 6 | "SubPcb/SubPcb.kicad_pcb": { 7 | "anchor": "Q1" 8 | } 9 | }, 10 | "sheet": { 11 | "/6d991a4b-c375-434a-9bb2-d732e04d9857": { 12 | "checked": true 13 | }, 14 | "/c6c75fec-0368-4360-ab22-fe9626928152": { 15 | "checked": true 16 | }, 17 | "/600193de-35c9-4fdb-a9dc-528e28be004e": { 18 | "checked": true 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /examples/FolderStructure/FolderStructure.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "All Layers", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "fffffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "FolderStructure.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/FolderStructure/FolderStructure.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.5 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "ignore", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.0, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.5, 121 | "min_hole_clearance": 0.25, 122 | "min_hole_to_hole": 0.25, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 2, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.8, 128 | "min_text_thickness": 0.08, 129 | "min_through_hole_diameter": 0.3, 130 | "min_track_width": 0.0, 131 | "min_via_annular_width": 0.1, 132 | "min_via_diameter": 0.5, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.4, 182 | 0.6 183 | ], 184 | "tuning_pattern_settings": { 185 | "diff_pair_defaults": { 186 | "corner_radius_percentage": 80, 187 | "corner_style": 1, 188 | "max_amplitude": 1.0, 189 | "min_amplitude": 0.2, 190 | "single_sided": false, 191 | "spacing": 1.0 192 | }, 193 | "diff_pair_skew_defaults": { 194 | "corner_radius_percentage": 80, 195 | "corner_style": 1, 196 | "max_amplitude": 1.0, 197 | "min_amplitude": 0.2, 198 | "single_sided": false, 199 | "spacing": 0.6 200 | }, 201 | "single_track_defaults": { 202 | "corner_radius_percentage": 80, 203 | "corner_style": 1, 204 | "max_amplitude": 1.0, 205 | "min_amplitude": 0.2, 206 | "single_sided": false, 207 | "spacing": 0.6 208 | } 209 | }, 210 | "via_dimensions": [ 211 | { 212 | "diameter": 0.0, 213 | "drill": 0.0 214 | } 215 | ], 216 | "zones_allow_external_fillets": false 217 | }, 218 | "ipc2581": { 219 | "dist": "", 220 | "distpn": "", 221 | "internal_id": "", 222 | "mfg": "", 223 | "mpn": "" 224 | }, 225 | "layer_presets": [], 226 | "viewports": [] 227 | }, 228 | "boards": [], 229 | "cvpcb": { 230 | "equivalence_files": [] 231 | }, 232 | "erc": { 233 | "erc_exclusions": [], 234 | "meta": { 235 | "version": 0 236 | }, 237 | "pin_map": [ 238 | [ 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 0, 245 | 1, 246 | 0, 247 | 0, 248 | 0, 249 | 0, 250 | 2 251 | ], 252 | [ 253 | 0, 254 | 2, 255 | 0, 256 | 1, 257 | 0, 258 | 0, 259 | 1, 260 | 0, 261 | 2, 262 | 2, 263 | 2, 264 | 2 265 | ], 266 | [ 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 0, 273 | 1, 274 | 0, 275 | 1, 276 | 0, 277 | 1, 278 | 2 279 | ], 280 | [ 281 | 0, 282 | 1, 283 | 0, 284 | 0, 285 | 0, 286 | 0, 287 | 1, 288 | 1, 289 | 2, 290 | 1, 291 | 1, 292 | 2 293 | ], 294 | [ 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 0, 301 | 1, 302 | 0, 303 | 0, 304 | 0, 305 | 0, 306 | 2 307 | ], 308 | [ 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 2 321 | ], 322 | [ 323 | 1, 324 | 1, 325 | 1, 326 | 1, 327 | 1, 328 | 0, 329 | 1, 330 | 1, 331 | 1, 332 | 1, 333 | 1, 334 | 2 335 | ], 336 | [ 337 | 0, 338 | 0, 339 | 0, 340 | 1, 341 | 0, 342 | 0, 343 | 1, 344 | 0, 345 | 0, 346 | 0, 347 | 0, 348 | 2 349 | ], 350 | [ 351 | 0, 352 | 2, 353 | 1, 354 | 2, 355 | 0, 356 | 0, 357 | 1, 358 | 0, 359 | 2, 360 | 2, 361 | 2, 362 | 2 363 | ], 364 | [ 365 | 0, 366 | 2, 367 | 0, 368 | 1, 369 | 0, 370 | 0, 371 | 1, 372 | 0, 373 | 2, 374 | 0, 375 | 0, 376 | 2 377 | ], 378 | [ 379 | 0, 380 | 2, 381 | 1, 382 | 1, 383 | 0, 384 | 0, 385 | 1, 386 | 0, 387 | 2, 388 | 0, 389 | 0, 390 | 2 391 | ], 392 | [ 393 | 2, 394 | 2, 395 | 2, 396 | 2, 397 | 2, 398 | 2, 399 | 2, 400 | 2, 401 | 2, 402 | 2, 403 | 2, 404 | 2 405 | ] 406 | ], 407 | "rule_severities": { 408 | "bus_definition_conflict": "error", 409 | "bus_entry_needed": "error", 410 | "bus_to_bus_conflict": "error", 411 | "bus_to_net_conflict": "error", 412 | "conflicting_netclasses": "error", 413 | "different_unit_footprint": "error", 414 | "different_unit_net": "error", 415 | "duplicate_reference": "error", 416 | "duplicate_sheet_names": "error", 417 | "endpoint_off_grid": "warning", 418 | "extra_units": "error", 419 | "global_label_dangling": "warning", 420 | "hier_label_mismatch": "error", 421 | "label_dangling": "error", 422 | "lib_symbol_issues": "warning", 423 | "missing_bidi_pin": "warning", 424 | "missing_input_pin": "warning", 425 | "missing_power_pin": "error", 426 | "missing_unit": "warning", 427 | "multiple_net_names": "warning", 428 | "net_not_bus_member": "warning", 429 | "no_connect_connected": "warning", 430 | "no_connect_dangling": "warning", 431 | "pin_not_connected": "error", 432 | "pin_not_driven": "error", 433 | "pin_to_pin": "warning", 434 | "power_pin_not_driven": "error", 435 | "similar_labels": "warning", 436 | "simulation_model_issue": "ignore", 437 | "unannotated": "error", 438 | "unit_value_mismatch": "error", 439 | "unresolved_variable": "error", 440 | "wire_dangling": "error" 441 | } 442 | }, 443 | "libraries": { 444 | "pinned_footprint_libs": [], 445 | "pinned_symbol_libs": [] 446 | }, 447 | "meta": { 448 | "filename": "FlatStructure.kicad_pro", 449 | "version": 1 450 | }, 451 | "net_settings": { 452 | "classes": [ 453 | { 454 | "bus_width": 12, 455 | "clearance": 0.2, 456 | "diff_pair_gap": 0.25, 457 | "diff_pair_via_gap": 0.25, 458 | "diff_pair_width": 0.2, 459 | "line_style": 0, 460 | "microvia_diameter": 0.3, 461 | "microvia_drill": 0.1, 462 | "name": "Default", 463 | "pcb_color": "rgba(0, 0, 0, 0.000)", 464 | "schematic_color": "rgba(0, 0, 0, 0.000)", 465 | "track_width": 0.2, 466 | "via_diameter": 0.6, 467 | "via_drill": 0.3, 468 | "wire_width": 6 469 | } 470 | ], 471 | "meta": { 472 | "version": 3 473 | }, 474 | "net_colors": null, 475 | "netclass_assignments": null, 476 | "netclass_patterns": [] 477 | }, 478 | "pcbnew": { 479 | "last_paths": { 480 | "gencad": "", 481 | "idf": "", 482 | "netlist": "", 483 | "plot": "", 484 | "pos_files": "", 485 | "specctra_dsn": "", 486 | "step": "", 487 | "svg": "", 488 | "vrml": "" 489 | }, 490 | "page_layout_descr_file": "" 491 | }, 492 | "schematic": { 493 | "annotate_start_num": 0, 494 | "bom_fmt_presets": [], 495 | "bom_fmt_settings": { 496 | "field_delimiter": ",", 497 | "keep_line_breaks": false, 498 | "keep_tabs": false, 499 | "name": "CSV", 500 | "ref_delimiter": ",", 501 | "ref_range_delimiter": "", 502 | "string_delimiter": "\"" 503 | }, 504 | "bom_presets": [], 505 | "bom_settings": { 506 | "exclude_dnp": false, 507 | "fields_ordered": [ 508 | { 509 | "group_by": false, 510 | "label": "Reference", 511 | "name": "Reference", 512 | "show": true 513 | }, 514 | { 515 | "group_by": true, 516 | "label": "Value", 517 | "name": "Value", 518 | "show": true 519 | }, 520 | { 521 | "group_by": false, 522 | "label": "Datasheet", 523 | "name": "Datasheet", 524 | "show": true 525 | }, 526 | { 527 | "group_by": false, 528 | "label": "Footprint", 529 | "name": "Footprint", 530 | "show": true 531 | }, 532 | { 533 | "group_by": false, 534 | "label": "Qty", 535 | "name": "${QUANTITY}", 536 | "show": true 537 | }, 538 | { 539 | "group_by": true, 540 | "label": "DNP", 541 | "name": "${DNP}", 542 | "show": true 543 | } 544 | ], 545 | "filter_string": "", 546 | "group_symbols": true, 547 | "name": "Grouped By Value", 548 | "sort_asc": true, 549 | "sort_field": "Reference" 550 | }, 551 | "connection_grid_size": 50.0, 552 | "drawing": { 553 | "dashed_lines_dash_length_ratio": 12.0, 554 | "dashed_lines_gap_length_ratio": 3.0, 555 | "default_line_thickness": 6.0, 556 | "default_text_size": 50.0, 557 | "field_names": [], 558 | "intersheets_ref_own_page": false, 559 | "intersheets_ref_prefix": "", 560 | "intersheets_ref_short": false, 561 | "intersheets_ref_show": false, 562 | "intersheets_ref_suffix": "", 563 | "junction_size_choice": 3, 564 | "label_size_ratio": 0.375, 565 | "operating_point_overlay_i_precision": 3, 566 | "operating_point_overlay_i_range": "~A", 567 | "operating_point_overlay_v_precision": 3, 568 | "operating_point_overlay_v_range": "~V", 569 | "overbar_offset_ratio": 1.23, 570 | "pin_symbol_size": 25.0, 571 | "text_offset_ratio": 0.15 572 | }, 573 | "legacy_lib_dir": "", 574 | "legacy_lib_list": [], 575 | "meta": { 576 | "version": 1 577 | }, 578 | "net_format_name": "", 579 | "page_layout_descr_file": "", 580 | "plot_directory": "", 581 | "spice_current_sheet_as_root": false, 582 | "spice_external_command": "spice \"%I\"", 583 | "spice_model_current_sheet_as_root": true, 584 | "spice_save_all_currents": false, 585 | "spice_save_all_dissipations": false, 586 | "spice_save_all_voltages": false, 587 | "subpart_first_id": 65, 588 | "subpart_id_separator": 0 589 | }, 590 | "sheets": [ 591 | [ 592 | "5225e552-cfab-4090-a502-b034bf1f2bf2", 593 | "Root" 594 | ], 595 | [ 596 | "600193de-35c9-4fdb-a9dc-528e28be004e", 597 | "Sinking Digital" 598 | ], 599 | [ 600 | "c6c75fec-0368-4360-ab22-fe9626928152", 601 | "Sinking Digital1" 602 | ], 603 | [ 604 | "6d991a4b-c375-434a-9bb2-d732e04d9857", 605 | "Sinking Digital2" 606 | ] 607 | ], 608 | "text_variables": {} 609 | } 610 | -------------------------------------------------------------------------------- /examples/FolderStructure/SubPcb/SubPcb.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "All Layers", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "fffffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "SubPcb.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/FolderStructure/SubPcb/SubPcb.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.0 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "ignore", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.0, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.5, 121 | "min_hole_clearance": 0.25, 122 | "min_hole_to_hole": 0.25, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 2, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.8, 128 | "min_text_thickness": 0.08, 129 | "min_through_hole_diameter": 0.3, 130 | "min_track_width": 0.0, 131 | "min_via_annular_width": 0.1, 132 | "min_via_diameter": 0.5, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.8 182 | ], 183 | "tuning_pattern_settings": { 184 | "diff_pair_defaults": { 185 | "corner_radius_percentage": 80, 186 | "corner_style": 1, 187 | "max_amplitude": 1.0, 188 | "min_amplitude": 0.2, 189 | "single_sided": false, 190 | "spacing": 1.0 191 | }, 192 | "diff_pair_skew_defaults": { 193 | "corner_radius_percentage": 80, 194 | "corner_style": 1, 195 | "max_amplitude": 1.0, 196 | "min_amplitude": 0.2, 197 | "single_sided": false, 198 | "spacing": 0.6 199 | }, 200 | "single_track_defaults": { 201 | "corner_radius_percentage": 80, 202 | "corner_style": 1, 203 | "max_amplitude": 1.0, 204 | "min_amplitude": 0.2, 205 | "single_sided": false, 206 | "spacing": 0.6 207 | } 208 | }, 209 | "via_dimensions": [ 210 | { 211 | "diameter": 0.0, 212 | "drill": 0.0 213 | } 214 | ], 215 | "zones_allow_external_fillets": false 216 | }, 217 | "ipc2581": { 218 | "dist": "", 219 | "distpn": "", 220 | "internal_id": "", 221 | "mfg": "", 222 | "mpn": "" 223 | }, 224 | "layer_presets": [], 225 | "viewports": [] 226 | }, 227 | "boards": [], 228 | "cvpcb": { 229 | "equivalence_files": [] 230 | }, 231 | "erc": { 232 | "erc_exclusions": [], 233 | "meta": { 234 | "version": 0 235 | }, 236 | "pin_map": [ 237 | [ 238 | 0, 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 1, 245 | 0, 246 | 0, 247 | 0, 248 | 0, 249 | 2 250 | ], 251 | [ 252 | 0, 253 | 2, 254 | 0, 255 | 1, 256 | 0, 257 | 0, 258 | 1, 259 | 0, 260 | 2, 261 | 2, 262 | 2, 263 | 2 264 | ], 265 | [ 266 | 0, 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 1, 273 | 0, 274 | 1, 275 | 0, 276 | 1, 277 | 2 278 | ], 279 | [ 280 | 0, 281 | 1, 282 | 0, 283 | 0, 284 | 0, 285 | 0, 286 | 1, 287 | 1, 288 | 2, 289 | 1, 290 | 1, 291 | 2 292 | ], 293 | [ 294 | 0, 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 1, 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 2 306 | ], 307 | [ 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 2 320 | ], 321 | [ 322 | 1, 323 | 1, 324 | 1, 325 | 1, 326 | 1, 327 | 0, 328 | 1, 329 | 1, 330 | 1, 331 | 1, 332 | 1, 333 | 2 334 | ], 335 | [ 336 | 0, 337 | 0, 338 | 0, 339 | 1, 340 | 0, 341 | 0, 342 | 1, 343 | 0, 344 | 0, 345 | 0, 346 | 0, 347 | 2 348 | ], 349 | [ 350 | 0, 351 | 2, 352 | 1, 353 | 2, 354 | 0, 355 | 0, 356 | 1, 357 | 0, 358 | 2, 359 | 2, 360 | 2, 361 | 2 362 | ], 363 | [ 364 | 0, 365 | 2, 366 | 0, 367 | 1, 368 | 0, 369 | 0, 370 | 1, 371 | 0, 372 | 2, 373 | 0, 374 | 0, 375 | 2 376 | ], 377 | [ 378 | 0, 379 | 2, 380 | 1, 381 | 1, 382 | 0, 383 | 0, 384 | 1, 385 | 0, 386 | 2, 387 | 0, 388 | 0, 389 | 2 390 | ], 391 | [ 392 | 2, 393 | 2, 394 | 2, 395 | 2, 396 | 2, 397 | 2, 398 | 2, 399 | 2, 400 | 2, 401 | 2, 402 | 2, 403 | 2 404 | ] 405 | ], 406 | "rule_severities": { 407 | "bus_definition_conflict": "error", 408 | "bus_entry_needed": "error", 409 | "bus_to_bus_conflict": "error", 410 | "bus_to_net_conflict": "error", 411 | "conflicting_netclasses": "error", 412 | "different_unit_footprint": "error", 413 | "different_unit_net": "error", 414 | "duplicate_reference": "error", 415 | "duplicate_sheet_names": "error", 416 | "endpoint_off_grid": "warning", 417 | "extra_units": "error", 418 | "global_label_dangling": "warning", 419 | "hier_label_mismatch": "error", 420 | "label_dangling": "error", 421 | "lib_symbol_issues": "warning", 422 | "missing_bidi_pin": "warning", 423 | "missing_input_pin": "warning", 424 | "missing_power_pin": "error", 425 | "missing_unit": "warning", 426 | "multiple_net_names": "warning", 427 | "net_not_bus_member": "warning", 428 | "no_connect_connected": "warning", 429 | "no_connect_dangling": "warning", 430 | "pin_not_connected": "error", 431 | "pin_not_driven": "error", 432 | "pin_to_pin": "warning", 433 | "power_pin_not_driven": "error", 434 | "similar_labels": "warning", 435 | "simulation_model_issue": "ignore", 436 | "unannotated": "error", 437 | "unit_value_mismatch": "error", 438 | "unresolved_variable": "error", 439 | "wire_dangling": "error" 440 | } 441 | }, 442 | "libraries": { 443 | "pinned_footprint_libs": [], 444 | "pinned_symbol_libs": [] 445 | }, 446 | "meta": { 447 | "filename": "SubPcb.kicad_pro", 448 | "version": 1 449 | }, 450 | "net_settings": { 451 | "classes": [ 452 | { 453 | "bus_width": 12, 454 | "clearance": 0.2, 455 | "diff_pair_gap": 0.25, 456 | "diff_pair_via_gap": 0.25, 457 | "diff_pair_width": 0.2, 458 | "line_style": 0, 459 | "microvia_diameter": 0.3, 460 | "microvia_drill": 0.1, 461 | "name": "Default", 462 | "pcb_color": "rgba(0, 0, 0, 0.000)", 463 | "schematic_color": "rgba(0, 0, 0, 0.000)", 464 | "track_width": 0.2, 465 | "via_diameter": 0.6, 466 | "via_drill": 0.3, 467 | "wire_width": 6 468 | } 469 | ], 470 | "meta": { 471 | "version": 3 472 | }, 473 | "net_colors": null, 474 | "netclass_assignments": null, 475 | "netclass_patterns": [] 476 | }, 477 | "pcbnew": { 478 | "last_paths": { 479 | "gencad": "", 480 | "idf": "", 481 | "netlist": "", 482 | "plot": "", 483 | "pos_files": "", 484 | "specctra_dsn": "", 485 | "step": "", 486 | "svg": "", 487 | "vrml": "" 488 | }, 489 | "page_layout_descr_file": "" 490 | }, 491 | "schematic": { 492 | "annotate_start_num": 0, 493 | "bom_fmt_presets": [], 494 | "bom_fmt_settings": { 495 | "field_delimiter": ",", 496 | "keep_line_breaks": false, 497 | "keep_tabs": false, 498 | "name": "CSV", 499 | "ref_delimiter": ",", 500 | "ref_range_delimiter": "", 501 | "string_delimiter": "\"" 502 | }, 503 | "bom_presets": [], 504 | "bom_settings": { 505 | "exclude_dnp": false, 506 | "fields_ordered": [ 507 | { 508 | "group_by": false, 509 | "label": "Reference", 510 | "name": "Reference", 511 | "show": true 512 | }, 513 | { 514 | "group_by": true, 515 | "label": "Value", 516 | "name": "Value", 517 | "show": true 518 | }, 519 | { 520 | "group_by": false, 521 | "label": "Datasheet", 522 | "name": "Datasheet", 523 | "show": true 524 | }, 525 | { 526 | "group_by": false, 527 | "label": "Footprint", 528 | "name": "Footprint", 529 | "show": true 530 | }, 531 | { 532 | "group_by": false, 533 | "label": "Qty", 534 | "name": "${QUANTITY}", 535 | "show": true 536 | }, 537 | { 538 | "group_by": true, 539 | "label": "DNP", 540 | "name": "${DNP}", 541 | "show": true 542 | } 543 | ], 544 | "filter_string": "", 545 | "group_symbols": true, 546 | "name": "Grouped By Value", 547 | "sort_asc": true, 548 | "sort_field": "Reference" 549 | }, 550 | "connection_grid_size": 50.0, 551 | "drawing": { 552 | "dashed_lines_dash_length_ratio": 12.0, 553 | "dashed_lines_gap_length_ratio": 3.0, 554 | "default_line_thickness": 6.0, 555 | "default_text_size": 50.0, 556 | "field_names": [], 557 | "intersheets_ref_own_page": false, 558 | "intersheets_ref_prefix": "", 559 | "intersheets_ref_short": false, 560 | "intersheets_ref_show": false, 561 | "intersheets_ref_suffix": "", 562 | "junction_size_choice": 3, 563 | "label_size_ratio": 0.375, 564 | "operating_point_overlay_i_precision": 3, 565 | "operating_point_overlay_i_range": "~A", 566 | "operating_point_overlay_v_precision": 3, 567 | "operating_point_overlay_v_range": "~V", 568 | "overbar_offset_ratio": 1.23, 569 | "pin_symbol_size": 25.0, 570 | "text_offset_ratio": 0.15 571 | }, 572 | "legacy_lib_dir": "", 573 | "legacy_lib_list": [], 574 | "meta": { 575 | "version": 1 576 | }, 577 | "net_format_name": "", 578 | "page_layout_descr_file": "", 579 | "plot_directory": "", 580 | "spice_current_sheet_as_root": false, 581 | "spice_external_command": "spice \"%I\"", 582 | "spice_model_current_sheet_as_root": true, 583 | "spice_save_all_currents": false, 584 | "spice_save_all_dissipations": false, 585 | "spice_save_all_voltages": false, 586 | "subpart_first_id": 65, 587 | "subpart_id_separator": 0 588 | }, 589 | "sheets": [ 590 | [ 591 | "e0597968-f11c-42e6-8091-8a167b37e076", 592 | "Root" 593 | ] 594 | ], 595 | "text_variables": {} 596 | } 597 | -------------------------------------------------------------------------------- /examples/FolderStructure/SubPcb/fp-info-cache: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /examples/TripleRecursion/FolderStructure.kicad_pcb.hierpcb.json: -------------------------------------------------------------------------------- 1 | { 2 | "subpcb": { 3 | "FolderStructure.kicad_pcb": { 4 | "anchor": "J1" 5 | }, 6 | "SubPcb/SubPcb.kicad_pcb": { 7 | "anchor": "J1" 8 | } 9 | }, 10 | "sheet": { 11 | "": { 12 | "checked": false 13 | }, 14 | "/49012fb7-72e8-47b6-a88d-87b862b9e662": { 15 | "checked": true 16 | }, 17 | "/49012fb7-72e8-47b6-a88d-87b862b9e662/3003978c-fd0f-4020-b143-0f02e8f5a798": { 18 | "checked": false 19 | }, 20 | "/49012fb7-72e8-47b6-a88d-87b862b9e662/6135ce1c-b744-4b77-ae78-eb08171e0e4b": { 21 | "checked": false 22 | }, 23 | "/83e2f251-2ecf-45c5-9ae3-81741f3a62ab": { 24 | "checked": true 25 | }, 26 | "/83e2f251-2ecf-45c5-9ae3-81741f3a62ab/3003978c-fd0f-4020-b143-0f02e8f5a798": { 27 | "checked": false 28 | }, 29 | "/83e2f251-2ecf-45c5-9ae3-81741f3a62ab/6135ce1c-b744-4b77-ae78-eb08171e0e4b": { 30 | "checked": false 31 | }, 32 | "/d64c8c11-70fa-4e7f-b08d-3fb288553916": { 33 | "checked": true 34 | }, 35 | "/d64c8c11-70fa-4e7f-b08d-3fb288553916/3003978c-fd0f-4020-b143-0f02e8f5a798": { 36 | "checked": false 37 | }, 38 | "/d64c8c11-70fa-4e7f-b08d-3fb288553916/6135ce1c-b744-4b77-ae78-eb08171e0e4b": { 39 | "checked": false 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /examples/TripleRecursion/FolderStructure.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "All Layers", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "fffffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "FolderStructure.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/TripleRecursion/FolderStructure.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.0 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "ignore", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.0, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.5, 121 | "min_hole_clearance": 0.25, 122 | "min_hole_to_hole": 0.25, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 2, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.8, 128 | "min_text_thickness": 0.08, 129 | "min_through_hole_diameter": 0.3, 130 | "min_track_width": 0.0, 131 | "min_via_annular_width": 0.1, 132 | "min_via_diameter": 0.5, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.4, 182 | 0.6 183 | ], 184 | "tuning_pattern_settings": { 185 | "diff_pair_defaults": { 186 | "corner_radius_percentage": 80, 187 | "corner_style": 1, 188 | "max_amplitude": 1.0, 189 | "min_amplitude": 0.2, 190 | "single_sided": false, 191 | "spacing": 1.0 192 | }, 193 | "diff_pair_skew_defaults": { 194 | "corner_radius_percentage": 80, 195 | "corner_style": 1, 196 | "max_amplitude": 1.0, 197 | "min_amplitude": 0.2, 198 | "single_sided": false, 199 | "spacing": 0.6 200 | }, 201 | "single_track_defaults": { 202 | "corner_radius_percentage": 80, 203 | "corner_style": 1, 204 | "max_amplitude": 1.0, 205 | "min_amplitude": 0.2, 206 | "single_sided": false, 207 | "spacing": 0.6 208 | } 209 | }, 210 | "via_dimensions": [ 211 | { 212 | "diameter": 0.0, 213 | "drill": 0.0 214 | } 215 | ], 216 | "zones_allow_external_fillets": false 217 | }, 218 | "ipc2581": { 219 | "dist": "", 220 | "distpn": "", 221 | "internal_id": "", 222 | "mfg": "", 223 | "mpn": "" 224 | }, 225 | "layer_presets": [], 226 | "viewports": [] 227 | }, 228 | "boards": [], 229 | "cvpcb": { 230 | "equivalence_files": [] 231 | }, 232 | "erc": { 233 | "erc_exclusions": [], 234 | "meta": { 235 | "version": 0 236 | }, 237 | "pin_map": [ 238 | [ 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 0, 245 | 1, 246 | 0, 247 | 0, 248 | 0, 249 | 0, 250 | 2 251 | ], 252 | [ 253 | 0, 254 | 2, 255 | 0, 256 | 1, 257 | 0, 258 | 0, 259 | 1, 260 | 0, 261 | 2, 262 | 2, 263 | 2, 264 | 2 265 | ], 266 | [ 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 0, 273 | 1, 274 | 0, 275 | 1, 276 | 0, 277 | 1, 278 | 2 279 | ], 280 | [ 281 | 0, 282 | 1, 283 | 0, 284 | 0, 285 | 0, 286 | 0, 287 | 1, 288 | 1, 289 | 2, 290 | 1, 291 | 1, 292 | 2 293 | ], 294 | [ 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 0, 301 | 1, 302 | 0, 303 | 0, 304 | 0, 305 | 0, 306 | 2 307 | ], 308 | [ 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 2 321 | ], 322 | [ 323 | 1, 324 | 1, 325 | 1, 326 | 1, 327 | 1, 328 | 0, 329 | 1, 330 | 1, 331 | 1, 332 | 1, 333 | 1, 334 | 2 335 | ], 336 | [ 337 | 0, 338 | 0, 339 | 0, 340 | 1, 341 | 0, 342 | 0, 343 | 1, 344 | 0, 345 | 0, 346 | 0, 347 | 0, 348 | 2 349 | ], 350 | [ 351 | 0, 352 | 2, 353 | 1, 354 | 2, 355 | 0, 356 | 0, 357 | 1, 358 | 0, 359 | 2, 360 | 2, 361 | 2, 362 | 2 363 | ], 364 | [ 365 | 0, 366 | 2, 367 | 0, 368 | 1, 369 | 0, 370 | 0, 371 | 1, 372 | 0, 373 | 2, 374 | 0, 375 | 0, 376 | 2 377 | ], 378 | [ 379 | 0, 380 | 2, 381 | 1, 382 | 1, 383 | 0, 384 | 0, 385 | 1, 386 | 0, 387 | 2, 388 | 0, 389 | 0, 390 | 2 391 | ], 392 | [ 393 | 2, 394 | 2, 395 | 2, 396 | 2, 397 | 2, 398 | 2, 399 | 2, 400 | 2, 401 | 2, 402 | 2, 403 | 2, 404 | 2 405 | ] 406 | ], 407 | "rule_severities": { 408 | "bus_definition_conflict": "error", 409 | "bus_entry_needed": "error", 410 | "bus_to_bus_conflict": "error", 411 | "bus_to_net_conflict": "error", 412 | "conflicting_netclasses": "error", 413 | "different_unit_footprint": "error", 414 | "different_unit_net": "error", 415 | "duplicate_reference": "error", 416 | "duplicate_sheet_names": "error", 417 | "endpoint_off_grid": "warning", 418 | "extra_units": "error", 419 | "global_label_dangling": "warning", 420 | "hier_label_mismatch": "error", 421 | "label_dangling": "error", 422 | "lib_symbol_issues": "warning", 423 | "missing_bidi_pin": "warning", 424 | "missing_input_pin": "warning", 425 | "missing_power_pin": "error", 426 | "missing_unit": "warning", 427 | "multiple_net_names": "warning", 428 | "net_not_bus_member": "warning", 429 | "no_connect_connected": "warning", 430 | "no_connect_dangling": "warning", 431 | "pin_not_connected": "error", 432 | "pin_not_driven": "error", 433 | "pin_to_pin": "warning", 434 | "power_pin_not_driven": "error", 435 | "similar_labels": "warning", 436 | "simulation_model_issue": "ignore", 437 | "unannotated": "error", 438 | "unit_value_mismatch": "error", 439 | "unresolved_variable": "error", 440 | "wire_dangling": "error" 441 | } 442 | }, 443 | "libraries": { 444 | "pinned_footprint_libs": [], 445 | "pinned_symbol_libs": [] 446 | }, 447 | "meta": { 448 | "filename": "FolderStructure.kicad_pro", 449 | "version": 1 450 | }, 451 | "net_settings": { 452 | "classes": [ 453 | { 454 | "bus_width": 12, 455 | "clearance": 0.2, 456 | "diff_pair_gap": 0.25, 457 | "diff_pair_via_gap": 0.25, 458 | "diff_pair_width": 0.2, 459 | "line_style": 0, 460 | "microvia_diameter": 0.3, 461 | "microvia_drill": 0.1, 462 | "name": "Default", 463 | "pcb_color": "rgba(0, 0, 0, 0.000)", 464 | "schematic_color": "rgba(0, 0, 0, 0.000)", 465 | "track_width": 0.2, 466 | "via_diameter": 0.6, 467 | "via_drill": 0.3, 468 | "wire_width": 6 469 | } 470 | ], 471 | "meta": { 472 | "version": 3 473 | }, 474 | "net_colors": null, 475 | "netclass_assignments": null, 476 | "netclass_patterns": [] 477 | }, 478 | "pcbnew": { 479 | "last_paths": { 480 | "gencad": "", 481 | "idf": "", 482 | "netlist": "", 483 | "plot": "", 484 | "pos_files": "", 485 | "specctra_dsn": "", 486 | "step": "", 487 | "svg": "", 488 | "vrml": "" 489 | }, 490 | "page_layout_descr_file": "" 491 | }, 492 | "schematic": { 493 | "annotate_start_num": 0, 494 | "bom_fmt_presets": [], 495 | "bom_fmt_settings": { 496 | "field_delimiter": ",", 497 | "keep_line_breaks": false, 498 | "keep_tabs": false, 499 | "name": "CSV", 500 | "ref_delimiter": ",", 501 | "ref_range_delimiter": "", 502 | "string_delimiter": "\"" 503 | }, 504 | "bom_presets": [], 505 | "bom_settings": { 506 | "exclude_dnp": false, 507 | "fields_ordered": [ 508 | { 509 | "group_by": false, 510 | "label": "Reference", 511 | "name": "Reference", 512 | "show": true 513 | }, 514 | { 515 | "group_by": true, 516 | "label": "Value", 517 | "name": "Value", 518 | "show": true 519 | }, 520 | { 521 | "group_by": false, 522 | "label": "Datasheet", 523 | "name": "Datasheet", 524 | "show": true 525 | }, 526 | { 527 | "group_by": false, 528 | "label": "Footprint", 529 | "name": "Footprint", 530 | "show": true 531 | }, 532 | { 533 | "group_by": false, 534 | "label": "Qty", 535 | "name": "${QUANTITY}", 536 | "show": true 537 | }, 538 | { 539 | "group_by": true, 540 | "label": "DNP", 541 | "name": "${DNP}", 542 | "show": true 543 | } 544 | ], 545 | "filter_string": "", 546 | "group_symbols": true, 547 | "name": "Grouped By Value", 548 | "sort_asc": true, 549 | "sort_field": "Reference" 550 | }, 551 | "connection_grid_size": 50.0, 552 | "drawing": { 553 | "dashed_lines_dash_length_ratio": 12.0, 554 | "dashed_lines_gap_length_ratio": 3.0, 555 | "default_line_thickness": 6.0, 556 | "default_text_size": 50.0, 557 | "field_names": [], 558 | "intersheets_ref_own_page": false, 559 | "intersheets_ref_prefix": "", 560 | "intersheets_ref_short": false, 561 | "intersheets_ref_show": false, 562 | "intersheets_ref_suffix": "", 563 | "junction_size_choice": 3, 564 | "label_size_ratio": 0.375, 565 | "operating_point_overlay_i_precision": 3, 566 | "operating_point_overlay_i_range": "~A", 567 | "operating_point_overlay_v_precision": 3, 568 | "operating_point_overlay_v_range": "~V", 569 | "overbar_offset_ratio": 1.23, 570 | "pin_symbol_size": 25.0, 571 | "text_offset_ratio": 0.15 572 | }, 573 | "legacy_lib_dir": "", 574 | "legacy_lib_list": [], 575 | "meta": { 576 | "version": 1 577 | }, 578 | "net_format_name": "", 579 | "page_layout_descr_file": "", 580 | "plot_directory": "", 581 | "spice_current_sheet_as_root": false, 582 | "spice_external_command": "spice \"%I\"", 583 | "spice_model_current_sheet_as_root": true, 584 | "spice_save_all_currents": false, 585 | "spice_save_all_dissipations": false, 586 | "spice_save_all_voltages": false, 587 | "subpart_first_id": 65, 588 | "subpart_id_separator": 0 589 | }, 590 | "sheets": [ 591 | [ 592 | "5225e552-cfab-4090-a502-b034bf1f2bf2", 593 | "Root" 594 | ], 595 | [ 596 | "d64c8c11-70fa-4e7f-b08d-3fb288553916", 597 | "SubPcb" 598 | ], 599 | [ 600 | "6135ce1c-b744-4b77-ae78-eb08171e0e4b", 601 | "nType" 602 | ], 603 | [ 604 | "3003978c-fd0f-4020-b143-0f02e8f5a798", 605 | "pType" 606 | ], 607 | [ 608 | "49012fb7-72e8-47b6-a88d-87b862b9e662", 609 | "SubPcb1" 610 | ], 611 | [ 612 | "6135ce1c-b744-4b77-ae78-eb08171e0e4b", 613 | "nType" 614 | ], 615 | [ 616 | "3003978c-fd0f-4020-b143-0f02e8f5a798", 617 | "pType" 618 | ], 619 | [ 620 | "83e2f251-2ecf-45c5-9ae3-81741f3a62ab", 621 | "SubPcb2" 622 | ], 623 | [ 624 | "6135ce1c-b744-4b77-ae78-eb08171e0e4b", 625 | "nType" 626 | ], 627 | [ 628 | "3003978c-fd0f-4020-b143-0f02e8f5a798", 629 | "pType" 630 | ] 631 | ], 632 | "text_variables": {} 633 | } 634 | -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/SubPcb.kicad_pcb.hierpcb.json: -------------------------------------------------------------------------------- 1 | { 2 | "subpcb": { 3 | "nType/nType.kicad_pcb": { 4 | "anchor": "Q1" 5 | }, 6 | "pType/pType.kicad_pcb": { 7 | "anchor": "Q1" 8 | }, 9 | "SubPcb.kicad_pcb": { 10 | "anchor": "J1" 11 | } 12 | }, 13 | "sheet": { 14 | "": { 15 | "checked": false 16 | }, 17 | "/3003978c-fd0f-4020-b143-0f02e8f5a798": { 18 | "checked": true 19 | }, 20 | "/6135ce1c-b744-4b77-ae78-eb08171e0e4b": { 21 | "checked": true 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/SubPcb.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "All Layers", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "fffffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "SubPcb.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/SubPcb.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.0 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "ignore", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.0, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.5, 121 | "min_hole_clearance": 0.25, 122 | "min_hole_to_hole": 0.25, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 2, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.8, 128 | "min_text_thickness": 0.08, 129 | "min_through_hole_diameter": 0.3, 130 | "min_track_width": 0.0, 131 | "min_via_annular_width": 0.1, 132 | "min_via_diameter": 0.5, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.8 182 | ], 183 | "tuning_pattern_settings": { 184 | "diff_pair_defaults": { 185 | "corner_radius_percentage": 80, 186 | "corner_style": 1, 187 | "max_amplitude": 1.0, 188 | "min_amplitude": 0.2, 189 | "single_sided": false, 190 | "spacing": 1.0 191 | }, 192 | "diff_pair_skew_defaults": { 193 | "corner_radius_percentage": 80, 194 | "corner_style": 1, 195 | "max_amplitude": 1.0, 196 | "min_amplitude": 0.2, 197 | "single_sided": false, 198 | "spacing": 0.6 199 | }, 200 | "single_track_defaults": { 201 | "corner_radius_percentage": 80, 202 | "corner_style": 1, 203 | "max_amplitude": 1.0, 204 | "min_amplitude": 0.2, 205 | "single_sided": false, 206 | "spacing": 0.6 207 | } 208 | }, 209 | "via_dimensions": [ 210 | { 211 | "diameter": 0.0, 212 | "drill": 0.0 213 | } 214 | ], 215 | "zones_allow_external_fillets": false 216 | }, 217 | "ipc2581": { 218 | "dist": "", 219 | "distpn": "", 220 | "internal_id": "", 221 | "mfg": "", 222 | "mpn": "" 223 | }, 224 | "layer_presets": [], 225 | "viewports": [] 226 | }, 227 | "boards": [], 228 | "cvpcb": { 229 | "equivalence_files": [] 230 | }, 231 | "erc": { 232 | "erc_exclusions": [], 233 | "meta": { 234 | "version": 0 235 | }, 236 | "pin_map": [ 237 | [ 238 | 0, 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 1, 245 | 0, 246 | 0, 247 | 0, 248 | 0, 249 | 2 250 | ], 251 | [ 252 | 0, 253 | 2, 254 | 0, 255 | 1, 256 | 0, 257 | 0, 258 | 1, 259 | 0, 260 | 2, 261 | 2, 262 | 2, 263 | 2 264 | ], 265 | [ 266 | 0, 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 1, 273 | 0, 274 | 1, 275 | 0, 276 | 1, 277 | 2 278 | ], 279 | [ 280 | 0, 281 | 1, 282 | 0, 283 | 0, 284 | 0, 285 | 0, 286 | 1, 287 | 1, 288 | 2, 289 | 1, 290 | 1, 291 | 2 292 | ], 293 | [ 294 | 0, 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 1, 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 2 306 | ], 307 | [ 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 2 320 | ], 321 | [ 322 | 1, 323 | 1, 324 | 1, 325 | 1, 326 | 1, 327 | 0, 328 | 1, 329 | 1, 330 | 1, 331 | 1, 332 | 1, 333 | 2 334 | ], 335 | [ 336 | 0, 337 | 0, 338 | 0, 339 | 1, 340 | 0, 341 | 0, 342 | 1, 343 | 0, 344 | 0, 345 | 0, 346 | 0, 347 | 2 348 | ], 349 | [ 350 | 0, 351 | 2, 352 | 1, 353 | 2, 354 | 0, 355 | 0, 356 | 1, 357 | 0, 358 | 2, 359 | 2, 360 | 2, 361 | 2 362 | ], 363 | [ 364 | 0, 365 | 2, 366 | 0, 367 | 1, 368 | 0, 369 | 0, 370 | 1, 371 | 0, 372 | 2, 373 | 0, 374 | 0, 375 | 2 376 | ], 377 | [ 378 | 0, 379 | 2, 380 | 1, 381 | 1, 382 | 0, 383 | 0, 384 | 1, 385 | 0, 386 | 2, 387 | 0, 388 | 0, 389 | 2 390 | ], 391 | [ 392 | 2, 393 | 2, 394 | 2, 395 | 2, 396 | 2, 397 | 2, 398 | 2, 399 | 2, 400 | 2, 401 | 2, 402 | 2, 403 | 2 404 | ] 405 | ], 406 | "rule_severities": { 407 | "bus_definition_conflict": "error", 408 | "bus_entry_needed": "error", 409 | "bus_to_bus_conflict": "error", 410 | "bus_to_net_conflict": "error", 411 | "conflicting_netclasses": "error", 412 | "different_unit_footprint": "error", 413 | "different_unit_net": "error", 414 | "duplicate_reference": "error", 415 | "duplicate_sheet_names": "error", 416 | "endpoint_off_grid": "warning", 417 | "extra_units": "error", 418 | "global_label_dangling": "warning", 419 | "hier_label_mismatch": "error", 420 | "label_dangling": "error", 421 | "lib_symbol_issues": "warning", 422 | "missing_bidi_pin": "warning", 423 | "missing_input_pin": "warning", 424 | "missing_power_pin": "error", 425 | "missing_unit": "warning", 426 | "multiple_net_names": "warning", 427 | "net_not_bus_member": "warning", 428 | "no_connect_connected": "warning", 429 | "no_connect_dangling": "warning", 430 | "pin_not_connected": "error", 431 | "pin_not_driven": "error", 432 | "pin_to_pin": "warning", 433 | "power_pin_not_driven": "error", 434 | "similar_labels": "warning", 435 | "simulation_model_issue": "ignore", 436 | "unannotated": "error", 437 | "unit_value_mismatch": "error", 438 | "unresolved_variable": "error", 439 | "wire_dangling": "error" 440 | } 441 | }, 442 | "libraries": { 443 | "pinned_footprint_libs": [], 444 | "pinned_symbol_libs": [] 445 | }, 446 | "meta": { 447 | "filename": "SubPcb.kicad_pro", 448 | "version": 1 449 | }, 450 | "net_settings": { 451 | "classes": [ 452 | { 453 | "bus_width": 12, 454 | "clearance": 0.2, 455 | "diff_pair_gap": 0.25, 456 | "diff_pair_via_gap": 0.25, 457 | "diff_pair_width": 0.2, 458 | "line_style": 0, 459 | "microvia_diameter": 0.3, 460 | "microvia_drill": 0.1, 461 | "name": "Default", 462 | "pcb_color": "rgba(0, 0, 0, 0.000)", 463 | "schematic_color": "rgba(0, 0, 0, 0.000)", 464 | "track_width": 0.2, 465 | "via_diameter": 0.6, 466 | "via_drill": 0.3, 467 | "wire_width": 6 468 | } 469 | ], 470 | "meta": { 471 | "version": 3 472 | }, 473 | "net_colors": null, 474 | "netclass_assignments": null, 475 | "netclass_patterns": [] 476 | }, 477 | "pcbnew": { 478 | "last_paths": { 479 | "gencad": "", 480 | "idf": "", 481 | "netlist": "", 482 | "plot": "", 483 | "pos_files": "", 484 | "specctra_dsn": "", 485 | "step": "", 486 | "svg": "", 487 | "vrml": "" 488 | }, 489 | "page_layout_descr_file": "" 490 | }, 491 | "schematic": { 492 | "annotate_start_num": 0, 493 | "bom_fmt_presets": [], 494 | "bom_fmt_settings": { 495 | "field_delimiter": ",", 496 | "keep_line_breaks": false, 497 | "keep_tabs": false, 498 | "name": "CSV", 499 | "ref_delimiter": ",", 500 | "ref_range_delimiter": "", 501 | "string_delimiter": "\"" 502 | }, 503 | "bom_presets": [], 504 | "bom_settings": { 505 | "exclude_dnp": false, 506 | "fields_ordered": [ 507 | { 508 | "group_by": false, 509 | "label": "Reference", 510 | "name": "Reference", 511 | "show": true 512 | }, 513 | { 514 | "group_by": true, 515 | "label": "Value", 516 | "name": "Value", 517 | "show": true 518 | }, 519 | { 520 | "group_by": false, 521 | "label": "Datasheet", 522 | "name": "Datasheet", 523 | "show": true 524 | }, 525 | { 526 | "group_by": false, 527 | "label": "Footprint", 528 | "name": "Footprint", 529 | "show": true 530 | }, 531 | { 532 | "group_by": false, 533 | "label": "Qty", 534 | "name": "${QUANTITY}", 535 | "show": true 536 | }, 537 | { 538 | "group_by": true, 539 | "label": "DNP", 540 | "name": "${DNP}", 541 | "show": true 542 | } 543 | ], 544 | "filter_string": "", 545 | "group_symbols": true, 546 | "name": "Grouped By Value", 547 | "sort_asc": true, 548 | "sort_field": "Reference" 549 | }, 550 | "connection_grid_size": 50.0, 551 | "drawing": { 552 | "dashed_lines_dash_length_ratio": 12.0, 553 | "dashed_lines_gap_length_ratio": 3.0, 554 | "default_line_thickness": 6.0, 555 | "default_text_size": 50.0, 556 | "field_names": [], 557 | "intersheets_ref_own_page": false, 558 | "intersheets_ref_prefix": "", 559 | "intersheets_ref_short": false, 560 | "intersheets_ref_show": false, 561 | "intersheets_ref_suffix": "", 562 | "junction_size_choice": 3, 563 | "label_size_ratio": 0.375, 564 | "operating_point_overlay_i_precision": 3, 565 | "operating_point_overlay_i_range": "~A", 566 | "operating_point_overlay_v_precision": 3, 567 | "operating_point_overlay_v_range": "~V", 568 | "overbar_offset_ratio": 1.23, 569 | "pin_symbol_size": 25.0, 570 | "text_offset_ratio": 0.15 571 | }, 572 | "legacy_lib_dir": "", 573 | "legacy_lib_list": [], 574 | "meta": { 575 | "version": 1 576 | }, 577 | "net_format_name": "", 578 | "page_layout_descr_file": "", 579 | "plot_directory": "", 580 | "spice_current_sheet_as_root": false, 581 | "spice_external_command": "spice \"%I\"", 582 | "spice_model_current_sheet_as_root": true, 583 | "spice_save_all_currents": false, 584 | "spice_save_all_dissipations": false, 585 | "spice_save_all_voltages": false, 586 | "subpart_first_id": 65, 587 | "subpart_id_separator": 0 588 | }, 589 | "sheets": [ 590 | [ 591 | "e0597968-f11c-42e6-8091-8a167b37e076", 592 | "Root" 593 | ], 594 | [ 595 | "6135ce1c-b744-4b77-ae78-eb08171e0e4b", 596 | "nType" 597 | ], 598 | [ 599 | "3003978c-fd0f-4020-b143-0f02e8f5a798", 600 | "pType" 601 | ] 602 | ], 603 | "text_variables": {} 604 | } 605 | -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/fp-info-cache: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/nType/fp-info-cache: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/nType/nType.kicad_pcb.hierpcb.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/nType/nType.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "All Layers", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "fffffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "nType.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/nType/nType.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.0 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "ignore", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.0, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.5, 121 | "min_hole_clearance": 0.25, 122 | "min_hole_to_hole": 0.25, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 2, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.8, 128 | "min_text_thickness": 0.08, 129 | "min_through_hole_diameter": 0.3, 130 | "min_track_width": 0.0, 131 | "min_via_annular_width": 0.1, 132 | "min_via_diameter": 0.5, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.8 182 | ], 183 | "tuning_pattern_settings": { 184 | "diff_pair_defaults": { 185 | "corner_radius_percentage": 80, 186 | "corner_style": 1, 187 | "max_amplitude": 1.0, 188 | "min_amplitude": 0.2, 189 | "single_sided": false, 190 | "spacing": 1.0 191 | }, 192 | "diff_pair_skew_defaults": { 193 | "corner_radius_percentage": 80, 194 | "corner_style": 1, 195 | "max_amplitude": 1.0, 196 | "min_amplitude": 0.2, 197 | "single_sided": false, 198 | "spacing": 0.6 199 | }, 200 | "single_track_defaults": { 201 | "corner_radius_percentage": 80, 202 | "corner_style": 1, 203 | "max_amplitude": 1.0, 204 | "min_amplitude": 0.2, 205 | "single_sided": false, 206 | "spacing": 0.6 207 | } 208 | }, 209 | "via_dimensions": [ 210 | { 211 | "diameter": 0.0, 212 | "drill": 0.0 213 | } 214 | ], 215 | "zones_allow_external_fillets": false 216 | }, 217 | "ipc2581": { 218 | "dist": "", 219 | "distpn": "", 220 | "internal_id": "", 221 | "mfg": "", 222 | "mpn": "" 223 | }, 224 | "layer_presets": [], 225 | "viewports": [] 226 | }, 227 | "boards": [], 228 | "cvpcb": { 229 | "equivalence_files": [] 230 | }, 231 | "erc": { 232 | "erc_exclusions": [], 233 | "meta": { 234 | "version": 0 235 | }, 236 | "pin_map": [ 237 | [ 238 | 0, 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 1, 245 | 0, 246 | 0, 247 | 0, 248 | 0, 249 | 2 250 | ], 251 | [ 252 | 0, 253 | 2, 254 | 0, 255 | 1, 256 | 0, 257 | 0, 258 | 1, 259 | 0, 260 | 2, 261 | 2, 262 | 2, 263 | 2 264 | ], 265 | [ 266 | 0, 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 1, 273 | 0, 274 | 1, 275 | 0, 276 | 1, 277 | 2 278 | ], 279 | [ 280 | 0, 281 | 1, 282 | 0, 283 | 0, 284 | 0, 285 | 0, 286 | 1, 287 | 1, 288 | 2, 289 | 1, 290 | 1, 291 | 2 292 | ], 293 | [ 294 | 0, 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 1, 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 2 306 | ], 307 | [ 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 2 320 | ], 321 | [ 322 | 1, 323 | 1, 324 | 1, 325 | 1, 326 | 1, 327 | 0, 328 | 1, 329 | 1, 330 | 1, 331 | 1, 332 | 1, 333 | 2 334 | ], 335 | [ 336 | 0, 337 | 0, 338 | 0, 339 | 1, 340 | 0, 341 | 0, 342 | 1, 343 | 0, 344 | 0, 345 | 0, 346 | 0, 347 | 2 348 | ], 349 | [ 350 | 0, 351 | 2, 352 | 1, 353 | 2, 354 | 0, 355 | 0, 356 | 1, 357 | 0, 358 | 2, 359 | 2, 360 | 2, 361 | 2 362 | ], 363 | [ 364 | 0, 365 | 2, 366 | 0, 367 | 1, 368 | 0, 369 | 0, 370 | 1, 371 | 0, 372 | 2, 373 | 0, 374 | 0, 375 | 2 376 | ], 377 | [ 378 | 0, 379 | 2, 380 | 1, 381 | 1, 382 | 0, 383 | 0, 384 | 1, 385 | 0, 386 | 2, 387 | 0, 388 | 0, 389 | 2 390 | ], 391 | [ 392 | 2, 393 | 2, 394 | 2, 395 | 2, 396 | 2, 397 | 2, 398 | 2, 399 | 2, 400 | 2, 401 | 2, 402 | 2, 403 | 2 404 | ] 405 | ], 406 | "rule_severities": { 407 | "bus_definition_conflict": "error", 408 | "bus_entry_needed": "error", 409 | "bus_to_bus_conflict": "error", 410 | "bus_to_net_conflict": "error", 411 | "conflicting_netclasses": "error", 412 | "different_unit_footprint": "error", 413 | "different_unit_net": "error", 414 | "duplicate_reference": "error", 415 | "duplicate_sheet_names": "error", 416 | "endpoint_off_grid": "warning", 417 | "extra_units": "error", 418 | "global_label_dangling": "warning", 419 | "hier_label_mismatch": "error", 420 | "label_dangling": "error", 421 | "lib_symbol_issues": "warning", 422 | "missing_bidi_pin": "warning", 423 | "missing_input_pin": "warning", 424 | "missing_power_pin": "error", 425 | "missing_unit": "warning", 426 | "multiple_net_names": "warning", 427 | "net_not_bus_member": "warning", 428 | "no_connect_connected": "warning", 429 | "no_connect_dangling": "warning", 430 | "pin_not_connected": "error", 431 | "pin_not_driven": "error", 432 | "pin_to_pin": "warning", 433 | "power_pin_not_driven": "error", 434 | "similar_labels": "warning", 435 | "simulation_model_issue": "ignore", 436 | "unannotated": "error", 437 | "unit_value_mismatch": "error", 438 | "unresolved_variable": "error", 439 | "wire_dangling": "error" 440 | } 441 | }, 442 | "libraries": { 443 | "pinned_footprint_libs": [], 444 | "pinned_symbol_libs": [] 445 | }, 446 | "meta": { 447 | "filename": "SubPcb.kicad_pro", 448 | "version": 1 449 | }, 450 | "net_settings": { 451 | "classes": [ 452 | { 453 | "bus_width": 12, 454 | "clearance": 0.2, 455 | "diff_pair_gap": 0.25, 456 | "diff_pair_via_gap": 0.25, 457 | "diff_pair_width": 0.2, 458 | "line_style": 0, 459 | "microvia_diameter": 0.3, 460 | "microvia_drill": 0.1, 461 | "name": "Default", 462 | "pcb_color": "rgba(0, 0, 0, 0.000)", 463 | "schematic_color": "rgba(0, 0, 0, 0.000)", 464 | "track_width": 0.2, 465 | "via_diameter": 0.6, 466 | "via_drill": 0.3, 467 | "wire_width": 6 468 | } 469 | ], 470 | "meta": { 471 | "version": 3 472 | }, 473 | "net_colors": null, 474 | "netclass_assignments": null, 475 | "netclass_patterns": [] 476 | }, 477 | "pcbnew": { 478 | "last_paths": { 479 | "gencad": "", 480 | "idf": "", 481 | "netlist": "", 482 | "plot": "", 483 | "pos_files": "", 484 | "specctra_dsn": "", 485 | "step": "", 486 | "svg": "", 487 | "vrml": "" 488 | }, 489 | "page_layout_descr_file": "" 490 | }, 491 | "schematic": { 492 | "annotate_start_num": 0, 493 | "bom_fmt_presets": [], 494 | "bom_fmt_settings": { 495 | "field_delimiter": ",", 496 | "keep_line_breaks": false, 497 | "keep_tabs": false, 498 | "name": "CSV", 499 | "ref_delimiter": ",", 500 | "ref_range_delimiter": "", 501 | "string_delimiter": "\"" 502 | }, 503 | "bom_presets": [], 504 | "bom_settings": { 505 | "exclude_dnp": false, 506 | "fields_ordered": [ 507 | { 508 | "group_by": false, 509 | "label": "Reference", 510 | "name": "Reference", 511 | "show": true 512 | }, 513 | { 514 | "group_by": true, 515 | "label": "Value", 516 | "name": "Value", 517 | "show": true 518 | }, 519 | { 520 | "group_by": false, 521 | "label": "Datasheet", 522 | "name": "Datasheet", 523 | "show": true 524 | }, 525 | { 526 | "group_by": false, 527 | "label": "Footprint", 528 | "name": "Footprint", 529 | "show": true 530 | }, 531 | { 532 | "group_by": false, 533 | "label": "Qty", 534 | "name": "${QUANTITY}", 535 | "show": true 536 | }, 537 | { 538 | "group_by": true, 539 | "label": "DNP", 540 | "name": "${DNP}", 541 | "show": true 542 | } 543 | ], 544 | "filter_string": "", 545 | "group_symbols": true, 546 | "name": "Grouped By Value", 547 | "sort_asc": true, 548 | "sort_field": "Reference" 549 | }, 550 | "connection_grid_size": 50.0, 551 | "drawing": { 552 | "dashed_lines_dash_length_ratio": 12.0, 553 | "dashed_lines_gap_length_ratio": 3.0, 554 | "default_line_thickness": 6.0, 555 | "default_text_size": 50.0, 556 | "field_names": [], 557 | "intersheets_ref_own_page": false, 558 | "intersheets_ref_prefix": "", 559 | "intersheets_ref_short": false, 560 | "intersheets_ref_show": false, 561 | "intersheets_ref_suffix": "", 562 | "junction_size_choice": 3, 563 | "label_size_ratio": 0.375, 564 | "operating_point_overlay_i_precision": 3, 565 | "operating_point_overlay_i_range": "~A", 566 | "operating_point_overlay_v_precision": 3, 567 | "operating_point_overlay_v_range": "~V", 568 | "overbar_offset_ratio": 1.23, 569 | "pin_symbol_size": 25.0, 570 | "text_offset_ratio": 0.15 571 | }, 572 | "legacy_lib_dir": "", 573 | "legacy_lib_list": [], 574 | "meta": { 575 | "version": 1 576 | }, 577 | "net_format_name": "", 578 | "page_layout_descr_file": "", 579 | "plot_directory": "", 580 | "spice_current_sheet_as_root": false, 581 | "spice_external_command": "spice \"%I\"", 582 | "spice_model_current_sheet_as_root": true, 583 | "spice_save_all_currents": false, 584 | "spice_save_all_dissipations": false, 585 | "spice_save_all_voltages": false, 586 | "subpart_first_id": 65, 587 | "subpart_id_separator": 0 588 | }, 589 | "sheets": [ 590 | [ 591 | "e0597968-f11c-42e6-8091-8a167b37e076", 592 | "Root" 593 | ] 594 | ], 595 | "text_variables": {} 596 | } 597 | -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/pType/fp-info-cache: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/pType/pType.kicad_pcb.hierpcb.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/pType/pType.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "All Layers", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.6 16 | }, 17 | "selection_filter": { 18 | "dimensions": true, 19 | "footprints": true, 20 | "graphics": true, 21 | "keepouts": true, 22 | "lockedItems": false, 23 | "otherItems": true, 24 | "pads": true, 25 | "text": true, 26 | "tracks": true, 27 | "vias": true, 28 | "zones": true 29 | }, 30 | "visible_items": [ 31 | 0, 32 | 1, 33 | 2, 34 | 3, 35 | 4, 36 | 5, 37 | 8, 38 | 9, 39 | 10, 40 | 11, 41 | 12, 42 | 13, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36, 64 | 39, 65 | 40 66 | ], 67 | "visible_layers": "fffffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "git": { 71 | "repo_password": "", 72 | "repo_type": "", 73 | "repo_username": "", 74 | "ssh_key": "" 75 | }, 76 | "meta": { 77 | "filename": "pType.kicad_prl", 78 | "version": 3 79 | }, 80 | "project": { 81 | "files": [] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/TripleRecursion/SubPcb/pType/pType.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.0 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "diff_pair_gap_out_of_range": "error", 73 | "diff_pair_uncoupled_length_too_long": "error", 74 | "drill_out_of_range": "error", 75 | "duplicate_footprints": "warning", 76 | "extra_footprint": "warning", 77 | "footprint": "error", 78 | "footprint_symbol_mismatch": "warning", 79 | "footprint_type_mismatch": "ignore", 80 | "hole_clearance": "error", 81 | "hole_near_hole": "error", 82 | "invalid_outline": "error", 83 | "isolated_copper": "warning", 84 | "item_on_disabled_layer": "error", 85 | "items_not_allowed": "error", 86 | "length_out_of_range": "error", 87 | "lib_footprint_issues": "warning", 88 | "lib_footprint_mismatch": "warning", 89 | "malformed_courtyard": "error", 90 | "microvia_drill_out_of_range": "error", 91 | "missing_courtyard": "ignore", 92 | "missing_footprint": "warning", 93 | "net_conflict": "warning", 94 | "npth_inside_courtyard": "ignore", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "ignore", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_thickness": "warning", 106 | "through_hole_pad_without_hole": "error", 107 | "too_many_vias": "error", 108 | "track_dangling": "warning", 109 | "track_width": "error", 110 | "tracks_crossing": "error", 111 | "unconnected_items": "error", 112 | "unresolved_variable": "error", 113 | "via_dangling": "warning", 114 | "zones_intersect": "error" 115 | }, 116 | "rules": { 117 | "max_error": 0.005, 118 | "min_clearance": 0.0, 119 | "min_connection": 0.0, 120 | "min_copper_edge_clearance": 0.5, 121 | "min_hole_clearance": 0.25, 122 | "min_hole_to_hole": 0.25, 123 | "min_microvia_diameter": 0.2, 124 | "min_microvia_drill": 0.1, 125 | "min_resolved_spokes": 2, 126 | "min_silk_clearance": 0.0, 127 | "min_text_height": 0.8, 128 | "min_text_thickness": 0.08, 129 | "min_through_hole_diameter": 0.3, 130 | "min_track_width": 0.0, 131 | "min_via_annular_width": 0.1, 132 | "min_via_diameter": 0.5, 133 | "solder_mask_to_copper_clearance": 0.0, 134 | "use_height_for_length_calcs": true 135 | }, 136 | "teardrop_options": [ 137 | { 138 | "td_onpadsmd": true, 139 | "td_onroundshapesonly": false, 140 | "td_ontrackend": false, 141 | "td_onviapad": true 142 | } 143 | ], 144 | "teardrop_parameters": [ 145 | { 146 | "td_allow_use_two_tracks": true, 147 | "td_curve_segcount": 0, 148 | "td_height_ratio": 1.0, 149 | "td_length_ratio": 0.5, 150 | "td_maxheight": 2.0, 151 | "td_maxlen": 1.0, 152 | "td_on_pad_in_zone": false, 153 | "td_target_name": "td_round_shape", 154 | "td_width_to_size_filter_ratio": 0.9 155 | }, 156 | { 157 | "td_allow_use_two_tracks": true, 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_on_pad_in_zone": false, 164 | "td_target_name": "td_rect_shape", 165 | "td_width_to_size_filter_ratio": 0.9 166 | }, 167 | { 168 | "td_allow_use_two_tracks": true, 169 | "td_curve_segcount": 0, 170 | "td_height_ratio": 1.0, 171 | "td_length_ratio": 0.5, 172 | "td_maxheight": 2.0, 173 | "td_maxlen": 1.0, 174 | "td_on_pad_in_zone": false, 175 | "td_target_name": "td_track_end", 176 | "td_width_to_size_filter_ratio": 0.9 177 | } 178 | ], 179 | "track_widths": [ 180 | 0.0, 181 | 0.8, 182 | 1.0 183 | ], 184 | "tuning_pattern_settings": { 185 | "diff_pair_defaults": { 186 | "corner_radius_percentage": 80, 187 | "corner_style": 1, 188 | "max_amplitude": 1.0, 189 | "min_amplitude": 0.2, 190 | "single_sided": false, 191 | "spacing": 1.0 192 | }, 193 | "diff_pair_skew_defaults": { 194 | "corner_radius_percentage": 80, 195 | "corner_style": 1, 196 | "max_amplitude": 1.0, 197 | "min_amplitude": 0.2, 198 | "single_sided": false, 199 | "spacing": 0.6 200 | }, 201 | "single_track_defaults": { 202 | "corner_radius_percentage": 80, 203 | "corner_style": 1, 204 | "max_amplitude": 1.0, 205 | "min_amplitude": 0.2, 206 | "single_sided": false, 207 | "spacing": 0.6 208 | } 209 | }, 210 | "via_dimensions": [ 211 | { 212 | "diameter": 0.0, 213 | "drill": 0.0 214 | } 215 | ], 216 | "zones_allow_external_fillets": false 217 | }, 218 | "ipc2581": { 219 | "dist": "", 220 | "distpn": "", 221 | "internal_id": "", 222 | "mfg": "", 223 | "mpn": "" 224 | }, 225 | "layer_presets": [], 226 | "viewports": [] 227 | }, 228 | "boards": [], 229 | "cvpcb": { 230 | "equivalence_files": [] 231 | }, 232 | "erc": { 233 | "erc_exclusions": [], 234 | "meta": { 235 | "version": 0 236 | }, 237 | "pin_map": [ 238 | [ 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 0, 245 | 1, 246 | 0, 247 | 0, 248 | 0, 249 | 0, 250 | 2 251 | ], 252 | [ 253 | 0, 254 | 2, 255 | 0, 256 | 1, 257 | 0, 258 | 0, 259 | 1, 260 | 0, 261 | 2, 262 | 2, 263 | 2, 264 | 2 265 | ], 266 | [ 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 0, 273 | 1, 274 | 0, 275 | 1, 276 | 0, 277 | 1, 278 | 2 279 | ], 280 | [ 281 | 0, 282 | 1, 283 | 0, 284 | 0, 285 | 0, 286 | 0, 287 | 1, 288 | 1, 289 | 2, 290 | 1, 291 | 1, 292 | 2 293 | ], 294 | [ 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 0, 301 | 1, 302 | 0, 303 | 0, 304 | 0, 305 | 0, 306 | 2 307 | ], 308 | [ 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 0, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 0, 320 | 2 321 | ], 322 | [ 323 | 1, 324 | 1, 325 | 1, 326 | 1, 327 | 1, 328 | 0, 329 | 1, 330 | 1, 331 | 1, 332 | 1, 333 | 1, 334 | 2 335 | ], 336 | [ 337 | 0, 338 | 0, 339 | 0, 340 | 1, 341 | 0, 342 | 0, 343 | 1, 344 | 0, 345 | 0, 346 | 0, 347 | 0, 348 | 2 349 | ], 350 | [ 351 | 0, 352 | 2, 353 | 1, 354 | 2, 355 | 0, 356 | 0, 357 | 1, 358 | 0, 359 | 2, 360 | 2, 361 | 2, 362 | 2 363 | ], 364 | [ 365 | 0, 366 | 2, 367 | 0, 368 | 1, 369 | 0, 370 | 0, 371 | 1, 372 | 0, 373 | 2, 374 | 0, 375 | 0, 376 | 2 377 | ], 378 | [ 379 | 0, 380 | 2, 381 | 1, 382 | 1, 383 | 0, 384 | 0, 385 | 1, 386 | 0, 387 | 2, 388 | 0, 389 | 0, 390 | 2 391 | ], 392 | [ 393 | 2, 394 | 2, 395 | 2, 396 | 2, 397 | 2, 398 | 2, 399 | 2, 400 | 2, 401 | 2, 402 | 2, 403 | 2, 404 | 2 405 | ] 406 | ], 407 | "rule_severities": { 408 | "bus_definition_conflict": "error", 409 | "bus_entry_needed": "error", 410 | "bus_to_bus_conflict": "error", 411 | "bus_to_net_conflict": "error", 412 | "conflicting_netclasses": "error", 413 | "different_unit_footprint": "error", 414 | "different_unit_net": "error", 415 | "duplicate_reference": "error", 416 | "duplicate_sheet_names": "error", 417 | "endpoint_off_grid": "warning", 418 | "extra_units": "error", 419 | "global_label_dangling": "warning", 420 | "hier_label_mismatch": "error", 421 | "label_dangling": "error", 422 | "lib_symbol_issues": "warning", 423 | "missing_bidi_pin": "warning", 424 | "missing_input_pin": "warning", 425 | "missing_power_pin": "error", 426 | "missing_unit": "warning", 427 | "multiple_net_names": "warning", 428 | "net_not_bus_member": "warning", 429 | "no_connect_connected": "warning", 430 | "no_connect_dangling": "warning", 431 | "pin_not_connected": "error", 432 | "pin_not_driven": "error", 433 | "pin_to_pin": "warning", 434 | "power_pin_not_driven": "error", 435 | "similar_labels": "warning", 436 | "simulation_model_issue": "ignore", 437 | "unannotated": "error", 438 | "unit_value_mismatch": "error", 439 | "unresolved_variable": "error", 440 | "wire_dangling": "error" 441 | } 442 | }, 443 | "libraries": { 444 | "pinned_footprint_libs": [], 445 | "pinned_symbol_libs": [] 446 | }, 447 | "meta": { 448 | "filename": "nType.kicad_pro", 449 | "version": 1 450 | }, 451 | "net_settings": { 452 | "classes": [ 453 | { 454 | "bus_width": 12, 455 | "clearance": 0.2, 456 | "diff_pair_gap": 0.25, 457 | "diff_pair_via_gap": 0.25, 458 | "diff_pair_width": 0.2, 459 | "line_style": 0, 460 | "microvia_diameter": 0.3, 461 | "microvia_drill": 0.1, 462 | "name": "Default", 463 | "pcb_color": "rgba(0, 0, 0, 0.000)", 464 | "schematic_color": "rgba(0, 0, 0, 0.000)", 465 | "track_width": 0.2, 466 | "via_diameter": 0.6, 467 | "via_drill": 0.3, 468 | "wire_width": 6 469 | } 470 | ], 471 | "meta": { 472 | "version": 3 473 | }, 474 | "net_colors": null, 475 | "netclass_assignments": null, 476 | "netclass_patterns": [] 477 | }, 478 | "pcbnew": { 479 | "last_paths": { 480 | "gencad": "", 481 | "idf": "", 482 | "netlist": "", 483 | "plot": "", 484 | "pos_files": "", 485 | "specctra_dsn": "", 486 | "step": "", 487 | "svg": "", 488 | "vrml": "" 489 | }, 490 | "page_layout_descr_file": "" 491 | }, 492 | "schematic": { 493 | "annotate_start_num": 0, 494 | "bom_fmt_presets": [], 495 | "bom_fmt_settings": { 496 | "field_delimiter": ",", 497 | "keep_line_breaks": false, 498 | "keep_tabs": false, 499 | "name": "CSV", 500 | "ref_delimiter": ",", 501 | "ref_range_delimiter": "", 502 | "string_delimiter": "\"" 503 | }, 504 | "bom_presets": [], 505 | "bom_settings": { 506 | "exclude_dnp": false, 507 | "fields_ordered": [ 508 | { 509 | "group_by": false, 510 | "label": "Reference", 511 | "name": "Reference", 512 | "show": true 513 | }, 514 | { 515 | "group_by": true, 516 | "label": "Value", 517 | "name": "Value", 518 | "show": true 519 | }, 520 | { 521 | "group_by": false, 522 | "label": "Datasheet", 523 | "name": "Datasheet", 524 | "show": true 525 | }, 526 | { 527 | "group_by": false, 528 | "label": "Footprint", 529 | "name": "Footprint", 530 | "show": true 531 | }, 532 | { 533 | "group_by": false, 534 | "label": "Qty", 535 | "name": "${QUANTITY}", 536 | "show": true 537 | }, 538 | { 539 | "group_by": true, 540 | "label": "DNP", 541 | "name": "${DNP}", 542 | "show": true 543 | } 544 | ], 545 | "filter_string": "", 546 | "group_symbols": true, 547 | "name": "Grouped By Value", 548 | "sort_asc": true, 549 | "sort_field": "Reference" 550 | }, 551 | "connection_grid_size": 50.0, 552 | "drawing": { 553 | "dashed_lines_dash_length_ratio": 12.0, 554 | "dashed_lines_gap_length_ratio": 3.0, 555 | "default_line_thickness": 6.0, 556 | "default_text_size": 50.0, 557 | "field_names": [], 558 | "intersheets_ref_own_page": false, 559 | "intersheets_ref_prefix": "", 560 | "intersheets_ref_short": false, 561 | "intersheets_ref_show": false, 562 | "intersheets_ref_suffix": "", 563 | "junction_size_choice": 3, 564 | "label_size_ratio": 0.375, 565 | "operating_point_overlay_i_precision": 3, 566 | "operating_point_overlay_i_range": "~A", 567 | "operating_point_overlay_v_precision": 3, 568 | "operating_point_overlay_v_range": "~V", 569 | "overbar_offset_ratio": 1.23, 570 | "pin_symbol_size": 25.0, 571 | "text_offset_ratio": 0.15 572 | }, 573 | "legacy_lib_dir": "", 574 | "legacy_lib_list": [], 575 | "meta": { 576 | "version": 1 577 | }, 578 | "net_format_name": "", 579 | "page_layout_descr_file": "", 580 | "plot_directory": "", 581 | "spice_current_sheet_as_root": false, 582 | "spice_external_command": "spice \"%I\"", 583 | "spice_model_current_sheet_as_root": true, 584 | "spice_save_all_currents": false, 585 | "spice_save_all_dissipations": false, 586 | "spice_save_all_voltages": false, 587 | "subpart_first_id": 65, 588 | "subpart_id_separator": 0 589 | }, 590 | "sheets": [ 591 | [ 592 | "e0597968-f11c-42e6-8091-8a167b37e076", 593 | "Root" 594 | ] 595 | ], 596 | "text_variables": {} 597 | } 598 | -------------------------------------------------------------------------------- /hdata.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import dataclasses 3 | import logging 4 | import string 5 | from dataclasses import dataclass 6 | from pathlib import Path 7 | from typing import Dict, List, Optional, Tuple 8 | from .cfgman import ConfigMan 9 | 10 | import pcbnew 11 | import wx 12 | 13 | logger = logging.getLogger("hierpcb") 14 | 15 | 16 | SchPath = Tuple[str, ...] 17 | 18 | FootprintID = str # Unique id for a footprint across all sub-schematics. 19 | 20 | 21 | class PCBRoom: 22 | """Holds the data for a 'room' in the PCB layout, correponding to the layout for a schematic sheet.""" 23 | 24 | def __init__(self, path: Path): 25 | self.path = path 26 | # Load the room data from the file. 27 | self.subboard = pcbnew.LoadBoard(str(path)) 28 | assert self.subboard is not None 29 | logger.info( 30 | f"Imported {path}, with " 31 | f"{len(self.subboard.GetFootprints())} footprints, " 32 | f"{len(self.subboard.GetTracks())} tracks, and " 33 | f"{len(self.subboard.GetDrawings())} drawings." 34 | ) 35 | 36 | self.selected_anchor: Optional[wx.FOOTPRINT] = None 37 | 38 | def get_anchor_ref(self) -> Optional[str]: 39 | if self.selected_anchor is None: 40 | return None 41 | return self.selected_anchor.GetReference() 42 | 43 | def set_anchor_ref(self, ref: str): 44 | # Find the footprint with the given reference: 45 | for fp in self.subboard.GetFootprints(): 46 | if fp.GetReference() == ref: 47 | self.selected_anchor = fp 48 | return 49 | logger.warning(f"Could not find footprint {ref} in board {self.path}.") 50 | 51 | def get_heuristic_anchor_ref(self) -> Optional[str]: 52 | """Guess the reference prefix of the footprint that will be used as an anchor.""" 53 | prefixes = collections.defaultdict(lambda: 0) 54 | 55 | def prefix(s: str) -> str: 56 | return s.rstrip(string.digits) 57 | 58 | fps = [] 59 | for fp in self.subboard.GetFootprints(): 60 | prefixes[prefix(fp.GetReference())] += 1 61 | fps.append(fp) 62 | 63 | heuristic_list = [] 64 | for fp in fps: 65 | ref = fp.GetReference() 66 | heuristic_list.append((-fp.GetArea(), prefixes[prefix(ref)], ref)) 67 | 68 | _, _, min_ref = min(heuristic_list) 69 | return min_ref 70 | 71 | def get_anchor_refs(self) -> Dict[FootprintID, str]: 72 | """Get the references of the footprints that can be used as anchors.""" 73 | rv = {} 74 | for fp in self.subboard.GetFootprints(): 75 | path = fp.GetPath().AsString() 76 | if path is None or path == "": 77 | logger.warn(f"Missing path: {fp.GetReference()} ({self.path})") 78 | continue 79 | rv[path] = fp.GetReference() 80 | 81 | return rv 82 | 83 | def __str__(self) -> str: 84 | # Head line with the sheet name and whether it has a PCB layout. 85 | rv = [f"PCB {self.path}"] 86 | for k, v in self.get_anchor_refs().items(): 87 | ksumm = "/".join(k[:8] for k in k.split("/")) 88 | rv.append(f" {ksumm}: {v}") 89 | 90 | return "\n".join(rv) 91 | 92 | @property 93 | def is_legal(self) -> bool: 94 | """Check if the room can be handled by our engine.""" 95 | # For a room to be legal, there must be at least one footprint. That's it. 96 | return len(self.get_anchor_refs()) > 0 97 | 98 | 99 | class SchSheet: 100 | """Class to hold information about a schematic sheet.""" 101 | 102 | def __init__(self, sheetid: str = "", parent: Optional["SchSheet"] = None) -> None: 103 | # The sheet identifier, if "", this is the root sheet. 104 | self.sheetid: str = sheetid 105 | # The parent sheet. If None, this is the root sheet. 106 | self.parent: Optional["SchSheet"] = parent 107 | 108 | # The immediate child sheets of this node. 109 | self.children: Dict[str, "SchSheet"] = {} 110 | 111 | # These are only available if the sheet is referenced by a footprint: 112 | # The file name of the schematic sheet. 113 | self.file: Optional[Path] = None 114 | # The human-readable name of the schematic sheet. 115 | self.name: Optional[str] = None 116 | 117 | # TODO: Pointer to the PCB layout data. 118 | self.pcb: Optional[PCBRoom] = None 119 | 120 | # Metadata for UI rendering: 121 | self.list_ref: Optional[wx.TreeListItem] = None 122 | self.checked: bool = False 123 | 124 | def set_metadata(self, file: Path, name: str) -> None: 125 | self.file = file 126 | self.name = name 127 | 128 | def has_metadata(self): 129 | return self.file is not None and self.name is not None 130 | 131 | def get(self, key: SchPath, create=False) -> "SchSheet": 132 | """Get a sheet by its key.""" 133 | # If the key is empty, we are done. 134 | if len(key) == 0: 135 | return self 136 | # Otherwise the first element of the key is the sheet identifier. 137 | cons, rest = key[0], key[1:] 138 | if cons not in self.children: 139 | if not create: 140 | raise KeyError(f"Sheet {key} not found in {self.identifier}.") 141 | self.children[cons] = SchSheet(cons, self) 142 | # Recur on the rest in the child sheet. 143 | return self.children[cons].get(rest, create) 144 | 145 | def tree_iter(self, skip_root=False): 146 | """Iterate over the tree.""" 147 | if not skip_root: 148 | yield self 149 | for _, child in sorted(self.children.items()): 150 | yield from child.tree_iter() 151 | 152 | def set_checked_default(self, ancestor_checked: bool = False): 153 | """Set the tree to its default state recursively.""" 154 | self.checked = False 155 | # Check if the sheet has a PCB: 156 | if self.pcb and self.pcb.is_legal: 157 | # If an ancestor is checked, this sheet cannot be checked. 158 | # Otherwise, this is the first sheet from the root to be elligible, 159 | if not ancestor_checked: 160 | self.checked = True 161 | 162 | # Recur on the children: 163 | for child in self.children.values(): 164 | child.set_checked_default(child, self.checked or ancestor_checked) 165 | 166 | def cleanup_checked(self, ancestor_checked: bool = False): 167 | """Make sure that there are no paths from the root that include more than one checked sheet.""" 168 | if ancestor_checked: 169 | self.checked = False 170 | # Recur on the children: 171 | for child in self.children.values(): 172 | child.set_checked_default(child, self.checked or ancestor_checked) 173 | 174 | @property 175 | def identifier(self) -> str: 176 | if self.parent is None: 177 | return self.sheetid 178 | return self.parent.identifier + "/" + self.sheetid 179 | 180 | @property 181 | def human_name(self) -> str: 182 | return self.name if self.name is not None else self.sheetid[:8] 183 | 184 | @property 185 | def human_path(self) -> str: 186 | if self.parent is None: 187 | return self.human_name 188 | return self.parent.human_path + "/" + self.human_name 189 | 190 | def __str__(self) -> str: 191 | # Head line with the sheet name and whether it has a PCB layout. 192 | rv = [self.human_name + (f" (+ PCB {self.pcb.path})" if self.pcb else "")] 193 | # If there are children, print them. 194 | for _, child in sorted(self.children.items()): 195 | c_str = str(child).splitlines() 196 | # The first line is the child's name, so it should look like a node in the tree. 197 | rv.append(f"├─ {c_str[0]}") 198 | for line in c_str[1:]: 199 | rv.append(f"│ {line}") 200 | return "\n".join(rv) 201 | 202 | 203 | class HierarchicalData: 204 | def __init__(self, board: pcbnew.BOARD): 205 | self.board = board 206 | self.basedir = Path(board.GetFileName()).parent 207 | self.root_sheet, self.pcb_rooms = get_sheet_hierarchy(board, self.basedir) 208 | 209 | def load(self, cfg: ConfigMan): 210 | # Load the PCB rooms: 211 | for subpcb in self.pcb_rooms.values(): 212 | anchor = cfg.get( 213 | "subpcb", 214 | str(subpcb.path.relative_to(self.basedir)), 215 | "anchor", 216 | default=subpcb.get_heuristic_anchor_ref(), 217 | ) 218 | subpcb.set_anchor_ref(anchor) 219 | 220 | for sheet in self.root_sheet.tree_iter(): 221 | sheet.checked = cfg.get("sheet", sheet.identifier, "checked", default=False) 222 | sheet.cleanup_checked() 223 | 224 | def save(self, cfg: ConfigMan): 225 | # Save the current state: 226 | cfg.clear("subpcb") 227 | for subpcb in self.pcb_rooms.values(): 228 | cfg.set( 229 | "subpcb", 230 | str(subpcb.path.relative_to(self.basedir)), 231 | "anchor", 232 | value=subpcb.get_anchor_ref(), 233 | ) 234 | cfg.clear("sheet") 235 | for sheet in self.root_sheet.tree_iter(): 236 | cfg.set("sheet", sheet.identifier, "checked", value=sheet.checked) 237 | 238 | 239 | def get_sheet_key_from_footprint(fp: pcbnew.FOOTPRINT) -> Optional[SchPath]: 240 | key = fp.GetPath().AsString().split("/") 241 | if len(key) <= 1: 242 | # Skip footprints that are not on a sheet. 243 | logger.debug(f"Footprint {fp.GetReference()} is not on a sheet, skipping...") 244 | return None 245 | assert key[0] == "" 246 | return tuple(key[1:-1]) 247 | 248 | 249 | def get_sheet_hierarchy( 250 | board: pcbnew.BOARD, basedir: Path 251 | ) -> Tuple[SchSheet, Dict[Path, PCBRoom]]: 252 | """Infer the sheet hierarchy from footprint data. 253 | 254 | While this should be better handled by examining the schematics, we can't yet do that in KiCad. 255 | Note that this cannot find sheets that are not referenced by at least one footprint. 256 | """ 257 | 258 | # None means the sheet is known not to have a PCB layout. 259 | pcb_rooms: Dict[str, Optional[PCBRoom]] = {} 260 | root_sheet: Optional[SchSheet] = SchSheet("", None) 261 | 262 | for fp in board.GetFootprints(): 263 | key = get_sheet_key_from_footprint(fp) 264 | # Skip unknown sheets. 265 | if key is None: 266 | continue 267 | # Get the sheet for this footprint, creating it if necessary. 268 | curr_sheet = root_sheet.get(key, create=True) 269 | 270 | if not curr_sheet.has_metadata(): 271 | try: 272 | sheet_file = Path(fp.GetSheetfile()) 273 | sheet_name = fp.GetSheetname() 274 | except KeyError: 275 | logger.debug(f"No Sheetfile for {fp.GetReference()}, skipping.") 276 | continue 277 | 278 | curr_sheet.set_metadata(sheet_file, sheet_name) 279 | 280 | if sheet_file not in pcb_rooms: 281 | # If it is not known if the sheet_file does not have an associated PCB layout, 282 | # then we look for one. 283 | pcb_file = basedir / sheet_file.with_suffix(".kicad_pcb") 284 | pcb_rooms[sheet_file] = PCBRoom(pcb_file) if pcb_file.exists() else None 285 | 286 | curr_sheet.pcb = pcb_rooms[sheet_file] 287 | 288 | return root_sheet, {k: v for k, v in pcb_rooms.items() if v is not None} 289 | -------------------------------------------------------------------------------- /hplugin.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import pprint 4 | import sys 5 | import time 6 | import traceback 7 | from pathlib import Path 8 | 9 | import pcbnew 10 | import wx 11 | 12 | from .cfgman import ConfigMan 13 | from .hdata import HierarchicalData 14 | from .interface import DlgHPCBRun 15 | from .placement import enforce_position 16 | 17 | logger = logging.getLogger("hierpcb") 18 | logger.setLevel(logging.DEBUG) 19 | 20 | 21 | class HierarchicalPCBPlugin(pcbnew.ActionPlugin): 22 | def __init__(self): 23 | super().__init__() 24 | self.version = "0.0.1" 25 | 26 | def defaults(self): 27 | self.name = "HierarchicalPCB" 28 | self.category = "Layout" 29 | self.description = ( 30 | "True hierarchical layouts to go with the hierarchical schematics." 31 | "You can define 'rooms' for different schematics throughout the hierarchy " 32 | "and this plugin will enforce them on the PCB." 33 | ) 34 | self.icon_file_name = str(Path(__file__).parent / "icon.png") 35 | self.show_toolbar_button = True 36 | 37 | def Run(self): 38 | # grab PCB editor frame 39 | wx_frame = wx.FindWindowByName("PcbFrame") 40 | board = pcbnew.GetBoard() 41 | 42 | for lH in list(logger.handlers): 43 | logger.removeHandler(lH) 44 | logger.addHandler( 45 | logging.FileHandler(filename=board.GetFileName() + ".hierpcb.log", mode="w") 46 | ) 47 | 48 | # set up logger 49 | logger.info( 50 | f"Plugin v{self.version} running on KiCad {pcbnew.GetBuildVersion()} and Python {sys.version} on {sys.platform}." 51 | ) 52 | 53 | with ConfigMan(Path(board.GetFileName() + ".hierpcb.json")) as cfg: 54 | RunActual(cfg, wx_frame, board) 55 | 56 | 57 | def RunActual(cfg: ConfigMan, wx_frame: wx.Window, board: pcbnew.BOARD): 58 | hD = HierarchicalData(board) 59 | logger.debug(str(hD.root_sheet)) 60 | for room in hD.pcb_rooms.values(): 61 | logger.debug(str(room)) 62 | hD.load(cfg) # Load defaults 63 | 64 | if DlgHPCBRun(cfg, wx_frame, hD).ShowModal() == wx.ID_OK: 65 | enforce_position(hD, board) 66 | 67 | hD.save(cfg) 68 | logger.info("Saved.") 69 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/icon.png -------------------------------------------------------------------------------- /images/03_kle_placement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/images/03_kle_placement.png -------------------------------------------------------------------------------- /images/04_input_hierarchical_pcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/images/04_input_hierarchical_pcb.png -------------------------------------------------------------------------------- /images/05_hierarchical_pcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/images/05_hierarchical_pcb.png -------------------------------------------------------------------------------- /images/06_menubutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/images/06_menubutton.png -------------------------------------------------------------------------------- /images/11_even_with_rotations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/images/11_even_with_rotations.png -------------------------------------------------------------------------------- /images/12_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/images/12_menu.png -------------------------------------------------------------------------------- /images/14_subpcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/images/14_subpcb.png -------------------------------------------------------------------------------- /images/14_subsch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/images/14_subsch.png -------------------------------------------------------------------------------- /images/icon-orig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauravmm/HierarchicalPcb/999d3938cc3d3e61fa61edb82425e912530cfe61/images/icon-orig.png -------------------------------------------------------------------------------- /interface/DlgHPCBRun.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from pathlib import Path 3 | from typing import Any, Callable, Dict, Optional 4 | 5 | import wx 6 | 7 | from ..cfgman import ConfigMan 8 | from ..hdata import HierarchicalData, PCBRoom, SchSheet 9 | from .DlgHPCBRun_Base import DlgHPCBRun_Base 10 | 11 | logger = logging.getLogger("hierpcb") 12 | 13 | 14 | def set_checked_default( 15 | tree: wx.dataview.TreeListCtrl, sheet: SchSheet, ancestor_checked: bool = False 16 | ): 17 | """Set the tree to its default state starting from node `sheet`.""" 18 | 19 | sheet.checked = False 20 | # Skip nodes that don't have list refs: 21 | if sheet.list_ref: 22 | # Check if the sheet has a PCB: 23 | if sheet.pcb and sheet.pcb.is_legal: 24 | # If an ancestor is checked, this sheet cannot be checked. 25 | # Otherwise, this is the first sheet from the root to be elligible, 26 | if not ancestor_checked: 27 | sheet.checked = True 28 | 29 | tree.SetItemBold(sheet.list_ref, sheet.checked) 30 | 31 | # Recur on the children: 32 | for _, child in sorted(sheet.children.items()): 33 | set_checked_default(tree, child, sheet.checked or ancestor_checked) 34 | 35 | 36 | def apply_checked(tree: wx.dataview.TreeListCtrl, sheet: SchSheet): 37 | """Mutate the tree to reflect the checked state of the sheets.""" 38 | # Skip nodes that don't have list refs: 39 | if sheet.list_ref: 40 | tree.SetItemBold(sheet.list_ref, sheet.checked) 41 | # Recur on the children: 42 | for _, child in sorted(sheet.children.items()): 43 | apply_checked(tree, child) 44 | 45 | 46 | def is_checkable(tree: wx.dataview.TreeListCtrl, sheet: SchSheet) -> bool: 47 | """Check if the sheet may be checked.""" 48 | if sheet.pcb and sheet.pcb.is_legal: 49 | # Now that we know it can be checked, verify that no ancestor is checked: 50 | node = sheet.parent 51 | while node is not None and node.list_ref is not None: 52 | logger.info(f"Checking {node.identifier}: {node.checked}.") 53 | if node.checked: 54 | return False 55 | node = node.parent 56 | return True 57 | return False 58 | 59 | 60 | class DlgHPCBRun(DlgHPCBRun_Base): 61 | def __init__(self, cfg: ConfigMan, parent: wx.Window, hD: HierarchicalData): 62 | # Set up the user interface from the designer. 63 | super().__init__(parent) 64 | # Populate the dialog with data: 65 | self.hD = hD 66 | 67 | #Create the root or else our first added layout will be 68 | self.treeApplyTo.AddRoot( 69 | text="" 70 | ) 71 | 72 | for sheet in hD.root_sheet.tree_iter(skip_root=True): 73 | # Look up the parent, if it is in the tree already. 74 | parent_item: wx.TreeListItem = ( 75 | sheet.parent.list_ref or self.treeApplyTo.GetRootItem() 76 | ) 77 | 78 | # If the sheet has a PCB, mention it in the appropriate column: 79 | row_text = sheet.human_name 80 | if sheet.pcb is not None: 81 | row_text += f": {sheet.pcb.path.relative_to(hD.basedir)}" 82 | if not sheet.pcb.is_legal: 83 | row_text += " No Footprints!" 84 | 85 | item: wx.TreeListItem = self.treeApplyTo.AppendItem( 86 | parent=parent_item, text=row_text, data=sheet 87 | ) 88 | 89 | # Add the sheet to the tree, in case it is a future parent: 90 | sheet.list_ref = item 91 | 92 | self.treeApplyTo.ExpandAll() 93 | 94 | # Set the default state of the tree: 95 | # TODO: Mutate the tree structure and 96 | # set_checked_default(self.treeApplyTo, hD.root_sheet) 97 | apply_checked(self.treeApplyTo, hD.root_sheet) 98 | 99 | # Populate the list of available sub-PCBs: 100 | self.subPCBList.AppendTextColumn("Name") 101 | self.subPCBList.AppendTextColumn("Anchor Footprint") 102 | self.pcb_rooms_lookup = [subpcb for _, subpcb in sorted(hD.pcb_rooms.items())] 103 | for subpcb in self.pcb_rooms_lookup: 104 | self.subPCBList.AppendItem([subpcb.path.name, subpcb.get_anchor_ref()]) 105 | 106 | def handleSelection(self, evt: wx.dataview.TreeListEvent): 107 | """Handle a click on a tree item.""" 108 | item = evt.GetItem() 109 | # Get the sheet associated with the item: 110 | sheet: SchSheet = self.treeApplyTo.GetItemData(item) 111 | # If the sheet is now unchecked, do nothing: 112 | 113 | if is_checkable(self.treeApplyTo, sheet): 114 | if sheet.checked: 115 | sheet.checked = False 116 | self.treeApplyTo.SetItemBold(sheet.list_ref, sheet.checked) 117 | else: 118 | # Check it and uncheck all descendants: 119 | set_checked_default(self.treeApplyTo, sheet) 120 | 121 | def resetToDefault(self, event): 122 | """Reset the tree to its default state.""" 123 | set_checked_default(self.treeApplyTo, self.hD.root_sheet) 124 | 125 | def getSelectedSubPCB(self) -> Optional[PCBRoom]: 126 | selRow = self.subPCBList.GetSelectedRow() 127 | try: 128 | subpcb: PCBRoom = self.pcb_rooms_lookup[selRow] 129 | logger.info(f"Selected for {selRow} : {subpcb.path}") 130 | return subpcb 131 | except IndexError: 132 | logger.info(f"Invalid index: {selRow}") 133 | return None 134 | 135 | def changeSelectedSubPCB(self, event): 136 | """Update the choices of anchors for the selected sub-PCB.""" 137 | # Get the selected sub-PCB: 138 | subpcb = self.getSelectedSubPCB() 139 | if subpcb is None: 140 | return 141 | 142 | # Get the current selection 143 | curr = subpcb.get_anchor_ref() or subpcb.get_heuristic_anchor_ref() 144 | # Get the list of available anchors: 145 | self.anchors = sorted(subpcb.get_anchor_refs().values()) 146 | logger.info(f"Anchors ({curr}): {self.anchors}") 147 | self.anchorChoice.Clear() 148 | self.anchorChoice.AppendItems(self.anchors) 149 | # Select the current anchor: 150 | self.anchorChoice.SetSelection(self.anchors.index(curr)) 151 | 152 | def changeAnchor(self, event): 153 | # Set the anchor: 154 | subpcb = self.getSelectedSubPCB() 155 | if subpcb is None: 156 | return 157 | 158 | # Get the selected anchor: 159 | sel = self.anchorChoice.GetSelection() 160 | logger.info(f"Anchor {sel} for {subpcb.path}") 161 | if sel == wx.NOT_FOUND: 162 | logger.warning("No anchor selected!") 163 | return 164 | 165 | sel_ref = self.anchors[sel] 166 | subpcb.set_anchor_ref(sel_ref) 167 | # Update the display: 168 | self.subPCBList.SetTextValue(sel_ref, self.subPCBList.GetSelectedRow(), 1) 169 | 170 | def handleApply(self, event): 171 | """Submit the form.""" 172 | # Mutate the tree structure and 173 | self.EndModal(wx.ID_OK) 174 | -------------------------------------------------------------------------------- /interface/DlgHPCBRun_Base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ########################################################################### 4 | ## Python code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3) 5 | ## http://www.wxformbuilder.org/ 6 | ## 7 | ## PLEASE DO *NOT* EDIT THIS FILE! 8 | ########################################################################### 9 | 10 | import wx 11 | import wx.xrc 12 | import wx.dataview 13 | 14 | ########################################################################### 15 | ## Class DlgHPCBRun_Base 16 | ########################################################################### 17 | 18 | 19 | class DlgHPCBRun_Base(wx.Dialog): 20 | def __init__(self, parent): 21 | wx.Dialog.__init__( 22 | self, 23 | parent, 24 | id=wx.ID_ANY, 25 | title="HierarchicalPCB", 26 | pos=wx.DefaultPosition, 27 | size=wx.Size(465, 766), 28 | style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, 29 | ) 30 | 31 | self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) 32 | 33 | bSizerMain = wx.BoxSizer(wx.VERTICAL) 34 | 35 | bSizerMain.SetMinSize(wx.Size(-1, 600)) 36 | self.m_staticText1 = wx.StaticText( 37 | self, 38 | wx.ID_ANY, 39 | "Choose which sub-PCB layouts to apply:", 40 | wx.DefaultPosition, 41 | wx.DefaultSize, 42 | 0, 43 | ) 44 | self.m_staticText1.Wrap(-1) 45 | 46 | bSizerMain.Add(self.m_staticText1, 0, wx.ALL, 5) 47 | 48 | self.treeApplyTo = wx.TreeCtrl( 49 | self, 50 | wx.ID_ANY, 51 | wx.DefaultPosition, 52 | wx.DefaultSize, 53 | wx.TR_DEFAULT_STYLE 54 | | wx.TR_FULL_ROW_HIGHLIGHT 55 | | wx.TR_HIDE_ROOT 56 | | wx.TR_NO_LINES 57 | | wx.TR_SINGLE 58 | | wx.TR_TWIST_BUTTONS, 59 | ) 60 | self.treeApplyTo.SetMinSize(wx.Size(-1, 300)) 61 | 62 | bSizerMain.Add(self.treeApplyTo, 1, wx.ALL | wx.EXPAND, 5) 63 | 64 | bSizer6 = wx.BoxSizer(wx.HORIZONTAL) 65 | 66 | self.m_staticText6 = wx.StaticText( 67 | self, 68 | wx.ID_ANY, 69 | "Double-click to change.", 70 | wx.DefaultPosition, 71 | wx.DefaultSize, 72 | 0, 73 | ) 74 | self.m_staticText6.Wrap(-1) 75 | 76 | bSizer6.Add(self.m_staticText6, 1, wx.ALL, 5) 77 | 78 | self.m_staticText41 = wx.StaticText( 79 | self, wx.ID_ANY, "Reset to Default", wx.DefaultPosition, wx.DefaultSize, 0 80 | ) 81 | self.m_staticText41.Wrap(-1) 82 | 83 | self.m_staticText41.SetFont( 84 | wx.Font( 85 | 10, 86 | wx.FONTFAMILY_DEFAULT, 87 | wx.FONTSTYLE_NORMAL, 88 | wx.FONTWEIGHT_NORMAL, 89 | True, 90 | wx.EmptyString, 91 | ) 92 | ) 93 | self.m_staticText41.SetForegroundColour( 94 | wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) 95 | ) 96 | self.m_staticText41.SetToolTip("Double-click to reset the selection.") 97 | 98 | bSizer6.Add(self.m_staticText41, 0, wx.ALIGN_RIGHT | wx.ALL, 5) 99 | 100 | bSizerMain.Add(bSizer6, 0, wx.EXPAND, 5) 101 | 102 | self.m_staticline1 = wx.StaticLine( 103 | self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL 104 | ) 105 | bSizerMain.Add(self.m_staticline1, 0, wx.EXPAND | wx.ALL, 5) 106 | 107 | self.m_staticText2 = wx.StaticText( 108 | self, 109 | wx.ID_ANY, 110 | "Configure each sub-PCB layout:", 111 | wx.DefaultPosition, 112 | wx.DefaultSize, 113 | 0, 114 | ) 115 | self.m_staticText2.Wrap(-1) 116 | 117 | bSizerMain.Add(self.m_staticText2, 0, wx.ALL, 5) 118 | 119 | self.subPCBList = wx.dataview.DataViewListCtrl( 120 | self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 121 | ) 122 | self.subPCBList.SetMinSize(wx.Size(-1, 200)) 123 | 124 | bSizerMain.Add(self.subPCBList, 1, wx.EXPAND, 5) 125 | 126 | bSizer8 = wx.BoxSizer(wx.HORIZONTAL) 127 | 128 | self.m_staticText7 = wx.StaticText( 129 | self, wx.ID_ANY, "Change anchor:", wx.DefaultPosition, wx.DefaultSize, 0 130 | ) 131 | self.m_staticText7.Wrap(-1) 132 | 133 | bSizer8.Add(self.m_staticText7, 0, wx.ALL, 5) 134 | 135 | anchorChoiceChoices = [] 136 | self.anchorChoice = wx.Choice( 137 | self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, anchorChoiceChoices, 0 138 | ) 139 | self.anchorChoice.SetSelection(0) 140 | bSizer8.Add(self.anchorChoice, 1, wx.ALL | wx.EXPAND, 5) 141 | 142 | self.m_staticText4 = wx.StaticText( 143 | self, wx.ID_ANY, "Help", wx.DefaultPosition, wx.DefaultSize, 0 144 | ) 145 | self.m_staticText4.Wrap(-1) 146 | 147 | self.m_staticText4.SetFont( 148 | wx.Font( 149 | 10, 150 | wx.FONTFAMILY_DEFAULT, 151 | wx.FONTSTYLE_NORMAL, 152 | wx.FONTWEIGHT_NORMAL, 153 | True, 154 | wx.EmptyString, 155 | ) 156 | ) 157 | self.m_staticText4.SetForegroundColour( 158 | wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) 159 | ) 160 | self.m_staticText4.SetToolTip( 161 | "The anchor is the component in each sub-PCB around which all others are arranged." 162 | ) 163 | 164 | bSizer8.Add(self.m_staticText4, 0, wx.ALL, 5) 165 | 166 | bSizerMain.Add(bSizer8, 0, wx.EXPAND, 5) 167 | 168 | self.m_staticline2 = wx.StaticLine( 169 | self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL 170 | ) 171 | bSizerMain.Add(self.m_staticline2, 0, wx.EXPAND | wx.ALL, 5) 172 | 173 | m_sdbSizer1 = wx.StdDialogButtonSizer() 174 | self.m_sdbSizer1Apply = wx.Button(self, wx.ID_APPLY) 175 | m_sdbSizer1.AddButton(self.m_sdbSizer1Apply) 176 | self.m_sdbSizer1Cancel = wx.Button(self, wx.ID_CANCEL) 177 | m_sdbSizer1.AddButton(self.m_sdbSizer1Cancel) 178 | m_sdbSizer1.Realize() 179 | 180 | bSizerMain.Add(m_sdbSizer1, 0, wx.EXPAND, 5) 181 | 182 | self.SetSizer(bSizerMain) 183 | self.Layout() 184 | 185 | self.Centre(wx.BOTH) 186 | 187 | # Connect Events 188 | self.treeApplyTo.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.handleSelection) 189 | self.m_staticText41.Bind(wx.EVT_LEFT_DCLICK, self.resetToDefault) 190 | self.subPCBList.Bind( 191 | wx.dataview.EVT_DATAVIEW_ITEM_ACTIVATED, 192 | self.expandAnchorSelection, 193 | id=wx.ID_ANY, 194 | ) 195 | self.subPCBList.Bind( 196 | wx.dataview.EVT_DATAVIEW_SELECTION_CHANGED, 197 | self.changeSelectedSubPCB, 198 | id=wx.ID_ANY, 199 | ) 200 | self.anchorChoice.Bind(wx.EVT_CHOICE, self.changeAnchor) 201 | self.m_sdbSizer1Apply.Bind(wx.EVT_BUTTON, self.handleApply) 202 | 203 | def __del__(self): 204 | pass 205 | 206 | # Virtual event handlers, override them in your derived class 207 | def handleSelection(self, event): 208 | event.Skip() 209 | 210 | def resetToDefault(self, event): 211 | event.Skip() 212 | 213 | def expandAnchorSelection(self, event): 214 | event.Skip() 215 | 216 | def changeSelectedSubPCB(self, event): 217 | event.Skip() 218 | 219 | def changeAnchor(self, event): 220 | event.Skip() 221 | 222 | def handleApply(self, event): 223 | event.Skip() 224 | -------------------------------------------------------------------------------- /interface/__init__.py: -------------------------------------------------------------------------------- 1 | from .DlgHPCBRun import DlgHPCBRun 2 | -------------------------------------------------------------------------------- /placement.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import math 3 | from itertools import zip_longest 4 | from typing import Callable, Dict, List, Optional, Tuple 5 | 6 | import pcbnew 7 | 8 | from .hdata import HierarchicalData, PCBRoom, SchSheet 9 | 10 | logger = logging.getLogger("hierpcb") 11 | 12 | 13 | class ErrorLevel: 14 | INFO = 0 15 | WARNING = 1 16 | ERROR = 2 17 | 18 | 19 | class ReportedError: 20 | def __init__( 21 | self, 22 | title: str, 23 | message: Optional[str] = None, 24 | level: ErrorLevel = ErrorLevel.ERROR, 25 | footprint: pcbnew.FOOTPRINT = None, 26 | sheet: SchSheet = None, 27 | pcb: PCBRoom = None, 28 | ): 29 | self.title = title 30 | self.message = message 31 | self.level = level 32 | self.footprint = footprint 33 | self.sheet = sheet 34 | self.pcb = pcb 35 | 36 | logger.debug(str(self)) 37 | 38 | def __str__(self): 39 | msg = [f"ERR.{self.level}\t{self.title}"] 40 | if self.message: 41 | msg += [f" Message: {self.message}"] 42 | if self.footprint: 43 | msg += [f" Footprint: {self.footprint.GetReference()}"] 44 | if self.sheet: 45 | msg += [f" Sheet: {self.sheet.identifier}"] 46 | if self.pcb: 47 | msg += [f" SubPCB: {self.pcb.path}"] 48 | 49 | return "\n".join(msg) 50 | 51 | 52 | class PositionTransform: 53 | def __init__(self, template: pcbnew.FOOTPRINT, mutate: pcbnew.FOOTPRINT) -> None: 54 | # These are stored such that adding these to the position and rotation of the `template` 55 | # will yield the position and rotation of the `mutate`. 56 | self.anchor_template = template 57 | self.anchor_mutate = mutate 58 | 59 | def translate(self, pos_template: pcbnew.VECTOR2I) -> pcbnew.VECTOR2I: 60 | # Find the position of fp_template relative to the anchor_template: 61 | delta_x: int = pos_template.x - self.anchor_template.GetPosition().x 62 | delta_y: int = pos_template.y - self.anchor_template.GetPosition().y 63 | rotation = math.radians( 64 | self.anchor_mutate.GetOrientationDegrees() 65 | - self.anchor_template.GetOrientationDegrees() 66 | ) 67 | 68 | # With this information, we can compute the net position after any rotation: 69 | new_x = ( 70 | delta_y * math.sin(rotation) 71 | + delta_x * math.cos(rotation) 72 | + self.anchor_mutate.GetPosition().x 73 | ) 74 | new_y = ( 75 | delta_y * math.cos(rotation) 76 | - delta_x * math.sin(rotation) 77 | + self.anchor_mutate.GetPosition().y 78 | ) 79 | return pcbnew.VECTOR2I(int(new_x), int(new_y)) 80 | 81 | def orient(self, rot_template: float): 82 | return ( 83 | rot_template 84 | - self.anchor_template.GetOrientation() 85 | + self.anchor_mutate.GetOrientation() 86 | ) 87 | 88 | 89 | GroupMoverType = Callable[[pcbnew.BOARD_ITEM], bool] 90 | 91 | 92 | class GroupManager: 93 | def __init__(self, board: pcbnew.BOARD) -> None: 94 | self.board: pcbnew.board = board 95 | self.groups: Dict[str, pcbnew.PCB_GROUP] = { 96 | g.GetName(): g for g in board.Groups() 97 | } 98 | 99 | def create_or_get(self, group_name: str) -> pcbnew.PCB_GROUP: 100 | """Get a group by name, creating it if it doesn't exist.""" 101 | group = self.groups.get(group_name) 102 | if group is None: 103 | group = pcbnew.PCB_GROUP(None) 104 | group.SetName(group_name) 105 | self.board.Add(group) 106 | self.groups[group_name] = group 107 | return group 108 | 109 | def move(self, item: pcbnew.BOARD_ITEM, group: Optional[pcbnew.PCB_GROUP]) -> bool: 110 | """Force an item to be in a group., returning True if the item was moved.""" 111 | if group is None: 112 | return False 113 | moved = False 114 | 115 | # First, check if the footprint is already in the group: 116 | parent_group = item.GetParentGroup() 117 | # If the footprint is not already in the group, remove it from the current group: 118 | if parent_group and parent_group.GetName() != group.GetName(): 119 | moved = True 120 | parent_group.RemoveItem(item) 121 | parent_group = None 122 | # If the footprint is not in any group, or was in the wrong group, add it to the right one: 123 | if parent_group is None: 124 | group.AddItem(item) 125 | 126 | return moved 127 | 128 | def mover(self, group: pcbnew.PCB_GROUP) -> GroupMoverType: 129 | """Return a function that moves an item to the given group.""" 130 | return lambda item: self.move(item, group) 131 | 132 | 133 | def enforce_position(hd: HierarchicalData, targetBoard: pcbnew.BOARD): 134 | """Enforce the positions of objects in PCB template on PCB mutate.""" 135 | # Prepare a lookup table for footprints in the board: 136 | # Since board.FindFootprintByPath() operates on a custom type that we can't easily 137 | # construct, we prepare a table for efficient string lookups instead. 138 | fp_lookup = {fp.GetPath().AsString(): fp for fp in targetBoard.GetFootprints()} 139 | 140 | errors: List[ReportedError] = [] 141 | groupman: GroupManager = GroupManager(targetBoard) 142 | 143 | for sheet in hd.root_sheet.tree_iter(): 144 | if sheet.pcb and sheet.checked: 145 | # Check if the sub-PCB is legal: 146 | if not sheet.pcb.is_legal: 147 | errors.append(ReportedError("sub-PCB cannot be placed", pcb=sheet.pcb)) 148 | continue 149 | 150 | # Find the anchor footprint on the subPCB: 151 | anchor_subpcb = sheet.pcb.selected_anchor 152 | if not anchor_subpcb: 153 | errors.append( 154 | ReportedError( 155 | "sub-PCB has no anchor selected", 156 | pcb=sheet.pcb, 157 | level=ErrorLevel.ERROR, 158 | ) 159 | ) 160 | continue 161 | 162 | # Find the anchor footprint on the board: 163 | anchor_path = sheet.identifier + anchor_subpcb.GetPath().AsString() 164 | anchor_target = fp_lookup.get(anchor_path) 165 | 166 | if not anchor_target: 167 | errors.append( 168 | ReportedError( 169 | "anchor not found on target", 170 | message=f"Expected path {anchor_path}", 171 | pcb=sheet.pcb, 172 | level=ErrorLevel.ERROR, 173 | ) 174 | ) 175 | continue 176 | 177 | # We need to force all the footprints to be in the same group. To do that, 178 | # we automatically create a group for the anchor footprint if it doesn't exist and 179 | # move all the footprints into it. 180 | group_name = f"subpcb_{sheet.human_path}" 181 | group = groupman.create_or_get(group_name) 182 | 183 | # Clear Volatile items first 184 | clear_volatile_items(targetBoard, group) 185 | 186 | if groupman.move(anchor_target, group): 187 | errors.append( 188 | ReportedError( 189 | "anchor footprint is in the wrong group", 190 | message=f"Expected group {group_name}", 191 | pcb=sheet.pcb, 192 | level=ErrorLevel.ERROR, 193 | ) 194 | ) 195 | 196 | # Compute the transform for later use: 197 | transform = PositionTransform(anchor_subpcb, anchor_target) 198 | 199 | # First, move the footprints: 200 | footprintNetMapping, err_footprints = enforce_position_footprints( 201 | sheet, transform, fp_lookup, groupman.mover(group) 202 | ) 203 | errors.extend(err_footprints) 204 | 205 | # Recreate traces: 206 | err_traces = copy_traces(targetBoard, sheet, transform, groupman.mover(group), footprintNetMapping) 207 | errors.extend(err_traces) 208 | 209 | # Recreate Drawings 210 | err_drawings = copy_drawings(targetBoard,sheet,transform, groupman.mover(group)) 211 | errors.extend(err_drawings) 212 | 213 | # Recreate Zones Currently Using Work around 214 | # zone.SetPosition() doesn't change position 215 | # for some reason? 216 | err_zones = copy_zones(targetBoard,sheet,transform, groupman.mover(group), footprintNetMapping) 217 | errors.extend(err_zones) 218 | 219 | #Fixes issues with traces lingering after being deleted 220 | pcbnew.Refresh() 221 | 222 | 223 | def clear_volatile_items(targetBoard: pcbnew.BOARD, group: pcbnew.PCB_GROUP): 224 | """Remove all zones in a group.""" 225 | itemTypesToRemove = ( 226 | # Traces 227 | pcbnew.PCB_TRACK, pcbnew.ZONE, 228 | # Drawings 229 | pcbnew.PCB_SHAPE, pcbnew.PCB_TEXT, 230 | # Zones 231 | pcbnew.ZONE 232 | ) 233 | 234 | for item in group.GetItems(): 235 | 236 | # Gets all drawings in a group 237 | if isinstance(item.Cast(), itemTypesToRemove): 238 | # Remove every drawing 239 | targetBoard.RemoveNative(item) 240 | 241 | 242 | def copy_traces( 243 | targetBoard: pcbnew.BOARD, 244 | sheet: SchSheet, 245 | transform: PositionTransform, 246 | mover: GroupMoverType, 247 | footprintNetMapping: Dict[int, int], 248 | ): 249 | # Instead of figuring out which nets are connected to the sub-PCB, we just copy all the raw traces 250 | # and let KiCad figure it out. It seems to work so far. 251 | 252 | errors = [] 253 | for sourceTrack in sheet.pcb.subboard.Tracks(): 254 | # Copy track to trk: 255 | # logger.info(f"{track} {type(track)} {track.GetStart()} -> {track.GetEnd()}") 256 | 257 | newTrack = sourceTrack.Duplicate() 258 | 259 | # Sets the track end point 260 | # the start is handled by item.SetPosition 261 | targetBoard.Add(newTrack) 262 | 263 | sourceNetCode = sourceTrack.GetNetCode() 264 | newNetCode = footprintNetMapping.get(sourceNetCode, 0) 265 | newTrack.SetNet(targetBoard.FindNet(newNetCode)) 266 | 267 | newTrack.SetStart(transform.translate(sourceTrack.GetStart())) 268 | newTrack.SetEnd (transform.translate(sourceTrack.GetEnd() )) 269 | 270 | if type(newTrack) == pcbnew.PCB_VIA: 271 | newTrack.SetIsFree(False) 272 | 273 | mover(newTrack) 274 | 275 | return errors 276 | 277 | 278 | def copy_drawings( 279 | targetBoard: pcbnew.BOARD, 280 | sheet: SchSheet, 281 | transform: PositionTransform, 282 | mover: GroupMoverType, 283 | ): 284 | 285 | errors = [] 286 | for sourceDrawing in sheet.pcb.subboard.GetDrawings(): 287 | 288 | newDrawing = sourceDrawing.Duplicate() 289 | 290 | targetBoard.Add(newDrawing) 291 | 292 | # Set New Position 293 | newDrawing.SetPosition(transform.translate(sourceDrawing.GetPosition())) 294 | 295 | # Drawings dont have .SetOrientation() 296 | # instead do a relative rotation 297 | newDrawing.Rotate(newDrawing.GetPosition(), transform.orient(pcbnew.ANGLE_0)) 298 | 299 | mover(newDrawing) 300 | 301 | return errors 302 | 303 | 304 | def copy_zones( 305 | targetBoard: pcbnew.BOARD, 306 | sheet: SchSheet, 307 | transform: PositionTransform, 308 | mover: GroupMoverType, 309 | footprintNetMapping: Dict[int, int], 310 | ): 311 | 312 | errors = [] 313 | for sourceZone in sheet.pcb.subboard.Zones(): 314 | # if isinstance(drawing, pcbnew.PCB_SHAPE): 315 | # newShape = pcbnew.PCB_SHAPE() 316 | # elif isinstance(drawing, pcbnew.PCB_TEXT): 317 | # newShape = pcbnew.PCB_TEXT() 318 | 319 | newZone = sourceZone.Duplicate() 320 | 321 | sourceNetCode = sourceZone.GetNetCode() 322 | newNetCode = footprintNetMapping.get(sourceNetCode, 0) 323 | newZone.SetNet(targetBoard.FindNet(newNetCode)) 324 | 325 | targetBoard.Add(newZone) 326 | 327 | # Set New Position 328 | # newZone.SetPosition(transform.translate(zone.GetPosition())) 329 | # Temporary Workaround: 330 | 331 | # Move zone to 0,0 by moving relative 332 | newZone.Move(-newZone.GetPosition()) 333 | # Move zone to correct location 334 | newZone.Move(transform.translate(sourceZone.GetPosition())) 335 | 336 | # Drawings dont have .SetOrientation() 337 | # instead do a relative rotation 338 | newZone.Rotate(newZone.GetPosition(), transform.orient(pcbnew.ANGLE_0)) 339 | 340 | mover(newZone) 341 | 342 | return errors 343 | 344 | 345 | def copy_footprint_fields( 346 | sourceFootprint: pcbnew.FOOTPRINT, 347 | targetFootprint: pcbnew.FOOTPRINT, 348 | transform: PositionTransform, 349 | ): 350 | # NOTE: Non center aligned Fields position changes with rotation. 351 | # This is not a bug. The replicated pcbs are behaving the 352 | # exact same as the original would when rotated. 353 | 354 | # Do any other field values need preserved? 355 | originalReference = targetFootprint.GetReference() 356 | 357 | # Remove Existing footprint fields 358 | for existingField in targetFootprint.GetFields(): 359 | targetFootprint.RemoveNative(existingField) 360 | # Add all the source fields and move them 361 | for sourceField in sourceFootprint.GetFields(): 362 | newField = sourceField.CloneField() 363 | newField.SetParent(targetFootprint) 364 | 365 | newField.SetPosition(transform.translate(sourceField.GetPosition())) 366 | newField.Rotate(newField.GetPosition(), transform.orient(pcbnew.ANGLE_0)) 367 | 368 | targetFootprint.AddField(newField) 369 | 370 | targetFootprint.SetReference(originalReference) 371 | 372 | 373 | def copy_footprint_data( 374 | sourceFootprint: pcbnew.FOOTPRINT, 375 | targetFootprint: pcbnew.FOOTPRINT, 376 | transform: PositionTransform, 377 | ): 378 | if sourceFootprint.IsFlipped() != targetFootprint.IsFlipped(): 379 | targetFootprint.Flip(targetFootprint.GetPosition(), False) 380 | 381 | # The list of properties is from the ReplicateLayout plugin. Thanks @MitjaNemec! 382 | targetFootprint.SetLocalClearance(sourceFootprint.GetLocalClearance()) 383 | targetFootprint.SetLocalSolderMaskMargin(sourceFootprint.GetLocalSolderMaskMargin()) 384 | targetFootprint.SetLocalSolderPasteMargin(sourceFootprint.GetLocalSolderPasteMargin()) 385 | targetFootprint.SetLocalSolderPasteMarginRatio( 386 | sourceFootprint.GetLocalSolderPasteMarginRatio() 387 | ) 388 | targetFootprint.SetZoneConnection(sourceFootprint.GetZoneConnection()) 389 | 390 | # Move the footprint: 391 | targetFootprint.SetPosition(transform.translate(sourceFootprint.GetPosition())) 392 | targetFootprint.SetOrientation(transform.orient(sourceFootprint.GetOrientation())) 393 | 394 | 395 | def enforce_position_footprints( 396 | sheet: SchSheet, 397 | transform: PositionTransform, 398 | fp_lookup: Dict[str, pcbnew.FOOTPRINT], 399 | groupmv: GroupMoverType, 400 | ): 401 | errors = [] 402 | 403 | # The keys are the sub-pcb net codes 404 | # The values are the new net codes 405 | footprintNetMapping = {} 406 | 407 | # For each footprint in the sub-PCB, find the corresponding footprint on the board: 408 | for sourceFootprint in sheet.pcb.subboard.GetFootprints(): 409 | # Find the corresponding footprint on the board: 410 | fp_path = sheet.identifier + sourceFootprint.GetPath().AsString() 411 | targetFootprint = fp_lookup.get(fp_path) 412 | 413 | if not targetFootprint: 414 | errors.append( 415 | ReportedError( 416 | "footprint not found, skipping", 417 | message=f"Corresponding to {sourceFootprint.GetReference()} for sheet {sheet.human_name}", 418 | footprint=sourceFootprint, 419 | pcb=sheet.pcb, 420 | level=ErrorLevel.WARNING, 421 | ) 422 | ) 423 | continue 424 | 425 | # TODO: Ignore footprints outside the lower-right quadrant. 426 | 427 | # Copy the properties and move the template to the target: 428 | copy_footprint_data(sourceFootprint, targetFootprint, transform) 429 | 430 | copy_footprint_fields(sourceFootprint, targetFootprint, transform) 431 | 432 | # Assumes pads are ordered by the pad number 433 | for sourcePadNum, sourcePad in enumerate(sourceFootprint.Pads()): 434 | targetPad = targetFootprint.Pads()[sourcePadNum] 435 | 436 | sourceCode = sourcePad.GetNetCode() 437 | targetCode = targetPad.GetNetCode() 438 | 439 | footprintNetMapping[sourceCode] = targetCode 440 | 441 | # Move the footprint into the group if one is provided: 442 | if groupmv(targetFootprint): 443 | errors.append( 444 | ReportedError( 445 | f"footprint {targetFootprint} is in the wrong group", 446 | pcb=sheet.pcb, 447 | level=ErrorLevel.ERROR, 448 | ) 449 | ) 450 | 451 | return (footprintNetMapping, errors) 452 | --------------------------------------------------------------------------------