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