1 #include "plane.hpp"
2 #include "board.hpp"
3 #include "common/lut.hpp"
4 #include "nlohmann/json.hpp"
5 
6 namespace horizon {
7 
8 static const LutEnumStr<PlaneSettings::Style> style_lut = {
9         {"square", PlaneSettings::Style::SQUARE},
10         {"miter", PlaneSettings::Style::MITER},
11         {"round", PlaneSettings::Style::ROUND},
12 };
13 
14 static const LutEnumStr<PlaneSettings::ConnectStyle> connect_style_lut = {
15         {"solid", PlaneSettings::ConnectStyle::SOLID},
16         {"thermal", PlaneSettings::ConnectStyle::THERMAL},
17 };
18 
19 static const LutEnumStr<PlaneSettings::TextStyle> text_style_lut = {
20         {"expand", PlaneSettings::TextStyle::EXPAND},
21         {"bbox", PlaneSettings::TextStyle::BBOX},
22 };
23 
24 static const LutEnumStr<PlaneSettings::FillStyle> fill_style_lut = {
25         {"solid", PlaneSettings::FillStyle::SOLID},
26         {"hatch", PlaneSettings::FillStyle::HATCH},
27 };
28 
PlaneSettings(const json & j)29 PlaneSettings::PlaneSettings(const json &j)
30     : min_width(j.at("min_width")), keep_orphans(j.at("keep_orphans")),
31       thermal_gap_width(j.value("thermal_gap_width", 0.1_mm)),
32       thermal_spoke_width(j.value("thermal_spoke_width", 0.2_mm)),
33       hatch_border_width(j.value("hatch_border_width", 0.5_mm)), hatch_line_width(j.value("hatch_line_width", 0.2_mm)),
34       hatch_line_spacing(j.value("hatch_line_spacing", 0.5_mm))
35 {
36     if (j.count("style")) {
37         style = style_lut.lookup(j.at("style"));
38     }
39     if (j.count("connect_style")) {
40         connect_style = connect_style_lut.lookup(j.at("connect_style"));
41     }
42     if (j.count("text_style")) {
43         text_style = text_style_lut.lookup(j.at("text_style"));
44     }
45     if (j.count("fill_style")) {
46         fill_style = fill_style_lut.lookup(j.at("fill_style"));
47     }
48 }
49 
serialize() const50 json PlaneSettings::serialize() const
51 {
52     json j;
53     j["min_width"] = min_width;
54     j["keep_orphans"] = keep_orphans;
55     j["style"] = style_lut.lookup_reverse(style);
56     j["connect_style"] = connect_style_lut.lookup_reverse(connect_style);
57     j["thermal_gap_width"] = thermal_gap_width;
58     j["thermal_spoke_width"] = thermal_spoke_width;
59     j["text_style"] = text_style_lut.lookup_reverse(text_style);
60     j["fill_style"] = fill_style_lut.lookup_reverse(fill_style);
61     j["hatch_border_width"] = hatch_border_width;
62     j["hatch_line_spacing"] = hatch_line_spacing;
63     j["hatch_line_width"] = hatch_line_width;
64     return j;
65 }
66 
Plane(const UUID & uu,const json & j,Board & brd)67 Plane::Plane(const UUID &uu, const json &j, Board &brd)
68     : uuid(uu), net(&brd.block->nets.at(j.at("net").get<std::string>())),
69       polygon(&brd.polygons.at(j.at("polygon").get<std::string>())), from_rules(j.value("from_rules", true)),
70       priority(j.value("priority", 0))
71 {
72     if (j.count("settings")) {
73         settings = PlaneSettings(j.at("settings"));
74     }
75     if (j.count("fragments")) {
76         for (const auto &it : j.at("fragments")) {
77             fragments.emplace_back(it);
78         }
79     }
80 }
81 
Plane(const UUID & uu)82 Plane::Plane(const UUID &uu) : uuid(uu)
83 {
84 }
85 
get_type() const86 PolygonUsage::Type Plane::get_type() const
87 {
88     return PolygonUsage::Type::PLANE;
89 }
90 
get_uuid() const91 UUID Plane::get_uuid() const
92 {
93     return uuid;
94 }
95 
serialize() const96 json Plane::serialize() const
97 {
98     json j;
99     j["net"] = (std::string)net->uuid;
100     j["polygon"] = (std::string)polygon->uuid;
101     j["priority"] = priority;
102     j["settings"] = settings.serialize();
103     auto frags = json::array();
104     for (const auto &it : fragments) {
105         frags.push_back(it.serialize());
106     }
107     j["fragments"] = frags;
108     return j;
109 }
110 
contains(const Coordi & c) const111 bool Plane::Fragment::contains(const Coordi &c) const
112 {
113     ClipperLib::IntPoint pt(c.x, c.y);
114     if (ClipperLib::PointInPolygon(pt, paths.front()) != 0) {    // point is within or on contour
115         for (size_t i = 1; i < paths.size(); i++) {              // check all holes
116             if (ClipperLib::PointInPolygon(pt, paths[i]) == 1) { // point is in hole
117                 return false;
118             }
119         }
120         return true;
121     }
122     else { // point is outside of contour
123         return false;
124     }
125 }
126 
Fragment(const json & j)127 Plane::Fragment::Fragment(const json &j) : orphan(j.at("orphan"))
128 {
129     const auto &j_paths = j.at("paths");
130     paths.reserve(j_paths.size());
131     for (const auto &j_path : j_paths) {
132         paths.emplace_back();
133         auto &path = paths.back();
134         path.reserve(j_path.size());
135         for (const auto &it : j_path) {
136             path.emplace_back(it.at(0), it.at(1));
137         }
138     }
139 }
140 
clear()141 void Plane::clear()
142 {
143     fragments.clear();
144     revision++;
145 }
146 
serialize() const147 json Plane::Fragment::serialize() const
148 {
149     json j;
150     j["orphan"] = orphan;
151     auto j_paths = json::array();
152     for (const auto &path : paths) {
153         auto j_path = json::array();
154         for (const auto &it : path) {
155             j_path.push_back({it.X, it.Y});
156         }
157         j_paths.push_back(j_path);
158     }
159     j["paths"] = j_paths;
160     return j;
161 }
162 
163 } // namespace horizon
164