0x22 is a rare field wedon't see much. BB-AI is a V172 file, but
the 0x22 field has this element, so it must be at least V172, not
V174.
Add a block test to really nail this down (at least for V172!)
This allows BB-AI-64 to load successfully.
When multiple top-level sheets in a multi-root schematic reference the
same sub-sheet file, each sub-sheet must share a single SCH_SCREEN
object. Previously, loadHierarchy() only searched the current root
sheet's hierarchy for an existing screen, so screens loaded by earlier
top-level sheets were never found. This caused each top-level sheet to
create its own copy of the shared sub-sheet's screen, leading to data
loss on save as later writes would overwrite earlier ones.
Add m_loadedRootSheets to SCH_IO_KICAD_SEXPR to track root sheets from
prior LoadSchematicFile() calls within the same IO instance. During
loadHierarchy(), after the existing search fails, also search through
previously loaded root sheets. The cache is cleared when the SCHEMATIC
pointer changes, preventing stale references when the IO object is
reused for unrelated loads.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23403
When generating paths between board edge nodes from different Edge.Cuts
shapes (e.g., corners of two rectangular slots), both parents were added
to the IgnoreForTest list.
For POINT shapes (rectangle/polygon corners), the segments_intersect
function already excludes shared-endpoint intersections, so skipping
the parent shape is unnecessary. Only circle board edge shapes need
their parent in IgnoreForTest because segmentIntersectsCircle has no
endpoint exclusion.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23364
Zone-to-zone clearance knockouts and higher-priority zone subtraction
happen after the initial deflate/inflate minimum-width enforcement
cycle. These knockouts can carve out material and leave thin copper
strips that violate the zone's minimum width constraint.
Add a second deflate/inflate pass that runs only when knockouts were
actually applied. The result is intersected with the pre-deflate
boundary to prevent re-inflation into cleared areas (keepouts, pad
clearances, etc.).
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23332
A local label that only connects to other labels or hierarchical sheet
pins without any component pin on the same sheet serves no local purpose
and is likely mislabeled. Extend the existing bus-member check to all
local labels so that ercCheckLabels() flags these cases as
ERCE_LABEL_NOT_CONNECTED.
The allPins > 1 guard prevents double-reporting with
ERCE_LABEL_SINGLE_PIN which already covers the allPins == 1 case.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23346
PADSTACK in NORMAL mode stores one shape for all layers. When front
and back copper have different shapes, the second convertPadShape()
call overwrites the first. Pre-scanning copper layers and switching
to FRONT_INNER_BACK mode when shapes differ fixes this.
PADS stores connection style per-pin, not per-zone. The zone default
was THERMAL; it is now FULL (solid). Per-pad THERMAL overrides are
set from RT/ST thermal relief entries in the pad stack, carrying
over spoke width and angle.
The parser read NET_CLASS names and memberships but skipped per-class
RULE_SET entries for clearance and track width. RULE_SET parsing now
handles non-default rule sets and matches them to netclasses via the
FOR/NET_CLASS declaration.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23352
The text-based rule editor incorrectly warned about "duplicate conditions"
when two rules shared the same condition expression but had different layer
scopes (e.g., "(layer outer)" vs "(layer inner)"). Include the layer source
in the deduplication key so layer-differentiated rules are not flagged.
The graphical rule editor's save path had three related issues:
generateLayerClause() emitted multi-token "(layer "F.Cu" "B.Cu")" format
that the parser cannot handle (it expects a single token). Now detects
ExternalCuMask/InternalCuMask and emits "(layer outer)"/"(layer inner)".
The merge grouping key only used (ruleName, condition), causing rules with
different layer scopes to be incorrectly merged. Now includes layerSource.
The same-name validation in the graphical editor also excluded layer scope.
Add regression test verifying EvalRules returns correct per-layer clearances
for layer-scoped custom rules and zone fill produces no DRC violations.
Two bugs caused the creepage checker to miss violations through arcs.
segmentIntersectsArc did not exclude shared-endpoint intersections,
so paths starting at arc endpoints were incorrectly rejected as
crossing a board edge, unlike segments_intersect which already had
this exclusion. The temp_nodes filter in testCreepage also had
inverted conditions, leaving ConnectChildren unreachable and
preventing point-on-arc nodes from linking to their parent arc
surface.
Adds a regression test with a slot between HV and GND pads requiring
8mm creepage distance.
Three bugs prevented creepage paths from navigating around board
edge shapes (segments, rectangles, polygons):
1. TransformEdgeToCreepShapes never called SetParent() for SEGMENT,
RECTANGLE, and POLY shapes. Their CREEP_SHAPE parent remained
nullptr, causing GeneratePaths to exclude them from all path
generation since null-parent nodes were filtered out.
2. CU_SHAPE_SEGMENT never set m_pos (defaulting to 0,0) and
GetRadius() returned 0. The distance filter in GeneratePaths
compared center positions with a bounding sphere, so all
segment-to-edge-point pairs were rejected as too far apart.
Set m_pos to the segment midpoint and override GetRadius() to
return half the segment length plus half the width.
3. GeneratePaths only generated work items between nodes of
different parent BOARD_ITEMs. Nodes sharing the same parent
(e.g. multiple edge segments of the same gr_rect slot) were
never paired, so the algorithm could not route around slot
geometry. Added same-parent pairing for nodes with different
CREEP_SHAPE parents.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/20480
Connector pins (e.g. J12-1 through J12-53) were imported as
separate single-pin symbols instead of units of one shared
symbol. Add BuildMultiUnitConnectorSymbol which creates a
multi-unit LIB_SYMBOL where each unit has the connector decal
graphics and one pin with the correct number.
Multi-gate parts like TL082 (U1-A, U1-B) appeared as separate
symbols because AddHierarchicalReference was called with the
unsuffixed reference "U1-A" instead of the stripped "U1".
Also move the connector SetRef call to after ApplyPartAttributes,
which only strips alphabetic gate suffixes and would otherwise
override the base reference back to "J12-1".
When hierarchical pins connect to global power nets, the multichannel
repeat-layout tool incorrectly reports a topology mismatch. The net
isomorphism algorithm treats global nets as channel-internal, causing
two failures: connection counts differ when global net pads are shared
with external components, and net-consistency checks flag false
contradictions between channels. The fix identifies nets whose
footprints fall outside the channel's footprint set and excludes them
from connection building and net-consistency checks. Global power rails
do not define internal channel routing topology, so this is correct.
Adds a QA regression test and reproduction board.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/21739
Commit c1ef019d35 moved splitSelfTouchingOutlines() into
cacheTriangulation(), placing it before Fracture(). When a polygon
has holes, splitSelfTouchingOutlines() splits the outer boundary but
the new sub-polygon inherits no holes. Its triangulation then spans
the knockout areas, producing false DRC clearance violations.
Fix the order: call Fracture() first so holes are merged into the
outline, then call splitSelfTouchingOutlines().
After Fracture(), Clipper2 rounds corridor-cut vertices to integer
coordinates; they can land within 1nm of a segment endpoint.
SEG::Contains() accepts points within sqrt(3) nm as on-segment, so
those vertices trigger false pinch-point splits. Use SquaredDistance()
== 0, which requires exact coincidence, to detect only true pinch
points.
Fixes https://gitlab.com/kicad/code/kicad/issues/23123
PADS Logic V5.2 uses a different gate definition syntax in the PARTTYPE
section than V9.0+. V5.2 uses "G:decal1[:decal2:...] swap num_pins"
with dot-separated pin tokens (e.g. "1.0.U.AN"), while V9.0+ uses
"GATE num_variants num_pins swap" with space-separated fields.
The parser only recognized V9.0+ keywords (GATE, CONN), causing V5.2
parts to be silently dropped. Parts whose PARTTYPE name matched the
CAEDECAL name (e.g. RLY-SPDT) were resolved through a fallback path,
but parts with different names (e.g. RES0805 -> RESZ-H) were not.
Add detection of V5.2 format by checking for "G:" prefix on the first
content line after each part type header, and parse the colon-separated
decal names and dot-separated pin fields. Also handle V5.2 SIGPIN
format (dot-separated "pin.sort.net") and V5.2 special symbol
conversion ($GND_SYMS, $PWR_SYMS, $OSR_SYMS).
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23160
When placing a design block without keeping annotations, ClearAnnotation was
called before AnnotateSymbols, setting all references to "R?" form. This
caused AppendMultiUnitSymbol to skip building locked groups (it returns early
for unannotated refs), leaving the locked group map empty. Without locked
groups, the position-based annotation algorithm could assign units from
different multi-unit symbols to the same reference designator when symbols
shared the same value and lib symbol.
This preserves original annotations when auto-annotation is enabled, and
passes aResetAnnotation=true to AnnotateSymbols so it builds correct locked
groups from the original refs before resetting and reannotating.
When auto-annotation is disabled, ClearAnnotation is still called so the user
sees unannotated symbols as expected.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23183
GetSolderMaskExpansion() on PAD, PCB_TRACK, and PCB_SHAPE called
EvalRules(SOLDER_MASK_EXPANSION_CONSTRAINT) unconditionally during
DRC, even when no custom rules existed for that constraint type.
Now check HasRulesForConstraintType() first and fall back to the
direct property lookup chain when no custom rules exist.
Also fix a bug where PCB_TRACK and PCB_SHAPE returned 0 instead of
the board default mask expansion when no local override was set.
Apply the same optimization to solder paste margin lookups. Add
per-provider DRC timing via the existing KICAD_DRC_PROFILE trace mask.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23213
The knockout item deduplication introduced in 78b576537e omitted net
codes from PAD_KNOCKOUT_KEY and VIA_KNOCKOUT_KEY. Two pads (or vias)
at the same position with identical geometry but different nets were
treated as duplicates. When the first pad matched the zone net it
received thermal relief, and the second pad (different net) was
silently skipped, so no clearance was ever knocked out for it.
Add net code to both key structs so coincident items on different nets
are processed independently.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23123
On mixed-DPI multi-monitor setups, dialog sizes grow each time they are
reopened because logical pixel values change when a window moves between
monitors with different scaling factors. The std::max comparison during
restore guarantees monotonic growth since the DPI-scaled value always
exceeds the previously saved unscaled value.
Store width and height as DIP (device-independent pixels) using ToDIP()
on save and FromDIP() on restore. This makes the persisted size stable
regardless of which monitor the dialog was on when closed. A "dip" flag
in the JSON differentiates new DIP-format values from legacy logical
pixel values, which are converted in place on first load.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/20120
The font name line parser in parseSymbolDef() unconditionally
advanced the line index even when the current line was not a
quoted font name. Newer PADS formats (V9.0+) always include
two quoted font lines after the CAEDECAL header, but older
formats like V5.2 omit them. This caused a two-line
misalignment that corrupted attribute and graphic parsing for
the entire symbol definition.
Only advance past font lines when the line actually starts
with a quote character, matching the pattern already used in
parsePartPlacement().
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23149
The iterative refill path in refillZoneFromCache() was using
GetLocalClearance() with no safety margins for zone-to-zone knockouts,
while the initial fill path uses DRC-evaluated clearances with
extra_margin and m_maxError (ERROR_OUTSIDE equivalent). This mismatch
caused the iterative refill to produce fills that violate zone clearance
constraints by up to ~5um due to polygon approximation error.
The fix aligns refillZoneFromCache() with buildDifferentNetZoneClearances()
by using DRC rule evaluation for clearance computation and adding
extra_margin + m_maxError to the knockout inflation.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23053
ZONE::GetPosition() called GetCornerPosition(0) unconditionally, which
throws std::out_of_range when the zone's polygon has no vertices. This
crashes during PCB_SELECTION_TOOL::SelectMultiple() when sorting items
by position.
Guard GetPosition() to return {0,0} for empty zones. Also discard zones
with no outline vertices during kicad_pcb file loading and PADS binary
import, since they are degenerate and serve no purpose.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23125
This adds all the existing .brd files to the JSON declarative registry,
which means a board load test can be executed more granularly:
- qa_pcbnew -t "AllegroBoards/EVK_SOM"
Plus executing any expectation tests.
The all-loads-in-one test in test_allegro_import is then scrapped, since
that takes a long time and doesn't give per-board failures at a test
case level.
The other existing tests in test_allegro_import are keept basically
as-is, but some refactoring to use the new locations and some code
duplication reduction especially around the alg lookups.
The PreAmp/Proiect alg file seems to not actually pass the
OutlineEndpoint test and wasn't being used due to the different
name, so just leave it in the expected dir for now.
This is a system which allows board-level tests to be described
in a declarative way. The declarations are in JSON as we already
have all the utils for that.
Currently provide a very limited set, but it provides somewhere to
hang board load/test classes and plug in new expectations.
Use it for a small handful of Allegro boards, but this is not
specifically Allegro functionality.
This also centralises some of the board load/cache functionality
in the general pcbnew_utils QA library.
Instead of compiling in lots of binary data, implement a
declarative test registry which creates unit tests from a
pile of binary files and some JSON metadata.
This allows to drop in more tests without recompiling, but
still allows you to select a single test to run with a command
like one of these:
- qa_pcbnew -t AllegroBlock/CutiePi_V2_3_v166/Header
- qa_pcbnew -t AllegroBlocks/*/Header
- qa_pcbnew -t AllegroBlocks/*/Block_0x20*
At static init time, the tests are constructed so that the test
names can be known. The rest of the test consstruction from the
JSON, as well as the load of test data itself, is deferred until
the actual test run.
When routing a new track that connects to the middle of a locked track,
the router splits the existing segment into two pieces via
SplitAdjacentSegments. The PNS-level Clone preserves the MK_LOCKED
marker on both halves, but createBoardItem did not propagate the locked
state when creating the new board item for the second half.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/21564
When a jumper pin group contains a pin number that doesn't exist in the
symbol, SCH_SYMBOL::GetPin() returns nullptr. The connection graph code
in updateSymbolConnectivity() pushed these null pointers into a vector
and then dereferenced them in linkPinsInVec()
Skip null pins when building the jumper group connection list. Also add
validation in the symbol properties dialog to reject jumper pin groups
that reference non-existent pin numbers before they can be saved.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23058
The via stack parser included non-copper layers like
soldermask (layer 25) when computing the layer span and
pad size. For a 4-layer board, a soldermask entry with
effective_layer=25 made max_layer exceed layer_count,
so is_full_span was false and the via was classified as
blind instead of through-hole. The soldermask opening
was also larger than the copper pad, inflating the via
width from 36 mil to 63 mil. Since deduplication only
runs for VIATYPE::THROUGH, the misclassification also
caused duplicate vias at the same position.
Gate the span tracking and size accumulation to layers
in the [1, layer_count] copper range so non-copper
stack entries no longer affect via type or pad size.
Adds peka.asc test board and a test case verifying
through-hole classification, correct pad size, and no
duplicate vias.
Map PADS layers 18, 19, and 20 to KiCad documentation
layers so graphics on UNASSIGNED-type layers are no
longer silently dropped.
Skip thermal relief pad entries (RT, ST, RA, SA) when
building pad shapes so oval finger (OF) pads on parts
like U1 are not overwritten by spoke patterns.
Set pour record style from the header TYPE field instead
of the piece-level PIECETYPE. HATOUT and VOIDOUT are
header types, not piece geometry types, so checking the
piece line (always POLY) never matched. This caused
HATOUT fills to create duplicate zones instead of being
stored as pre-computed copper fills via SetFilledPolysList.
VOIDOUT records are now correctly added as holes in fill
polygons rather than treated as zone outlines.
Project horizontal dimension endpoints onto the same Y
coordinate so PCB_DIM_ALIGNED does not produce a skewed
measurement line when PADS base points differ on the
non-measured axis.
Adds Importer.asc test board and a test case covering
all four fixes.
loadCopperShapes() called NewOutline() then Append(SHAPE_LINE_CHAIN),
which silently created two polygon outlines per zone: an empty one from
NewOutline() and the real one from the implicit SHAPE_LINE_CHAIN to
SHAPE_POLY_SET conversion in the Append overload. The empty outline had
0 points but IsClosed()==true, so the renderer computed numPoints as
0+1=1 and crashed in CPoint(0) on the empty vector.
Replace with AddOutline() which properly adds the chain as a single
polygon outline. Also add a zone outline integrity check to
RunStructuralChecks to catch this class of bug across all test boards.
PADS files can contain PADTHERM/VIATHERM pour pieces with only 2
points (SEG type). The importer created ZONE objects from these with
empty or degenerate SHAPE_LINE_CHAIN outlines, which crashed the
renderer in DrawPolyline when accessing CPoint(0) on a zero-length
vector.
Guard all three zone creation paths against outlines with fewer than
3 points: loadZones() (pours), loadCopperShapes() (filled copper),
and loadKeepouts().
Also validate via layer pairs from getMappedLayer() before passing to
SetLayerPair(), since unmapped layers returned UNDEFINED_LAYER which
could cause assertion failures downstream.
Remove pcb_shape.h from drc_engine.h (unused, zero references to
PCB_SHAPE in the header). Remove netclass.h from drc_rule.h (unused,
zero references to NETCLASS). Add eda_units.h to drc_rule.h for
EDA_UNITS that was previously available through the netclass.h chain.
The layer map can contain negative sentinel values for
unmapped Eagle layers. Passing these to LSET::set() caused
a dynamic_bitset resize to SIZE_MAX, crashing with
std::bad_alloc. Skip UNDEFINED_LAYER and UNSELECTED_LAYER
when enabling layers after import.
The non-smashed text justification condition
abs(degrees) <= -180 could never be true since abs()
returns a non-negative value. Changed to
abs(degrees) >= 180 so text justification flips correctly
for footprints rotated 180 or 270 degrees.
Added regression tests for via net assignment and text
justification using boards from issues 21243 and 23016.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23016
The CLI test harness hardcoded KICAD9_SYMBOL_DIR and KICAD9_FOOTPRINT_DIR,
which stopped working after the version bump to KiCad 10. The versioned
env var fallback in KIwxExpandEnvVars could no longer resolve
KICAD8_FOOTPRINT_DIR references in fp-lib-table because the predefined
list only contains KICAD10_FOOTPRINT_DIR.
Dynamically detect the major version from kicad-cli at test startup and
set the matching versioned env vars. Also fix a pre-existing TypeError
when QA_DATA_ROOT is set (str vs Path).
Set footprint_link_issues to ignore in the test project to eliminate
environment-dependent ERC output, and update all three golden files
with the new ignored check entry.
Add board-level text import by scanning all 0x30 blocks
and excluding footprint-owned text. Fix zone fill matching
for boards with unnamed nets by assigning synthetic names
instead of collapsing to the unconnected net. Document the
V18 header format changes in FORMAT.md.
Use VIA_DEF definitions from the file to set default via size/drill and
populate the via dimensions preset list. Parse all CLEARANCE_RULE types
including OUTLINE_TO_* for copper-to-edge clearance. Detect groups of 4
consecutive non-copper COPPER segments forming axis-aligned rectangles
and emit a single PCB_SHAPE::RECTANGLE instead of 4 individual segments.
Build board stackup from LAYER DATA when meaningful thickness/dielectric
data is present.
We may be able to import v14 and v15 but we don't have any boards to
test this theory and v13 boards crash as they have a fundamentally
different format