TraceMaker
C++ API of the engine
Loading...
Searching...
No Matches
problem.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2#pragma once
3// Placement problem extracted from a board (design doc 04 §2): parts with courtyards, holes and pins per
4// rotation, weighted nets, the board outline, keepouts and spacing rules. Everything is in integer nm; a part's
5// geometry is stored as offsets from its footprint origin for each of the four 90° rotations.
6#include <array>
7#include <cstdint>
8#include <string>
9#include <vector>
10
11#include "geom/shape.hpp"
12#include "model/board.hpp"
13#include "model/rules.hpp"
14
15namespace tmk::place {
16
17using geom::Box;
18using geom::Point;
19using geom::Shape;
20
21inline std::size_t z(int i) { return static_cast<std::size_t>(i); }
22
23// Net weights are small integers so every cost is an exact integer (CLAUDE.md rule 2).
24inline constexpr int kSignalWeight = 10;
25inline constexpr int kPowerWeight = 1;
26inline constexpr Coord kEdgeTolerance = 250'000; // courtyard may extend this far past the board edge
27
28// A piece of copper with what KiCad's clearance check needs: layers, net and the clearance it asks for
29// (its net class, local overrides and board minimum; pair clearance = max of the two).
33 model::NetId net = 0; // board net id (0 = no net)
35};
36
37struct PartGeom { // one rotation of a part, offsets from the footprint origin
38 std::array<std::vector<Shape>, 2> cy; // courtyard polygons per side (empty = no courtyard on that side)
39 std::vector<Shape> through; // drilled holes and plated-through pad copper (obstacles on both sides)
40 std::vector<Shape> pads; // pad copper (for the copper-to-edge clearance)
41 std::vector<CopperShape> copper; // pads, footprint copper graphics/text, NPTH holes (copper clearance)
42 Box copper_box; // bounding box of `copper`
43 Box body; // bounding box of courtyards, through obstacles and copper
44 // Courtyards shrunk by kEdgeTolerance: what must stay inside the board outline. KiCad itself only checks pad
45 // copper against the edge (copper_edge_clearance); a courtyard may reach a little past it.
46 std::array<std::vector<Shape>, 2> cy_in;
47 Box edge_box; // bounding box of the inset courtyards and pads inflated by the edge clearance
48};
49
50// Orientation state of a part (Placement::rot): bits 0–1 = quarter turns r, bit 2 = flipped to the other side.
51// State r means angle0 + 90 r on the input side; state 4 + r means the KiCad flip (top/bottom about the origin:
52// offsets mirrored in y, orientation −angle0) followed by r quarter turns, i.e. −angle0 + 90 r on the other side.
53inline constexpr int kStates = 8;
54inline constexpr std::uint8_t kFlipBit = 4;
55inline bool flipped(int state) { return (state & kFlipBit) != 0; }
56// State with the same side as `state` and `r` quarter turns.
57inline std::uint8_t with_turn(int state, int r) { return static_cast<std::uint8_t>((state & kFlipBit) | (r & 3)); }
58inline Point mirror_y(Point p) { return Point{p.x, -p.y}; }
59// Exchanging two interchangeable parts a and b (same footprint and input side; angle0_b − angle0_a = 90° dk): the
60// state that gives a the absolute pose b has in `b_state`. Flipping negates angle0, so dk changes sign there.
61// (b takes a's pose with swap_state(a_state, −dk).)
62inline std::uint8_t swap_state(int b_state, int dk) { return with_turn(b_state, (b_state & 3) + (flipped(b_state) ? -dk : dk) + 4); }
63
64struct Part {
65 int fp = -1; // footprint index in the board
66 std::string ref, lib_id;
67 bool movable = false;
68 bool copper_only = false; // ExtractOptions::copper_only: only its copper and holes are tested against other parts
69 std::string fixed_reason; // why a part is fixed (locked, mounting hole, ...)
70 int side = 0; // 0 front, 1 back: the footprint's side in the input
71 // May move to the other side (states 4–7): movable, surface mount only (no holes), the board has two copper
72 // sides, flipping was asked for, and the writer can mirror the footprint (doc 04 §3 C/E, D48).
73 bool flippable = false;
74 std::string flip_reason; // why a movable part may not flip (empty when it may, or when flipping is off)
75 Point pos0; // original origin
76 double angle0 = 0; // original absolute orientation (degrees); see kStates for state angles
77 std::array<PartGeom, kStates> geom; // per state (states 4–7 only filled for flippable parts)
78 bool may_flip() const { return movable && flippable; } // movability can be revoked after extraction
79 int side_in(int state) const { return side ^ (flipped(state) ? 1 : 0); }
80 double angle_of(int state) const { return flipped(state) ? -angle0 + 90.0 * (state & 3) : angle0 + 90.0 * (state & 3); }
81 std::vector<int> pins; // indices into Problem::pins
82 Coord area = 0; // courtyard box area incl. clearance (nm², saturating), for spreading
83 std::uint64_t shape_key = 0; // equal keys = interchangeable footprints (swap moves)
84};
85
86struct Pin {
87 int part = -1;
88 int net = -1; // index into Problem::nets
89 std::array<Point, kStates> off; // offset from the part origin per state
90 bool one_side = false; // a surface-mount pad: on the part's side only (via estimate when sides differ)
91};
92
93struct PNet {
94 std::string name;
96 bool signal = true; // counted for airwire crossings
97 // Design-intent pseudo-net (decoupling capacitor to its IC's supply pin): part of the objective only, never
98 // reported as wirelength, never routed, never counted for congestion.
99 bool affinity = false;
100 std::vector<int> pins;
101 // Fixed coordinates inside the net's bounding box (an edge pull, doc 15 CONN-01): a one-pin net with an x anchor
102 // has HPWL |pin.x - ax|, the distance to a vertical board edge. Affinity nets only.
103 bool has_ax = false, has_ay = false;
104 Coord ax = 0, ay = 0;
105};
106
107struct Keepout {
109 bool side[2] = {false, false};
110};
111
112struct Problem {
113 std::vector<Part> parts;
114 std::vector<Pin> pins;
115 std::vector<PNet> nets;
116 std::vector<Point> outline; // largest Edge.Cuts loop (empty if none could be assembled)
117 std::vector<std::vector<Point>> cutouts; // other closed Edge.Cuts loops
118 std::vector<Shape> edges; // every Edge.Cuts piece as segments (r = 0)
119 std::vector<Keepout> keepouts;
120 std::vector<CopperShape> fixed_copper; // board copper that never moves: tracks, vias, copper graphics and text
121 Coord max_need = 0; // largest copper clearance any item asks for
123 Coord clearance = 250'000; // courtyard-to-courtyard clearance
124 bool clearance_is_default = true; // not from a board rule or the command line
125 Coord edge_clearance = 0; // pad copper to board edge
126 Box region; // where parts may go (outline bbox)
127 std::vector<std::string> notes; // extraction decisions worth reporting
128
129 int movable_count() const;
130 int flippable_count() const;
131};
132
133// A design-intent pseudo-net between two board pads (doc 15 P1): `pad_a` belongs to the part that should sit
134// near `pad_b` (an ESD part near its connector, a crystal near its IC's oscillator pins, ...).
136 int pad_a = -1, pad_b = -1; // board pad indices
138 std::string name; // "~XTAL-01 Y1-U1"
139};
140
141// An objective-only pull of a part toward the board outline (doc 15 CONN-01, opt-in): the body point nearest to the
142// outline in the input becomes a one-pin pseudo-net anchored on the nearest outline segment's axis (x for a mostly
143// vertical segment, y for a mostly horizontal one), so the pull is exact in the annealer's integer cost and
144// rotating the part away from the edge costs too.
145struct EdgePull {
146 int footprint = -1;
147 std::vector<Point> body; // absolute candidate points (courtyard outline, else pad corners)
149 std::string name; // "~CONN-01 J3 edge"
150};
151
153 bool fix_edge_connectors = true; // connectors (J*, P*, CN*, USB*) touching the outline stay put
154 Coord courtyard_clearance = -1; // override (-1: from the rules, else default_clearance)
155 Coord default_clearance = 250'000; // used when the board has no courtyard rule (KiCad's own default is 0)
156 bool decap_affinity = true; // tie each decoupling capacitor to the nearest supply pin of its IC
157 int decap_weight = kSignalWeight; // weight of those ties (D25: signal weight)
158 int crules_weight_pct = 100; // scale of the component-rule proximity weights (100 = as the rules say)
159 // Full mode with component rules: place with the decoupling ties only, then lock the tied capacitors and refine
160 // with the component-rule pulls (they compete for the space next to the IC otherwise; doc 15 §14.3).
161 bool crules_two_stage = true;
162 // Extra objective-only pseudo-nets from component rules (crules::placement_affinities). Parts already tied by
163 // decap_affinity are skipped.
164 std::vector<PadAffinity> affinities;
165 // Edge pulls for movable parts (crules::edge_attractions); fixed parts are skipped.
166 std::vector<EdgePull> edge_pulls;
167 // Side assignment (doc 04 §3 C/E, D48): movable surface-mount parts may move to the other side. `flip_ok` (one
168 // entry per footprint; empty = all) says whether the writer can mirror the footprint, `flip_why` why not;
169 // `keep_side` lists references the user pins to their side.
170 bool flip = false;
171 std::vector<std::uint8_t> flip_ok;
172 std::vector<std::string> flip_why;
173 std::vector<std::string> keep_side;
174 // References whose courtyard is ignored: the part is as large as its pads (box + 0.25 mm), like a footprint
175 // drawn without a courtyard. Full mode's fallback for parts whose courtyard fits nowhere (designers overlap
176 // courtyards routinely: 24 of the 40 boards of set H do; doc 04 §9).
177 std::vector<std::string> pads_only;
178 // References tested by copper and holes only: neither their outline nor the other parts' courtyards count
179 // against them (copper clearance, hole spacing and the board edge still do). The last resort before a part is
180 // left off the board: battery holders, modules and headers that the designer placed across other courtyards.
181 std::vector<std::string> copper_only;
182 // The input positions of movable parts mean nothing (a board straight from the schematic: parts piled or beside
183 // the board). A part that overhangs the edge where the input has it is then not held there.
184 bool scratch = false;
185};
186
187// Builds the problem. `rules` and `board_path` give the courtyard clearance (custom rules or .kicad_pro).
188Problem extract(const model::Board& b, const model::DesignRules& rules, const std::string& board_path,
189 const ExtractOptions& opt = {});
190
191// Geometry helpers.
193// Mirror in y about the origin (polygon winding kept).
195// Copper layer mask of the other side: bit i <-> bit n−1−i for n copper layers (KiCad FlipLayerMask).
197Point rot90(Point p, int r); // KiCad rotation by r * 90 degrees
198std::vector<Point> convex_hull(std::vector<Point> pts);
199// Inner parallel polygon of a convex polygon at distance t; a point at the centroid if it is thinner than 2t.
201bool power_like_name(const std::string& name);
202bool ground_like_name(const std::string& name);
203
204} // namespace tmk::place
The board model (design doc 02 §3): every object TraceMaker reads from a .kicad_pcb,...
std::int32_t NetId
0 = no net
Definition board.hpp:21
std::uint64_t LayerMask
bit i = copper layer i in stack order (0 = F.Cu, last = B.Cu)
Definition board.hpp:22
model::LayerMask flip_layers(model::LayerMask m, int n)
Copper layer mask of the other side: bit i <-> bit n−1−i for n copper layers (KiCad FlipLayerMask).
Shape mirrored(const Shape &s)
Mirror in y about the origin (polygon winding kept).
constexpr int kSignalWeight
Net weights are small integers so every cost is an exact integer (CLAUDE.md rule 2).
Definition problem.hpp:24
constexpr Coord kEdgeTolerance
courtyard may extend this far past the board edge
Definition problem.hpp:26
constexpr std::uint8_t kFlipBit
Definition problem.hpp:54
std::size_t z(int i)
Definition problem.hpp:21
bool ground_like_name(const std::string &name)
constexpr int kStates
Orientation state of a part (Placement::rot): bits 0–1 = quarter turns r, bit 2 = flipped to the othe...
Definition problem.hpp:53
Shape translated(const Shape &s, Point d)
Geometry helpers.
constexpr int kPowerWeight
Definition problem.hpp:25
std::uint8_t with_turn(int state, int r)
State with the same side as state and r quarter turns.
Definition problem.hpp:57
Shape inset_convex(const Shape &s, Coord t)
Inner parallel polygon of a convex polygon at distance t; a point at the centroid if it is thinner th...
Problem extract(const model::Board &b, const model::DesignRules &rules, const std::string &board_path, const ExtractOptions &opt={})
Builds the problem. rules and board_path give the courtyard clearance (custom rules or ....
Point mirror_y(Point p)
Definition problem.hpp:58
bool flipped(int state)
Definition problem.hpp:55
bool power_like_name(const std::string &name)
std::uint8_t swap_state(int b_state, int dk)
Exchanging two interchangeable parts a and b (same footprint and input side; angle0_b − angle0_a = 90...
Definition problem.hpp:62
std::vector< Point > convex_hull(std::vector< Point > pts)
Point rot90(Point p, int r)
KiCad rotation by r * 90 degrees.
std::int64_t Coord
Engine coordinate: signed 64-bit nanometres.
Definition units.hpp:12
Design rules as KiCad defines them (design doc 08 §3–4): board minimums and net classes from the ....
Exact integer geometry for clearance checks (design doc 03 §1).
A piece of copper with what KiCad's clearance check needs: layers, net and the clearance it asks for ...
Definition problem.hpp:30
model::LayerMask layers
Definition problem.hpp:32
model::NetId net
board net id (0 = no net)
Definition problem.hpp:33
An objective-only pull of a part toward the board outline (doc 15 CONN-01, opt-in): the body point ne...
Definition problem.hpp:145
std::string name
"~CONN-01 J3 edge"
Definition problem.hpp:149
std::vector< Point > body
absolute candidate points (courtyard outline, else pad corners)
Definition problem.hpp:147
bool flip
Side assignment (doc 04 §3 C/E, D48): movable surface-mount parts may move to the other side.
Definition problem.hpp:170
int decap_weight
weight of those ties (D25: signal weight)
Definition problem.hpp:157
bool crules_two_stage
Full mode with component rules: place with the decoupling ties only, then lock the tied capacitors an...
Definition problem.hpp:161
std::vector< std::string > copper_only
References tested by copper and holes only: neither their outline nor the other parts' courtyards cou...
Definition problem.hpp:181
std::vector< std::uint8_t > flip_ok
Definition problem.hpp:171
Coord courtyard_clearance
override (-1: from the rules, else default_clearance)
Definition problem.hpp:154
bool decap_affinity
tie each decoupling capacitor to the nearest supply pin of its IC
Definition problem.hpp:156
std::vector< std::string > pads_only
References whose courtyard is ignored: the part is as large as its pads (box + 0.25 mm),...
Definition problem.hpp:177
std::vector< std::string > flip_why
Definition problem.hpp:172
Coord default_clearance
used when the board has no courtyard rule (KiCad's own default is 0)
Definition problem.hpp:155
bool fix_edge_connectors
connectors (J*, P*, CN*, USB*) touching the outline stay put
Definition problem.hpp:153
std::vector< std::string > keep_side
Definition problem.hpp:173
std::vector< PadAffinity > affinities
Extra objective-only pseudo-nets from component rules (crules::placement_affinities).
Definition problem.hpp:164
std::vector< EdgePull > edge_pulls
Edge pulls for movable parts (crules::edge_attractions); fixed parts are skipped.
Definition problem.hpp:166
bool scratch
The input positions of movable parts mean nothing (a board straight from the schematic: parts piled o...
Definition problem.hpp:184
int crules_weight_pct
scale of the component-rule proximity weights (100 = as the rules say)
Definition problem.hpp:158
bool affinity
Design-intent pseudo-net (decoupling capacitor to its IC's supply pin): part of the objective only,...
Definition problem.hpp:99
bool signal
counted for airwire crossings
Definition problem.hpp:96
bool has_ax
Fixed coordinates inside the net's bounding box (an edge pull, doc 15 CONN-01): a one-pin net with an...
Definition problem.hpp:103
std::string name
Definition problem.hpp:94
std::vector< int > pins
Definition problem.hpp:100
A design-intent pseudo-net between two board pads (doc 15 P1): pad_a belongs to the part that should ...
Definition problem.hpp:135
int pad_b
board pad indices
Definition problem.hpp:136
std::string name
"~XTAL-01 Y1-U1"
Definition problem.hpp:138
std::array< std::vector< Shape >, 2 > cy_in
Courtyards shrunk by kEdgeTolerance: what must stay inside the board outline.
Definition problem.hpp:46
std::vector< Shape > pads
pad copper (for the copper-to-edge clearance)
Definition problem.hpp:40
std::array< std::vector< Shape >, 2 > cy
< one rotation of a part, offsets from the footprint origin
Definition problem.hpp:38
Box copper_box
bounding box of copper
Definition problem.hpp:42
Box edge_box
bounding box of the inset courtyards and pads inflated by the edge clearance
Definition problem.hpp:47
Box body
bounding box of courtyards, through obstacles and copper
Definition problem.hpp:43
std::vector< CopperShape > copper
pads, footprint copper graphics/text, NPTH holes (copper clearance)
Definition problem.hpp:41
std::vector< Shape > through
drilled holes and plated-through pad copper (obstacles on both sides)
Definition problem.hpp:39
bool flippable
May move to the other side (states 4–7): movable, surface mount only (no holes), the board has two co...
Definition problem.hpp:73
Coord area
courtyard box area incl. clearance (nm², saturating), for spreading
Definition problem.hpp:82
std::vector< int > pins
indices into Problem::pins
Definition problem.hpp:81
Point pos0
original origin
Definition problem.hpp:75
std::uint64_t shape_key
equal keys = interchangeable footprints (swap moves)
Definition problem.hpp:83
int fp
footprint index in the board
Definition problem.hpp:65
std::string fixed_reason
why a part is fixed (locked, mounting hole, ...)
Definition problem.hpp:69
std::string lib_id
Definition problem.hpp:66
double angle_of(int state) const
Definition problem.hpp:80
double angle0
original absolute orientation (degrees); see kStates for state angles
Definition problem.hpp:76
std::string ref
Definition problem.hpp:66
bool copper_only
ExtractOptions::copper_only: only its copper and holes are tested against other parts.
Definition problem.hpp:68
int side
0 front, 1 back: the footprint's side in the input
Definition problem.hpp:70
std::array< PartGeom, kStates > geom
per state (states 4–7 only filled for flippable parts)
Definition problem.hpp:77
int side_in(int state) const
Definition problem.hpp:79
bool may_flip() const
Definition problem.hpp:78
std::string flip_reason
why a movable part may not flip (empty when it may, or when flipping is off)
Definition problem.hpp:74
bool one_side
a surface-mount pad: on the part's side only (via estimate when sides differ)
Definition problem.hpp:90
int net
index into Problem::nets
Definition problem.hpp:88
std::array< Point, kStates > off
offset from the part origin per state
Definition problem.hpp:89
Box region
where parts may go (outline bbox)
Definition problem.hpp:126
int movable_count() const
Coord max_need
largest copper clearance any item asks for
Definition problem.hpp:121
std::vector< std::vector< Point > > cutouts
other closed Edge.Cuts loops
Definition problem.hpp:117
std::vector< Part > parts
Definition problem.hpp:113
std::vector< CopperShape > fixed_copper
board copper that never moves: tracks, vias, copper graphics and text
Definition problem.hpp:120
bool clearance_is_default
not from a board rule or the command line
Definition problem.hpp:124
Coord clearance
courtyard-to-courtyard clearance
Definition problem.hpp:123
std::vector< Pin > pins
Definition problem.hpp:114
std::vector< PNet > nets
Definition problem.hpp:115
std::vector< Shape > edges
every Edge.Cuts piece as segments (r = 0)
Definition problem.hpp:118
std::vector< Point > outline
largest Edge.Cuts loop (empty if none could be assembled)
Definition problem.hpp:116
Coord edge_clearance
pad copper to board edge
Definition problem.hpp:125
int flippable_count() const
std::vector< Keepout > keepouts
Definition problem.hpp:119
std::vector< std::string > notes
extraction decisions worth reporting
Definition problem.hpp:127