TraceMaker
C++ API of the engine
Loading...
Searching...
No Matches
obstacles.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2#pragma once
3// Obstacle model for routing: every copper item, hole, board edge and keepout, with exact legality tests that
4// use the DRC's rule engine, so the router and the DRC can never disagree about what is legal.
5#include <functional>
6#include <memory>
7#include <optional>
8#include <vector>
9
10#include "drc/connectivity.hpp"
11#include "drc/copper.hpp"
12#include "drc/rule_engine.hpp"
14#include "model/board.hpp"
15#include "model/rules.hpp"
16
17namespace tmk::route {
18
19class Obstacles {
20 public:
21 // `board` is the router's working copy; items added later must also be appended to it.
23
24 // Legality of a probe shape. Result: 0 = free, 1 = only conflicts with rippable routed copper (owners are
25 // appended to `owners` when given), 2 = blocked by fixed copper, holes, edges or keepouts.
26 // With `ignore_routed` = false, routed copper counts as blocking (result 0 or 2 only).
27 int disk_state(geom::Point p, int layer, Coord hw, model::NetId net, Coord margin, bool ignore_routed,
28 std::vector<int>* owners = nullptr) const;
29 int segment_state(geom::Point a, geom::Point b, int layer, Coord width, model::NetId net, bool ignore_routed,
30 std::vector<int>* owners = nullptr) const;
31 // A via on copper layers [l0, l1] only (blind or buried via, or a micro via with `type`); via_state covers all layers.
32 int via_state_span(geom::Point p, Coord d, Coord drill, model::NetId net, Coord margin, bool ignore_routed, std::vector<int>* owners, int l0,
33 int l1, model::ViaType type = model::ViaType::Blind) const;
34 int via_state(geom::Point p, Coord d, Coord drill, model::NetId net, Coord margin, bool ignore_routed,
35 std::vector<int>* owners = nullptr) const;
36 bool disk_ok(geom::Point p, int layer, Coord hw, model::NetId net, Coord margin) const {
37 return disk_state(p, layer, hw, net, margin, false) == 0;
38 }
39 bool segment_ok(geom::Point a, geom::Point b, int layer, Coord width, model::NetId net) const {
40 return segment_state(a, b, layer, width, net, false) == 0;
41 }
42 bool via_ok(geom::Point p, Coord d, Coord drill, model::NetId net, Coord margin) const {
43 return via_state(p, d, drill, net, margin, false) == 0;
44 }
45 // Custom disallow rules that depend on position, size or footprint (doc 05 §34), for new copper of `net`: a
46 // track whose copper is `s` with `width` on `layer`, or a via of diameter `d` spanning [l0, l1]. The state
47 // checks above include them (blocked, 2); a disk's rules are judged on its copper, without the margin.
48 bool positional_rules() const { return re_->positional_disallow(); }
49 bool track_disallowed(const geom::Shape& s, int layer, Coord width, model::NetId net) const;
50 bool via_disallowed(geom::Point p, Coord d, model::NetId net, int l0, int l1, model::ViaType type) const;
51
52 // Registers committed copper (the board copy must already contain the track/via at `index`). Returns the
53 // copper item index.
54 int add_track(int index, int owner);
55 int add_via(int index, int owner);
56 // Rips up a routed item (by copper item index).
57 void remove_item(int item);
58
59 // Fixed-obstacle code of a disk (fixed copper, fixed holes, edges, keepouts, outline), independent of the
60 // probe's own net: kFree, kBlocked, or the single net id whose own copper is the only conflict (so the disk
61 // is legal for that net only). Clearances use `probe_net`'s class; diff-pair relief is not applied (safe).
62 static constexpr std::int32_t kFree = -1, kBlocked = -2;
63 std::int32_t fixed_code(geom::Point p, int layer, Coord hw, Coord margin, model::NetId probe_net, bool via_probe = false) const;
64 std::int32_t fixed_via_code(geom::Point p, Coord d, Coord drill, Coord margin, model::NetId probe_net) const;
65 // Per-layer reference for fixed_via_code.
66 std::int32_t fixed_via_code_reference(geom::Point p, Coord d, Coord drill, Coord margin, model::NetId probe_net) const;
67 // Routed (rippable) copper only: 0 free, 1 conflicts (owners appended), 2 blocked when !soft.
68 int routed_state(const geom::Shape& s, int layer, model::NetId net, drc::ItemKind kind, bool soft, std::vector<int>* owners,
69 bool via_hole = false, Coord hole_r = 0) const;
70 // Maximum routed_state over `layers` for a via and its hole; layer-independent hole checks run once.
71 int routed_via_state(const geom::Shape& s, model::LayerMask layers, model::NetId net, bool soft, Coord hole_r) const;
72 // KiCad's diff-pair coupling for a new track of a pair net with a bounded gap (doc 05 §59): 0, 1 (a routed partner track; owners
73 // appended) or 2. Part of segment_state; the search also asks it for each step of a net that coupling_net() names.
74 int coupling_state(geom::Point a, geom::Point b, int layer, Coord width, model::NetId net, bool ignore_routed, std::vector<int>* owners) const;
75 bool coupling_net(model::NetId net) const {
76 return coupling_on_ && net > 0 && static_cast<std::size_t>(net) < net_tracks_.size() && re_->dp_partner(net) > 0;
77 }
78 // Rules outside the per-class cache require exact per-point checks.
79 bool needs_exact_routing() const { return re_->needs_exact_routing(); }
80 // Ids of live routed items whose box meets `box`.
81 void routed_items_in(const geom::Box& box, std::vector<int>& out) const;
82
83 const drc::CopperModel& copper() const { return cm_; }
84 // Copper text as the model holds it: one estimated rectangle per line (copper index, rectangle).
85 const std::vector<std::pair<int, geom::Shape>>& copper_texts() const { return texts_; }
86 const drc::RuleEngine& rules() const { return *re_; }
87 index::UniformGrid& grid() { return *grid_; }
88 geom::Box bounds() const { return bounds_; }
89 // Board outline polygon (largest Edge.Cuts loop) when it could be assembled; empty otherwise.
90 const std::vector<geom::Point>& outline() const { return outline_; }
91 bool inside_board(geom::Point p, Coord margin) const;
92 // Exact inside test against the outline and cut-outs: by the edges of the point's raster row, and the reference that
93 // tests every edge (the two must agree; test "[geom][route]" inside board).
96 // Solder-mask expansion the checks give untented vias (0 when the board tents them). Escape analysis sets it
97 // to 0 to ask "would a via fit here if vias were tented?"; routing never changes it.
98 Coord via_mask() const { return via_mask_; }
99 void set_via_mask(Coord m) { via_mask_ = m; }
100 // Zone fills are ignored as obstacles (they are refilled after routing); they remain connection targets.
101 void set_soft_zones(bool on) { soft_zones_ = on; }
102 // Net-less solder-mask openings drawn as footprint graphics (a solder-jumper's bridge, a MEMS microphone's pad group) no longer keep copper out; KiCad's
103 // mask checks flag what then lies in them (judgement, not a short). Off by default.
104 void set_soft_mask_graphics(bool on) { soft_mask_graphics_ = on; }
105 // Net-less copper drawn as BOARD graphics (an EasyEDA import's pours as polygons on a copper layer) is fixed copper that no track may touch, and pins inside it are
106 // enclosed; soft, it is ignored like a zone fill (KiCad then flags what touches it). Off by default.
107 void set_soft_copper_graphics(bool on) { soft_copper_graphics_ = on; } // (unused since the net inference of board copper graphics: Board::infer_graphic_nets)
108 bool soft_zones() const { return soft_zones_; }
109 // Vias keep clear of SMD pads narrower than `max_pad` (any net, net-class clearance), as if they were another
110 // net's copper; larger pads (exposed thermal pads) still take vias. set_pad_via_exempt(true) lifts this around the
111 // checks of one deliberate via in pad (the router's --via-in-pad).
112 void set_vias_off_pads(bool on, Coord max_pad) { vias_off_pads_ = on; vias_off_pads_max_ = max_pad; }
113 void set_pad_via_exempt(bool on) const { pad_via_exempt_ = on; }
114 // Reference path: test probes against every edge of a zone fill instead of the fill's edge index (the answers
115 // are the same; tests/test_design_rules.cpp).
116 void set_linear_zone_tests(bool on) { linear_zone_tests_ = on; }
117 // Rejection counters (diagnostics): outside board, copper, holes/edges/keepouts.
118 mutable long rej_outside = 0, rej_copper = 0, rej_other = 0, checks = 0;
119
120 private:
121 int copper_state(const geom::Shape& s, const drc::CopperItem& probe, int layer, bool ignore_routed, std::vector<int>* owners) const;
122 // True if `s` is closer than `req` to the copper of `it`. A zone fill answers from its edge index
123 // (geom::PolygonIndex, exact), so a probe costs the fill's nearby edges instead of all of them.
124 bool item_closer(const geom::Shape& s, const drc::CopperItem& it, Coord req) const;
125 int holes_edges_state(const geom::Shape& s, model::NetId net, int layer, bool is_via_hole, Coord hole_r, bool ignore_routed,
126 std::vector<int>* owners) const;
127 // Custom physical_hole_clearance rules, any net: new copper against existing holes, and a new via's hole
128 // against existing copper, on `layer`. 0 free, 1 conflict with routed copper (when soft), 2 blocked.
129 int physical_copper_state(const geom::Shape& s, drc::ItemKind kind, model::NetId net, int layer, bool fixed, bool routed, bool soft,
130 std::vector<int>* owners) const;
131 int physical_hole_state(const geom::Shape& hole, model::NetId net, int layer, bool fixed, bool routed, bool soft, std::vector<int>* owners) const;
132 int routed_copper_part(const geom::Shape& s, int layer, model::NetId net, drc::ItemKind kind, bool soft, std::vector<int>* owners) const;
133 int routed_via_holes_part(const geom::Shape& s, model::NetId net, Coord hc, bool soft, std::vector<int>* owners) const;
134 int routed_hole_copper_part(const geom::Shape& h, int layer, model::NetId net, Coord hc, bool soft, std::vector<int>* owners) const;
135 int routed_hole_to_hole_part(const geom::Shape& hole, bool soft, std::vector<int>* owners) const;
136 std::int32_t via_hole_code(geom::Point p, Coord drill, Coord margin, model::NetId probe_net, std::int32_t code) const;
137 void setup_coupling();
138 struct GapBounds {
139 std::optional<Coord> min, max;
140 bool set = false;
141 };
142 bool coupling_on_ = false; // some pair net has a diff_pair_gap rule with a minimum or maximum
143 std::vector<GapBounds> gap_bounds_; // by net * copper count + layer
144 std::vector<std::vector<int>> net_tracks_; // copper item ids of each net's tracks (fixed and routed; removed ones are skipped)
145
146 model::Board& b_;
147 const model::DesignRules& r_;
149 std::unique_ptr<drc::RuleEngine> re_;
150 std::unique_ptr<drc::ZoneFills> fills_; // edge index of every zone fill present at construction
151 bool linear_zone_tests_ = false;
152 std::unique_ptr<index::UniformGrid> grid_; // copper items (fixed and routed)
153 std::unique_ptr<index::UniformGrid> rgrid_; // routed copper items only
154 std::unique_ptr<index::UniformGrid> hgrid_; // holes
155 std::unique_ptr<index::UniformGrid> egrid_; // board-edge segments
156 std::vector<geom::Shape> edge_segs_;
157 geom::Box bounds_;
158 Coord reach_ = 0;
159 std::vector<geom::Point> outline_;
160 std::vector<std::vector<geom::Point>> cutouts_;
161 // Inside-board raster: 0 outside, 1 inside, 2 boundary cell (exact test needed).
162 std::vector<std::uint8_t> inside_raster_;
163 int ir_w_ = 0, ir_h_ = 0;
164 Coord ir_cell_ = 250'000;
165 // Edges of each loop (outline first, then the cut-outs) by raster row: the exact test of a point in a boundary cell
166 // needs only the edges whose y-range meets the point's row (LeeChee_1800: 572 outline edges, 28% of the run before).
167 struct LoopBands {
168 std::vector<std::uint32_t> start; // ir_h_ + 1 offsets into edges
169 std::vector<std::uint32_t> edges; // edge indices, ascending within a row
170 };
171 std::vector<LoopBands> loop_bands_;
172
173 bool soft_zones_ = false;
174 bool soft_mask_graphics_ = false;
175 bool soft_copper_graphics_ = false;
176 bool soft_item(const drc::CopperItem& it) const {
177 return soft_zones_ && it.kind == drc::ItemKind::Zone;
178 }
179 bool vias_off_pads_ = false;
180 Coord vias_off_pads_max_ = 2'000'000;
181 mutable bool pad_via_exempt_ = false;
182 bool via_keeps_off(const drc::CopperItem& it) const;
183 std::vector<std::pair<geom::Shape, const model::Zone*>> keepouts_;
184 std::vector<geom::Shape> mask_open_[2]; // solder-mask openings drawn as graphics (front, back)
185 struct Aperture {
186 std::vector<geom::Shape> shapes; // pad copper shape; the opening is this inflated by `margin`
187 Coord margin = 0;
188 model::NetId net = 0;
189 geom::Box box;
190 };
191 std::vector<Aperture> apertures_[2]; // pad solder-mask openings per side
192 std::unique_ptr<index::UniformGrid> agrid_[2];
193 std::vector<std::pair<int, geom::Shape>> texts_; // copper text boxes (copper index, rectangle)
194 Coord via_mask_ = 0;
195 Coord max_hole_local_ = 0; // largest local (pad/footprint) clearance on a hole // mask expansion of untented vias (0 when tented)
196 // Mask-opening and copper-text conflicts for new copper on `layer` (fixed obstacles only).
197 void aperture_codes(const geom::Shape& s, int layer, bool via_probe, const std::function<void(model::NetId)>& hit) const;
198};
199
200// Filled area of zone fill items (nm^2), shoelace over each item's outline.
201long double zone_fill_area(const drc::CopperModel& cm, const std::vector<int>& items);
202// With soft zones a fill of at least this area that no pad touches is its net's plane: a connection target of its
203// own (doc 05 §26). Smaller ones are slivers of a stale fill, which a refill may drop or reshape.
204inline constexpr long double kPlaneTargetArea = 1e12L; // 1 mm^2
205
206} // namespace tmk::route
The board model (design doc 02 §3): every object TraceMaker reads from a .kicad_pcb,...
void set_soft_zones(bool on)
Zone fills are ignored as obstacles (they are refilled after routing); they remain connection targets...
bool via_disallowed(geom::Point p, Coord d, model::NetId net, int l0, int l1, model::ViaType type) const
void set_soft_copper_graphics(bool on)
Net-less copper drawn as BOARD graphics (an EasyEDA import's pours as polygons on a copper layer) is ...
bool positional_rules() const
Custom disallow rules that depend on position, size or footprint (doc 05 §34), for new copper of net:...
Definition obstacles.hpp:48
int add_via(int index, int owner)
bool needs_exact_routing() const
Rules outside the per-class cache require exact per-point checks.
Definition obstacles.hpp:79
geom::Box bounds() const
Definition obstacles.hpp:88
bool coupling_net(model::NetId net) const
Definition obstacles.hpp:75
std::int32_t fixed_via_code(geom::Point p, Coord d, Coord drill, Coord margin, model::NetId probe_net) const
static constexpr std::int32_t kFree
Fixed-obstacle code of a disk (fixed copper, fixed holes, edges, keepouts, outline),...
Definition obstacles.hpp:62
int add_track(int index, int owner)
Registers committed copper (the board copy must already contain the track/via at index).
void set_vias_off_pads(bool on, Coord max_pad)
Vias keep clear of SMD pads narrower than max_pad (any net, net-class clearance), as if they were ano...
std::int32_t fixed_via_code_reference(geom::Point p, Coord d, Coord drill, Coord margin, model::NetId probe_net) const
Per-layer reference for fixed_via_code.
int routed_state(const geom::Shape &s, int layer, model::NetId net, drc::ItemKind kind, bool soft, std::vector< int > *owners, bool via_hole=false, Coord hole_r=0) const
Routed (rippable) copper only: 0 free, 1 conflicts (owners appended), 2 blocked when !...
void set_linear_zone_tests(bool on)
Reference path: test probes against every edge of a zone fill instead of the fill's edge index (the a...
const std::vector< std::pair< int, geom::Shape > > & copper_texts() const
Copper text as the model holds it: one estimated rectangle per line (copper index,...
Definition obstacles.hpp:85
const std::vector< geom::Point > & outline() const
Board outline polygon (largest Edge.Cuts loop) when it could be assembled; empty otherwise.
Definition obstacles.hpp:90
static constexpr std::int32_t kBlocked
Definition obstacles.hpp:62
Obstacles(model::Board &board, const model::DesignRules &rules)
board is the router's working copy; items added later must also be appended to it.
void set_pad_via_exempt(bool on) const
bool track_disallowed(const geom::Shape &s, int layer, Coord width, model::NetId net) const
bool disk_ok(geom::Point p, int layer, Coord hw, model::NetId net, Coord margin) const
Definition obstacles.hpp:36
int coupling_state(geom::Point a, geom::Point b, int layer, Coord width, model::NetId net, bool ignore_routed, std::vector< int > *owners) const
KiCad's diff-pair coupling for a new track of a pair net with a bounded gap (doc 05 §59): 0,...
bool via_ok(geom::Point p, Coord d, Coord drill, model::NetId net, Coord margin) const
Definition obstacles.hpp:42
void routed_items_in(const geom::Box &box, std::vector< int > &out) const
Ids of live routed items whose box meets box.
int disk_state(geom::Point p, int layer, Coord hw, model::NetId net, Coord margin, bool ignore_routed, std::vector< int > *owners=nullptr) const
Legality of a probe shape.
int segment_state(geom::Point a, geom::Point b, int layer, Coord width, model::NetId net, bool ignore_routed, std::vector< int > *owners=nullptr) const
Coord via_mask() const
Solder-mask expansion the checks give untented vias (0 when the board tents them).
Definition obstacles.hpp:98
bool soft_zones() const
void set_soft_mask_graphics(bool on)
Net-less solder-mask openings drawn as footprint graphics (a solder-jumper's bridge,...
bool segment_ok(geom::Point a, geom::Point b, int layer, Coord width, model::NetId net) const
Definition obstacles.hpp:39
index::UniformGrid & grid()
Definition obstacles.hpp:87
void set_via_mask(Coord m)
Definition obstacles.hpp:99
const drc::RuleEngine & rules() const
Definition obstacles.hpp:86
int via_state(geom::Point p, Coord d, Coord drill, model::NetId net, Coord margin, bool ignore_routed, std::vector< int > *owners=nullptr) const
int routed_via_state(const geom::Shape &s, model::LayerMask layers, model::NetId net, bool soft, Coord hole_r) const
Maximum routed_state over layers for a via and its hole; layer-independent hole checks run once.
void remove_item(int item)
Rips up a routed item (by copper item index).
bool inside_board(geom::Point p, Coord margin) const
bool inside_exact_linear(geom::Point p) const
std::int32_t fixed_code(geom::Point p, int layer, Coord hw, Coord margin, model::NetId probe_net, bool via_probe=false) const
long rej_outside
Rejection counters (diagnostics): outside board, copper, holes/edges/keepouts.
int via_state_span(geom::Point p, Coord d, Coord drill, model::NetId net, Coord margin, bool ignore_routed, std::vector< int > *owners, int l0, int l1, model::ViaType type=model::ViaType::Blind) const
A via on copper layers [l0, l1] only (blind or buried via, or a micro via with type); via_state cover...
const drc::CopperModel & copper() const
Definition obstacles.hpp:83
bool inside_exact(geom::Point p) const
Exact inside test against the outline and cut-outs: by the edges of the point's raster row,...
Copper connectivity with KiCad's semantics, shared by the DRC (unconnected/dangling checks) and the r...
Copper items with exact shapes, built from the board model, for DRC and routing obstacles.
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
constexpr long double kPlaneTargetArea
With soft zones a fill of at least this area that no pad touches is its net's plane: a connection tar...
long double zone_fill_area(const drc::CopperModel &cm, const std::vector< int > &items)
Filled area of zone fill items (nm^2), shoelace over each item's outline.
std::int64_t Coord
Engine coordinate: signed 64-bit nanometres.
Definition units.hpp:12
Resolves KiCad constraints for pairs of copper items (design doc 03 §6): net-class clearances,...
Design rules as KiCad defines them (design doc 08 §3–4): board minimums and net classes from the ....
Uniform spatial hash grid over a bounding box (design doc 03 §2): O(1) insert, box queries return eac...