1 #include "footprint_generator_dual.hpp"
2 #include "util/geom_util.hpp"
3 #include "widgets/pool_browser_button.hpp"
4 #include "document/idocument_package.hpp"
5 #include "pool/ipool.hpp"
6 #include "pool/package.hpp"
7 
8 namespace horizon {
FootprintGeneratorDual(IDocumentPackage & c)9 FootprintGeneratorDual::FootprintGeneratorDual(IDocumentPackage &c)
10     : Glib::ObjectBase(typeid(FootprintGeneratorDual)),
11       FootprintGeneratorBase("/org/horizon-eda/horizon/imp/footprint_generator/dual.svg", c)
12 {
13 
14     {
15         auto tbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4));
16         auto la = Gtk::manage(new Gtk::Label("Count:"));
17         tbox->pack_start(*la, false, false, 0);
18 
19         sp_count = Gtk::manage(new Gtk::SpinButton());
20         sp_count->set_range(2, 512);
21         sp_count->set_increments(2, 2);
22         sp_count->signal_input().connect([this](double *v) {
23             auto txt = sp_count->get_text();
24             int64_t va = 0;
25             try {
26                 va = MAX(round_multiple(std::stoi(txt), 2), 1);
27                 *v = va;
28             }
29             catch (const std::exception &e) {
30                 return false;
31             }
32             return true;
33         });
34         tbox->pack_start(*sp_count, false, false, 0);
35 
36         box_top->pack_start(*tbox, false, false, 0);
37     }
38 
39     update_preview();
40     sp_spacing = Gtk::manage(new SpinButtonDim());
41     sp_spacing->set_range(0, 100_mm);
42     sp_spacing->set_valign(Gtk::ALIGN_CENTER);
43     sp_spacing->set_halign(Gtk::ALIGN_START);
44     sp_spacing->set_value(3_mm);
45     sp_spacing_connections.push_back(
46             sp_spacing->signal_value_changed().connect([this] { update_spacing(Mode::SPACING); }));
47     overlay->add_at_sub(*sp_spacing, "#spacing");
48     sp_spacing->show();
49 
50     sp_spacing_inner = Gtk::manage(new SpinButtonDim());
51     sp_spacing_inner->set_range(0, 100_mm);
52     sp_spacing_inner->set_valign(Gtk::ALIGN_CENTER);
53     sp_spacing_inner->set_halign(Gtk::ALIGN_START);
54     sp_spacing_connections.push_back(
55             sp_spacing_inner->signal_value_changed().connect([this] { update_spacing(Mode::SPACING_INNER); }));
56     overlay->add_at_sub(*sp_spacing_inner, "#spacing_inner");
57     sp_spacing_inner->show();
58 
59     sp_spacing_outer = Gtk::manage(new SpinButtonDim());
60     sp_spacing_outer->set_range(0, 100_mm);
61     sp_spacing_outer->set_valign(Gtk::ALIGN_CENTER);
62     sp_spacing_outer->set_halign(Gtk::ALIGN_START);
63     sp_spacing_connections.push_back(
64             sp_spacing_outer->signal_value_changed().connect([this] { update_spacing(Mode::SPACING_OUTER); }));
65     overlay->add_at_sub(*sp_spacing_outer, "#spacing_outer");
66     sp_spacing_outer->show();
67 
68     sp_pitch = Gtk::manage(new SpinButtonDim());
69     sp_pitch->set_range(0, 50_mm);
70     sp_pitch->set_valign(Gtk::ALIGN_CENTER);
71     sp_pitch->set_halign(Gtk::ALIGN_START);
72     sp_pitch->set_value(1_mm);
73     overlay->add_at_sub(*sp_pitch, "#pitch");
74     sp_pitch->show();
75 
76     sp_pad_height = Gtk::manage(new SpinButtonDim());
77     sp_pad_height->set_range(0, 50_mm);
78     sp_pad_height->set_valign(Gtk::ALIGN_CENTER);
79     sp_pad_height->set_halign(Gtk::ALIGN_START);
80     sp_pad_height->set_value(2_mm);
81     sp_spacing_connections.push_back(
82             sp_pad_height->signal_value_changed().connect([this] { update_spacing(Mode::PAD_HEIGHT); }));
83     overlay->add_at_sub(*sp_pad_height, "#pad_height");
84     sp_pad_height->show();
85 
86     sp_pad_width = Gtk::manage(new SpinButtonDim());
87     sp_pad_width->set_range(0, 50_mm);
88     sp_pad_width->set_valign(Gtk::ALIGN_CENTER);
89     sp_pad_width->set_halign(Gtk::ALIGN_START);
90     sp_pad_width->set_value(.5_mm);
91     overlay->add_at_sub(*sp_pad_width, "#pad_width");
92     sp_pad_width->show();
93 
94     update_spacing(Mode::PAD_HEIGHT);
95 
96     sp_count->signal_value_changed().connect([this] {
97         pad_count = sp_count->get_value_as_int();
98         update_preview();
99     });
100 
101     {
102         auto tbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4));
103         auto la = Gtk::manage(new Gtk::Label("Mode:"));
104         tbox->pack_start(*la, false, false, 0);
105 
106         auto rbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
107         rbox->get_style_context()->add_class("linked");
108         auto rb1 = Gtk::manage(new Gtk::RadioButton("Circular"));
109         rb1->set_mode(false);
110         auto rb2 = Gtk::manage(new Gtk::RadioButton("Zigzag"));
111         rb2->signal_toggled().connect([this, rb2] {
112             zigzag = rb2->get_active();
113             update_preview();
114         });
115         // sp_count->signal_changed().connect([this]{set_pad_count(sp_count->get_value_as_int());});
116         rb2->set_mode(false);
117         rb2->join_group(*rb1);
118         rbox->pack_start(*rb1, false, false, 0);
119         rbox->pack_start(*rb2, false, false, 0);
120         rb1->set_active(true);
121 
122         tbox->pack_start(*rbox, false, false, 0);
123 
124         box_top->pack_start(*tbox, false, false, 0);
125     }
126     sp_count->set_value(4);
127 }
128 
generate()129 bool FootprintGeneratorDual::generate()
130 {
131     if (!property_can_generate())
132         return false;
133     int64_t spacing = sp_spacing->get_value_as_int();
134     int64_t pitch = sp_pitch->get_value_as_int();
135     int64_t y0 = (pad_count / 2 - 1) * (pitch / 2);
136     int64_t pad_width = sp_pad_width->get_value_as_int();
137     int64_t pad_height = sp_pad_height->get_value_as_int();
138     auto padstack = core.get_pool().get_padstack(browser_button->property_selected_uuid());
139     for (auto it : {-1, 1}) {
140         for (unsigned int i = 0; i < pad_count / 2; i++) {
141             auto uu = UUID::random();
142 
143             auto &pad = package.pads.emplace(uu, Pad(uu, padstack)).first->second;
144             pad.placement.shift = {it * spacing, y0 - pitch * i};
145             update_pad_parameters(*padstack, pad, pad_width, pad_height);
146             if (it < 0)
147                 pad.placement.set_angle_deg(270);
148             else
149                 pad.placement.set_angle_deg(90);
150             if (!zigzag) {
151                 if (it < 0) {
152                     pad.name = std::to_string(i + 1);
153                 }
154                 else {
155                     pad.name = std::to_string(pad_count - i);
156                 }
157             }
158             else {
159                 if (it < 0) {
160                     pad.name = std::to_string(i * 2 + 1);
161                 }
162                 else {
163                     pad.name = std::to_string(i * 2 + 2);
164                 }
165             }
166         }
167     }
168     return true;
169 }
170 
update_preview()171 void FootprintGeneratorDual::update_preview()
172 {
173     auto n = pad_count;
174     n &= ~1;
175     if (n < 2)
176         return;
177     if (!zigzag) {
178         if (n >= 8) {
179             overlay->sub_texts["#pad1"] = "1";
180             overlay->sub_texts["#pad2"] = "2";
181             overlay->sub_texts["#pad3"] = std::to_string(n / 2 - 1);
182             overlay->sub_texts["#pad4"] = std::to_string(n / 2);
183             overlay->sub_texts["#pad5"] = std::to_string(n / 2 + 1);
184             overlay->sub_texts["#pad6"] = std::to_string(n / 2 + 2);
185             overlay->sub_texts["#pad7"] = std::to_string(n - 1);
186             overlay->sub_texts["#pad8"] = std::to_string(n);
187         }
188         else if (n == 6) {
189             overlay->sub_texts["#pad1"] = "1";
190             overlay->sub_texts["#pad2"] = "2";
191             overlay->sub_texts["#pad3"] = "3";
192             overlay->sub_texts["#pad4"] = "X";
193             overlay->sub_texts["#pad5"] = "X";
194             overlay->sub_texts["#pad6"] = "4";
195             overlay->sub_texts["#pad7"] = "5";
196             overlay->sub_texts["#pad8"] = "6";
197         }
198         else if (n == 4) {
199             overlay->sub_texts["#pad1"] = "1";
200             overlay->sub_texts["#pad2"] = "2";
201             overlay->sub_texts["#pad3"] = "X";
202             overlay->sub_texts["#pad4"] = "X";
203             overlay->sub_texts["#pad5"] = "X";
204             overlay->sub_texts["#pad6"] = "X";
205             overlay->sub_texts["#pad7"] = "3";
206             overlay->sub_texts["#pad8"] = "4";
207         }
208         else if (n == 2) {
209             overlay->sub_texts["#pad1"] = "1";
210             overlay->sub_texts["#pad2"] = "X";
211             overlay->sub_texts["#pad3"] = "X";
212             overlay->sub_texts["#pad4"] = "X";
213             overlay->sub_texts["#pad5"] = "X";
214             overlay->sub_texts["#pad6"] = "X";
215             overlay->sub_texts["#pad7"] = "X";
216             overlay->sub_texts["#pad8"] = "2";
217         }
218     }
219     else {
220         if (n >= 8) {
221             overlay->sub_texts["#pad1"] = "1";
222             overlay->sub_texts["#pad8"] = "2";
223             overlay->sub_texts["#pad2"] = "3";
224             overlay->sub_texts["#pad7"] = "4";
225             overlay->sub_texts["#pad5"] = std::to_string(n);
226             overlay->sub_texts["#pad4"] = std::to_string(n - 1);
227             overlay->sub_texts["#pad6"] = std::to_string(n - 2);
228             overlay->sub_texts["#pad3"] = std::to_string(n - 3);
229         }
230         else if (n == 6) {
231             overlay->sub_texts["#pad1"] = "1";
232             overlay->sub_texts["#pad8"] = "2";
233             overlay->sub_texts["#pad2"] = "3";
234             overlay->sub_texts["#pad7"] = "4";
235             overlay->sub_texts["#pad5"] = "X";
236             overlay->sub_texts["#pad4"] = "X";
237             overlay->sub_texts["#pad6"] = "6";
238             overlay->sub_texts["#pad3"] = "5";
239         }
240         else if (n == 4) {
241             overlay->sub_texts["#pad1"] = "1";
242             overlay->sub_texts["#pad8"] = "2";
243             overlay->sub_texts["#pad2"] = "3";
244             overlay->sub_texts["#pad7"] = "4";
245             overlay->sub_texts["#pad5"] = "X";
246             overlay->sub_texts["#pad4"] = "X";
247             overlay->sub_texts["#pad6"] = "X";
248             overlay->sub_texts["#pad3"] = "X";
249         }
250         else if (n == 2) {
251             overlay->sub_texts["#pad1"] = "1";
252             overlay->sub_texts["#pad8"] = "2";
253             overlay->sub_texts["#pad2"] = "X";
254             overlay->sub_texts["#pad7"] = "X";
255             overlay->sub_texts["#pad5"] = "X";
256             overlay->sub_texts["#pad4"] = "X";
257             overlay->sub_texts["#pad6"] = "X";
258             overlay->sub_texts["#pad3"] = "X";
259         }
260     }
261     overlay->queue_draw();
262 }
263 
update_spacing(Mode mode)264 void FootprintGeneratorDual::update_spacing(Mode mode)
265 {
266     for (auto &it : sp_spacing_connections) {
267         it.block();
268     }
269     int64_t spacing = sp_spacing->get_value_as_int();
270     int64_t spacing_inner = sp_spacing_inner->get_value_as_int();
271     int64_t spacing_outer = sp_spacing_outer->get_value_as_int();
272     int64_t pad_height = sp_pad_height->get_value_as_int();
273 
274     switch (mode) {
275     case Mode::SPACING:
276     case Mode::PAD_HEIGHT:
277         sp_spacing_inner->set_value(spacing * 2 - pad_height / 2);
278         sp_spacing_outer->set_value(spacing * 2 + pad_height / 2);
279         break;
280 
281     case Mode::SPACING_INNER:
282     case Mode::SPACING_OUTER:
283         sp_pad_height->set_value((spacing_outer - spacing_inner) / 2);
284         sp_spacing->set_value((spacing_outer + spacing_inner) / 4);
285         break;
286     }
287 
288     for (auto &it : sp_spacing_connections) {
289         it.unblock();
290     }
291 }
292 
293 } // namespace horizon
294