1 #include "padstack.hpp"
2 #include "board/board_layers.hpp"
3 #include "common/lut.hpp"
4 #include "util/util.hpp"
5 #include "nlohmann/json.hpp"
6 #include "util/bbox_accumulator.hpp"
7
8 namespace horizon {
9
10 const LutEnumStr<Padstack::Type> Padstack::type_lut = {
11 {"top", Padstack::Type::TOP}, {"bottom", Padstack::Type::BOTTOM}, {"through", Padstack::Type::THROUGH},
12 {"via", Padstack::Type::VIA}, {"hole", Padstack::Type::HOLE}, {"mechanical", Padstack::Type::MECHANICAL}};
13
MyParameterProgram(Padstack * p,const std::string & c)14 Padstack::MyParameterProgram::MyParameterProgram(Padstack *p, const std::string &c) : ParameterProgramPolygon(c), ps(p)
15 {
16 }
17
set_shape(const TokenCommand & cmd)18 std::optional<std::string> Padstack::MyParameterProgram::set_shape(const TokenCommand &cmd)
19 {
20 if (cmd.arguments.size() < 2 || cmd.arguments.at(0)->type != Token::Type::STR
21 || cmd.arguments.at(1)->type != Token::Type::STR)
22 return "not enough arguments";
23
24 const auto &pclass = dynamic_cast<TokenString &>(*cmd.arguments.at(0).get()).string;
25 const auto &form = dynamic_cast<TokenString &>(*cmd.arguments.at(1).get()).string;
26
27 if (form == "rectangle") {
28 int64_t width, height;
29 if (stack_pop(height) || stack_pop(width))
30 return "empty stack";
31 for (auto &it : ps->shapes) {
32 if (it.second.parameter_class == pclass) {
33 it.second.form = Shape::Form::RECTANGLE;
34 it.second.params = {width, height};
35 }
36 }
37 }
38 else if (form == "circle") {
39 int64_t diameter;
40 if (stack_pop(diameter))
41 return "empty stack";
42 for (auto &it : ps->shapes) {
43 if (it.second.parameter_class == pclass) {
44 it.second.form = Shape::Form::CIRCLE;
45 it.second.params = {diameter};
46 }
47 }
48 }
49 else if (form == "obround") {
50 int64_t width, height;
51 if (stack_pop(height) || stack_pop(width))
52 return "empty stack";
53 for (auto &it : ps->shapes) {
54 if (it.second.parameter_class == pclass) {
55 it.second.form = Shape::Form::OBROUND;
56 it.second.params = {width, height};
57 }
58 }
59 }
60 else if (form == "position") {
61 int64_t x, y;
62 if (stack_pop(y) || stack_pop(x))
63 return "empty stack";
64 for (auto &it : ps->shapes) {
65 if (it.second.parameter_class == pclass) {
66 it.second.placement.shift = {x, y};
67 }
68 }
69 }
70
71 else {
72 return "unknown form " + form;
73 }
74
75 return {};
76 }
77
set_hole(const TokenCommand & cmd)78 std::optional<std::string> Padstack::MyParameterProgram::set_hole(const TokenCommand &cmd)
79 {
80 if (cmd.arguments.size() < 2 || cmd.arguments.at(0)->type != Token::Type::STR
81 || cmd.arguments.at(1)->type != Token::Type::STR)
82 return "not enough arguments";
83
84 const auto &pclass = dynamic_cast<TokenString &>(*cmd.arguments.at(0).get()).string;
85 const auto &shape = dynamic_cast<TokenString &>(*cmd.arguments.at(1).get()).string;
86
87 if (shape == "round") {
88 int64_t diameter;
89 if (stack_pop(diameter))
90 return "empty stack";
91 for (auto &it : ps->holes) {
92 if (it.second.parameter_class == pclass) {
93 it.second.shape = Hole::Shape::ROUND;
94 it.second.diameter = diameter;
95 }
96 }
97 }
98 else if (shape == "slot") {
99 int64_t diameter, length;
100 if (stack_pop(length) || stack_pop(diameter))
101 return "empty stack";
102 for (auto &it : ps->holes) {
103 if (it.second.parameter_class == pclass) {
104 it.second.shape = Hole::Shape::SLOT;
105 it.second.diameter = diameter;
106 it.second.length = length;
107 }
108 }
109 }
110
111 else {
112 return "unknown shape " + shape;
113 }
114
115 return {};
116 }
117
get_polygons()118 std::map<UUID, Polygon> &Padstack::MyParameterProgram::get_polygons()
119 {
120 return ps->polygons;
121 }
122
123
get_command(const std::string & cmd)124 ParameterProgram::CommandHandler Padstack::MyParameterProgram::get_command(const std::string &cmd)
125 {
126 using namespace std::placeholders;
127 if (auto r = ParameterProgramPolygon::get_command(cmd)) {
128 return r;
129 }
130 else if (cmd == "set-shape") {
131 return static_cast<CommandHandler>(&Padstack::MyParameterProgram::set_shape);
132 }
133 else if (cmd == "set-hole") {
134 return static_cast<CommandHandler>(&Padstack::MyParameterProgram::set_hole);
135 }
136 return nullptr;
137 }
138
Padstack(const Padstack & ps)139 Padstack::Padstack(const Padstack &ps)
140 : uuid(ps.uuid), name(ps.name), type(ps.type), polygons(ps.polygons), holes(ps.holes), shapes(ps.shapes),
141 parameter_set(ps.parameter_set), parameters_required(ps.parameters_required),
142 parameter_program(ps.parameter_program), version(ps.version)
143 {
144 update_refs();
145 }
146
operator =(Padstack const & ps)147 void Padstack::operator=(Padstack const &ps)
148 {
149 uuid = ps.uuid;
150 name = ps.name;
151 type = ps.type;
152 polygons = ps.polygons;
153 holes = ps.holes;
154 shapes = ps.shapes;
155 parameter_set = ps.parameter_set;
156 parameters_required = ps.parameters_required;
157 parameter_program = ps.parameter_program;
158 version = ps.version;
159 update_refs();
160 }
161
update_refs()162 void Padstack::update_refs()
163 {
164 parameter_program.ps = this;
165 }
166
167 static const unsigned int app_version = 0;
168
get_app_version()169 unsigned int Padstack::get_app_version()
170 {
171 return app_version;
172 }
173
Padstack(const UUID & uu,const json & j)174 Padstack::Padstack(const UUID &uu, const json &j)
175 : uuid(uu), name(j.at("name").get<std::string>()), well_known_name(j.value("well_known_name", "")),
176 parameter_program(this, j.value("parameter_program", "")), version(app_version, j)
177 {
178 check_object_type(j, ObjectType::PADSTACK);
179 version.check(ObjectType::PADSTACK, name, uuid);
180 {
181 const json &o = j["polygons"];
182 for (auto it = o.cbegin(); it != o.cend(); ++it) {
183 auto u = UUID(it.key());
184 polygons.emplace(std::make_pair(u, Polygon(u, it.value())));
185 }
186 }
187 map_erase_if(polygons, [](const auto &a) { return a.second.vertices.size() == 0; });
188 {
189 const json &o = j["holes"];
190 for (auto it = o.cbegin(); it != o.cend(); ++it) {
191 auto u = UUID(it.key());
192 holes.emplace(std::make_pair(u, Hole(u, it.value())));
193 }
194 }
195 if (j.count("shapes")) {
196 const json &o = j["shapes"];
197 for (auto it = o.cbegin(); it != o.cend(); ++it) {
198 auto u = UUID(it.key());
199 shapes.emplace(std::make_pair(u, Shape(u, it.value())));
200 }
201 }
202 if (j.count("padstack_type")) {
203 type = type_lut.lookup(j.at("padstack_type"));
204 }
205 if (j.count("parameter_set")) {
206 parameter_set = parameter_set_from_json(j.at("parameter_set"));
207 }
208 if (j.count("parameters_required")) {
209 const json &o = j["parameters_required"];
210 for (auto it = o.cbegin(); it != o.cend(); ++it) {
211 parameters_required.insert(parameter_id_from_string(it.value()));
212 }
213 }
214 } // namespace horizon
215
new_from_file(const std::string & filename)216 Padstack Padstack::new_from_file(const std::string &filename)
217 {
218 auto j = load_json_from_file(filename);
219 return Padstack(UUID(j.at("uuid").get<std::string>()), j);
220 }
221
Padstack(const UUID & uu)222 Padstack::Padstack(const UUID &uu) : uuid(uu), parameter_program(this, ""), version(app_version)
223 {
224 }
225
serialize() const226 json Padstack::serialize() const
227 {
228 json j;
229 version.serialize(j);
230 j["uuid"] = (std::string)uuid;
231 j["type"] = "padstack";
232 j["name"] = name;
233 j["well_known_name"] = well_known_name;
234 j["padstack_type"] = type_lut.lookup_reverse(type);
235 j["parameter_program"] = parameter_program.get_code();
236 j["parameter_set"] = parameter_set_serialize(parameter_set);
237 j["polygons"] = json::object();
238 for (const auto &it : polygons) {
239 j["polygons"][(std::string)it.first] = it.second.serialize();
240 }
241 j["holes"] = json::object();
242 for (const auto &it : holes) {
243 j["holes"][(std::string)it.first] = it.second.serialize();
244 }
245 j["shapes"] = json::object();
246 for (const auto &it : shapes) {
247 j["shapes"][(std::string)it.first] = it.second.serialize();
248 }
249 j["parameters_required"] = json::array();
250 for (const auto &it : parameters_required) {
251 j["parameters_required"].push_back(parameter_id_to_string(it));
252 }
253 return j;
254 }
255
get_uuid() const256 UUID Padstack::get_uuid() const
257 {
258 return uuid;
259 }
260
get_bbox(bool copper_only) const261 std::pair<Coordi, Coordi> Padstack::get_bbox(bool copper_only) const
262 {
263 BBoxAccumulator<Coordi::type> acc;
264 for (const auto &it : polygons) {
265 if (!copper_only || BoardLayers::is_copper(it.second.layer)) {
266 acc.accumulate(it.second.get_bbox());
267 }
268 }
269 for (const auto &it : shapes) {
270 if (!copper_only || BoardLayers::is_copper(it.second.layer)) {
271 const auto bb = it.second.placement.transform_bb(it.second.get_bbox());
272 acc.accumulate(bb);
273 }
274 }
275 return acc.get_or_0();
276 }
277
get_layers() const278 const std::map<int, Layer> &Padstack::get_layers() const
279 {
280 static const std::map<int, Layer> layers = {{30, {30, "Top Paste"}},
281 {10, {10, "Top Mask"}},
282 {0, {0, "Top Copper", false, true}},
283 {-1, {-1, "Inner", false, true}},
284 {-100, {-100, "Bottom Copper", true, true}},
285 {-110, {-110, "Bottom Mask", true}},
286 {-130, {-130, "Bottom Paste"}}};
287 return layers;
288 }
289
copy_param(ParameterSet & dest,const ParameterSet & src,ParameterID id)290 static void copy_param(ParameterSet &dest, const ParameterSet &src, ParameterID id)
291 {
292 if (src.count(id))
293 dest[id] = src.at(id);
294 }
295
copy_param(ParameterSet & dest,const ParameterSet & src,const std::set<ParameterID> & ids)296 static void copy_param(ParameterSet &dest, const ParameterSet &src, const std::set<ParameterID> &ids)
297 {
298 for (const auto id : ids) {
299 copy_param(dest, src, id);
300 }
301 }
302
apply_parameter_set(const ParameterSet & ps)303 std::optional<std::string> Padstack::apply_parameter_set(const ParameterSet &ps)
304 {
305 auto ps_this = parameter_set;
306 copy_param(ps_this, ps,
307 {ParameterID::PAD_HEIGHT, ParameterID::PAD_WIDTH, ParameterID::PAD_DIAMETER,
308 ParameterID::SOLDER_MASK_EXPANSION, ParameterID::PASTE_MASK_CONTRACTION, ParameterID::HOLE_DIAMETER,
309 ParameterID::HOLE_LENGTH, ParameterID::VIA_DIAMETER, ParameterID::HOLE_SOLDER_MASK_EXPANSION,
310 ParameterID::VIA_SOLDER_MASK_EXPANSION, ParameterID::HOLE_ANNULAR_RING, ParameterID::CORNER_RADIUS});
311 return parameter_program.run(ps_this);
312 }
313
expand_inner(unsigned int n_inner)314 void Padstack::expand_inner(unsigned int n_inner)
315 {
316 if (n_inner == 0) {
317 for (auto it = shapes.begin(); it != shapes.end();) {
318 if (it->second.layer == -1) {
319 shapes.erase(it++);
320 }
321 else {
322 it++;
323 }
324 }
325 for (auto it = polygons.begin(); it != polygons.end();) {
326 if (it->second.layer == -1) {
327 polygons.erase(it++);
328 }
329 else {
330 it++;
331 }
332 }
333 }
334 std::map<UUID, Polygon> new_polygons;
335 for (const auto &it : polygons) {
336 if (it.second.layer == -1) {
337 for (unsigned int i = 0; i < (n_inner - 1); i++) {
338 auto uu = UUID::random();
339 auto &np = new_polygons.emplace(uu, it.second).first->second;
340 np.uuid = uu;
341 np.layer = -2 - i;
342 }
343 }
344 }
345 polygons.insert(new_polygons.begin(), new_polygons.end());
346
347 std::map<UUID, Shape> new_shapes;
348 for (const auto &it : shapes) {
349 if (it.second.layer == -1) {
350 for (unsigned int i = 0; i < (n_inner - 1); i++) {
351 auto uu = UUID::random();
352 auto &np = new_shapes.emplace(uu, it.second).first->second;
353 np.uuid = uu;
354 np.layer = -2 - i;
355 }
356 }
357 }
358 shapes.insert(new_shapes.begin(), new_shapes.end());
359 }
360 } // namespace horizon
361