1 #include "pns_horizon_iface.hpp"
2 #include "board/board.hpp"
3 #include "board/board_layers.hpp"
4 #include "canvas/canvas_gl.hpp"
5 #include "clipper/clipper.hpp"
6 #include "geometry/shape_simple.h"
7 #include "geometry/shape_rect.h"
8 #include "router/pns_debug_decorator.h"
9 #include "router/pns_solid.h"
10 #include "router/pns_topology.h"
11 #include "router/pns_via.h"
12 #include "util/geom_util.hpp"
13 #include "logger/logger.hpp"
14 #include "pool/ipool.hpp"
15
16 namespace PNS {
17
layer_to_router(int l)18 int PNS_HORIZON_IFACE::layer_to_router(int l)
19 {
20 switch (l) {
21 case horizon::BoardLayers::TOP_COPPER:
22 return F_Cu;
23 case horizon::BoardLayers::BOTTOM_COPPER:
24 return B_Cu;
25 case horizon::BoardLayers::IN1_COPPER:
26 return In1_Cu;
27 case horizon::BoardLayers::IN2_COPPER:
28 return In2_Cu;
29 case horizon::BoardLayers::IN3_COPPER:
30 return In3_Cu;
31 case horizon::BoardLayers::IN4_COPPER:
32 return In4_Cu;
33 default:
34 return UNDEFINED_LAYER;
35 }
36
37 return F_Cu;
38 }
39
layer_from_router(int l)40 int PNS_HORIZON_IFACE::layer_from_router(int l)
41 {
42 int lo = 0;
43 switch (l) {
44 case F_Cu:
45 lo = horizon::BoardLayers::TOP_COPPER;
46 break;
47 case B_Cu:
48 lo = horizon::BoardLayers::BOTTOM_COPPER;
49 break;
50 case In1_Cu:
51 lo = horizon::BoardLayers::IN1_COPPER;
52 break;
53 case In2_Cu:
54 lo = horizon::BoardLayers::IN2_COPPER;
55 break;
56 case In3_Cu:
57 lo = horizon::BoardLayers::IN3_COPPER;
58 break;
59 case In4_Cu:
60 lo = horizon::BoardLayers::IN4_COPPER;
61 break;
62 case UNDEFINED_LAYER:
63 lo = 10000;
64 break;
65 default:
66 assert(false);
67 }
68 assert(l == layer_to_router(lo));
69 return lo;
70 }
71
72 class PNS_HORIZON_RULE_RESOLVER : public PNS::RULE_RESOLVER {
73 public:
74 PNS_HORIZON_RULE_RESOLVER(const horizon::Board *aBoard, const horizon::BoardRules *aRules, PNS::ROUTER *aRouter);
75 virtual ~PNS_HORIZON_RULE_RESOLVER();
76
77 int Clearance(const PNS::ITEM *aA, const PNS::ITEM *aB) const override;
78 int Clearance(int aNetCode) const override;
79 int DpCoupledNet(int aNet) override;
80 int DpNetPolarity(int aNet) override;
81 bool DpNetPair(PNS::ITEM *aItem, int &aNetP, int &aNetN) override;
82 std::string NetName(int aNet) override;
83
84 private:
85 PNS::ROUTER *m_router;
86 const horizon::BoardRules *m_rules;
87 PNS_HORIZON_IFACE *m_iface = nullptr;
88 std::vector<const horizon::RuleClearanceCopperKeepout *> m_rules_keepout;
89 };
90
PNS_HORIZON_RULE_RESOLVER(const horizon::Board * aBoard,const horizon::BoardRules * rules,PNS::ROUTER * aRouter)91 PNS_HORIZON_RULE_RESOLVER::PNS_HORIZON_RULE_RESOLVER(const horizon::Board *aBoard, const horizon::BoardRules *rules,
92 PNS::ROUTER *aRouter)
93 : m_router(aRouter), m_rules(rules)
94 {
95 PNS::NODE *world = m_router->GetWorld();
96
97 PNS::TOPOLOGY topo(world);
98
99 m_iface = static_cast<PNS_HORIZON_IFACE *>(m_router->GetInterface());
100 m_rules_keepout =
101 m_rules->get_rules_sorted<horizon::RuleClearanceCopperKeepout>(horizon::RuleID::CLEARANCE_COPPER_KEEPOUT);
102 }
103
~PNS_HORIZON_RULE_RESOLVER()104 PNS_HORIZON_RULE_RESOLVER::~PNS_HORIZON_RULE_RESOLVER()
105 {
106 }
107
patch_type_from_kind(PNS::ITEM::PnsKind kind)108 static horizon::PatchType patch_type_from_kind(PNS::ITEM::PnsKind kind)
109 {
110 switch (kind) {
111 case PNS::ITEM::VIA_T:
112 return horizon::PatchType::VIA;
113 case PNS::ITEM::SOLID_T:
114 return horizon::PatchType::PAD;
115 case PNS::ITEM::LINE_T:
116 return horizon::PatchType::TRACK;
117 case PNS::ITEM::SEGMENT_T:
118 return horizon::PatchType::TRACK;
119 default:;
120 }
121 assert(false);
122 return horizon::PatchType::OTHER;
123 }
124
125 static const PNS_HORIZON_PARENT_ITEM parent_dummy_outline;
126
Clearance(const PNS::ITEM * aA,const PNS::ITEM * aB) const127 int PNS_HORIZON_RULE_RESOLVER::Clearance(const PNS::ITEM *aA, const PNS::ITEM *aB) const
128 {
129 auto net_a = m_iface->get_net_for_code(aA->Net());
130 auto net_b = m_iface->get_net_for_code(aB->Net());
131
132 auto pt_a = patch_type_from_kind(aA->Kind());
133 auto pt_b = patch_type_from_kind(aB->Kind());
134
135 auto parent_a = aA->Parent();
136 auto parent_b = aB->Parent();
137
138 auto layers_a = aA->Layers();
139 auto layers_b = aB->Layers();
140
141 if (parent_a && parent_a->pad && parent_a->pad->padstack.type == horizon::Padstack::Type::THROUGH)
142 pt_a = horizon::PatchType::PAD_TH;
143 else if (parent_a && parent_a->hole && parent_a->hole->padstack.type == horizon::Padstack::Type::HOLE)
144 pt_a = horizon::PatchType::HOLE_PTH;
145 else if (parent_a && parent_a->hole && parent_a->hole->padstack.type == horizon::Padstack::Type::MECHANICAL)
146 pt_a = horizon::PatchType::HOLE_NPTH;
147
148 if (parent_b && parent_b->pad && parent_b->pad->padstack.type == horizon::Padstack::Type::THROUGH)
149 pt_b = horizon::PatchType::PAD_TH;
150 else if (parent_b && parent_b->hole && parent_b->hole->padstack.type == horizon::Padstack::Type::HOLE)
151 pt_b = horizon::PatchType::HOLE_PTH;
152 else if (parent_b && parent_b->hole && parent_b->hole->padstack.type == horizon::Padstack::Type::MECHANICAL)
153 pt_b = horizon::PatchType::HOLE_NPTH;
154
155 // std::cout << "pt " << static_cast<int>(pt_a) << " " << static_cast<int>(pt_b) << std::endl;
156
157 if (parent_a == &parent_dummy_outline || parent_b == &parent_dummy_outline) { // one is the board edge
158 auto a_is_edge = parent_a == &parent_dummy_outline;
159
160 // use layers of non-edge thing
161 auto layers = a_is_edge ? layers_b : layers_a;
162 auto pt = a_is_edge ? pt_b : pt_a;
163 auto net = net_a ? net_a : net_b; // only one has net
164
165 // fixme: handle multiple layers for non-edge thing
166 auto layer = layers.Start();
167
168 auto clearance = m_rules->get_clearance_copper_other(net, PNS_HORIZON_IFACE::layer_from_router(layer));
169 int64_t routing_offset = clearance->routing_offset;
170 if (m_iface->get_override_routing_offset() >= 0)
171 routing_offset = m_iface->get_override_routing_offset();
172 return clearance->get_clearance(pt, horizon::PatchType::BOARD_EDGE) + routing_offset;
173 }
174
175 if ((parent_a && parent_a->pad && parent_a->pad->padstack.type == horizon::Padstack::Type::MECHANICAL)
176 || (parent_b && parent_b->pad && parent_b->pad->padstack.type == horizon::Padstack::Type::MECHANICAL)
177 || (parent_a && parent_a->hole && parent_a->hole->padstack.type == horizon::Padstack::Type::MECHANICAL)
178 || (parent_b && parent_b->hole && parent_b->hole->padstack.type == horizon::Padstack::Type::MECHANICAL)) {
179 // std::cout << "npth" << std::endl;
180 // any is mechanical (NPTH)
181 auto net = net_a ? net_a : net_b; // only one has net
182 auto a_is_npth =
183 (parent_a && parent_a->pad && parent_a->pad->padstack.type == horizon::Padstack::Type::MECHANICAL)
184 || (parent_a && parent_a->hole && parent_a->hole->padstack.type == horizon::Padstack::Type::MECHANICAL);
185
186 // use layers of non-npth ting
187 auto layers = a_is_npth ? layers_b : layers_a;
188 auto pt = a_is_npth ? pt_b : pt_a;
189
190 // fixme: handle multiple layers for non-npth thing
191 auto layer = layers.Start();
192
193 auto clearance = m_rules->get_clearance_copper_other(net, PNS_HORIZON_IFACE::layer_from_router(layer));
194 int64_t routing_offset = clearance->routing_offset;
195 if (m_iface->get_override_routing_offset() >= 0)
196 routing_offset = m_iface->get_override_routing_offset();
197 return clearance->get_clearance(pt, horizon::PatchType::HOLE_NPTH) + routing_offset;
198 }
199
200 if ((parent_a && parent_a->keepout) || (parent_b && parent_b->keepout)) { // one is keepout
201 bool a_is_keepout = parent_a && parent_a->keepout;
202 auto net = net_a ? net_a : net_b; // only one has net
203 horizon::KeepoutContour keepout_contour;
204 keepout_contour.keepout = a_is_keepout ? parent_a->keepout : parent_b->keepout;
205 keepout_contour.pkg = a_is_keepout ? parent_a->package : parent_b->package;
206 for (const auto &rule : m_rules_keepout) {
207 if (rule->enabled && rule->match.match(net) && rule->match_keepout.match(&keepout_contour)) {
208 auto cl = rule->get_clearance(a_is_keepout ? pt_b : pt_a);
209 int64_t routing_offset = rule->routing_offset;
210 if (m_iface->get_override_routing_offset() >= 0)
211 routing_offset = m_iface->get_override_routing_offset();
212 return routing_offset + cl;
213 }
214 }
215 return 0;
216 }
217
218 int layer = UNDEFINED_LAYER;
219 if (!layers_a.IsMultilayer() && !layers_b.IsMultilayer()) // all on single layer
220 layer = layers_b.Start();
221 else if (!layers_a.IsMultilayer() && layers_b.IsMultilayer()) // b is multi
222 layer = layers_a.Start();
223 else if (layers_a.IsMultilayer() && !layers_b.IsMultilayer()) // a is muli
224 layer = layers_b.Start();
225 else if (layers_a.IsMultilayer() && layers_b.IsMultilayer()) // both are multi
226 layer = layers_b.Start(); // fixme, good enough for now
227
228 assert(layer != UNDEFINED_LAYER);
229
230 auto clearance = m_rules->get_clearance_copper(net_a, net_b, PNS_HORIZON_IFACE::layer_from_router(layer));
231
232 int64_t routing_offset = clearance->routing_offset;
233 if (m_iface->get_override_routing_offset() >= 0)
234 routing_offset = m_iface->get_override_routing_offset();
235
236 return clearance->get_clearance(pt_a, pt_b) + routing_offset;
237 }
238
Clearance(int aNetCode) const239 int PNS_HORIZON_RULE_RESOLVER::Clearance(int aNetCode) const
240 {
241 // only used for display purposes, can return dummy value
242 return .1e6;
243 }
244
DpCoupledNet(int aNet)245 int PNS_HORIZON_RULE_RESOLVER::DpCoupledNet(int aNet)
246 {
247 auto net = m_iface->get_net_for_code(aNet);
248 if (net->diffpair) {
249 return m_iface->get_net_code(net->diffpair->uuid);
250 }
251 return -1;
252 }
253
DpNetPolarity(int aNet)254 int PNS_HORIZON_RULE_RESOLVER::DpNetPolarity(int aNet)
255 {
256 if (m_iface->get_net_for_code(aNet)->diffpair_master)
257 return 1;
258 else
259 return -1;
260 }
261
DpNetPair(PNS::ITEM * aItem,int & aNetP,int & aNetN)262 bool PNS_HORIZON_RULE_RESOLVER::DpNetPair(PNS::ITEM *aItem, int &aNetP, int &aNetN)
263 {
264 if (!aItem)
265 return false;
266 auto net = m_iface->get_net_for_code(aItem->Net());
267 if (net->diffpair) {
268 if (net->diffpair_master) {
269 aNetP = m_iface->get_net_code(net->uuid);
270 aNetN = m_iface->get_net_code(net->diffpair->uuid);
271 }
272 else {
273 aNetN = m_iface->get_net_code(net->uuid);
274 aNetP = m_iface->get_net_code(net->diffpair->uuid);
275 }
276 return true;
277 }
278 return false;
279 }
280
NetName(int aNet)281 std::string PNS_HORIZON_RULE_RESOLVER::NetName(int aNet)
282 {
283 auto net = m_iface->get_net_for_code(aNet);
284 if (net)
285 return net->name;
286 else
287 return "";
288 }
289
290 class PNS_HORIZON_DEBUG_DECORATOR : public PNS::DEBUG_DECORATOR {
291 public:
PNS_HORIZON_DEBUG_DECORATOR(horizon::CanvasGL * canvas)292 PNS_HORIZON_DEBUG_DECORATOR(horizon::CanvasGL *canvas) : PNS::DEBUG_DECORATOR(), m_canvas(canvas)
293 {
294 }
295
~PNS_HORIZON_DEBUG_DECORATOR()296 ~PNS_HORIZON_DEBUG_DECORATOR()
297 {
298 try {
299 Clear();
300 }
301 catch (const std::exception &e) {
302 horizon::Logger::log_critical("exception thrown in ~PNS_HORIZON_DEBUG_DECORATOR",
303 horizon::Logger::Domain::TOOL, e.what());
304 }
305 }
306
AddPoint(VECTOR2I aP,int aColor)307 void AddPoint(VECTOR2I aP, int aColor) override
308 {
309 SHAPE_LINE_CHAIN l;
310
311 l.Append(aP - VECTOR2I(-50000, -50000));
312 l.Append(aP + VECTOR2I(-50000, -50000));
313
314 AddLine(l, aColor, 10000);
315
316 l.Clear();
317 l.Append(aP - VECTOR2I(50000, -50000));
318 l.Append(aP + VECTOR2I(50000, -50000));
319
320 AddLine(l, aColor, 10000);
321 }
322
AddBox(BOX2I aB,int aColor)323 void AddBox(BOX2I aB, int aColor) override
324 {
325 SHAPE_LINE_CHAIN l;
326
327 VECTOR2I o = aB.GetOrigin();
328 VECTOR2I s = aB.GetSize();
329
330 l.Append(o);
331 l.Append(o.x + s.x, o.y);
332 l.Append(o.x + s.x, o.y + s.y);
333 l.Append(o.x, o.y + s.y);
334 l.Append(o);
335
336 AddLine(l, aColor, 10000);
337 }
338
AddSegment(SEG aS,int aColor)339 void AddSegment(SEG aS, int aColor) override
340 {
341 SHAPE_LINE_CHAIN l;
342
343 l.Append(aS.A);
344 l.Append(aS.B);
345
346 AddLine(l, aColor, 10000);
347 }
348
AddDirections(VECTOR2D aP,int aMask,int aColor)349 void AddDirections(VECTOR2D aP, int aMask, int aColor) override
350 {
351 BOX2I b(aP - VECTOR2I(10000, 10000), VECTOR2I(20000, 20000));
352
353 AddBox(b, aColor);
354 for (int i = 0; i < 8; i++) {
355 if ((1 << i) & aMask) {
356 VECTOR2I v = DIRECTION_45((DIRECTION_45::Directions)i).ToVector() * 100000;
357 AddSegment(SEG(aP, aP + v), aColor);
358 }
359 }
360 }
361
AddLine(const SHAPE_LINE_CHAIN & aLine,int aType,int aWidth)362 void AddLine(const SHAPE_LINE_CHAIN &aLine, int aType, int aWidth) override
363 {
364 auto npts = aLine.PointCount();
365 std::deque<horizon::Coordi> pts;
366 for (int i = 0; i < npts; i++) {
367 auto pt = aLine.CPoint(i);
368 pts.emplace_back(pt.x, pt.y);
369 }
370 if (aLine.IsClosed()) {
371 auto pt = aLine.CPoint(0);
372 pts.emplace_back(pt.x, pt.y);
373 }
374 lines.insert(m_canvas->add_line(pts, aWidth, horizon::ColorP::AIRWIRE_ROUTER, 10000));
375 }
376
Clear()377 void Clear() override
378 {
379 // std::cout << "debug clear" << std::endl;
380 for (auto &li : lines) {
381 m_canvas->remove_obj(li);
382 }
383 lines.clear();
384 }
385
386 private:
387 horizon::CanvasGL *m_canvas;
388 std::set<horizon::ObjectRef> lines;
389 };
390
PNS_HORIZON_IFACE()391 PNS_HORIZON_IFACE::PNS_HORIZON_IFACE()
392 {
393 }
394
SetBoard(horizon::Board * brd)395 void PNS_HORIZON_IFACE::SetBoard(horizon::Board *brd)
396 {
397 board = brd;
398 }
399
SetCanvas(horizon::CanvasGL * ca)400 void PNS_HORIZON_IFACE::SetCanvas(horizon::CanvasGL *ca)
401 {
402 canvas = ca;
403 }
404
SetRules(const horizon::BoardRules * ru)405 void PNS_HORIZON_IFACE::SetRules(const horizon::BoardRules *ru)
406 {
407 rules = ru;
408 }
409
SetPool(horizon::IPool * p)410 void PNS_HORIZON_IFACE::SetPool(horizon::IPool *p)
411 {
412 pool = p;
413 }
414
get_net_code(const horizon::UUID & uu)415 int PNS_HORIZON_IFACE::get_net_code(const horizon::UUID &uu)
416 {
417 if (net_code_map.count(uu)) {
418 return net_code_map.at(uu);
419 }
420 else {
421 net_code_map_r.emplace_back(&board->block->nets.at(uu));
422 auto nc = net_code_map_r.size() - 1;
423 net_code_map.emplace(uu, nc);
424 return nc;
425 }
426 }
427
get_net_for_code(int code)428 horizon::Net *PNS_HORIZON_IFACE::get_net_for_code(int code)
429 {
430 if (code == PNS::ITEM::UnusedNet)
431 return nullptr;
432 if (code < 0)
433 return nullptr;
434 if (code >= (int)net_code_map_r.size())
435 return nullptr;
436 return net_code_map_r.at(code);
437 }
438
get_parent(const horizon::Track * track)439 const PNS_HORIZON_PARENT_ITEM *PNS_HORIZON_IFACE::get_parent(const horizon::Track *track)
440 {
441 return get_or_create_parent(PNS_HORIZON_PARENT_ITEM(track));
442 }
443
get_parent(const horizon::BoardHole * hole)444 const PNS_HORIZON_PARENT_ITEM *PNS_HORIZON_IFACE::get_parent(const horizon::BoardHole *hole)
445 {
446 return get_or_create_parent(PNS_HORIZON_PARENT_ITEM(hole));
447 }
448
get_parent(const horizon::Via * via)449 const PNS_HORIZON_PARENT_ITEM *PNS_HORIZON_IFACE::get_parent(const horizon::Via *via)
450 {
451 return get_or_create_parent(PNS_HORIZON_PARENT_ITEM(via));
452 }
453
get_parent(const horizon::BoardPackage * pkg,const horizon::Pad * pad)454 const PNS_HORIZON_PARENT_ITEM *PNS_HORIZON_IFACE::get_parent(const horizon::BoardPackage *pkg, const horizon::Pad *pad)
455 {
456 return get_or_create_parent(PNS_HORIZON_PARENT_ITEM(pkg, pad));
457 }
458
get_parent(const horizon::Keepout * k,const horizon::BoardPackage * pkg)459 const PNS_HORIZON_PARENT_ITEM *PNS_HORIZON_IFACE::get_parent(const horizon::Keepout *k,
460 const horizon::BoardPackage *pkg)
461 {
462 return get_or_create_parent(PNS_HORIZON_PARENT_ITEM(k, pkg));
463 }
464
get_or_create_parent(const PNS_HORIZON_PARENT_ITEM & it)465 const PNS_HORIZON_PARENT_ITEM *PNS_HORIZON_IFACE::get_or_create_parent(const PNS_HORIZON_PARENT_ITEM &it)
466 {
467 auto r = std::find(parents.begin(), parents.end(), it);
468 if (r != parents.end())
469 return &(*r);
470 parents.emplace_back(it);
471 return &parents.back();
472 }
473
474
syncTrack(const horizon::Track * track)475 std::unique_ptr<PNS::SEGMENT> PNS_HORIZON_IFACE::syncTrack(const horizon::Track *track)
476 {
477 auto from = track->from.get_position();
478 auto to = track->to.get_position();
479 int net = PNS::ITEM::UnusedNet;
480 if (track->net)
481 net = get_net_code(track->net->uuid);
482 std::unique_ptr<PNS::SEGMENT> segment(new PNS::SEGMENT(SEG(from.x, from.y, to.x, to.y), net));
483
484 segment->SetWidth(track->width);
485 segment->SetLayer(layer_to_router(track->layer));
486 segment->SetParent(get_parent(track));
487
488 if (track->locked)
489 segment->Mark(PNS::MK_LOCKED);
490
491 return segment;
492 }
493
syncOutline(const horizon::Polygon * ipoly,PNS::NODE * aWorld)494 void PNS_HORIZON_IFACE::syncOutline(const horizon::Polygon *ipoly, PNS::NODE *aWorld)
495 {
496 auto poly = ipoly->remove_arcs();
497 for (size_t i = 0; i < poly.vertices.size(); i++) {
498 auto from = poly.vertices[i].position;
499 auto to = poly.vertices[(i + 1) % poly.vertices.size()].position;
500
501 std::set<int> layers = {horizon::BoardLayers::TOP_COPPER, horizon::BoardLayers::BOTTOM_COPPER};
502 for (unsigned int j = 0; j < board->get_n_inner_layers(); j++) {
503 layers.insert(-j - 1);
504 }
505 for (const auto layer : layers) {
506 std::unique_ptr<PNS::SEGMENT> segment(
507 new PNS::SEGMENT(SEG(from.x, from.y, to.x, to.y), PNS::ITEM::UnusedNet));
508 segment->SetWidth(10); // very small
509 segment->SetLayers(layer_to_router(layer));
510 segment->SetParent(&parent_dummy_outline);
511 segment->Mark(PNS::MK_LOCKED);
512 aWorld->Add(std::move(segment));
513 }
514 }
515 }
516
syncKeepout(const horizon::KeepoutContour * keepout_contour,PNS::NODE * aWorld)517 void PNS_HORIZON_IFACE::syncKeepout(const horizon::KeepoutContour *keepout_contour, PNS::NODE *aWorld)
518 {
519 auto keepout = keepout_contour->keepout;
520 if (!horizon::BoardLayers::is_copper(keepout->polygon->layer))
521 return;
522 if (!(keepout->patch_types_cu.count(horizon::PatchType::TRACK)))
523 return;
524
525 std::set<int> layers;
526 if (keepout->all_cu_layers) {
527 for (unsigned int j = 0; j < board->get_n_inner_layers(); j++) {
528 layers.insert(-j - 1);
529 }
530 layers.insert(horizon::BoardLayers::TOP_COPPER);
531 layers.insert(horizon::BoardLayers::BOTTOM_COPPER);
532 }
533 else {
534 layers.insert(keepout->polygon->layer);
535 }
536 const auto &contour = keepout_contour->contour;
537 for (auto layer : layers) {
538 for (size_t i = 0; i < contour.size(); i++) {
539 auto from = contour[i];
540 auto to = contour[(i + 1) % contour.size()];
541
542 std::unique_ptr<PNS::SEGMENT> segment(
543 new PNS::SEGMENT(SEG(from.X, from.Y, to.X, to.Y), PNS::ITEM::UnusedNet));
544 segment->SetWidth(10); // very small
545 segment->SetLayers(layer_to_router(layer));
546 segment->SetParent(get_parent(keepout_contour->keepout, keepout_contour->pkg));
547 segment->Mark(PNS::MK_LOCKED);
548 aWorld->Add(std::move(segment));
549 }
550 }
551 }
552
syncPad(const horizon::BoardPackage * pkg,const horizon::Pad * pad)553 std::unique_ptr<PNS::SOLID> PNS_HORIZON_IFACE::syncPad(const horizon::BoardPackage *pkg, const horizon::Pad *pad)
554 {
555 horizon::Placement tr(pkg->placement);
556 if (pkg->flip) {
557 tr.invert_angle();
558 }
559 tr.accumulate(pad->placement);
560 auto solid = syncPadstack(&pad->padstack, tr);
561 if (!solid)
562 return nullptr;
563
564 if (pad->net)
565 solid->SetNet(get_net_code(pad->net->uuid));
566 solid->SetParent(get_parent(pkg, pad));
567
568 return solid;
569 }
570
syncHole(const horizon::BoardHole * hole)571 std::unique_ptr<PNS::SOLID> PNS_HORIZON_IFACE::syncHole(const horizon::BoardHole *hole)
572 {
573 auto solid = syncPadstack(&hole->padstack, hole->placement);
574 if (!solid)
575 return nullptr;
576
577 if (hole->net)
578 solid->SetNet(get_net_code(hole->net->uuid));
579 solid->SetParent(get_parent(hole));
580
581 return solid;
582 }
583
shape_is_at_origin(const horizon::Shape & sh)584 bool shape_is_at_origin(const horizon::Shape &sh)
585 {
586 return sh.placement.shift == horizon::Coordi();
587 }
588
angle_is_rect(int angle)589 bool angle_is_rect(int angle)
590 {
591 return (angle % 16384) == 0; // multiple of 90°
592 }
593
angle_needs_swap(int angle)594 bool angle_needs_swap(int angle)
595 {
596 return angle == 16384 || angle == 49152; // 90° or 270°
597 }
598
syncPadstack(const horizon::Padstack * padstack,const horizon::Placement & tr)599 std::unique_ptr<PNS::SOLID> PNS_HORIZON_IFACE::syncPadstack(const horizon::Padstack *padstack,
600 const horizon::Placement &tr)
601 {
602 int layer_min = 10000;
603 int layer_max = -10000;
604
605 ClipperLib::Clipper clipper;
606
607 if (padstack->type != horizon::Padstack::Type::MECHANICAL) { // normal pad
608
609 auto n_shapes_cu = std::count_if(padstack->shapes.begin(), padstack->shapes.end(), [](const auto &it) {
610 return horizon::BoardLayers::is_copper(it.second.layer);
611 });
612
613 auto n_polys_cu = std::count_if(padstack->polygons.begin(), padstack->polygons.end(), [](const auto &it) {
614 return horizon::BoardLayers::is_copper(it.second.layer);
615 });
616
617 if (n_shapes_cu == 1 && n_polys_cu == 0) { // single SMD pad
618 const auto &shape = std::find_if(padstack->shapes.begin(), padstack->shapes.end(), [](const auto &it) {
619 return horizon::BoardLayers::is_copper(it.second.layer);
620 })->second;
621 if (shape_is_at_origin(shape)) {
622 const int angle = tr.get_angle() + shape.placement.get_angle();
623 if (shape.form == horizon::Shape::Form::CIRCLE) {
624 std::unique_ptr<PNS::SOLID> solid(new PNS::SOLID);
625
626 solid->SetLayers(LAYER_RANGE(layer_to_router(shape.layer)));
627
628 solid->SetOffset(VECTOR2I(0, 0));
629 solid->SetPos(VECTOR2I(tr.shift.x, tr.shift.y));
630
631 auto *sshape = new SHAPE_CIRCLE(VECTOR2I(tr.shift.x, tr.shift.y), shape.params.at(0) / 2);
632
633 solid->SetShape(sshape);
634
635 return solid;
636 }
637 else if (shape.form == horizon::Shape::Form::RECTANGLE && angle_is_rect(angle)) {
638 std::unique_ptr<PNS::SOLID> solid(new PNS::SOLID);
639
640 solid->SetLayers(LAYER_RANGE(layer_to_router(shape.layer)));
641
642 solid->SetOffset(VECTOR2I(0, 0));
643 const auto c = VECTOR2I(tr.shift.x, tr.shift.y);
644 solid->SetPos(c);
645 auto sz = VECTOR2I(shape.params.at(0), shape.params.at(1));
646 if (angle_needs_swap(angle))
647 std::swap(sz.x, sz.y);
648
649 auto *sshape = new SHAPE_RECT(c - sz / 2, sz.x, sz.y);
650
651 solid->SetShape(sshape);
652
653 return solid;
654 }
655 }
656 }
657
658
659 auto add_polygon = [&clipper, &tr](const auto &poly) {
660 ClipperLib::Path path;
661 if (poly.vertices.size() == 0) {
662 throw std::runtime_error("polygon without vertices in padstack at"
663 + horizon::coord_to_string(tr.shift));
664 }
665 for (auto &v : poly.vertices) {
666 auto p = tr.transform(v.position);
667 path.emplace_back(p.x, p.y);
668 }
669 if (ClipperLib::Orientation(path)) {
670 std::reverse(path.begin(), path.end());
671 }
672 clipper.AddPath(path, ClipperLib::ptSubject, true);
673 };
674 for (auto &it : padstack->shapes) {
675 if (horizon::BoardLayers::is_copper(it.second.layer)) { // on copper layer
676 layer_min = std::min(layer_min, it.second.layer);
677 layer_max = std::max(layer_max, it.second.layer);
678 add_polygon(it.second.to_polygon().remove_arcs());
679 }
680 }
681 for (auto &it : padstack->polygons) {
682 if (horizon::BoardLayers::is_copper(it.second.layer)) { // on copper layer
683 layer_min = std::min(layer_min, it.second.layer);
684 layer_max = std::max(layer_max, it.second.layer);
685 add_polygon(it.second.remove_arcs());
686 }
687 }
688 }
689 else { // npth pad
690 layer_min = horizon::BoardLayers::BOTTOM_COPPER;
691 layer_max = horizon::BoardLayers::TOP_COPPER;
692 for (auto &it : padstack->holes) {
693 auto poly = it.second.to_polygon().remove_arcs();
694 ClipperLib::Path path;
695 for (auto &v : poly.vertices) {
696 auto p = tr.transform(v.position);
697 path.emplace_back(p.x, p.y);
698 }
699 if (ClipperLib::Orientation(path)) {
700 std::reverse(path.begin(), path.end());
701 }
702 clipper.AddPath(path, ClipperLib::ptSubject, true);
703 }
704 }
705
706 ClipperLib::Paths poly_union;
707 clipper.Execute(ClipperLib::ctUnion, poly_union, ClipperLib::pftNonZero);
708 if (poly_union.size() == 0) // nothing we care about
709 return nullptr;
710 if (poly_union.size() != 1) {
711 throw std::runtime_error("invalid pad polygons: " + std::to_string(poly_union.size()));
712 }
713
714 std::unique_ptr<PNS::SOLID> solid(new PNS::SOLID);
715
716 solid->SetLayers(LAYER_RANGE(layer_to_router(layer_max), layer_to_router(layer_min)));
717
718 solid->SetOffset(VECTOR2I(0, 0));
719 solid->SetPos(VECTOR2I(tr.shift.x, tr.shift.y));
720
721 SHAPE_SIMPLE *shape = new SHAPE_SIMPLE();
722
723 for (auto &pt : poly_union.front()) {
724 shape->Append(pt.X, pt.Y);
725 }
726 solid->SetShape(shape);
727
728 return solid;
729 }
730
syncVia(const horizon::Via * via)731 std::unique_ptr<PNS::VIA> PNS_HORIZON_IFACE::syncVia(const horizon::Via *via)
732 {
733 auto pos = via->junction->position;
734 int net = PNS::ITEM::UnusedNet;
735 if (via->junction->net)
736 net = get_net_code(via->junction->net->uuid);
737 std::unique_ptr<PNS::VIA> pvia(new PNS::VIA(VECTOR2I(pos.x, pos.y),
738 LAYER_RANGE(layer_to_router(horizon::BoardLayers::TOP_COPPER),
739 layer_to_router(horizon::BoardLayers::BOTTOM_COPPER)),
740 via->parameter_set.at(horizon::ParameterID::VIA_DIAMETER),
741 via->parameter_set.at(horizon::ParameterID::HOLE_DIAMETER), net,
742 VIA_THROUGH));
743
744 // via->SetParent( aVia );
745 pvia->SetParent(get_parent(via));
746
747 if (via->locked)
748 pvia->Mark(PNS::MK_LOCKED);
749
750 return pvia;
751 }
752
SyncWorld(PNS::NODE * aWorld)753 void PNS_HORIZON_IFACE::SyncWorld(PNS::NODE *aWorld)
754 {
755 // std::cout << "!!!sync world" << std::endl;
756 if (!board) {
757 wxLogTrace("PNS", "No board attached, aborting sync.");
758 return;
759 }
760 parents.clear();
761 junctions_maybe_erased.clear();
762
763 for (const auto &it : board->tracks) {
764 auto segment = syncTrack(&it.second);
765 if (segment) {
766 aWorld->Add(std::move(segment));
767 }
768 }
769
770 for (const auto &it : board->vias) {
771 auto via = syncVia(&it.second);
772 if (via) {
773 aWorld->Add(std::move(via));
774 }
775 }
776
777 for (const auto &it : board->holes) {
778 auto hole = syncHole(&it.second);
779 if (hole) {
780 aWorld->Add(std::move(hole));
781 }
782 }
783
784 for (const auto &it : board->packages) {
785 for (auto &it_pad : it.second.package.pads) {
786 auto pad = syncPad(&it.second, &it_pad.second);
787 if (pad) {
788 aWorld->Add(std::move(pad));
789 }
790 }
791 }
792 for (const auto &it : board->polygons) {
793 if (it.second.layer == horizon::BoardLayers::L_OUTLINE) {
794 syncOutline(&it.second, aWorld);
795 }
796 }
797
798 auto keepout_contours = board->get_keepout_contours();
799 for (const auto &it : keepout_contours) {
800 if (horizon::BoardLayers::is_copper(it.keepout->polygon->layer))
801 syncKeepout(&it, aWorld);
802 }
803
804 int worstClearance = rules->get_max_clearance();
805
806 delete m_ruleResolver;
807 m_ruleResolver = new PNS_HORIZON_RULE_RESOLVER(board, rules, m_router);
808
809 aWorld->SetRuleResolver(m_ruleResolver);
810 aWorld->SetMaxClearance(4 * worstClearance);
811 }
812
EraseView()813 void PNS_HORIZON_IFACE::EraseView()
814 {
815 // std::cout << "iface erase view" << std::endl;
816
817 canvas->show_all_obj();
818
819 for (const auto &it : m_preview_items) {
820 canvas->remove_obj(it);
821 }
822 m_preview_items.clear();
823 if (m_debugDecorator)
824 m_debugDecorator->Clear();
825 }
826
DisplayItem(const PNS::ITEM * aItem,int aColor,int aClearance,bool aEdit)827 void PNS_HORIZON_IFACE::DisplayItem(const PNS::ITEM *aItem, int aColor, int aClearance, bool aEdit)
828 {
829 wxLogTrace("PNS", "DisplayItem %p %s", aItem, aItem->KindStr().c_str());
830 if (aItem->Kind() == PNS::ITEM::LINE_T) {
831 auto line_item = dynamic_cast<const PNS::LINE *>(aItem);
832 auto npts = line_item->PointCount();
833 std::deque<horizon::Coordi> pts;
834 for (int i = 0; i < npts; i++) {
835 auto pt = line_item->CPoint(i);
836 pts.emplace_back(pt.x, pt.y);
837 }
838 int la = line_item->Layer();
839 m_preview_items.insert(
840 canvas->add_line(pts, line_item->Width(), horizon::ColorP::LAYER_HIGHLIGHT, layer_from_router(la)));
841 }
842 else if (aItem->Kind() == PNS::ITEM::SEGMENT_T) {
843 auto seg_item = dynamic_cast<const PNS::SEGMENT *>(aItem);
844 auto seg = seg_item->Seg();
845 std::deque<horizon::Coordi> pts;
846 pts.emplace_back(seg.A.x, seg.A.y);
847 pts.emplace_back(seg.B.x, seg.B.y);
848 int la = seg_item->Layer();
849 m_preview_items.insert(
850 canvas->add_line(pts, seg_item->Width(), horizon::ColorP::LAYER_HIGHLIGHT, layer_from_router(la)));
851 }
852 else if (aItem->Kind() == PNS::ITEM::VIA_T) {
853 auto via_item = dynamic_cast<const PNS::VIA *>(aItem);
854 auto pos = via_item->Pos();
855
856 std::deque<horizon::Coordi> pts;
857 pts.emplace_back(pos.x, pos.y);
858 pts.emplace_back(pos.x + 10, pos.y);
859 int la = via_item->Layers().Start();
860 m_preview_items.insert(
861 canvas->add_line(pts, via_item->Diameter(), horizon::ColorP::LAYER_HIGHLIGHT, layer_from_router(la)));
862 la = via_item->Layers().End();
863 m_preview_items.insert(
864 canvas->add_line(pts, via_item->Diameter(), horizon::ColorP::LAYER_HIGHLIGHT, layer_from_router(la)));
865 }
866 else if (aItem->Kind() == PNS::ITEM::SOLID_T) {
867 // it's okay
868 }
869 else {
870 assert(false);
871 }
872 }
873
HideItem(PNS::ITEM * aItem)874 void PNS_HORIZON_IFACE::HideItem(PNS::ITEM *aItem)
875 {
876 auto parent = aItem->Parent();
877 if (parent) {
878 if (parent->track) {
879 horizon::ObjectRef ref(horizon::ObjectType::TRACK, parent->track->uuid);
880 canvas->hide_obj(ref);
881 }
882 else if (parent->via) {
883 horizon::ObjectRef ref(horizon::ObjectType::VIA, parent->via->uuid);
884 canvas->hide_obj(ref);
885 }
886 }
887 }
888
RemoveItem(PNS::ITEM * aItem)889 void PNS_HORIZON_IFACE::RemoveItem(PNS::ITEM *aItem)
890 {
891
892 auto parent = aItem->Parent();
893 // std::cout << "!!!iface remove item " << parent << " " << aItem->KindStr() << std::endl;
894 if (parent) {
895 if (parent->track) {
896 for (auto &it_ft : {parent->track->from, parent->track->to}) {
897 if (it_ft.junc)
898 junctions_maybe_erased.insert(it_ft.junc);
899 }
900 board->tracks.erase(parent->track->uuid);
901 }
902 else if (parent->via) {
903 board->vias.erase(parent->via->uuid);
904 }
905 }
906 // board->expand(true);
907 }
908
find_pad(int layer,const horizon::Coordi & c)909 std::pair<horizon::BoardPackage *, horizon::Pad *> PNS_HORIZON_IFACE::find_pad(int layer, const horizon::Coordi &c)
910 {
911 for (auto &it : board->packages) {
912 for (auto &it_pad : it.second.package.pads) {
913 auto pt = it_pad.second.padstack.type;
914 if ((pt == horizon::Padstack::Type::TOP && layer == horizon::BoardLayers::TOP_COPPER)
915 || (pt == horizon::Padstack::Type::BOTTOM && layer == horizon::BoardLayers::BOTTOM_COPPER)
916 || (pt == horizon::Padstack::Type::THROUGH)) {
917 auto tr = it.second.placement;
918 if (it.second.flip) {
919 tr.invert_angle();
920 }
921 auto pos = tr.transform(it_pad.second.placement.shift);
922 if (pos == c) {
923 return {&it.second, &it_pad.second};
924 }
925 }
926 }
927 }
928 return {nullptr, nullptr};
929 }
930
find_junction(int layer,const horizon::Coordi & c)931 horizon::BoardJunction *PNS_HORIZON_IFACE::find_junction(int layer, const horizon::Coordi &c)
932 {
933 for (auto &it : board->junctions) {
934 if ((layer == 10000 || it.second.layer == layer || it.second.has_via) && it.second.position == c) {
935 return &it.second;
936 }
937 }
938 return nullptr;
939 }
940
AddItem(PNS::ITEM * aItem)941 void PNS_HORIZON_IFACE::AddItem(PNS::ITEM *aItem)
942 {
943 // std::cout << "!!!iface add item" << std::endl;
944 switch (aItem->Kind()) {
945 case PNS::ITEM::SEGMENT_T: {
946 PNS::SEGMENT *seg = static_cast<PNS::SEGMENT *>(aItem);
947 const SEG &s = seg->Seg();
948 auto uu = horizon::UUID::random();
949 auto track = &board->tracks.emplace(uu, uu).first->second;
950 horizon::Coordi from(s.A.x, s.A.y);
951 horizon::Coordi to(s.B.x, s.B.y);
952 track->width = seg->Width();
953 track->net = get_net_for_code(seg->Net());
954
955 auto layer = layer_from_router(seg->Layer());
956 track->layer = layer;
957
958 auto connect = [this, &layer, track](horizon::Track::Connection &conn, const horizon::Coordi &c) {
959 auto p = find_pad(layer, c);
960 if (p.first) {
961 conn.connect(p.first, p.second);
962 }
963 else {
964 auto j = find_junction(layer, c);
965 if (j) {
966 conn.connect(j);
967 }
968 else {
969 auto juu = horizon::UUID::random();
970 auto ju = &board->junctions.emplace(juu, juu).first->second;
971 ju->layer = layer;
972 ju->position = c;
973 ju->net = track->net;
974 conn.connect(ju);
975 }
976 }
977 };
978 connect(track->from, from);
979 connect(track->to, to);
980 track->width_from_rules = m_router->Sizes().WidthFromRules();
981 aItem->SetParent(get_parent(track));
982
983 } break;
984
985 case PNS::ITEM::VIA_T: {
986 PNS::VIA *pvia = static_cast<PNS::VIA *>(aItem);
987 auto uu = horizon::UUID::random();
988 auto net = get_net_for_code(pvia->Net());
989 auto padstack = pool->get_padstack(rules->get_via_padstack_uuid(net));
990 if (padstack) {
991 auto ps = rules->get_via_parameter_set(net);
992 auto via = &board->vias
993 .emplace(std::piecewise_construct, std::forward_as_tuple(uu),
994 std::forward_as_tuple(uu, padstack))
995 .first->second;
996 via->parameter_set = ps;
997
998 horizon::Coordi c(pvia->Pos().x, pvia->Pos().y);
999 auto j = find_junction(10000, c);
1000 if (j) {
1001 via->junction = j;
1002 }
1003 else {
1004 auto juu = horizon::UUID::random();
1005 auto ju = &board->junctions.emplace(juu, juu).first->second;
1006 ju->position = c;
1007 ju->net = net;
1008 via->junction = ju;
1009 }
1010 via->junction->has_via = true;
1011 via->expand(*board);
1012 aItem->SetParent(get_parent(via));
1013 }
1014 } break;
1015
1016 default:;
1017 // std::cout << "!!!unhandled add " << aItem->KindStr() << std::endl;
1018 }
1019 }
1020
Commit()1021 void PNS_HORIZON_IFACE::Commit()
1022 {
1023 board->update_junction_connections();
1024 for (auto ju : junctions_maybe_erased) {
1025 if (!(ju->connected_arcs.size() || ju->connected_lines.size() || ju->connected_vias.size()
1026 || ju->connected_tracks.size())) {
1027 for (auto &it : ju->connected_connection_lines)
1028 board->connection_lines.erase(it);
1029 for (auto &[net, airwires] : board->airwires) {
1030 airwires.remove_if([ju](const auto &aw) { return aw.from.junc == ju || aw.to.junc == ju; });
1031 }
1032 board->junctions.erase(ju->uuid);
1033 }
1034 }
1035 junctions_maybe_erased.clear();
1036 EraseView();
1037 }
1038
UpdateNet(int aNetCode)1039 void PNS_HORIZON_IFACE::UpdateNet(int aNetCode)
1040 {
1041 if (const auto net = get_net_for_code(aNetCode)) {
1042 board->update_airwires(false, {net->uuid});
1043 }
1044 wxLogTrace("PNS", "Update-net %d", aNetCode);
1045 }
1046
GetRuleResolver()1047 PNS::RULE_RESOLVER *PNS_HORIZON_IFACE::GetRuleResolver()
1048 {
1049 return m_ruleResolver;
1050 }
1051
create_debug_decorator(horizon::CanvasGL * ca)1052 void PNS_HORIZON_IFACE::create_debug_decorator(horizon::CanvasGL *ca)
1053 {
1054 if (!m_debugDecorator) {
1055 m_debugDecorator = new PNS_HORIZON_DEBUG_DECORATOR(ca);
1056 }
1057 }
1058
GetDebugDecorator()1059 PNS::DEBUG_DECORATOR *PNS_HORIZON_IFACE::GetDebugDecorator()
1060 {
1061 return m_debugDecorator;
1062 }
1063
~PNS_HORIZON_IFACE()1064 PNS_HORIZON_IFACE::~PNS_HORIZON_IFACE()
1065 {
1066 delete m_ruleResolver;
1067 delete m_debugDecorator;
1068 }
1069
SetRouter(PNS::ROUTER * aRouter)1070 void PNS_HORIZON_IFACE::SetRouter(PNS::ROUTER *aRouter)
1071 {
1072 m_router = aRouter;
1073 }
1074
IsAnyLayerVisible(const LAYER_RANGE & aLayer)1075 bool PNS_HORIZON_IFACE::IsAnyLayerVisible(const LAYER_RANGE &aLayer)
1076 {
1077 throw std::runtime_error("IsAnyLayerVisible not implemented");
1078 return true;
1079 }
1080
IsItemVisible(const PNS::ITEM * aItem)1081 bool PNS_HORIZON_IFACE::IsItemVisible(const PNS::ITEM *aItem)
1082 {
1083 throw std::runtime_error("IsItemVisible not implemented");
1084 return true;
1085 }
1086
1087
1088 } // namespace PNS
1089