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