kicad-sch-api 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kicad-sch-api might be problematic. Click here for more details.
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/CHANGELOG.md +31 -0
- {kicad_sch_api-0.2.1/kicad_sch_api.egg-info → kicad_sch_api-0.2.2}/PKG-INFO +1 -1
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/formatter.py +7 -1
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/parser.py +102 -11
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/schematic.py +53 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/types.py +35 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2/kicad_sch_api.egg-info}/PKG-INFO +1 -1
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api.egg-info/SOURCES.txt +3 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/pyproject.toml +1 -1
- kicad_sch_api-0.2.2/tests/test_parse_reference_rectangles.py +48 -0
- kicad_sch_api-0.2.2/tests/test_rectangle.py +88 -0
- kicad_sch_api-0.2.2/tests/test_rectangle_roundtrip.py +111 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/.claude/commands/dev/dead-code-analysis.md +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/.claude/commands/dev/publish-pypi.md +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/.claude/commands/dev/review-implementation.md +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/.claude/commands/dev/run-tests.md +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/.claude/commands/dev/update-and-commit.md +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/.claude/commands/dev/update-memory-bank.md +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/.claude/commands/test/run-reference-tests.md +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/LICENSE +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/MANIFEST.in +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/README.md +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/examples/advanced_usage.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/examples/basic_usage.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/examples/mcp_basic_example.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/examples/mcp_integration.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/examples/pin_to_pin_wiring_demo.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/examples/simple_circuit_with_pin_wiring.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/examples/simple_two_resistor_routing.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/__init__.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/cli.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/__init__.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/component_bounds.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/components.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/config.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/geometry.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/ic_manager.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/junctions.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/manhattan_routing.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/pin_utils.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/simple_manhattan.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/wire_routing.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/core/wires.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/discovery/__init__.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/discovery/search_index.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/library/__init__.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/library/cache.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/py.typed +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/utils/__init__.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api/utils/validation.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api.egg-info/dependency_links.txt +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api.egg-info/entry_points.txt +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api.egg-info/requires.txt +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/kicad_sch_api.egg-info/top_level.txt +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/setup.cfg +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/README.md +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/blank_schematic/blank_schematic.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/blank_schematic/blank_schematic.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/multi_unit_7400/multi_unit_7400.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/multi_unit_7400/multi_unit_7400.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/power_symbols/power_symbols.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/power_symbols/power_symbols.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/resistor_divider/resistor_divider.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/resistor_divider/resistor_divider.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/sch_title/sch_title.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/sch_title/sch_title.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_extended_component/single_extended_component.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_extended_component/single_extended_component.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_hierarchical_sheet/single_hierarchical_sheet.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_hierarchical_sheet/single_hierarchical_sheet.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_hierarchical_sheet/subcircuit1.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_label/single_label.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_label/single_label.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_label_hierarchical/single_label_hierarchical.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_label_hierarchical/single_label_hierarchical.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_resistor/single_resistor.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_resistor/single_resistor.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_text/single_text.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_text/single_text.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_text_box/single_text_box.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_text_box/single_text_box.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_wire/single_wire.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/single_wire/single_wire.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/two_resistors/two_resistors.kicad_pro +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/two_resistors/two_resistors.kicad_sch +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/test_bounding_box_rectangles.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/test_component_removal.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/test_element_removal.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/test_grid_snapping.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/test_manhattan_routing.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/test_pin_positioning.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/test_pin_to_pin_wiring.py +0 -0
- {kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/test_removal_against_references.py +0 -0
|
@@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.1] - 2025-01-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Professional PyPI Release**: First official release to Python Package Index
|
|
12
|
+
- **Enhanced Bounding Box Visualization**:
|
|
13
|
+
- Colored rectangle support with all KiCAD stroke types
|
|
14
|
+
- Support for solid, dash, dot, dash_dot, dash_dot_dot line styles
|
|
15
|
+
- Component bounding box visualization with color customization
|
|
16
|
+
- **Improved Manhattan Routing**:
|
|
17
|
+
- Enhanced obstacle avoidance algorithms
|
|
18
|
+
- Perfect KiCAD grid alignment (1.27mm grid)
|
|
19
|
+
- Multiple routing strategies and clearance options
|
|
20
|
+
- **Code Quality Improvements**:
|
|
21
|
+
- Formatted with black for consistent style
|
|
22
|
+
- Import sorting with isort
|
|
23
|
+
- Enhanced type checking coverage
|
|
24
|
+
- **Comprehensive Testing**:
|
|
25
|
+
- 71 passing tests with 6 intentionally skipped
|
|
26
|
+
- Enhanced test coverage for new features
|
|
27
|
+
- Format preservation validation
|
|
28
|
+
|
|
29
|
+
### Enhanced
|
|
30
|
+
- **Exact Format Preservation**: Improved KiCAD format compatibility
|
|
31
|
+
- **Parser & Formatter**: Enhanced S-expression handling
|
|
32
|
+
- **Performance**: Optimized for professional use cases
|
|
33
|
+
|
|
34
|
+
### Technical
|
|
35
|
+
- **Dependencies**: Updated build system and packaging
|
|
36
|
+
- **Documentation**: Enhanced API documentation
|
|
37
|
+
- **CI/CD**: Professional package validation and testing
|
|
38
|
+
|
|
8
39
|
## [0.3.1] - 2025-01-20
|
|
9
40
|
|
|
10
41
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kicad-sch-api
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Professional KiCAD schematic manipulation library with exact format preservation
|
|
5
5
|
Author-email: Circuit-Synth <shane@circuit-synth.com>
|
|
6
6
|
Maintainer-email: Circuit-Synth <shane@circuit-synth.com>
|
|
@@ -55,7 +55,7 @@ class ExactFormatter:
|
|
|
55
55
|
self.rules["generator"] = FormatRule(inline=True, quote_indices={1})
|
|
56
56
|
self.rules["generator_version"] = FormatRule(inline=True, quote_indices={1})
|
|
57
57
|
self.rules["uuid"] = FormatRule(inline=True, quote_indices={1})
|
|
58
|
-
self.rules["paper"] = FormatRule(inline=True,
|
|
58
|
+
self.rules["paper"] = FormatRule(inline=True) # No quotes for paper size (A4, A3, etc.)
|
|
59
59
|
|
|
60
60
|
# Title block
|
|
61
61
|
self.rules["title_block"] = FormatRule(inline=False)
|
|
@@ -112,6 +112,12 @@ class ExactFormatter:
|
|
|
112
112
|
self.rules["junction"] = FormatRule(inline=False)
|
|
113
113
|
self.rules["diameter"] = FormatRule(inline=True)
|
|
114
114
|
|
|
115
|
+
# Graphical elements
|
|
116
|
+
self.rules["rectangle"] = FormatRule(inline=False)
|
|
117
|
+
self.rules["start"] = FormatRule(inline=True)
|
|
118
|
+
self.rules["end"] = FormatRule(inline=True)
|
|
119
|
+
self.rules["fill"] = FormatRule(inline=False)
|
|
120
|
+
|
|
115
121
|
# Labels
|
|
116
122
|
self.rules["label"] = FormatRule(inline=False, quote_indices={1})
|
|
117
123
|
self.rules["global_label"] = FormatRule(inline=False, quote_indices={1})
|
|
@@ -187,6 +187,7 @@ class SExpressionParser:
|
|
|
187
187
|
"wires": [],
|
|
188
188
|
"junctions": [],
|
|
189
189
|
"labels": [],
|
|
190
|
+
"rectangles": [],
|
|
190
191
|
"nets": [],
|
|
191
192
|
"lib_symbols": {},
|
|
192
193
|
"sheet_instances": [],
|
|
@@ -232,6 +233,10 @@ class SExpressionParser:
|
|
|
232
233
|
label = self._parse_label(item)
|
|
233
234
|
if label:
|
|
234
235
|
schematic_data["labels"].append(label)
|
|
236
|
+
elif element_type == "rectangle":
|
|
237
|
+
rectangle = self._parse_rectangle(item)
|
|
238
|
+
if rectangle:
|
|
239
|
+
schematic_data["rectangles"].append(rectangle)
|
|
235
240
|
elif element_type == "lib_symbols":
|
|
236
241
|
schematic_data["lib_symbols"] = self._parse_lib_symbols(item)
|
|
237
242
|
elif element_type == "sheet_instances":
|
|
@@ -304,10 +309,14 @@ class SExpressionParser:
|
|
|
304
309
|
for text_box in schematic_data.get("text_boxes", []):
|
|
305
310
|
sexp_data.append(self._text_box_to_sexp(text_box))
|
|
306
311
|
|
|
307
|
-
# Add graphics (rectangles
|
|
312
|
+
# Add graphics (rectangles from schematic.draw_bounding_box)
|
|
308
313
|
for graphic in schematic_data.get("graphics", []):
|
|
309
314
|
sexp_data.append(self._graphic_to_sexp(graphic))
|
|
310
315
|
|
|
316
|
+
# Add rectangles (rectangles from add_rectangle API)
|
|
317
|
+
for rectangle in schematic_data.get("rectangles", []):
|
|
318
|
+
sexp_data.append(self._rectangle_to_sexp(rectangle))
|
|
319
|
+
|
|
311
320
|
# Add sheet_instances (required by KiCAD)
|
|
312
321
|
sheet_instances = schematic_data.get("sheet_instances", [])
|
|
313
322
|
if sheet_instances:
|
|
@@ -425,6 +434,37 @@ class SExpressionParser:
|
|
|
425
434
|
# Implementation for label parsing
|
|
426
435
|
return {}
|
|
427
436
|
|
|
437
|
+
def _parse_rectangle(self, item: List[Any]) -> Optional[Dict[str, Any]]:
|
|
438
|
+
"""Parse a rectangle graphical element."""
|
|
439
|
+
rectangle = {}
|
|
440
|
+
|
|
441
|
+
for elem in item[1:]:
|
|
442
|
+
if not isinstance(elem, list):
|
|
443
|
+
continue
|
|
444
|
+
|
|
445
|
+
elem_type = str(elem[0])
|
|
446
|
+
|
|
447
|
+
if elem_type == "start" and len(elem) >= 3:
|
|
448
|
+
rectangle["start"] = {"x": float(elem[1]), "y": float(elem[2])}
|
|
449
|
+
elif elem_type == "end" and len(elem) >= 3:
|
|
450
|
+
rectangle["end"] = {"x": float(elem[1]), "y": float(elem[2])}
|
|
451
|
+
elif elem_type == "stroke":
|
|
452
|
+
for stroke_elem in elem[1:]:
|
|
453
|
+
if isinstance(stroke_elem, list):
|
|
454
|
+
stroke_type = str(stroke_elem[0])
|
|
455
|
+
if stroke_type == "width" and len(stroke_elem) >= 2:
|
|
456
|
+
rectangle["stroke_width"] = float(stroke_elem[1])
|
|
457
|
+
elif stroke_type == "type" and len(stroke_elem) >= 2:
|
|
458
|
+
rectangle["stroke_type"] = str(stroke_elem[1])
|
|
459
|
+
elif elem_type == "fill":
|
|
460
|
+
for fill_elem in elem[1:]:
|
|
461
|
+
if isinstance(fill_elem, list) and str(fill_elem[0]) == "type":
|
|
462
|
+
rectangle["fill_type"] = str(fill_elem[1]) if len(fill_elem) >= 2 else "none"
|
|
463
|
+
elif elem_type == "uuid" and len(elem) >= 2:
|
|
464
|
+
rectangle["uuid"] = str(elem[1])
|
|
465
|
+
|
|
466
|
+
return rectangle if rectangle else None
|
|
467
|
+
|
|
428
468
|
def _parse_lib_symbols(self, item: List[Any]) -> Dict[str, Any]:
|
|
429
469
|
"""Parse lib_symbols section."""
|
|
430
470
|
# Implementation for lib_symbols parsing
|
|
@@ -529,14 +569,28 @@ class SExpressionParser:
|
|
|
529
569
|
# Add instances section (required by KiCAD)
|
|
530
570
|
from .config import config
|
|
531
571
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
572
|
+
# Get project name from config or properties
|
|
573
|
+
project_name = symbol_data.get("properties", {}).get("project_name")
|
|
574
|
+
if not project_name:
|
|
575
|
+
project_name = getattr(self, "project_name", config.defaults.project_name)
|
|
576
|
+
|
|
577
|
+
# CRITICAL FIX: Use the FULL hierarchy_path from properties if available
|
|
578
|
+
# For hierarchical schematics, this contains the complete path: /root_uuid/sheet_symbol_uuid/...
|
|
579
|
+
# This ensures KiCad can properly annotate components in sub-sheets
|
|
580
|
+
hierarchy_path = symbol_data.get("properties", {}).get("hierarchy_path")
|
|
581
|
+
if hierarchy_path:
|
|
582
|
+
# Use the full hierarchical path (includes root + all sheet symbols)
|
|
583
|
+
instance_path = hierarchy_path
|
|
584
|
+
logger.debug(f"🔧 Using FULL hierarchy_path: {instance_path} for component {symbol_data.get('reference', 'unknown')}")
|
|
585
|
+
else:
|
|
586
|
+
# Fallback: use root_uuid or schematic_uuid for flat designs
|
|
587
|
+
root_uuid = symbol_data.get("properties", {}).get("root_uuid") or schematic_uuid or str(uuid.uuid4())
|
|
588
|
+
instance_path = f"/{root_uuid}"
|
|
589
|
+
logger.debug(f"🔧 Using root UUID path: {instance_path} for component {symbol_data.get('reference', 'unknown')}")
|
|
590
|
+
|
|
591
|
+
logger.debug(f"🔧 Component properties keys: {list(symbol_data.get('properties', {}).keys())}")
|
|
592
|
+
logger.debug(f"🔧 Using project name: '{project_name}'")
|
|
593
|
+
|
|
540
594
|
sexp.append(
|
|
541
595
|
[
|
|
542
596
|
sexpdata.Symbol("instances"),
|
|
@@ -545,7 +599,7 @@ class SExpressionParser:
|
|
|
545
599
|
project_name,
|
|
546
600
|
[
|
|
547
601
|
sexpdata.Symbol("path"),
|
|
548
|
-
|
|
602
|
+
instance_path,
|
|
549
603
|
[sexpdata.Symbol("reference"), symbol_data.get("reference", "U?")],
|
|
550
604
|
[sexpdata.Symbol("unit"), symbol_data.get("unit", 1)],
|
|
551
605
|
],
|
|
@@ -772,7 +826,10 @@ class SExpressionParser:
|
|
|
772
826
|
effects = [sexpdata.Symbol("effects")]
|
|
773
827
|
font = [sexpdata.Symbol("font"), [sexpdata.Symbol("size"), size, size]]
|
|
774
828
|
effects.append(font)
|
|
775
|
-
|
|
829
|
+
|
|
830
|
+
# Use justification from data if provided, otherwise default to "left"
|
|
831
|
+
justify = hlabel_data.get("justify", "left")
|
|
832
|
+
effects.append([sexpdata.Symbol("justify"), sexpdata.Symbol(justify)])
|
|
776
833
|
sexp.append(effects)
|
|
777
834
|
|
|
778
835
|
# Add UUID
|
|
@@ -1034,6 +1091,40 @@ class SExpressionParser:
|
|
|
1034
1091
|
|
|
1035
1092
|
return sexp
|
|
1036
1093
|
|
|
1094
|
+
def _rectangle_to_sexp(self, rectangle_data: Dict[str, Any]) -> List[Any]:
|
|
1095
|
+
"""Convert rectangle element to S-expression."""
|
|
1096
|
+
sexp = [sexpdata.Symbol("rectangle")]
|
|
1097
|
+
|
|
1098
|
+
# Add start point
|
|
1099
|
+
start = rectangle_data["start"]
|
|
1100
|
+
start_x, start_y = start["x"], start["y"]
|
|
1101
|
+
sexp.append([sexpdata.Symbol("start"), start_x, start_y])
|
|
1102
|
+
|
|
1103
|
+
# Add end point
|
|
1104
|
+
end = rectangle_data["end"]
|
|
1105
|
+
end_x, end_y = end["x"], end["y"]
|
|
1106
|
+
sexp.append([sexpdata.Symbol("end"), end_x, end_y])
|
|
1107
|
+
|
|
1108
|
+
# Add stroke
|
|
1109
|
+
stroke_width = rectangle_data.get("stroke_width", 0)
|
|
1110
|
+
stroke_type = rectangle_data.get("stroke_type", "default")
|
|
1111
|
+
stroke_sexp = [sexpdata.Symbol("stroke")]
|
|
1112
|
+
stroke_sexp.append([sexpdata.Symbol("width"), stroke_width])
|
|
1113
|
+
stroke_sexp.append([sexpdata.Symbol("type"), sexpdata.Symbol(stroke_type)])
|
|
1114
|
+
sexp.append(stroke_sexp)
|
|
1115
|
+
|
|
1116
|
+
# Add fill
|
|
1117
|
+
fill_type = rectangle_data.get("fill_type", "none")
|
|
1118
|
+
fill_sexp = [sexpdata.Symbol("fill")]
|
|
1119
|
+
fill_sexp.append([sexpdata.Symbol("type"), sexpdata.Symbol(fill_type)])
|
|
1120
|
+
sexp.append(fill_sexp)
|
|
1121
|
+
|
|
1122
|
+
# Add UUID
|
|
1123
|
+
if "uuid" in rectangle_data:
|
|
1124
|
+
sexp.append([sexpdata.Symbol("uuid"), rectangle_data["uuid"]])
|
|
1125
|
+
|
|
1126
|
+
return sexp
|
|
1127
|
+
|
|
1037
1128
|
def _lib_symbols_to_sexp(self, lib_symbols: Dict[str, Any]) -> List[Any]:
|
|
1038
1129
|
"""Convert lib_symbols to S-expression."""
|
|
1039
1130
|
sexp = [sexpdata.Symbol("lib_symbols")]
|
|
@@ -1138,6 +1138,59 @@ class Schematic:
|
|
|
1138
1138
|
logger.debug(f"Added text box: '{text}' at {position} size {size}")
|
|
1139
1139
|
return text_box.uuid
|
|
1140
1140
|
|
|
1141
|
+
def add_rectangle(
|
|
1142
|
+
self,
|
|
1143
|
+
start: Union[Point, Tuple[float, float]],
|
|
1144
|
+
end: Union[Point, Tuple[float, float]],
|
|
1145
|
+
stroke_width: float = 0.0,
|
|
1146
|
+
stroke_type: str = "default",
|
|
1147
|
+
fill_type: str = "none"
|
|
1148
|
+
) -> str:
|
|
1149
|
+
"""
|
|
1150
|
+
Add a graphical rectangle element.
|
|
1151
|
+
|
|
1152
|
+
Args:
|
|
1153
|
+
start: Rectangle start point (top-left)
|
|
1154
|
+
end: Rectangle end point (bottom-right)
|
|
1155
|
+
stroke_width: Border line width
|
|
1156
|
+
stroke_type: Border line type (default, solid, dash, dot, etc.)
|
|
1157
|
+
fill_type: Fill type (none, solid, etc.)
|
|
1158
|
+
|
|
1159
|
+
Returns:
|
|
1160
|
+
UUID of created rectangle element
|
|
1161
|
+
"""
|
|
1162
|
+
if isinstance(start, tuple):
|
|
1163
|
+
start = Point(start[0], start[1])
|
|
1164
|
+
if isinstance(end, tuple):
|
|
1165
|
+
end = Point(end[0], end[1])
|
|
1166
|
+
|
|
1167
|
+
from .types import SchematicRectangle
|
|
1168
|
+
|
|
1169
|
+
rectangle = SchematicRectangle(
|
|
1170
|
+
uuid=str(uuid.uuid4()),
|
|
1171
|
+
start=start,
|
|
1172
|
+
end=end,
|
|
1173
|
+
stroke_width=stroke_width,
|
|
1174
|
+
stroke_type=stroke_type,
|
|
1175
|
+
fill_type=fill_type
|
|
1176
|
+
)
|
|
1177
|
+
|
|
1178
|
+
if "rectangles" not in self._data:
|
|
1179
|
+
self._data["rectangles"] = []
|
|
1180
|
+
|
|
1181
|
+
self._data["rectangles"].append({
|
|
1182
|
+
"uuid": rectangle.uuid,
|
|
1183
|
+
"start": {"x": rectangle.start.x, "y": rectangle.start.y},
|
|
1184
|
+
"end": {"x": rectangle.end.x, "y": rectangle.end.y},
|
|
1185
|
+
"stroke_width": rectangle.stroke_width,
|
|
1186
|
+
"stroke_type": rectangle.stroke_type,
|
|
1187
|
+
"fill_type": rectangle.fill_type
|
|
1188
|
+
})
|
|
1189
|
+
self._modified = True
|
|
1190
|
+
|
|
1191
|
+
logger.debug(f"Added rectangle: {start} to {end}")
|
|
1192
|
+
return rectangle.uuid
|
|
1193
|
+
|
|
1141
1194
|
def set_title_block(
|
|
1142
1195
|
self,
|
|
1143
1196
|
title: str = "",
|
|
@@ -338,6 +338,40 @@ class TextBox:
|
|
|
338
338
|
self.uuid = str(uuid4())
|
|
339
339
|
|
|
340
340
|
|
|
341
|
+
@dataclass
|
|
342
|
+
class SchematicRectangle:
|
|
343
|
+
"""Graphical rectangle element in schematic."""
|
|
344
|
+
|
|
345
|
+
uuid: str
|
|
346
|
+
start: Point
|
|
347
|
+
end: Point
|
|
348
|
+
stroke_width: float = 0.0
|
|
349
|
+
stroke_type: str = "default"
|
|
350
|
+
fill_type: str = "none"
|
|
351
|
+
|
|
352
|
+
def __post_init__(self):
|
|
353
|
+
if not self.uuid:
|
|
354
|
+
self.uuid = str(uuid4())
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def width(self) -> float:
|
|
358
|
+
"""Rectangle width."""
|
|
359
|
+
return abs(self.end.x - self.start.x)
|
|
360
|
+
|
|
361
|
+
@property
|
|
362
|
+
def height(self) -> float:
|
|
363
|
+
"""Rectangle height."""
|
|
364
|
+
return abs(self.end.y - self.start.y)
|
|
365
|
+
|
|
366
|
+
@property
|
|
367
|
+
def center(self) -> Point:
|
|
368
|
+
"""Rectangle center point."""
|
|
369
|
+
return Point(
|
|
370
|
+
(self.start.x + self.end.x) / 2,
|
|
371
|
+
(self.start.y + self.end.y) / 2
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
|
|
341
375
|
@dataclass
|
|
342
376
|
class Net:
|
|
343
377
|
"""Electrical net connecting components."""
|
|
@@ -434,6 +468,7 @@ class Schematic:
|
|
|
434
468
|
labels: List[Label] = field(default_factory=list)
|
|
435
469
|
nets: List[Net] = field(default_factory=list)
|
|
436
470
|
sheets: List[Sheet] = field(default_factory=list)
|
|
471
|
+
rectangles: List[SchematicRectangle] = field(default_factory=list)
|
|
437
472
|
lib_symbols: Dict[str, Any] = field(default_factory=dict)
|
|
438
473
|
|
|
439
474
|
def __post_init__(self):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kicad-sch-api
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Professional KiCAD schematic manipulation library with exact format preservation
|
|
5
5
|
Author-email: Circuit-Synth <shane@circuit-synth.com>
|
|
6
6
|
Maintainer-email: Circuit-Synth <shane@circuit-synth.com>
|
|
@@ -53,8 +53,11 @@ tests/test_component_removal.py
|
|
|
53
53
|
tests/test_element_removal.py
|
|
54
54
|
tests/test_grid_snapping.py
|
|
55
55
|
tests/test_manhattan_routing.py
|
|
56
|
+
tests/test_parse_reference_rectangles.py
|
|
56
57
|
tests/test_pin_positioning.py
|
|
57
58
|
tests/test_pin_to_pin_wiring.py
|
|
59
|
+
tests/test_rectangle.py
|
|
60
|
+
tests/test_rectangle_roundtrip.py
|
|
58
61
|
tests/test_removal_against_references.py
|
|
59
62
|
tests/reference_tests/reference_kicad_projects/README.md
|
|
60
63
|
tests/reference_tests/reference_kicad_projects/blank_schematic/blank_schematic.kicad_pro
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test parsing rectangles from the reference RP2040 schematic.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from kicad_sch_api import load_schematic
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
REFERENCE_SCHEMATIC = "/Users/shanemattner/Desktop/circuit-synth-examples/pcbs/rp2040-minimal/RP2040_Minimal/RP2040_Minimal.kicad_sch"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_parse_reference_schematic_rectangles():
|
|
13
|
+
"""Test that we can parse rectangles from the reference schematic."""
|
|
14
|
+
try:
|
|
15
|
+
sch = load_schematic(REFERENCE_SCHEMATIC)
|
|
16
|
+
|
|
17
|
+
# The reference schematic should have rectangles (bounding boxes)
|
|
18
|
+
assert "rectangles" in sch._data
|
|
19
|
+
rectangles = sch._data["rectangles"]
|
|
20
|
+
|
|
21
|
+
# Should have at least one rectangle
|
|
22
|
+
assert len(rectangles) > 0, "Expected to find rectangles in reference schematic"
|
|
23
|
+
|
|
24
|
+
# Verify rectangle structure
|
|
25
|
+
rect = rectangles[0]
|
|
26
|
+
assert "start" in rect
|
|
27
|
+
assert "end" in rect
|
|
28
|
+
assert "x" in rect["start"]
|
|
29
|
+
assert "y" in rect["start"]
|
|
30
|
+
assert "x" in rect["end"]
|
|
31
|
+
assert "y" in rect["end"]
|
|
32
|
+
assert "uuid" in rect
|
|
33
|
+
|
|
34
|
+
# Verify stroke and fill
|
|
35
|
+
assert "stroke_width" in rect
|
|
36
|
+
assert "stroke_type" in rect
|
|
37
|
+
assert "fill_type" in rect
|
|
38
|
+
|
|
39
|
+
print(f"Successfully parsed {len(rectangles)} rectangles from reference schematic")
|
|
40
|
+
print(f"First rectangle: start=({rect['start']['x']}, {rect['start']['y']}), "
|
|
41
|
+
f"end=({rect['end']['x']}, {rect['end']['y']})")
|
|
42
|
+
|
|
43
|
+
except FileNotFoundError:
|
|
44
|
+
pytest.skip(f"Reference schematic not found: {REFERENCE_SCHEMATIC}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
pytest.main([__file__, "-v", "-s"])
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test rectangle functionality in kicad-sch-api.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from kicad_sch_api.core.schematic import Schematic
|
|
7
|
+
from kicad_sch_api.core.types import Point
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_add_rectangle():
|
|
11
|
+
"""Test adding a rectangle to a schematic."""
|
|
12
|
+
# Create a new schematic
|
|
13
|
+
sch = Schematic()
|
|
14
|
+
|
|
15
|
+
# Add a rectangle
|
|
16
|
+
rect_uuid = sch.add_rectangle(
|
|
17
|
+
start=(10.0, 20.0),
|
|
18
|
+
end=(50.0, 60.0),
|
|
19
|
+
stroke_width=0.127,
|
|
20
|
+
stroke_type="solid",
|
|
21
|
+
fill_type="none"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Verify UUID was returned
|
|
25
|
+
assert rect_uuid is not None
|
|
26
|
+
assert isinstance(rect_uuid, str)
|
|
27
|
+
|
|
28
|
+
# Verify rectangle was added to internal data
|
|
29
|
+
assert "rectangles" in sch._data
|
|
30
|
+
assert len(sch._data["rectangles"]) == 1
|
|
31
|
+
|
|
32
|
+
# Verify rectangle properties
|
|
33
|
+
rect = sch._data["rectangles"][0]
|
|
34
|
+
assert rect["uuid"] == rect_uuid
|
|
35
|
+
assert rect["start"]["x"] == 10.0
|
|
36
|
+
assert rect["start"]["y"] == 20.0
|
|
37
|
+
assert rect["end"]["x"] == 50.0
|
|
38
|
+
assert rect["end"]["y"] == 60.0
|
|
39
|
+
assert rect["stroke_width"] == 0.127
|
|
40
|
+
assert rect["stroke_type"] == "solid"
|
|
41
|
+
assert rect["fill_type"] == "none"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_add_rectangle_with_point_objects():
|
|
45
|
+
"""Test adding rectangle using Point objects."""
|
|
46
|
+
sch = Schematic()
|
|
47
|
+
|
|
48
|
+
start = Point(100.0, 200.0)
|
|
49
|
+
end = Point(150.0, 250.0)
|
|
50
|
+
|
|
51
|
+
rect_uuid = sch.add_rectangle(
|
|
52
|
+
start=start,
|
|
53
|
+
end=end,
|
|
54
|
+
stroke_width=0.254
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
assert rect_uuid is not None
|
|
58
|
+
rect = sch._data["rectangles"][0]
|
|
59
|
+
assert rect["start"]["x"] == 100.0
|
|
60
|
+
assert rect["start"]["y"] == 200.0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_add_multiple_rectangles():
|
|
64
|
+
"""Test adding multiple rectangles."""
|
|
65
|
+
sch = Schematic()
|
|
66
|
+
|
|
67
|
+
rect1_uuid = sch.add_rectangle((0, 0), (10, 10))
|
|
68
|
+
rect2_uuid = sch.add_rectangle((20, 20), (30, 30))
|
|
69
|
+
rect3_uuid = sch.add_rectangle((40, 40), (50, 50))
|
|
70
|
+
|
|
71
|
+
assert len(sch._data["rectangles"]) == 3
|
|
72
|
+
assert rect1_uuid != rect2_uuid != rect3_uuid
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_rectangle_default_values():
|
|
76
|
+
"""Test rectangle default parameter values."""
|
|
77
|
+
sch = Schematic()
|
|
78
|
+
|
|
79
|
+
rect_uuid = sch.add_rectangle((0, 0), (10, 10))
|
|
80
|
+
|
|
81
|
+
rect = sch._data["rectangles"][0]
|
|
82
|
+
assert rect["stroke_width"] == 0.0
|
|
83
|
+
assert rect["stroke_type"] == "default"
|
|
84
|
+
assert rect["fill_type"] == "none"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
pytest.main([__file__, "-v"])
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test rectangle round-trip: create, save, load, verify.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import tempfile
|
|
7
|
+
import pytest
|
|
8
|
+
from kicad_sch_api import create_schematic, load_schematic
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_rectangle_roundtrip():
|
|
12
|
+
"""Test creating, saving, and loading a schematic with rectangles."""
|
|
13
|
+
# Create a schematic with rectangles
|
|
14
|
+
sch = create_schematic("Rectangle Test")
|
|
15
|
+
|
|
16
|
+
# Add multiple rectangles
|
|
17
|
+
rect1_uuid = sch.add_rectangle(
|
|
18
|
+
start=(10.0, 20.0),
|
|
19
|
+
end=(50.0, 60.0),
|
|
20
|
+
stroke_width=0.127,
|
|
21
|
+
stroke_type="solid",
|
|
22
|
+
fill_type="none"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
rect2_uuid = sch.add_rectangle(
|
|
26
|
+
start=(100.0, 100.0),
|
|
27
|
+
end=(200.0, 150.0),
|
|
28
|
+
stroke_width=0.254,
|
|
29
|
+
stroke_type="default",
|
|
30
|
+
fill_type="none"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Save to temporary file
|
|
34
|
+
with tempfile.NamedTemporaryFile(suffix=".kicad_sch", delete=False, mode='w') as f:
|
|
35
|
+
temp_path = f.name
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
sch.save(temp_path)
|
|
39
|
+
|
|
40
|
+
# Load the schematic back
|
|
41
|
+
sch_loaded = load_schematic(temp_path)
|
|
42
|
+
|
|
43
|
+
# Verify rectangles were preserved
|
|
44
|
+
assert "rectangles" in sch_loaded._data
|
|
45
|
+
assert len(sch_loaded._data["rectangles"]) == 2
|
|
46
|
+
|
|
47
|
+
# Verify first rectangle
|
|
48
|
+
rect1 = sch_loaded._data["rectangles"][0]
|
|
49
|
+
assert rect1["uuid"] == rect1_uuid
|
|
50
|
+
assert rect1["start"]["x"] == 10.0
|
|
51
|
+
assert rect1["start"]["y"] == 20.0
|
|
52
|
+
assert rect1["end"]["x"] == 50.0
|
|
53
|
+
assert rect1["end"]["y"] == 60.0
|
|
54
|
+
assert rect1["stroke_width"] == 0.127
|
|
55
|
+
assert rect1["stroke_type"] == "solid"
|
|
56
|
+
|
|
57
|
+
# Verify second rectangle
|
|
58
|
+
rect2 = sch_loaded._data["rectangles"][1]
|
|
59
|
+
assert rect2["uuid"] == rect2_uuid
|
|
60
|
+
assert rect2["start"]["x"] == 100.0
|
|
61
|
+
assert rect2["start"]["y"] == 100.0
|
|
62
|
+
assert rect2["stroke_width"] == 0.254
|
|
63
|
+
|
|
64
|
+
finally:
|
|
65
|
+
# Cleanup
|
|
66
|
+
if os.path.exists(temp_path):
|
|
67
|
+
os.remove(temp_path)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_rectangle_format_preservation():
|
|
71
|
+
"""Test that rectangle S-expression format matches KiCAD."""
|
|
72
|
+
sch = create_schematic("Format Test")
|
|
73
|
+
|
|
74
|
+
# Add rectangle with specific parameters
|
|
75
|
+
sch.add_rectangle(
|
|
76
|
+
start=(91.821, 32.211),
|
|
77
|
+
end=(155.829, 148.049),
|
|
78
|
+
stroke_width=0.127,
|
|
79
|
+
stroke_type="solid",
|
|
80
|
+
fill_type="none"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Save to string
|
|
84
|
+
with tempfile.NamedTemporaryFile(suffix=".kicad_sch", delete=False, mode='w') as f:
|
|
85
|
+
temp_path = f.name
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
sch.save(temp_path)
|
|
89
|
+
|
|
90
|
+
# Read file content
|
|
91
|
+
with open(temp_path, 'r') as f:
|
|
92
|
+
content = f.read()
|
|
93
|
+
|
|
94
|
+
# Verify S-expression structure
|
|
95
|
+
assert "(rectangle" in content
|
|
96
|
+
assert "(start 91.821 32.211)" in content
|
|
97
|
+
assert "(end 155.829 148.049)" in content
|
|
98
|
+
assert "(stroke" in content
|
|
99
|
+
assert "(width 0.127)" in content
|
|
100
|
+
assert "(type solid)" in content
|
|
101
|
+
assert "(fill" in content
|
|
102
|
+
assert "(type none)" in content
|
|
103
|
+
assert "(uuid" in content
|
|
104
|
+
|
|
105
|
+
finally:
|
|
106
|
+
if os.path.exists(temp_path):
|
|
107
|
+
os.remove(temp_path)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
111
|
+
pytest.main([__file__, "-v"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kicad_sch_api-0.2.1 → kicad_sch_api-0.2.2}/tests/reference_tests/reference_kicad_projects/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|